]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: don't skip defined types when reporting cycles
authorRobert Griesemer <gri@golang.org>
Tue, 8 Oct 2019 22:45:14 +0000 (15:45 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 8 Oct 2019 23:32:42 +0000 (23:32 +0000)
The newly introduced "late-stage" cycle detection for types
(https://golang.org/cl/196338/) "skips" named types on the
RHS of a type declaration when reporting a cycle. For instance,
for:

type (
   A B
   B [10]C
   C A
)

the reported cycle is:

illegal cycle in declaration of C
       C refers to
       C

because the underlying type of C resolves to [10]C (note that
cmd/compile does the same but simply says invalid recursive
type C).

This CL introduces the Named.orig field which always refers
to the RHS type in a type definition (and is never changed).
By using Named.orig rather than Named.underlying for the type
validity check, the cycle as written in the source code is
reported:

  illegal cycle in declaration of A
         A refers to
         B refers to
         C refers to
         A

Fixes #34771.

Change-Id: I41e260ceb3f9a15da87ffae6a3921bd8280e2ac4
Reviewed-on: https://go-review.googlesource.com/c/go/+/199937
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/go/types/decl.go
src/go/types/testdata/cycles.src
src/go/types/type.go

index d0027aeb8e8fa0fd73754ed87c44d7a45c178a8d..a4fb2b81ccd11afbfeb514d833c3c3d587a3e01e 100644 (file)
@@ -311,10 +311,16 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
                }
 
        case *Named:
+               // don't report a 2nd error if we already know the type is invalid
+               // (e.g., if a cycle was detected earlier, via Checker.underlying).
+               if t.underlying == Typ[Invalid] {
+                       t.info = invalid
+                       return invalid
+               }
                switch t.info {
                case unknown:
                        t.info = marked
-                       t.info = check.validType(t.underlying, append(path, t.obj))
+                       t.info = check.validType(t.orig, append(path, t.obj))
                case marked:
                        // cycle detected
                        for i, tn := range path {
@@ -535,7 +541,7 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bo
                obj.typ = named // make sure recursive type declarations terminate
 
                // determine underlying type of named
-               check.definedType(typ, named)
+               named.orig = check.definedType(typ, named)
 
                // The underlying type of named may be itself a named type that is
                // incomplete:
index 7f9fc8945e3dfc97474b7fcb704548d6567b9832..b2ee8ecd5f6001076e336779a9b5150c1e524162 100644 (file)
@@ -23,10 +23,8 @@ type (
        A0 /* ERROR cycle */ [10]A0
        A1 [10]*A1
 
-       // TODO(gri) It would be nicer to report the cycle starting
-       //           with A2 (also below, for S4). See issue #34771.
-       A2 [10]A3
-       A3 /* ERROR cycle */ [10]A4
+       A2 /* ERROR cycle */ [10]A3
+       A3 [10]A4
        A4 A2
 
        A5 [10]A6
@@ -41,8 +39,8 @@ type (
        S2 struct{ _ *S2 }
        S3 struct{ *S3 }
 
-       S4 struct{ S5 }
-       S5 /* ERROR cycle */ struct{ S6 }
+       S4 /* ERROR cycle */ struct{ S5 }
+       S5 struct{ S6 }
        S6 S4
 
        // pointers
@@ -73,6 +71,15 @@ type (
        C0 chan C0
 )
 
+// test case for issue #34771
+type (
+       AA /* ERROR cycle */ B
+       B C
+       C [10]D
+       D E
+       E AA
+)
+
 func _() {
        type (
                t1 /* ERROR cycle */ t1
index a490d920091b1104095a0346f6c981930f78b64c..087cda429d689ab83c714a5f18caef0fdd442e16 100644 (file)
@@ -450,6 +450,7 @@ func (c *Chan) Elem() Type { return c.elem }
 type Named struct {
        info       typeInfo  // for cycle detection
        obj        *TypeName // corresponding declared object
+       orig       Type      // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
        underlying Type      // possibly a *Named during setup; never a *Named once set up completely
        methods    []*Func   // methods declared for this type (not the method set of this type); signatures are type-checked lazily
 }
@@ -461,7 +462,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
        if _, ok := underlying.(*Named); ok {
                panic("types.NewNamed: underlying type must not be *Named")
        }
-       typ := &Named{obj: obj, underlying: underlying, methods: methods}
+       typ := &Named{obj: obj, orig: underlying, underlying: underlying, methods: methods}
        if obj.typ == nil {
                obj.typ = typ
        }