]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: delay interface check for type bounds
authorRobert Griesemer <gri@golang.org>
Tue, 29 Jun 2021 19:22:21 +0000 (12:22 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 1 Jul 2021 16:42:34 +0000 (16:42 +0000)
While at it, clean up code for collecting/declaring type parameters.

For #40789.

Change-Id: I0855137d5ee85c0ae2fa60d33b28c24a33132fbc
Reviewed-on: https://go-review.googlesource.com/c/go/+/331690
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/signature.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2 [new file with mode: 0644]
src/cmd/compile/internal/types2/type.go

index 00b4ef70107063205f9d4d188f13a52b4f2d5108..d36da06f421a357ca84b546bd1ef238743a3812b 100644 (file)
@@ -674,75 +674,52 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
 
 }
 
-func (check *Checker) collectTypeParams(list []*syntax.Field) (tparams []*TypeName) {
-       // Type parameter lists should not be empty. The parser will
-       // complain but we still may get an incorrect AST: ignore it.
-       if len(list) == 0 {
-               return
-       }
+func (check *Checker) collectTypeParams(list []*syntax.Field) []*TypeName {
+       tparams := make([]*TypeName, len(list))
 
-       // Declare type parameters up-front, with empty interface as type bound.
+       // Declare type parameters up-front.
        // The scope of type parameters starts at the beginning of the type parameter
-       // list (so we can have mutually recursive parameterized interfaces).
-       for _, f := range list {
-               tparams = check.declareTypeParam(tparams, f.Name)
+       // list (so we can have mutually recursive parameterized type bounds).
+       for i, f := range list {
+               tparams[i] = check.declareTypeParam(i, f.Name)
        }
 
        var bound Type
-       for i, j := 0, 0; i < len(list); i = j {
-               f := list[i]
-
-               // determine the range of type parameters list[i:j] with identical type bound
-               // (declared as in (type a, b, c B))
-               j = i + 1
-               for j < len(list) && list[j].Type == f.Type {
-                       j++
-               }
-
-               // this should never be the case, but be careful
-               if f.Type == nil {
-                       continue
-               }
-
-               // The predeclared identifier "any" is visible only as a constraint
-               // in a type parameter list. Look for it before general constraint
-               // resolution.
-               if tident, _ := unparen(f.Type).(*syntax.Name); tident != nil && tident.Value == "any" && check.lookup("any") == nil {
-                       bound = universeAny
-               } else {
-                       bound = check.typ(f.Type)
-               }
-
-               // type bound must be an interface
-               // TODO(gri) We should delay the interface check because
-               //           we may not have a complete interface yet:
-               //           type C(type T C) interface {}
-               //           (issue #39724).
-               if _, ok := under(bound).(*Interface); ok {
-                       // set the type bounds
-                       for i < j {
-                               tparams[i].typ.(*TypeParam).bound = bound
-                               i++
-                       }
-               } else if bound != Typ[Invalid] {
-                       check.errorf(f.Type, "%s is not an interface", bound)
+       for i, f := range list {
+               // Optimization: Re-use the previous type bound if it hasn't changed.
+               // This also preserves the grouped output of type parameter lists
+               // when printing type strings.
+               if i == 0 || f.Type != list[i-1].Type {
+                       bound = check.boundType(f.Type)
                }
+               tparams[i].typ.(*TypeParam).bound = bound
        }
 
-       return
+       return tparams
 }
 
-func (check *Checker) declareTypeParam(tparams []*TypeName, name *syntax.Name) []*TypeName {
+func (check *Checker) declareTypeParam(index int, name *syntax.Name) *TypeName {
        tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
-       check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
+       check.NewTypeParam(tpar, index, nil)                    // assigns type to tpar as a side-effect
        check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
-       tparams = append(tparams, tpar)
+       return tpar
+}
 
-       if check.conf.Trace {
-               check.trace(name.Pos(), "type param = %v", tparams[len(tparams)-1])
+// boundType type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type must be an interface, including the predeclared type "any".
+func (check *Checker) boundType(e syntax.Expr) Type {
+       // The predeclared identifier "any" is visible only as a type bound in a type parameter list.
+       if name, _ := unparen(e).(*syntax.Name); name != nil && name.Value == "any" && check.lookup("any") == nil {
+               return universeAny
        }
 
-       return tparams
+       bound := check.typ(e)
+       check.later(func() {
+               if _, ok := under(bound).(*Interface); !ok && bound != Typ[Invalid] {
+                       check.errorf(e, "%s is not an interface", bound)
+               }
+       })
+       return bound
 }
 
 func (check *Checker) collectMethods(obj *TypeName) {
index a7edc5ac03d78aff4a7f2211df7685948e842788..01158187ba71476cceecfb59356c0741983fb0ab 100644 (file)
@@ -48,10 +48,9 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
                                // blank identifiers were found => use rewritten receiver type
                                recvTyp = isubst(recvPar.Type, smap)
                        }
-                       // TODO(gri) rework declareTypeParams
-                       sig.rparams = nil
-                       for _, rparam := range rparams {
-                               sig.rparams = check.declareTypeParam(sig.rparams, rparam)
+                       sig.rparams = make([]*TypeName, len(rparams))
+                       for i, rparam := range rparams {
+                               sig.rparams[i] = check.declareTypeParam(i, rparam)
                        }
                        // determine receiver type to get its type parameters
                        // and the respective type parameter bounds
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2
new file mode 100644 (file)
index 0000000..9eea4ad
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+func main() {
+       m := map[string]int{
+               "a": 6,
+               "b": 7,
+       }
+       fmt.Println(copyMap[map[string]int, string, int](m))
+}
+
+type Map[K comparable, V any] interface {
+       map[K] V
+}
+
+func copyMap[M Map[K, V], K comparable, V any](m M) M {
+       m1 := make(M)
+       for k, v := range m {
+               m1[k] = v
+       }
+       return m1
+}
+
+// simpler test case from the same issue
+
+type A[X comparable] interface {
+       []X
+}
+
+func f[B A[X], X comparable]() B {
+       return nil
+}
index 2cfcabbdb550a634961e96024ce358e073e86cd1..05e6d77d2248b7a17e333e6968af5e19f1654e05 100644 (file)
@@ -626,7 +626,11 @@ func (t *TypeParam) SetId(id uint64) {
 }
 
 func (t *TypeParam) Bound() *Interface {
-       iface := asInterface(t.bound)
+       // we may not have an interface (error reported elsewhere)
+       iface, _ := under(t.bound).(*Interface)
+       if iface == nil {
+               return &emptyInterface
+       }
        // use the type bound position if we have one
        pos := nopos
        if n, _ := t.bound.(*Named); n != nil {