diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index 6d39f99424..f33b7c4396 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -531,3 +531,28 @@ func TestIssue34921(t *testing.T) { pkg = res // res is imported by the next package in this test } } + +func TestIssue43088(t *testing.T) { + // type T1 struct { + // x T2 + // } + // + // type T2 struct { + // x struct { + // x T2 + // } + // } + n1 := NewTypeName(syntax.Pos{}, nil, "T1", nil) + T1 := NewNamed(n1, nil, nil) + n2 := NewTypeName(syntax.Pos{}, nil, "T2", nil) + T2 := NewNamed(n2, nil, nil) + s1 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "x", T2, false)}, nil) + T1.SetUnderlying(s1) + s2 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "x", T2, false)}, nil) + s3 := NewStruct([]*Var{NewField(syntax.Pos{}, nil, "x", s2, false)}, nil) + T2.SetUnderlying(s3) + + // These calls must terminate (no endless recursion). + Comparable(T1) + Comparable(T2) +} diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index f3a5818b3f..048519471c 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -87,6 +87,11 @@ func IsInterface(typ Type) bool { // Comparable reports whether values of type T are comparable. func Comparable(T Type) bool { + return comparable(T, nil) +} + +// comparable should only be called by Comparable. +func comparable(T Type, seen map[Type]bool) bool { // If T is a type parameter not constraint by any type // list (i.e., it's underlying type is the top type), // T is comparable if it has the == method. Otherwise, @@ -99,6 +104,14 @@ func Comparable(T Type) bool { return t.Bound().IsComparable() } + if seen[T] { + return true + } + if seen == nil { + seen = make(map[Type]bool) + } + seen[T] = true + switch t := optype(T.Under()).(type) { case *Basic: // assume invalid types to be comparable @@ -108,13 +121,13 @@ func Comparable(T Type) bool { return true case *Struct: for _, f := range t.fields { - if !Comparable(f.typ) { + if !comparable(f.typ, seen) { return false } } return true case *Array: - return Comparable(t.elem) + return comparable(t.elem, seen) case *Sum: return t.is(Comparable) case *TypeParam: diff --git a/test/fixedbugs/issue8507.go b/test/fixedbugs/issue8507.go index ad6ba8ac68..277b3dc721 100644 --- a/test/fixedbugs/issue8507.go +++ b/test/fixedbugs/issue8507.go @@ -9,7 +9,7 @@ package p -type T struct{ T } // ERROR "invalid recursive type T" +type T struct{ T } // ERROR "invalid recursive type T|cycle" func f() { println(T{} == T{}) diff --git a/test/run.go b/test/run.go index a3e2ac5e32..32c74e8210 100644 --- a/test/run.go +++ b/test/run.go @@ -2130,6 +2130,4 @@ var excluded = map[string]bool{ "fixedbugs/issue7525e.go": true, "fixedbugs/issue7742.go": true, // type-checking doesn't terminate "fixedbugs/issue7746.go": true, // type-checking doesn't terminate - "fixedbugs/issue8501.go": true, // crashes - "fixedbugs/issue8507.go": true, // crashes }