aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Findley <rfindley@google.com>2021-09-08 18:27:31 -0400
committerRobert Findley <rfindley@google.com>2021-09-09 12:56:36 +0000
commitd2a77f1c76dcc960d8548fa47ec29fcb1b2e5833 (patch)
treebdb706cb01e6cf42a9ab4b1f540e1a492ac3305c
parent9e1eea6f8b0d0266c481df5bc3c808defeaa6f44 (diff)
downloadgo-d2a77f1c76dcc960d8548fa47ec29fcb1b2e5833.tar.gz
go-d2a77f1c76dcc960d8548fa47ec29fcb1b2e5833.zip
go/types: handle recursive type parameter constraints
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>
-rw-r--r--src/go/types/decl.go19
-rw-r--r--src/go/types/signature.go2
-rw-r--r--src/go/types/testdata/fixedbugs/issue45550.go210
-rw-r--r--src/go/types/testdata/fixedbugs/issue47796.go233
4 files changed, 58 insertions, 6 deletions
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index f679c33a94..8ebe7c6f5b 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -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)
}
diff --git a/src/go/types/signature.go b/src/go/types/signature.go
index 88ea07d5d3..ec2030a689 100644
--- a/src/go/types/signature.go
+++ b/src/go/types/signature.go
@@ -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
index 0000000000..c3e9e34b87
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue45550.go2
@@ -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
index 0000000000..9c10683e22
--- /dev/null
+++ b/src/go/types/testdata/fixedbugs/issue47796.go2
@@ -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
+}