Skip to content

Commit 84eefdc

Browse files
committed
go/types, types2: pass the seen map through _TypeSet.IsComparable
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 <[email protected]> Run-TryBot: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 671e115 commit 84eefdc

File tree

8 files changed

+88
-8
lines changed

8 files changed

+88
-8
lines changed

src/cmd/compile/internal/types2/interface.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
8686
func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
8787

8888
// IsComparable reports whether each type in interface t's type set is comparable.
89-
func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
89+
func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
9090

9191
// IsMethodSet reports whether the interface t is fully described by its method set.
9292
func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }

src/cmd/compile/internal/types2/predicates.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func comparable(T Type, seen map[Type]bool) bool {
131131
case *Array:
132132
return comparable(t.elem, seen)
133133
case *Interface:
134-
return !isTypeParam(T) || t.IsComparable()
134+
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
135135
}
136136
return false
137137
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// The first example from the issue.
8+
type Numeric interface {
9+
~int | ~int8 | ~int16 | ~int32 | ~int64
10+
}
11+
12+
// numericAbs matches numeric types with an Abs method.
13+
type numericAbs[T Numeric] interface {
14+
~struct{ Value T }
15+
Abs() T
16+
}
17+
18+
// AbsDifference computes the absolute value of the difference of
19+
// a and b, where the absolute value is determined by the Abs method.
20+
func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T {
21+
// TODO: the error below should probably be positioned on the '-'.
22+
d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
23+
return d.Abs()
24+
}
25+
26+
// The second example from the issue.
27+
type T[P int] struct{ f P }
28+
29+
func _[P T[P /* ERROR "P does not implement int" */ ]]() {}
30+
31+
// Additional tests
32+
func _[P T[T /* ERROR "T\[P\] does not implement int" */ [P /* ERROR "P does not implement int" */ ]]]() {}
33+
func _[P T[Q /* ERROR "Q does not implement int" */ ], Q T[P /* ERROR "P does not implement int" */ ]]() {}
34+
func _[P T[Q], Q int]() {}
35+
36+
type C[P comparable] struct{ f P }
37+
func _[P C[C[P]]]() {}
38+
func _[P C[C /* ERROR "C\[Q\] does not implement comparable" */ [Q /* ERROR "Q does not implement comparable" */]], Q func()]() {}
39+
func _[P [10]C[P]]() {}
40+
func _[P struct{ f C[C[P]]}]() {}

src/cmd/compile/internal/types2/typeset.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func (s *_TypeSet) IsAll() bool {
3434
func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
3535

3636
// IsComparable reports whether each type in the set is comparable.
37-
func (s *_TypeSet) IsComparable() bool {
37+
func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
3838
if s.terms.isAll() {
3939
return s.comparable
4040
}
4141
return s.is(func(t *term) bool {
42-
return t != nil && Comparable(t.typ)
42+
return t != nil && comparable(t.typ, seen)
4343
})
4444
}
4545

src/go/types/interface.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
111111
func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
112112

113113
// IsComparable reports whether each type in interface t's type set is comparable.
114-
func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
114+
func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
115115

116116
// IsMethodSet reports whether the interface t is fully described by its method
117117
// set.

src/go/types/predicates.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func comparable(T Type, seen map[Type]bool) bool {
133133
case *Array:
134134
return comparable(t.elem, seen)
135135
case *Interface:
136-
return !isTypeParam(T) || t.IsComparable()
136+
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
137137
}
138138
return false
139139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// The first example from the issue.
8+
type Numeric interface {
9+
~int | ~int8 | ~int16 | ~int32 | ~int64
10+
}
11+
12+
// numericAbs matches numeric types with an Abs method.
13+
type numericAbs[T Numeric] interface {
14+
~struct{ Value T }
15+
Abs() T
16+
}
17+
18+
// AbsDifference computes the absolute value of the difference of
19+
// a and b, where the absolute value is determined by the Abs method.
20+
func absDifference[T numericAbs[T /* ERROR T does not implement Numeric */]](a, b T) T {
21+
// TODO: the error below should probably be positioned on the '-'.
22+
d := a /* ERROR "invalid operation: operator - not defined" */ .Value - b.Value
23+
return d.Abs()
24+
}
25+
26+
// The second example from the issue.
27+
type T[P int] struct{ f P }
28+
29+
func _[P T[P /* ERROR "P does not implement int" */ ]]() {}
30+
31+
// Additional tests
32+
func _[P T[T /* ERROR "T\[P\] does not implement int" */ [P /* ERROR "P does not implement int" */ ]]]() {}
33+
func _[P T[Q /* ERROR "Q does not implement int" */ ], Q T[P /* ERROR "P does not implement int" */ ]]() {}
34+
func _[P T[Q], Q int]() {}
35+
36+
type C[P comparable] struct{ f P }
37+
func _[P C[C[P]]]() {}
38+
func _[P C[C /* ERROR "C\[Q\] does not implement comparable" */ [Q /* ERROR "Q does not implement comparable" */]], Q func()]() {}
39+
func _[P [10]C[P]]() {}
40+
func _[P struct{ f C[C[P]]}]() {}

src/go/types/typeset.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ func (s *_TypeSet) IsAll() bool { return !s.comparable && len(s.methods) == 0 &&
3232
func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
3333

3434
// IsComparable reports whether each type in the set is comparable.
35-
func (s *_TypeSet) IsComparable() bool {
35+
func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
3636
if s.terms.isAll() {
3737
return s.comparable
3838
}
3939
return s.is(func(t *term) bool {
40-
return t != nil && Comparable(t.typ)
40+
return t != nil && comparable(t.typ, seen)
4141
})
4242
}
4343

0 commit comments

Comments
 (0)