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>
This commit is contained in:
Robert Findley 2021-09-08 18:27:31 -04:00
parent 9e1eea6f8b
commit d2a77f1c76
4 changed files with 58 additions and 6 deletions

View 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)
}

View 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.)

View file

@ -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 */]
}

View file

@ -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
}