@@ -53,11 +53,11 @@ func pathString(path []Object) string {
53
53
// For the meaning of def, see Checker.definedType, in typexpr.go.
54
54
func (check * Checker ) objDecl (obj Object , def * Named ) {
55
55
if trace {
56
- check .trace (obj .Pos (), "-- checking %s %s ( objPath = %s)" , obj .color (), obj , pathString (check .objPath ))
56
+ check .trace (obj .Pos (), "-- checking %s (%s, objPath = %s)" , obj , obj .color (), pathString (check .objPath ))
57
57
check .indent ++
58
58
defer func () {
59
59
check .indent --
60
- check .trace (obj .Pos (), "=> %s" , obj )
60
+ check .trace (obj .Pos (), "=> %s (%s) " , obj , obj . color () )
61
61
}()
62
62
}
63
63
@@ -198,13 +198,6 @@ func (check *Checker) objDecl(obj Object, def *Named) {
198
198
}
199
199
}
200
200
201
- // indir is a sentinel type name that is pushed onto the object path
202
- // to indicate an "indirection" in the dependency from one type name
203
- // to the next. For instance, for "type p *p" the object path contains
204
- // p followed by indir, indicating that there's an indirection *p.
205
- // Indirections are used to break type cycles.
206
- var indir = NewTypeName (token .NoPos , nil , "*" , nil )
207
-
208
201
// typeCycle checks if the cycle starting with obj is valid and
209
202
// reports an error if it is not.
210
203
// TODO(gri) rename s/typeCycle/cycle/ once we don't need the other
@@ -221,52 +214,34 @@ func (check *Checker) typeCycle(obj Object) (isCycle bool) {
221
214
}
222
215
}
223
216
224
- // Given the number of constants and variables (nval) in the cycle
225
- // and the cycle length (ncycle = number of named objects in the cycle),
226
- // we distinguish between cycles involving only constants and variables
227
- // (nval = ncycle), cycles involving types (and functions) only
228
- // (nval == 0), and mixed cycles (nval != 0 && nval != ncycle).
229
- // We ignore functions at the moment (taking them into account correctly
230
- // is complicated and it doesn't improve error reporting significantly).
231
- //
232
- // A cycle must have at least one indirection and one type definition
233
- // to be permitted: If there is no indirection, the size of the type
234
- // cannot be computed (it's either infinite or 0); if there is no type
235
- // definition, we have a sequence of alias type names which will expand
236
- // ad infinitum.
237
- var nval , ncycle int
238
- var hasIndir , hasTDef bool
217
+ // Count cycle objects.
239
218
assert (obj .color () >= grey )
240
219
start := obj .color () - grey // index of obj in objPath
241
220
cycle := check .objPath [start :]
242
- ncycle = len (cycle ) // including indirections
221
+ nval := 0 // number of (constant or variable) values in the cycle
222
+ ndef := 0 // number of type definitions in the cycle
243
223
for _ , obj := range cycle {
244
224
switch obj := obj .(type ) {
245
225
case * Const , * Var :
246
226
nval ++
247
227
case * TypeName :
248
- if obj == indir {
249
- ncycle -- // don't count (indirections are not objects)
250
- hasIndir = true
228
+ // Determine if the type name is an alias or not. For
229
+ // package-level objects, use the object map which
230
+ // provides syntactic information (which doesn't rely
231
+ // on the order in which the objects are set up). For
232
+ // local objects, we can rely on the order, so use
233
+ // the object's predicate.
234
+ // TODO(gri) It would be less fragile to always access
235
+ // the syntactic information. We should consider storing
236
+ // this information explicitly in the object.
237
+ var alias bool
238
+ if d := check .objMap [obj ]; d != nil {
239
+ alias = d .alias // package-level object
251
240
} else {
252
- // Determine if the type name is an alias or not. For
253
- // package-level objects, use the object map which
254
- // provides syntactic information (which doesn't rely
255
- // on the order in which the objects are set up). For
256
- // local objects, we can rely on the order, so use
257
- // the object's predicate.
258
- // TODO(gri) It would be less fragile to always access
259
- // the syntactic information. We should consider storing
260
- // this information explicitly in the object.
261
- var alias bool
262
- if d := check .objMap [obj ]; d != nil {
263
- alias = d .alias // package-level object
264
- } else {
265
- alias = obj .IsAlias () // function local object
266
- }
267
- if ! alias {
268
- hasTDef = true
269
- }
241
+ alias = obj .IsAlias () // function local object
242
+ }
243
+ if ! alias {
244
+ ndef ++
270
245
}
271
246
case * Func :
272
247
// ignored for now
@@ -276,8 +251,8 @@ func (check *Checker) typeCycle(obj Object) (isCycle bool) {
276
251
}
277
252
278
253
if trace {
279
- check .trace (obj .Pos (), "## cycle detected: objPath = %s->%s (len = %d)" , pathString (cycle ), obj .Name (), ncycle )
280
- check .trace (obj .Pos (), "## cycle contains: %d values, has indirection = %v, has type definition = %v " , nval , hasIndir , hasTDef )
254
+ check .trace (obj .Pos (), "## cycle detected: objPath = %s->%s (len = %d)" , pathString (cycle ), obj .Name (), len ( cycle ) )
255
+ check .trace (obj .Pos (), "## cycle contains: %d values, %d type definitions " , nval , ndef )
281
256
defer func () {
282
257
if isCycle {
283
258
check .trace (obj .Pos (), "=> error: cycle is invalid" )
@@ -288,30 +263,108 @@ func (check *Checker) typeCycle(obj Object) (isCycle bool) {
288
263
// A cycle involving only constants and variables is invalid but we
289
264
// ignore them here because they are reported via the initialization
290
265
// cycle check.
291
- if nval == ncycle {
266
+ if nval == len ( cycle ) {
292
267
return false
293
268
}
294
269
295
- // A cycle involving only types (and possibly functions) must have at
296
- // least one indirection and one type definition to be permitted: If
297
- // there is no indirection, the size of the type cannot be computed
298
- // (it's either infinite or 0); if there is no type definition, we
270
+ // A cycle involving only types (and possibly functions) must have at least
271
+ // one type definition to be permitted: If there is no type definition, we
299
272
// have a sequence of alias type names which will expand ad infinitum.
300
- if nval == 0 && hasIndir && hasTDef {
273
+ if nval == 0 && ndef > 0 {
301
274
return false // cycle is permitted
302
275
}
303
276
304
- // report cycle
305
- check .errorf (obj .Pos (), "illegal cycle in declaration of %s" , obj .Name ())
306
- for _ , obj := range cycle {
307
- if obj == indir {
308
- continue // don't print indir sentinels
277
+ check .cycleError (cycle )
278
+
279
+ return true
280
+ }
281
+
282
+ type typeInfo uint
283
+
284
+ // validType verifies that the given type does not "expand" infinitely
285
+ // producing a cycle in the type graph. Cycles are detected by marking
286
+ // defined types.
287
+ // (Cycles involving alias types, as in "type A = [10]A" are detected
288
+ // earlier, via the objDecl cycle detection mechanism.)
289
+ func (check * Checker ) validType (typ Type , path []Object ) typeInfo {
290
+ const (
291
+ unknown typeInfo = iota
292
+ marked
293
+ valid
294
+ invalid
295
+ )
296
+
297
+ switch t := typ .(type ) {
298
+ case * Array :
299
+ return check .validType (t .elem , path )
300
+
301
+ case * Struct :
302
+ for _ , f := range t .fields {
303
+ if check .validType (f .typ , path ) == invalid {
304
+ return invalid
305
+ }
306
+ }
307
+
308
+ case * Interface :
309
+ for _ , etyp := range t .embeddeds {
310
+ if check .validType (etyp , path ) == invalid {
311
+ return invalid
312
+ }
309
313
}
314
+
315
+ case * Named :
316
+ switch t .info {
317
+ case unknown :
318
+ t .info = marked
319
+ t .info = check .validType (t .underlying , append (path , t .obj ))
320
+ case marked :
321
+ // cycle detected
322
+ for i , tn := range path {
323
+ if tn == t .obj {
324
+ check .cycleError (path [i :])
325
+ t .info = invalid
326
+ t .underlying = Typ [Invalid ]
327
+ return t .info
328
+ }
329
+ }
330
+ panic ("internal error: cycle start not found" )
331
+ }
332
+ return t .info
333
+ }
334
+
335
+ return valid
336
+ }
337
+
338
+ // cycleError reports a declaration cycle starting with
339
+ // the object in cycle that is "first" in the source.
340
+ func (check * Checker ) cycleError (cycle []Object ) {
341
+ // TODO(gri) Should we start with the last (rather than the first) object in the cycle
342
+ // since that is the earliest point in the source where we start seeing the
343
+ // cycle? That would be more consistent with other error messages.
344
+ i := firstInSrc (cycle )
345
+ obj := cycle [i ]
346
+ check .errorf (obj .Pos (), "illegal cycle in declaration of %s" , obj .Name ())
347
+ for range cycle {
310
348
check .errorf (obj .Pos (), "\t %s refers to" , obj .Name ()) // secondary error, \t indented
349
+ i ++
350
+ if i >= len (cycle ) {
351
+ i = 0
352
+ }
353
+ obj = cycle [i ]
311
354
}
312
355
check .errorf (obj .Pos (), "\t %s" , obj .Name ())
356
+ }
313
357
314
- return true
358
+ // firstInSrc reports the index of the object with the "smallest"
359
+ // source position in path. path must not be empty.
360
+ func firstInSrc (path []Object ) int {
361
+ fst , pos := 0 , path [0 ].Pos ()
362
+ for i , t := range path [1 :] {
363
+ if t .Pos () < pos {
364
+ fst , pos = i + 1 , t .Pos ()
365
+ }
366
+ }
367
+ return fst
315
368
}
316
369
317
370
func (check * Checker ) constDecl (obj * Const , typ , init ast.Expr ) {
@@ -409,15 +462,53 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
409
462
410
463
// underlying returns the underlying type of typ; possibly by following
411
464
// forward chains of named types. Such chains only exist while named types
412
- // are incomplete.
413
- func underlying (typ Type ) Type {
465
+ // are incomplete. If an underlying type is found, resolve the chain by
466
+ // setting the underlying type for each defined type in the chain before
467
+ // returning it.
468
+ //
469
+ // If no underlying type is found, a cycle error is reported and Typ[Invalid]
470
+ // is used as underlying type for each defined type in the chain and returned
471
+ // as result.
472
+ func (check * Checker ) underlying (typ Type ) Type {
473
+ // If typ is not a defined type, its underlying type is itself.
474
+ n0 , _ := typ .(* Named )
475
+ if n0 == nil {
476
+ return typ // nothing to do
477
+ }
478
+
479
+ // If the underlying type of a defined type is not a defined
480
+ // type, then that is the desired underlying type.
481
+ typ = n0 .underlying
482
+ n , _ := typ .(* Named )
483
+ if n == nil {
484
+ return typ // common case
485
+ }
486
+
487
+ // Otherwise, follow the forward chain.
488
+ seen := map [* Named ]int {n0 : 0 , n : 1 }
489
+ path := []Object {n0 .obj , n .obj }
414
490
for {
415
- n , _ := typ .(* Named )
491
+ typ = n .underlying
492
+ n , _ = typ .(* Named )
416
493
if n == nil {
494
+ break // end of chain
495
+ }
496
+
497
+ if i , ok := seen [n ]; ok {
498
+ // cycle
499
+ check .cycleError (path [i :])
500
+ typ = Typ [Invalid ]
417
501
break
418
502
}
419
- typ = n .underlying
503
+
504
+ seen [n ] = len (seen )
505
+ path = append (path , n .obj )
420
506
}
507
+
508
+ for n := range seen {
509
+ n .underlying = typ
510
+ }
511
+
421
512
return typ
422
513
}
423
514
@@ -430,6 +521,10 @@ func (n *Named) setUnderlying(typ Type) {
430
521
func (check * Checker ) typeDecl (obj * TypeName , typ ast.Expr , def * Named , alias bool ) {
431
522
assert (obj .typ == nil )
432
523
524
+ check .later (func () {
525
+ check .validType (obj .typ , nil )
526
+ })
527
+
433
528
if alias {
434
529
435
530
obj .typ = Typ [Invalid ]
@@ -456,8 +551,8 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bo
456
551
// The type of C is the (named) type of A which is incomplete,
457
552
// and which has as its underlying type the named type B.
458
553
// Determine the (final, unnamed) underlying type by resolving
459
- // any forward chain (they always end in an unnamed type) .
460
- named .underlying = underlying (named . underlying )
554
+ // any forward chain.
555
+ named .underlying = check . underlying (named )
461
556
462
557
}
463
558
0 commit comments