Skip to content

Commit d2a77f1

Browse files
committed
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 <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 9e1eea6 commit d2a77f1

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed

src/go/types/decl.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
615615
if tdecl.TypeParams != nil {
616616
check.openScope(tdecl, "type parameters")
617617
defer check.closeScope()
618-
named.tparams = check.collectTypeParams(tdecl.TypeParams)
618+
check.collectTypeParams(&named.tparams, tdecl.TypeParams)
619619
}
620620

621621
// determine underlying type of named
@@ -647,7 +647,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
647647
}
648648
}
649649

650-
func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
650+
func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) {
651651
var tparams []*TypeParam
652652
// Declare type parameters up-front, with empty interface as type bound.
653653
// 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 {
656656
tparams = check.declareTypeParams(tparams, f.Names)
657657
}
658658

659+
// Set the type parameters before collecting the type constraints because
660+
// the parameterized type may be used by the constraints (issue #47887).
661+
// Example: type T[P T[P]] interface{}
662+
*dst = bindTParams(tparams)
663+
659664
index := 0
660665
var bound Type
661666
for _, f := range list.List {
@@ -670,14 +675,18 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList {
670675
next:
671676
index += len(f.Names)
672677
}
673-
674-
return bindTParams(tparams)
675678
}
676679

677680
func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam {
681+
// Use Typ[Invalid] for the type constraint to ensure that a type
682+
// is present even if the actual constraint has not been assigned
683+
// yet.
684+
// TODO(gri) Need to systematically review all uses of type parameter
685+
// constraints to make sure we don't rely on them if they
686+
// are not properly set yet.
678687
for _, name := range names {
679688
tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
680-
tpar := check.newTypeParam(tname, &emptyInterface) // assigns type to tpar as a side-effect
689+
tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect
681690
check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
682691
tparams = append(tparams, tpar)
683692
}

src/go/types/signature.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
152152
}
153153

154154
if ftyp.TypeParams != nil {
155-
sig.tparams = check.collectTypeParams(ftyp.TypeParams)
155+
check.collectTypeParams(&sig.tparams, ftyp.TypeParams)
156156
// Always type-check method type parameters but complain that they are not allowed.
157157
// (A separate check is needed when type-checking interface method signatures because
158158
// they don't have a receiver specification.)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type Builder[T interface{ struct{ Builder[T] } }] struct{}
8+
type myBuilder struct {
9+
Builder[myBuilder /* ERROR myBuilder does not satisfy */]
10+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// parameterized types with self-recursive constraints
8+
type (
9+
T1[P T1[P]] interface{}
10+
T2[P, Q T2[P, Q]] interface{}
11+
T3[P T2[P, Q], Q interface{ ~string }] interface{}
12+
13+
T4a[P T4a[P]] interface{ ~int }
14+
T4b[P T4b[int]] interface{ ~int }
15+
T4c[P T4c[string /* ERROR string does not satisfy T4c\[string\] */]] interface{ ~int }
16+
17+
// mutually recursive constraints
18+
T5[P T6[P]] interface{ int }
19+
T6[P T5[P]] interface{ int }
20+
)
21+
22+
// verify that constraints are checked as expected
23+
var (
24+
_ T1[int]
25+
_ T2[int, string]
26+
_ T3[int, string]
27+
)
28+
29+
// test case from issue
30+
31+
type Eq[a Eq[a]] interface {
32+
Equal(that a) bool
33+
}

0 commit comments

Comments
 (0)