From d2a77f1c76dcc960d8548fa47ec29fcb1b2e5833 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 8 Sep 2021 18:27:31 -0400 Subject: [PATCH] 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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/decl.go | 19 ++++++++--- src/go/types/signature.go | 2 +- .../types/testdata/fixedbugs/issue45550.go2 | 10 ++++++ .../types/testdata/fixedbugs/issue47796.go2 | 33 +++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/go/types/testdata/fixedbugs/issue45550.go2 create mode 100644 src/go/types/testdata/fixedbugs/issue47796.go2 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 +} -- 2.50.0