]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: handle recursive type parameter constraints
authorRobert Findley <rfindley@google.com>
Wed, 8 Sep 2021 22:27:31 +0000 (18:27 -0400)
committerRobert Findley <rfindley@google.com>
Thu, 9 Sep 2021 12:56:36 +0000 (12:56 +0000)
This is a port of CL 348090 to go/types. Notably, unlike in types2,
declareTypeParams was previously setting the default constraint to the
empty interface, not nil, because this was missed in CL 335034 (no
changes were made to declareTypeParams). This CL fixes this discrepancy.

Change-Id: I0fa54a660ba14c6cbefa81a27ab7eb193df3be20
Reviewed-on: https://go-review.googlesource.com/c/go/+/348690
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/decl.go
src/go/types/signature.go
src/go/types/testdata/fixedbugs/issue45550.go2 [new file with mode: 0644]
src/go/types/testdata/fixedbugs/issue47796.go2 [new file with mode: 0644]

index f679c33a94e77ee278336c4183ff4d12886910cc..8ebe7c6f5b69baa5c8fcd74da8dc1ddf6e0702a1 100644 (file)
@@ -615,7 +615,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
        if tdecl.TypeParams != nil {
                check.openScope(tdecl, "type parameters")
                defer check.closeScope()
-               named.tparams = check.collectTypeParams(tdecl.TypeParams)
+               check.collectTypeParams(&named.tparams, tdecl.TypeParams)
        }
 
        // determine underlying type of named
@@ -647,7 +647,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
        }
 }
 
-func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
+func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) {
        var tparams []*TypeParam
        // Declare type parameters up-front, with empty interface as type bound.
        // The scope of type parameters starts at the beginning of the type parameter
@@ -656,6 +656,11 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
                tparams = check.declareTypeParams(tparams, f.Names)
        }
 
+       // Set the type parameters before collecting the type constraints because
+       // the parameterized type may be used by the constraints (issue #47887).
+       // Example: type T[P T[P]] interface{}
+       *dst = bindTParams(tparams)
+
        index := 0
        var bound Type
        for _, f := range list.List {
@@ -670,14 +675,18 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
        next:
                index += len(f.Names)
        }
-
-       return bindTParams(tparams)
 }
 
 func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam {
+       // Use Typ[Invalid] for the type constraint to ensure that a type
+       // is present even if the actual constraint has not been assigned
+       // yet.
+       // TODO(gri) Need to systematically review all uses of type parameter
+       //           constraints to make sure we don't rely on them if they
+       //           are not properly set yet.
        for _, name := range names {
                tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
-               tpar := check.newTypeParam(tname, &emptyInterface)       // assigns type to tpar as a side-effect
+               tpar := check.newTypeParam(tname, Typ[Invalid])          // assigns type to tpar as a side-effect
                check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
                tparams = append(tparams, tpar)
        }
index 88ea07d5d34263cb00952bae321e0f96e420331a..ec2030a689f1530908b6786506f12a1c29f12e62 100644 (file)
@@ -152,7 +152,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
        }
 
        if ftyp.TypeParams != nil {
-               sig.tparams = check.collectTypeParams(ftyp.TypeParams)
+               check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
                // Always type-check method type parameters but complain that they are not allowed.
                // (A separate check is needed when type-checking interface method signatures because
                // they don't have a receiver specification.)
diff --git a/src/go/types/testdata/fixedbugs/issue45550.go2 b/src/go/types/testdata/fixedbugs/issue45550.go2
new file mode 100644 (file)
index 0000000..c3e9e34
--- /dev/null
@@ -0,0 +1,10 @@
+// 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 p
+
+type Builder[T interface{ struct{ Builder[T] } }] struct{}
+type myBuilder struct {
+       Builder[myBuilder /* ERROR myBuilder does not satisfy */]
+}
diff --git a/src/go/types/testdata/fixedbugs/issue47796.go2 b/src/go/types/testdata/fixedbugs/issue47796.go2
new file mode 100644 (file)
index 0000000..9c10683
--- /dev/null
@@ -0,0 +1,33 @@
+// 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 p
+
+// parameterized types with self-recursive constraints
+type (
+       T1[P T1[P]]                            interface{}
+       T2[P, Q T2[P, Q]]                      interface{}
+       T3[P T2[P, Q], Q interface{ ~string }] interface{}
+
+       T4a[P T4a[P]]                                                        interface{ ~int }
+       T4b[P T4b[int]]                                                      interface{ ~int }
+       T4c[P T4c[string /* ERROR string does not satisfy T4c\[string\] */]] interface{ ~int }
+
+       // mutually recursive constraints
+       T5[P T6[P]] interface{ int }
+       T6[P T5[P]] interface{ int }
+)
+
+// verify that constraints are checked as expected
+var (
+       _ T1[int]
+       _ T2[int, string]
+       _ T3[int, string]
+)
+
+// test case from issue
+
+type Eq[a Eq[a]] interface {
+       Equal(that a) bool
+}