}
// see if we can extract a more specific error
var cause string
- comparable(typ, nil, func(format string, args ...interface{}) {
+ comparable(typ, true, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
// If T is comparable, V must be comparable.
// Remember as a pending error and report only if we don't have a more specific error.
var pending error
- if Ti.IsComparable() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
+ if Ti.IsComparable() && !comparable(V, false, nil, nil) {
pending = errorf("%s does not implement comparable", V)
}
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparable(T, nil, nil)
+ return comparable(T, true, nil, nil)
}
+// If dynamic is set, non-type parameter interfaces are always comparable.
// If reportf != nil, it may be used to report why T is not comparable.
-func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
return true
case *Struct:
for _, f := range t.fields {
- if !comparable(f.typ, seen, nil) {
+ if !comparable(f.typ, dynamic, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
}
return true
case *Array:
- if !comparable(t.elem, seen, nil) {
+ if !comparable(t.elem, dynamic, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
}
return true
case *Interface:
- return !isTypeParam(T) || t.typeSet().IsComparable(seen)
+ return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
}
return false
}
--- /dev/null
+// 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
+
+func f[_ comparable]() {}
+
+type S1 struct{ x int }
+type S2 struct{ x any }
+type S3 struct{ x [10]interface{ m() } }
+
+func _[P1 comparable, P2 S2]() {
+ _ = f[S1]
+ _ = f[S2 /* ERROR S2 does not implement comparable */ ]
+ _ = f[S3 /* ERROR S3 does not implement comparable */ ]
+
+ type L1 struct { x P1 }
+ type L2 struct { x P2 }
+ _ = f[L1]
+ _ = f[L2 /* ERROR L2 does not implement comparable */ ]
+}
+
+
+// example from issue
+
+type Set[T comparable] map[T]struct{}
+
+func NewSetFromSlice[T comparable](items []T) *Set[T] {
+ s := Set[T]{}
+
+ for _, item := range items {
+ s[item] = struct{}{}
+ }
+
+ return &s
+}
+
+type T struct{ x any }
+
+func main() {
+ NewSetFromSlice( /* ERROR T does not implement comparable */ []T{
+ {"foo"},
+ {5},
+ })
+}
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparable(t.typ, seen, nil)
+ return t != nil && comparable(t.typ, false, seen, nil)
})
}
}
// see if we can extract a more specific error
var cause string
- comparable(typ, nil, func(format string, args ...interface{}) {
+ comparable(typ, true, nil, func(format string, args ...interface{}) {
cause = check.sprintf(format, args...)
})
return cause
// If T is comparable, V must be comparable.
// Remember as a pending error and report only if we don't have a more specific error.
var pending error
- if Ti.IsComparable() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
+ if Ti.IsComparable() && !comparable(V, false, nil, nil) {
pending = errorf("%s does not implement comparable", V)
}
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparable(T, nil, nil)
+ return comparable(T, true, nil, nil)
}
+// If dynamic is set, non-type parameter interfaces are always comparable.
// If reportf != nil, it may be used to report why T is not comparable.
-func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
if seen[T] {
return true
}
return true
case *Struct:
for _, f := range t.fields {
- if !comparable(f.typ, seen, nil) {
+ if !comparable(f.typ, dynamic, seen, nil) {
if reportf != nil {
reportf("struct containing %s cannot be compared", f.typ)
}
}
return true
case *Array:
- if !comparable(t.elem, seen, nil) {
+ if !comparable(t.elem, dynamic, seen, nil) {
if reportf != nil {
reportf("%s cannot be compared", t)
}
}
return true
case *Interface:
- return !isTypeParam(T) || t.typeSet().IsComparable(seen)
+ return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
}
return false
}
--- /dev/null
+// 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
+
+func f[_ comparable]() {}
+
+type S1 struct{ x int }
+type S2 struct{ x any }
+type S3 struct{ x [10]interface{ m() } }
+
+func _[P1 comparable, P2 S2]() {
+ _ = f[S1]
+ _ = f[S2 /* ERROR S2 does not implement comparable */ ]
+ _ = f[S3 /* ERROR S3 does not implement comparable */ ]
+
+ type L1 struct { x P1 }
+ type L2 struct { x P2 }
+ _ = f[L1]
+ _ = f[L2 /* ERROR L2 does not implement comparable */ ]
+}
+
+
+// example from issue
+
+type Set[T comparable] map[T]struct{}
+
+func NewSetFromSlice[T comparable](items []T) *Set[T] {
+ s := Set[T]{}
+
+ for _, item := range items {
+ s[item] = struct{}{}
+ }
+
+ return &s
+}
+
+type T struct{ x any }
+
+func main() {
+ NewSetFromSlice /* ERROR T does not implement comparable */ ([]T{
+ {"foo"},
+ {5},
+ })
+}
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparable(t.typ, seen, nil)
+ return t != nil && comparable(t.typ, false, seen, nil)
})
}