go/types: don't skip defined types when reporting cycles
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>
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index d0027ae..a4fb2b8 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -311,10 +311,16 @@
}
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 @@
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:
diff --git a/src/go/types/testdata/cycles.src b/src/go/types/testdata/cycles.src
index 7f9fc89..b2ee8ec 100644
--- a/src/go/types/testdata/cycles.src
+++ b/src/go/types/testdata/cycles.src
@@ -23,10 +23,8 @@
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 @@
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 @@
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
diff --git a/src/go/types/type.go b/src/go/types/type.go
index a490d92..087cda4 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -450,6 +450,7 @@
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 @@
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
}