]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: pass the seen map through _TypeSet.IsComparable
authorRobert Findley <rfindley@google.com>
Mon, 24 Jan 2022 20:42:52 +0000 (15:42 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 24 Jan 2022 22:28:27 +0000 (22:28 +0000)
While checking comparability of type parameters, we recurse through
_TypeSet.IsComparable, but do not pass the cycle-tracking seen map,
resulting in infinite recursion in some cases.

Refactor to pass the seen map through this recursion.

Fixes #50782

Change-Id: I2c2bcfed3398c11eb9aa0c871da59e348bfba5f7
Reviewed-on: https://go-review.googlesource.com/c/go/+/380504
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50782.go2 [new file with mode: 0644]
src/cmd/compile/internal/types2/typeset.go
src/go/types/interface.go
src/go/types/predicates.go
src/go/types/testdata/fixedbugs/issue50782.go2 [new file with mode: 0644]
src/go/types/typeset.go

index 4ce75c476c4c11d3831a4c2acb196c7ace0d2597..ca5140d09272d3d45b918d79aeeedb4bbaf01137 100644 (file)
@@ -86,7 +86,7 @@ func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
 func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
 
 // IsComparable reports whether each type in interface t's type set is comparable.
-func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
+func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
 
 // IsMethodSet reports whether the interface t is fully described by its method set.
 func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }
index d982866f8ed8e7f7ff0bb75aaf749749e04c7fc1..cc3c76e69511b4376604e4e7707cda06960f4855 100644 (file)
@@ -131,7 +131,7 @@ func comparable(T Type, seen map[Type]bool) bool {
        case *Array:
                return comparable(t.elem, seen)
        case *Interface:
-               return !isTypeParam(T) || t.IsComparable()
+               return !isTypeParam(T) || t.typeSet().IsComparable(seen)
        }
        return false
 }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50782.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50782.go2
new file mode 100644 (file)
index 0000000..8f41b84
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+// The first example from the issue.
+type Numeric interface {
+       ~int | ~int8 | ~int16 | ~int32 | ~int64
+}
+
+// numericAbs matches numeric types with an Abs method.
+type numericAbs[T Numeric] interface {
+       ~struct{ Value T }
+       Abs() T
+}
+
+// AbsDifference computes the absolute value of the difference of
+// a and b, where the absolute value is determined by the Abs method.
+func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T {
+       // TODO: the error below should probably be positioned on the '-'.
+       d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
+       return d.Abs()
+}
+
+// The second example from the issue.
+type T[P int] struct{ f P }
+
+func _[P T[P /* ERROR "P does not implement int" */ ]]() {}
+
+// Additional tests
+func _[P T[T /* ERROR "T\[P\] does not implement int" */ [P /* ERROR "P does not implement int" */ ]]]() {}
+func _[P T[Q /* ERROR "Q does not implement int" */ ], Q T[P /* ERROR "P does not implement int" */ ]]() {}
+func _[P T[Q], Q int]() {}
+
+type C[P comparable] struct{ f P }
+func _[P C[C[P]]]() {}
+func _[P C[C /* ERROR "C\[Q\] does not implement comparable" */ [Q /* ERROR "Q does not implement comparable" */]], Q func()]() {}
+func _[P [10]C[P]]() {}
+func _[P struct{ f C[C[P]]}]() {}
index 8670c17861f16e66c8bb6b174a8cf7d1df4ff8c7..348b8150d37accaa957d1ec064bfd5813d2778ba 100644 (file)
@@ -34,12 +34,12 @@ func (s *_TypeSet) IsAll() bool {
 func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
 
 // IsComparable reports whether each type in the set is comparable.
-func (s *_TypeSet) IsComparable() bool {
+func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
        if s.terms.isAll() {
                return s.comparable
        }
        return s.is(func(t *term) bool {
-               return t != nil && Comparable(t.typ)
+               return t != nil && comparable(t.typ, seen)
        })
 }
 
index 1ff90157804366acac2503a1996781b9468433ea..b9d4660eb46630be9eb4dd3f9e9cb6936a55dbcb 100644 (file)
@@ -111,7 +111,7 @@ func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
 func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
 
 // IsComparable reports whether each type in interface t's type set is comparable.
-func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
+func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
 
 // IsMethodSet reports whether the interface t is fully described by its method
 // set.
index 1202db4049e822f90820a621b1c4875d41d20a51..1ba0043327de6f2e56698c6aaf03992f15dd937c 100644 (file)
@@ -133,7 +133,7 @@ func comparable(T Type, seen map[Type]bool) bool {
        case *Array:
                return comparable(t.elem, seen)
        case *Interface:
-               return !isTypeParam(T) || t.IsComparable()
+               return !isTypeParam(T) || t.typeSet().IsComparable(seen)
        }
        return false
 }
diff --git a/src/go/types/testdata/fixedbugs/issue50782.go2 b/src/go/types/testdata/fixedbugs/issue50782.go2
new file mode 100644 (file)
index 0000000..8f41b84
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+// The first example from the issue.
+type Numeric interface {
+       ~int | ~int8 | ~int16 | ~int32 | ~int64
+}
+
+// numericAbs matches numeric types with an Abs method.
+type numericAbs[T Numeric] interface {
+       ~struct{ Value T }
+       Abs() T
+}
+
+// AbsDifference computes the absolute value of the difference of
+// a and b, where the absolute value is determined by the Abs method.
+func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T {
+       // TODO: the error below should probably be positioned on the '-'.
+       d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
+       return d.Abs()
+}
+
+// The second example from the issue.
+type T[P int] struct{ f P }
+
+func _[P T[P /* ERROR "P does not implement int" */ ]]() {}
+
+// Additional tests
+func _[P T[T /* ERROR "T\[P\] does not implement int" */ [P /* ERROR "P does not implement int" */ ]]]() {}
+func _[P T[Q /* ERROR "Q does not implement int" */ ], Q T[P /* ERROR "P does not implement int" */ ]]() {}
+func _[P T[Q], Q int]() {}
+
+type C[P comparable] struct{ f P }
+func _[P C[C[P]]]() {}
+func _[P C[C /* ERROR "C\[Q\] does not implement comparable" */ [Q /* ERROR "Q does not implement comparable" */]], Q func()]() {}
+func _[P [10]C[P]]() {}
+func _[P struct{ f C[C[P]]}]() {}
index 3739cd83d6c4e6866a9f5c757623f9a2252fd526..2317177f034d0e997e80450d761b9ad9aef7ac36 100644 (file)
@@ -32,12 +32,12 @@ func (s *_TypeSet) IsAll() bool { return !s.comparable && len(s.methods) == 0 &&
 func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
 
 // IsComparable reports whether each type in the set is comparable.
-func (s *_TypeSet) IsComparable() bool {
+func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
        if s.terms.isAll() {
                return s.comparable
        }
        return s.is(func(t *term) bool {
-               return t != nil && Comparable(t.typ)
+               return t != nil && comparable(t.typ, seen)
        })
 }