Skip to content

Commit cc14fca

Browse files
committed
cmd/compile/internal/types2: disallow type cycles through type parameter lists
If we reach a generic type that is part of a cycle and we are in a type parameter list, we have a cycle through a type parameter list, which is invalid. Fixes #49439. Change-Id: Ia6cf97e1748ca0c0e61c02841202050091365b0b Reviewed-on: https://go-review.googlesource.com/c/go/+/361922 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 318c024 commit cc14fca

File tree

12 files changed

+91
-33
lines changed

12 files changed

+91
-33
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type context struct {
4646
pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
4747
iota constant.Value // value of iota in a constant declaration; nil otherwise
4848
errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer
49+
inTParamList bool // set if inside a type parameter list
4950
sig *Signature // function signature if inside a function; nil otherwise
5051
isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
5152
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions

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

+43-14
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,23 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
228228
assert(obj.color() >= grey)
229229
start := obj.color() - grey // index of obj in objPath
230230
cycle := check.objPath[start:]
231-
nval := 0 // number of (constant or variable) values in the cycle
232-
ndef := 0 // number of type definitions in the cycle
231+
tparCycle := false // if set, the cycle is through a type parameter list
232+
nval := 0 // number of (constant or variable) values in the cycle; valid if !generic
233+
ndef := 0 // number of type definitions in the cycle; valid if !generic
234+
loop:
233235
for _, obj := range cycle {
234236
switch obj := obj.(type) {
235237
case *Const, *Var:
236238
nval++
237239
case *TypeName:
240+
// If we reach a generic type that is part of a cycle
241+
// and we are in a type parameter list, we have a cycle
242+
// through a type parameter list, which is invalid.
243+
if check.inTParamList && isGeneric(obj.typ) {
244+
tparCycle = true
245+
break loop
246+
}
247+
238248
// Determine if the type name is an alias or not. For
239249
// package-level objects, use the object map which
240250
// provides syntactic information (which doesn't rely
@@ -262,26 +272,32 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
262272

263273
if check.conf.Trace {
264274
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
265-
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
275+
if tparCycle {
276+
check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list")
277+
} else {
278+
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
279+
}
266280
defer func() {
267281
if !valid {
268282
check.trace(obj.Pos(), "=> error: cycle is invalid")
269283
}
270284
}()
271285
}
272286

273-
// A cycle involving only constants and variables is invalid but we
274-
// ignore them here because they are reported via the initialization
275-
// cycle check.
276-
if nval == len(cycle) {
277-
return true
278-
}
287+
if !tparCycle {
288+
// A cycle involving only constants and variables is invalid but we
289+
// ignore them here because they are reported via the initialization
290+
// cycle check.
291+
if nval == len(cycle) {
292+
return true
293+
}
279294

280-
// A cycle involving only types (and possibly functions) must have at least
281-
// one type definition to be permitted: If there is no type definition, we
282-
// have a sequence of alias type names which will expand ad infinitum.
283-
if nval == 0 && ndef > 0 {
284-
return true
295+
// A cycle involving only types (and possibly functions) must have at least
296+
// one type definition to be permitted: If there is no type definition, we
297+
// have a sequence of alias type names which will expand ad infinitum.
298+
if nval == 0 && ndef > 0 {
299+
return true
300+
}
285301
}
286302

287303
check.cycleError(cycle)
@@ -624,6 +640,19 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel
624640
// Example: type T[P T[P]] interface{}
625641
*dst = bindTParams(tparams)
626642

643+
// Signal to cycle detection that we are in a type parameter list.
644+
// We can only be inside one type parameter list at any given time:
645+
// function closures may appear inside a type parameter list but they
646+
// cannot be generic, and their bodies are processed in delayed and
647+
// sequential fashion. Note that with each new declaration, we save
648+
// the existing context and restore it when done; thus inTParamList
649+
// is true exactly only when we are in a specific type parameter list.
650+
assert(!check.inTParamList)
651+
check.inTParamList = true
652+
defer func() {
653+
check.inTParamList = false
654+
}()
655+
627656
// Keep track of bounds for later validation.
628657
var bound Type
629658
var bounds []Type

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package p
66

7-
type Builder[T interface{ struct{ Builder[T] } }] struct{}
7+
type Builder /* ERROR illegal cycle */ [T interface{ struct{ Builder[T] } }] struct{}
88
type myBuilder struct {
9-
Builder[myBuilder /* ERROR myBuilder does not satisfy */]
9+
Builder[myBuilder]
1010
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
package p
66

77
// test case 1
8-
type T[U interface{ M() T[U] }] int
8+
type T /* ERROR illegal cycle */ [U interface{ M() T[U] }] int
99

1010
type X int
1111

1212
func (X) M() T[X] { return 0 }
1313

1414
// test case 2
15-
type A[T interface{ A[T] }] interface{}
15+
type A /* ERROR illegal cycle */ [T interface{ A[T] }] interface{}
1616

1717
// test case 3
18-
type A2[U interface{ A2[U] }] interface{ M() A2[U] }
18+
type A2 /* ERROR illegal cycle */ [U interface{ A2[U] }] interface{ M() A2[U] }
1919

2020
type I interface{ A2[I]; M() A2[I] }

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ package p
66

77
// parameterized types with self-recursive constraints
88
type (
9-
T1[P T1[P]] interface{}
10-
T2[P, Q T2[P, Q]] interface{}
9+
T1 /* ERROR illegal cycle */ [P T1[P]] interface{}
10+
T2 /* ERROR illegal cycle */ [P, Q T2[P, Q]] interface{}
1111
T3[P T2[P, Q], Q interface{ ~string }] interface{}
1212

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 }
13+
T4a /* ERROR illegal cycle */ [P T4a[P]] interface{ ~int }
14+
T4b /* ERROR illegal cycle */ [P T4b[int]] interface{ ~int }
15+
T4c /* ERROR illegal cycle */ [P T4c[string]] interface{ ~int }
1616

1717
// mutually recursive constraints
18-
T5[P T6[P]] interface{ int }
18+
T5 /* ERROR illegal cycle */ [P T6[P]] interface{ int }
1919
T6[P T5[P]] interface{ int }
2020
)
2121

@@ -28,6 +28,6 @@ var (
2828

2929
// test case from issue
3030

31-
type Eq[a Eq[a]] interface {
31+
type Eq /* ERROR illegal cycle */ [a Eq[a]] interface {
3232
Equal(that a) bool
3333
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package p
66

7-
type T[U interface{ M() T /* ERROR "got 2 arguments but 1 type parameters" */ [U, int] }] int
7+
type T /* ERROR illegal cycle */ [U interface{ M() T[U, int] }] int
88

99
type X int
1010

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
import "unsafe"
8+
9+
type T0 /* ERROR illegal cycle */ [P T0[P]] struct{}
10+
11+
type T1 /* ERROR illegal cycle */ [P T2[P]] struct{}
12+
type T2[P T1[P]] struct{}
13+
14+
type T3 /* ERROR illegal cycle */ [P interface{ ~struct{ f T3[int] } }] struct{}
15+
16+
// valid cycle in M
17+
type N[P M[P]] struct{}
18+
type M[Q any] struct { F *M[Q] }
19+
20+
// "crazy" case
21+
type TC[P [unsafe.Sizeof(func() {
22+
type T [P [unsafe.Sizeof(func(){})]byte] struct{}
23+
})]byte] struct{}
24+
25+
// test case from issue
26+
type X /* ERROR illegal cycle */ [T any, PT X[T]] interface{}

test/typeparam/issue46461.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
// compile -G=3
1+
// errorcheck -G=3
22

33
// Copyright 2021 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
55
// license that can be found in the LICENSE file.
66

77
package p
88

9-
type T[U interface{ M() T[U] }] int
9+
type T[U interface{ M() T[U] }] int // ERROR "invalid recursive type T"
1010

1111
type X int
1212

test/typeparam/issue46461b.dir/a.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
package a
66

7-
type T[U interface{ M() T[U] }] int
7+
type T[U interface{ M() int }] int

test/typeparam/issue46461b.dir/b.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ import "./a"
88

99
type X int
1010

11-
func (X) M() a.T[X] { return 0 }
11+
func (X) M() int { return 0 }
12+
13+
type _ a.T[X]

test/typeparam/issue48280.dir/a.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package a
66

7-
type I[T I[T]] interface {
7+
type I[T any] interface {
88
F() T
99
}
1010

test/typeparam/issue48306.dir/a.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
package a
66

7-
type I[T I[T]] interface {
7+
type I[T any] interface {
88
F() T
99
}

0 commit comments

Comments
 (0)