Skip to content

Commit 08335a4

Browse files
griesemerjproberts
authored andcommitted
go/types, types2: consider type parameters for cycle detection
In validType, when we see an instantiated type, proceed as with non-generic types but provide an environment in which to look up the values (the corresponding type arguments) of type parameters of the instantiated type. For each type parameter for which there is a type argument, proceed with validating that type argument. This corresponds to applying validType to the instantiated type without actually instantiating the type (and running into infinite instantiations in case of invalid recursive types). Also, when creating a type instance, use the correct source position for the instance (the start of the qualified identifier if we have an imported type). Fixes golang#48962. Change-Id: I196c78bf066e4a56284d53368b2eb71bd8d8a780 Reviewed-on: https://go-review.googlesource.com/c/go/+/379414 Trust: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 9d64e61 commit 08335a4

File tree

21 files changed

+316
-137
lines changed

21 files changed

+316
-137
lines changed

src/cmd/compile/internal/types2/testdata/check/issues.go2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ type List3[TElem any] struct {
145145
}
146146

147147
// Infinite generic type declarations must lead to an error.
148-
type inf1 /* ERROR illegal cycle */ [T any] struct{ _ inf1[T] }
149-
type inf2 /* ERROR illegal cycle */ [T any] struct{ inf2[T] }
148+
type inf1[T any] struct{ _ inf1 /* ERROR illegal cycle */ [T] }
149+
type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
150150

151151
// The implementation of conversions T(x) between integers and floating-point
152152
// numbers checks that both T and x have either integer or floating-point

src/cmd/compile/internal/types2/testdata/check/typeinst.go2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,5 @@ var _ T3[int] = T3[int](List[int]{1, 2, 3})
5858

5959
// Self-recursive generic types are not permitted
6060

61-
type self1 /* ERROR illegal cycle */ [P any] self1[P]
61+
type self1[P any] self1 /* ERROR illegal cycle */ [P]
6262
type self2[P any] *self2[P] // this is ok

src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func main7() { var _ foo7 = x7[int]{} }
3737
// func main8() {}
3838

3939
// crash 9
40-
type foo9 /* ERROR illegal cycle */ [A any] interface { foo9[A] }
40+
type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] }
4141
func _() { var _ = new(foo9[int]) }
4242

4343
// crash 12

src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,53 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Check "infinite expansion" cycle errors across instantiated types.
6-
// We can't detect these errors anymore at the moment. See #48962 for
7-
// details.
8-
95
package p
106

7+
// All but E2 and E5 provide an "indirection" and break infinite expansion of a type.
118
type E0[P any] []P
129
type E1[P any] *P
1310
type E2[P any] struct{ _ P }
1411
type E3[P any] struct{ _ *P }
12+
type E5[P any] struct{ _ [10]P }
1513

16-
type T0 /* illegal cycle */ struct {
14+
type T0 struct {
1715
_ E0[T0]
1816
}
1917

20-
type T0_ /* illegal cycle */ struct {
18+
type T0_ struct {
2119
E0[T0_]
2220
}
2321

2422
type T1 struct {
2523
_ E1[T1]
2624
}
2725

28-
type T2 /* illegal cycle */ struct {
26+
type T2 /* ERROR illegal cycle */ struct {
2927
_ E2[T2]
3028
}
3129

3230
type T3 struct {
3331
_ E3[T3]
3432
}
3533

36-
// some more complex cases
37-
38-
type T4 /* illegal cycle */ struct {
39-
_ E0[E2[T4]]
40-
}
34+
type T4 /* ERROR illegal cycle */ [10]E5[T4]
4135

4236
type T5 struct {
43-
_ E0[E2[E0[E1[E2[[10]T5]]]]]
37+
_ E0[E2[T5]]
4438
}
4539

46-
type T6 /* illegal cycle */ struct {
47-
_ E0[[10]E2[E0[E2[E2[T6]]]]]
40+
type T6 struct {
41+
_ E0[E2[E0[E1[E2[[10]T6]]]]]
4842
}
4943

5044
type T7 struct {
51-
_ E0[[]E2[E0[E2[E2[T6]]]]]
45+
_ E0[[10]E2[E0[E2[E2[T7]]]]]
5246
}
47+
48+
type T8 struct {
49+
_ E0[[]E2[E0[E2[E2[T8]]]]]
50+
}
51+
52+
type T9 /* ERROR illegal cycle */ [10]E2[E5[E2[T9]]]
53+
54+
type T10 [10]E2[E5[E2[func(T10)]]]

src/cmd/compile/internal/types2/testdata/fixedbugs/issue48951.go2

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
package p
66

77
type (
8-
A1 /* ERROR illegal cycle */ [P any] [10]A1[P]
9-
A2 /* ERROR illegal cycle */ [P any] [10]A2[*P]
8+
A1[P any] [10]A1 /* ERROR illegal cycle */ [P]
9+
A2[P any] [10]A2 /* ERROR illegal cycle */ [*P]
1010
A3[P any] [10]*A3[P]
1111

1212
L1[P any] []L1[P]
1313

14-
S1 /* ERROR illegal cycle */ [P any] struct{ f S1[P] }
15-
S2 /* ERROR illegal cycle */ [P any] struct{ f S2[*P] } // like example in issue
14+
S1[P any] struct{ f S1 /* ERROR illegal cycle */ [P] }
15+
S2[P any] struct{ f S2 /* ERROR illegal cycle */ [*P] } // like example in issue
1616
S3[P any] struct{ f *S3[P] }
1717

18-
I1 /* ERROR illegal cycle */ [P any] interface{ I1[P] }
19-
I2 /* ERROR illegal cycle */ [P any] interface{ I2[*P] }
18+
I1[P any] interface{ I1 /* ERROR illegal cycle */ [P] }
19+
I2[P any] interface{ I2 /* ERROR illegal cycle */ [*P] }
2020
I3[P any] interface{ *I3 /* ERROR interface contains type constraints */ [P] }
2121
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2022 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 T0[P any] struct {
8+
f P
9+
}
10+
11+
type T1 /* ERROR illegal cycle */ struct {
12+
_ T0[T1]
13+
}

src/cmd/compile/internal/types2/testdata/fixedbugs/issue49043.go2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ package p
66

77
// The example from the issue.
88
type (
9-
N /* ERROR illegal cycle */ [P any] M[P]
10-
M[P any] N[P]
9+
N[P any] M /* ERROR illegal cycle */ [P]
10+
M[P any] N /* ERROR illegal cycle */ [P]
1111
)
1212

1313
// A slightly more complicated case.
1414
type (
15-
A /* ERROR illegal cycle */ [P any] B[P]
15+
A[P any] B /* ERROR illegal cycle */ [P]
1616
B[P any] C[P]
1717
C[P any] A[P]
1818
)

src/cmd/compile/internal/types2/typexpr.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
440440
// validation below. Ensure that the validation (and resulting errors) runs
441441
// for each instantiated type in the source.
442442
if inst == nil {
443-
tname := NewTypeName(x.Pos(), orig.obj.pkg, orig.obj.name, nil)
443+
// x may be a selector for an imported type; use its start pos rather than x.Pos().
444+
tname := NewTypeName(syntax.StartPos(x), orig.obj.pkg, orig.obj.name, nil)
444445
inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
445446
inst.targs = newTypeList(targs)
446447
inst = ctxt.update(h, orig, targs, inst).(*Named)

src/cmd/compile/internal/types2/validtype.go

Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ package types2
1010
// (Cycles involving alias types, as in "type A = [10]A" are detected
1111
// earlier, via the objDecl cycle detection mechanism.)
1212
func (check *Checker) validType(typ *Named) {
13-
check.validType0(typ, nil)
13+
check.validType0(typ, nil, nil)
1414
}
1515

1616
type typeInfo uint
1717

18-
func (check *Checker) validType0(typ Type, path []Object) typeInfo {
18+
// validType0 checks if the given type is valid. If typ is a type parameter
19+
// its value is looked up in the provided environment. The environment is
20+
// nil if typ is not part of (the RHS of) an instantiated type, in that case
21+
// any type parameter encountered must be from an enclosing function and can
22+
// be ignored. The path is the list of type names that lead to the current typ.
23+
func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeInfo {
1924
const (
2025
unknown typeInfo = iota
2126
marked
@@ -24,43 +29,39 @@ func (check *Checker) validType0(typ Type, path []Object) typeInfo {
2429
)
2530

2631
switch t := typ.(type) {
32+
case nil:
33+
// We should never see a nil type but be conservative and panic
34+
// only in debug mode.
35+
if debug {
36+
panic("validType0(nil)")
37+
}
38+
2739
case *Array:
28-
return check.validType0(t.elem, path)
40+
return check.validType0(t.elem, env, path)
2941

3042
case *Struct:
3143
for _, f := range t.fields {
32-
if check.validType0(f.typ, path) == invalid {
44+
if check.validType0(f.typ, env, path) == invalid {
3345
return invalid
3446
}
3547
}
3648

3749
case *Union:
3850
for _, t := range t.terms {
39-
if check.validType0(t.typ, path) == invalid {
51+
if check.validType0(t.typ, env, path) == invalid {
4052
return invalid
4153
}
4254
}
4355

4456
case *Interface:
4557
for _, etyp := range t.embeddeds {
46-
if check.validType0(etyp, path) == invalid {
58+
if check.validType0(etyp, env, path) == invalid {
4759
return invalid
4860
}
4961
}
5062

5163
case *Named:
52-
// If t is parameterized, we should be considering the instantiated (expanded)
53-
// form of t, but in general we can't with this algorithm: if t is an invalid
54-
// type it may be so because it infinitely expands through a type parameter.
55-
// Instantiating such a type would lead to an infinite sequence of instantiations.
56-
// In general, we need "type flow analysis" to recognize those cases.
57-
// Example: type A[T any] struct{ x A[*T] } (issue #48951)
58-
// In this algorithm we always only consider the original, uninstantiated type.
59-
// This won't recognize some invalid cases with parameterized types, but it
60-
// will terminate.
61-
t = t.orig
62-
63-
// don't report a 2nd error if we already know the type is invalid
64+
// Don't report a 2nd error if we already know the type is invalid
6465
// (e.g., if a cycle was detected earlier, via under).
6566
if t.underlying == Typ[Invalid] {
6667
check.infoMap[t] = invalid
@@ -70,32 +71,77 @@ func (check *Checker) validType0(typ Type, path []Object) typeInfo {
7071
switch check.infoMap[t] {
7172
case unknown:
7273
check.infoMap[t] = marked
73-
check.infoMap[t] = check.validType0(t.fromRHS, append(path, t.obj))
74+
check.infoMap[t] = check.validType0(t.orig.fromRHS, env.push(t), append(path, t.obj))
7475
case marked:
75-
// cycle detected
76+
// We have seen type t before and thus must have a cycle.
77+
check.infoMap[t] = invalid
78+
// t cannot be in an imported package otherwise that package
79+
// would have reported a type cycle and couldn't have been
80+
// imported in the first place.
81+
assert(t.obj.pkg == check.pkg)
82+
t.underlying = Typ[Invalid] // t is in the current package (no race possibilty)
83+
// Find the starting point of the cycle and report it.
7684
for i, tn := range path {
77-
// Even though validType now can hande cycles through external
78-
// types, we can't have cycles through external types because
79-
// no such types are detected yet.
80-
// TODO(gri) Remove this check once we can detect such cycles,
81-
// and adjust cycleError accordingly.
82-
if t.obj.pkg != check.pkg {
83-
panic("type cycle via package-external type")
84-
}
8585
if tn == t.obj {
8686
check.cycleError(path[i:])
87-
check.infoMap[t] = invalid
88-
// don't modify imported types (leads to race condition, see #35049)
89-
if t.obj.pkg == check.pkg {
90-
t.underlying = Typ[Invalid]
91-
}
9287
return invalid
9388
}
9489
}
9590
panic("cycle start not found")
9691
}
9792
return check.infoMap[t]
93+
94+
case *TypeParam:
95+
// A type parameter stands for the type (argument) it was instantiated with.
96+
// Check the corresponding type argument for validity if we have one.
97+
if env != nil {
98+
if targ := env.tmap[t]; targ != nil {
99+
// Type arguments found in targ must be looked
100+
// up in the enclosing environment env.link.
101+
return check.validType0(targ, env.link, path)
102+
}
103+
}
98104
}
99105

100106
return valid
101107
}
108+
109+
// A tparamEnv provides the environment for looking up the type arguments
110+
// with which type parameters for a given instance were instantiated.
111+
// If we don't have an instance, the corresponding tparamEnv is nil.
112+
type tparamEnv struct {
113+
tmap substMap
114+
link *tparamEnv
115+
}
116+
117+
func (env *tparamEnv) push(typ *Named) *tparamEnv {
118+
// If typ is not an instantiated type there are no typ-specific
119+
// type parameters to look up and we don't need an environment.
120+
targs := typ.TypeArgs()
121+
if targs == nil {
122+
return nil // no instance => nil environment
123+
}
124+
125+
// Populate tmap: remember the type argument for each type parameter.
126+
// We cannot use makeSubstMap because the number of type parameters
127+
// and arguments may not match due to errors in the source (too many
128+
// or too few type arguments). Populate tmap "manually".
129+
tparams := typ.TypeParams()
130+
n, m := targs.Len(), tparams.Len()
131+
if n > m {
132+
n = m // too many targs
133+
}
134+
tmap := make(substMap, n)
135+
for i := 0; i < n; i++ {
136+
tmap[tparams.At(i)] = targs.At(i)
137+
}
138+
139+
return &tparamEnv{tmap: tmap, link: env}
140+
}
141+
142+
// TODO(gri) Alternative implementation:
143+
// We may not need to build a stack of environments to
144+
// look up the type arguments for type parameters. The
145+
// same information should be available via the path:
146+
// We should be able to just walk the path backwards
147+
// and find the type arguments in the instance objects.

src/go/types/testdata/check/issues.go2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ type List3[TElem any] struct {
145145
}
146146

147147
// Infinite generic type declarations must lead to an error.
148-
type inf1 /* ERROR illegal cycle */ [T any] struct{ _ inf1[T] }
149-
type inf2 /* ERROR illegal cycle */ [T any] struct{ inf2[T] }
148+
type inf1[T any] struct{ _ inf1 /* ERROR illegal cycle */ [T] }
149+
type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
150150

151151
// The implementation of conversions T(x) between integers and floating-point
152152
// numbers checks that both T and x have either integer or floating-point

src/go/types/testdata/check/typeinst.go2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,5 @@ var _ T3[int] = T3[int](List[int]{1, 2, 3})
5858

5959
// Self-recursive generic types are not permitted
6060

61-
type self1 /* ERROR illegal cycle */ [P any] self1[P]
61+
type self1[P any] self1 /* ERROR illegal cycle */ [P]
6262
type self2[P any] *self2[P] // this is ok

src/go/types/testdata/fixedbugs/issue39634.go2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func main7() { var _ foo7 = x7[int]{} }
3737
// func main8() {}
3838

3939
// crash 9
40-
type foo9 /* ERROR illegal cycle */ [A any] interface { foo9[A] }
40+
type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] }
4141
func _() { var _ = new(foo9[int]) }
4242

4343
// crash 12

0 commit comments

Comments
 (0)