return compositeKind(typ) + " can only be compared to nil"
}
// see if we can extract a more specific error
- var cause string
- comparableType(typ, true, nil, func(format string, args ...interface{}) {
- cause = check.sprintf(format, args...)
- })
- return cause
+ return comparableType(typ, true, nil).format(check)
}
// If e != nil, it must be the shift expression; it may be nil for non-constant shifts.
}
// If T is comparable, V must be comparable.
// If V is strictly comparable, we're done.
- if comparableType(V, false /* strict comparability */, nil, nil) {
+ if comparableType(V, false /* strict comparability */, nil) == nil {
return true
}
// For constraint satisfaction, use dynamic (spec) comparability
// so that ordinary, non-type parameter interfaces implement comparable.
- if constraint && comparableType(V, true /* spec comparability */, nil, nil) {
+ if constraint && comparableType(V, true /* spec comparability */, nil) == nil {
// V is comparable if we are at Go 1.20 or higher.
if check == nil || check.allowVersion(go1_20) {
return true
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparableType(T, true, nil, nil)
+ return comparableType(T, true, nil) == nil
}
+// If T is comparable, comparableType returns nil.
+// Otherwise it returns an error cause explaining why T is not comparable.
// 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 comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparableType(T Type, dynamic bool, seen map[Type]bool) *errorCause {
if seen[T] {
- return true
+ return nil
}
if seen == nil {
seen = make(map[Type]bool)
switch t := under(T).(type) {
case *Basic:
- // assume invalid types to be comparable
- // to avoid follow-up errors
- return t.kind != UntypedNil
+ // assume invalid types to be comparable to avoid follow-up errors
+ if t.kind == UntypedNil {
+ return newErrorCause("")
+ }
+
case *Pointer, *Chan:
- return true
+ // always comparable
+
case *Struct:
for _, f := range t.fields {
- if !comparableType(f.typ, dynamic, seen, nil) {
- if reportf != nil {
- reportf("struct containing %s cannot be compared", f.typ)
- }
- return false
+ if comparableType(f.typ, dynamic, seen) != nil {
+ return newErrorCause("struct containing %s cannot be compared", f.typ)
}
}
- return true
+
case *Array:
- if !comparableType(t.elem, dynamic, seen, nil) {
- if reportf != nil {
- reportf("%s cannot be compared", t)
- }
- return false
+ if comparableType(t.elem, dynamic, seen) != nil {
+ return newErrorCause("%s cannot be compared", T)
}
- return true
+
case *Interface:
if dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen) {
- return true
+ return nil
}
- if reportf != nil {
- if t.typeSet().IsEmpty() {
- reportf("empty type set")
- } else {
- reportf("incomparable types in type set")
- }
+ var cause string
+ if t.typeSet().IsEmpty() {
+ cause = "empty type set"
+ } else {
+ cause = "incomparable types in type set"
}
- // fallthrough
+ return newErrorCause(cause)
+
+ default:
+ return newErrorCause("")
}
- return false
+
+ return nil
}
// hasNil reports whether type t includes the nil value.
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparableType(t.typ, false, seen, nil)
+ return t != nil && comparableType(t.typ, false, seen) == nil
})
}
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if comparableType(t.typ, false /* strictly comparable */, nil, nil) {
+ if comparableType(t.typ, false /* strictly comparable */, nil) == nil {
terms[i] = t
i++
}
args []any
}
+var emptyErrorCause errorCause
+
func newErrorCause(format string, args ...any) *errorCause {
+ if format == "" {
+ return &emptyErrorCause
+ }
return &errorCause{format, args}
}
return compositeKind(typ) + " can only be compared to nil"
}
// see if we can extract a more specific error
- var cause string
- comparableType(typ, true, nil, func(format string, args ...interface{}) {
- cause = check.sprintf(format, args...)
- })
- return cause
+ return comparableType(typ, true, nil).format(check)
}
// If e != nil, it must be the shift expression; it may be nil for non-constant shifts.
}
// If T is comparable, V must be comparable.
// If V is strictly comparable, we're done.
- if comparableType(V, false /* strict comparability */, nil, nil) {
+ if comparableType(V, false /* strict comparability */, nil) == nil {
return true
}
// For constraint satisfaction, use dynamic (spec) comparability
// so that ordinary, non-type parameter interfaces implement comparable.
- if constraint && comparableType(V, true /* spec comparability */, nil, nil) {
+ if constraint && comparableType(V, true /* spec comparability */, nil) == nil {
// V is comparable if we are at Go 1.20 or higher.
if check == nil || check.allowVersion(go1_20) {
return true
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- return comparableType(T, true, nil, nil)
+ return comparableType(T, true, nil) == nil
}
+// If T is comparable, comparableType returns nil.
+// Otherwise it returns an error cause explaining why T is not comparable.
// 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 comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
+func comparableType(T Type, dynamic bool, seen map[Type]bool) *errorCause {
if seen[T] {
- return true
+ return nil
}
if seen == nil {
seen = make(map[Type]bool)
switch t := under(T).(type) {
case *Basic:
- // assume invalid types to be comparable
- // to avoid follow-up errors
- return t.kind != UntypedNil
+ // assume invalid types to be comparable to avoid follow-up errors
+ if t.kind == UntypedNil {
+ return newErrorCause("")
+ }
+
case *Pointer, *Chan:
- return true
+ // always comparable
+
case *Struct:
for _, f := range t.fields {
- if !comparableType(f.typ, dynamic, seen, nil) {
- if reportf != nil {
- reportf("struct containing %s cannot be compared", f.typ)
- }
- return false
+ if comparableType(f.typ, dynamic, seen) != nil {
+ return newErrorCause("struct containing %s cannot be compared", f.typ)
}
}
- return true
+
case *Array:
- if !comparableType(t.elem, dynamic, seen, nil) {
- if reportf != nil {
- reportf("%s cannot be compared", t)
- }
- return false
+ if comparableType(t.elem, dynamic, seen) != nil {
+ return newErrorCause("%s cannot be compared", T)
}
- return true
+
case *Interface:
if dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen) {
- return true
+ return nil
}
- if reportf != nil {
- if t.typeSet().IsEmpty() {
- reportf("empty type set")
- } else {
- reportf("incomparable types in type set")
- }
+ var cause string
+ if t.typeSet().IsEmpty() {
+ cause = "empty type set"
+ } else {
+ cause = "incomparable types in type set"
}
- // fallthrough
+ return newErrorCause(cause)
+
+ default:
+ return newErrorCause("")
}
- return false
+
+ return nil
}
// hasNil reports whether type t includes the nil value.
return s.comparable
}
return s.is(func(t *term) bool {
- return t != nil && comparableType(t.typ, false, seen, nil)
+ return t != nil && comparableType(t.typ, false, seen) == nil
})
}
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if comparableType(t.typ, false /* strictly comparable */, nil, nil) {
+ if comparableType(t.typ, false /* strictly comparable */, nil) == nil {
terms[i] = t
i++
}
args []any
}
+var emptyErrorCause errorCause
+
func newErrorCause(format string, args ...any) *errorCause {
+ if format == "" {
+ return &emptyErrorCause
+ }
return &errorCause{format, args}
}
func _() {
_ = nil == nil // ERROR "operator == not defined on untyped nil"
_ = b == b
- _ = a /* ERROR "[10]func() cannot be compared" */ == a
+ _ = a /* ERROR "A cannot be compared" */ == a
_ = l /* ERROR "slice can only be compared to nil" */ == l
_ = s /* ERROR "struct containing []byte cannot be compared" */ == s
_ = p == p