@@ -228,13 +228,23 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
228
228
assert (obj .color () >= grey )
229
229
start := obj .color () - grey // index of obj in objPath
230
230
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:
233
235
for _ , obj := range cycle {
234
236
switch obj := obj .(type ) {
235
237
case * Const , * Var :
236
238
nval ++
237
239
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
+
238
248
// Determine if the type name is an alias or not. For
239
249
// package-level objects, use the object map which
240
250
// provides syntactic information (which doesn't rely
@@ -262,26 +272,32 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
262
272
263
273
if check .conf .Trace {
264
274
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
+ }
266
280
defer func () {
267
281
if ! valid {
268
282
check .trace (obj .Pos (), "=> error: cycle is invalid" )
269
283
}
270
284
}()
271
285
}
272
286
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
+ }
279
294
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
+ }
285
301
}
286
302
287
303
check .cycleError (cycle )
@@ -624,6 +640,19 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel
624
640
// Example: type T[P T[P]] interface{}
625
641
* dst = bindTParams (tparams )
626
642
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
+
627
656
// Keep track of bounds for later validation.
628
657
var bound Type
629
658
var bounds []Type
0 commit comments