mirror of https://github.com/golang/go.git
go/types, types2: use errorCause instead of reportf in comparableType
If the error cause is not further specified (empty string), avoid allocating a new errorCause. This makes using errorCauses as boolean signals efficient. While at it, fix an error message for incomparable arrays: report the array type rather than its underlying type. Change-Id: I844b18a76695330ca726932ee760aa89635f6a38 Reviewed-on: https://go-review.googlesource.com/c/go/+/654575 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
584e631023
commit
5af3658eaa
|
|
@ -621,11 +621,7 @@ func (check *Checker) incomparableCause(typ Type) string {
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -296,12 +296,12 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
|
|||
}
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -148,14 +148,15 @@ func isGeneric(t Type) bool {
|
|||
|
||||
// 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)
|
||||
|
|
@ -164,43 +165,43 @@ func comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(strin
|
|||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
|||
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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool
|
|||
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++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,12 @@ type errorCause struct {
|
|||
args []any
|
||||
}
|
||||
|
||||
var emptyErrorCause errorCause
|
||||
|
||||
func newErrorCause(format string, args ...any) *errorCause {
|
||||
if format == "" {
|
||||
return &emptyErrorCause
|
||||
}
|
||||
return &errorCause{format, args}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -610,11 +610,7 @@ func (check *Checker) incomparableCause(typ Type) string {
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -299,12 +299,12 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
|
|||
}
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -151,14 +151,15 @@ func isGeneric(t Type) bool {
|
|||
|
||||
// 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)
|
||||
|
|
@ -167,43 +168,43 @@ func comparableType(T Type, dynamic bool, seen map[Type]bool, reportf func(strin
|
|||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
|||
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
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +334,7 @@ func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool
|
|||
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++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,12 @@ type errorCause struct {
|
|||
args []any
|
||||
}
|
||||
|
||||
var emptyErrorCause errorCause
|
||||
|
||||
func newErrorCause(format string, args ...any) *errorCause {
|
||||
if format == "" {
|
||||
return &emptyErrorCause
|
||||
}
|
||||
return &errorCause{format, args}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ var (
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue