assert(obj.color() >= grey)
start := obj.color() - grey // index of obj in objPath
cycle := check.objPath[start:]
- nval := 0 // number of (constant or variable) values in the cycle
- ndef := 0 // number of type definitions in the cycle
+ tparCycle := false // if set, the cycle is through a type parameter list
+ nval := 0 // number of (constant or variable) values in the cycle; valid if !generic
+ ndef := 0 // number of type definitions in the cycle; valid if !generic
+loop:
for _, obj := range cycle {
switch obj := obj.(type) {
case *Const, *Var:
nval++
case *TypeName:
+ // 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.
+ if check.context.inTParamList && isGeneric(obj.typ) {
+ tparCycle = true
+ break loop
+ }
+
// Determine if the type name is an alias or not. For
// package-level objects, use the object map which
// provides syntactic information (which doesn't rely
if trace {
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
- check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ if tparCycle {
+ check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list")
+ } else {
+ check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ }
defer func() {
if !valid {
check.trace(obj.Pos(), "=> error: cycle is invalid")
}()
}
- // A cycle involving only constants and variables is invalid but we
- // ignore them here because they are reported via the initialization
- // cycle check.
- if nval == len(cycle) {
- return true
- }
+ if !tparCycle {
+ // A cycle involving only constants and variables is invalid but we
+ // ignore them here because they are reported via the initialization
+ // cycle check.
+ if nval == len(cycle) {
+ return true
+ }
- // A cycle involving only types (and possibly functions) must have at least
- // one type definition to be permitted: If there is no type definition, we
- // have a sequence of alias type names which will expand ad infinitum.
- if nval == 0 && ndef > 0 {
- return true
+ // A cycle involving only types (and possibly functions) must have at least
+ // one type definition to be permitted: If there is no type definition, we
+ // have a sequence of alias type names which will expand ad infinitum.
+ if nval == 0 && ndef > 0 {
+ return true
+ }
}
check.cycleError(cycle)
// Example: type T[P T[P]] interface{}
*dst = bindTParams(tparams)
+ // Signal to cycle detection that we are in a type parameter list.
+ // We can only be inside one type parameter list at any given time:
+ // function closures may appear inside a type parameter list but they
+ // cannot be generic, and their bodies are processed in delayed and
+ // sequential fashion. Note that with each new declaration, we save
+ // the existing context and restore it when done; thus inTPList is
+ // true exactly only when we are in a specific type parameter list.
+ assert(!check.inTParamList)
+ check.inTParamList = true
+ defer func() {
+ check.inTParamList = false
+ }()
+
index := 0
var bounds []Type
var posns []positioner // bound positions
// parameterized types with self-recursive constraints
type (
- T1[P T1[P]] interface{}
- T2[P, Q T2[P, Q]] interface{}
+ T1 /* ERROR illegal cycle */ [P T1[P]] interface{}
+ T2 /* ERROR illegal cycle */ [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 }
+ T4a /* ERROR illegal cycle */ [P T4a[P]] interface{ ~int }
+ T4b /* ERROR illegal cycle */ [P T4b[int]] interface{ ~int }
+ T4c /* ERROR illegal cycle */ [P T4c[string]] interface{ ~int }
// mutually recursive constraints
- T5[P T6[P]] interface{ int }
+ T5 /* ERROR illegal cycle */ [P T6[P]] interface{ int }
T6[P T5[P]] interface{ int }
)
// test case from issue
-type Eq[a Eq[a]] interface {
+type Eq /* ERROR illegal cycle */ [a Eq[a]] interface {
Equal(that a) bool
}
--- /dev/null
+// 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
+
+import "unsafe"
+
+type T0 /* ERROR illegal cycle */ [P T0[P]] struct{}
+
+type T1 /* ERROR illegal cycle */ [P T2[P]] struct{}
+type T2[P T1[P]] struct{}
+
+type T3 /* ERROR illegal cycle */ [P interface{ ~struct{ f T3[int] } }] struct{}
+
+// valid cycle in M
+type N[P M[P]] struct{}
+type M[Q any] struct { F *M[Q] }
+
+// "crazy" case
+type TC[P [unsafe.Sizeof(func() {
+ type T [P [unsafe.Sizeof(func(){})]byte] struct{}
+})]byte] struct{}
+
+// test case from issue
+type X /* ERROR illegal cycle */ [T any, PT X[T]] interface{}