1
0
mirror of https://github.com/golang/go synced 2024-11-17 16:04:47 -07:00

reflect: rewrite value.Equal to avoid allocations

For #46746

Change-Id: I75ddb9ce24cd3394186562dae156fef9fe2d55d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/447798
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Ian Lance Taylor 2022-11-03 18:15:42 -07:00 committed by Gopher Robot
parent a74985092c
commit a343f4017b
2 changed files with 64 additions and 9 deletions

View File

@ -8335,7 +8335,7 @@ func TestValue_EqualNonComparable(t *testing.T) {
}
for _, value := range values {
// Panic when reflect.Value.Equal using two valid non-comparable values.
shouldPanic("reflect.Value.Equal using two non-comparable values", func() { value.Equal(value) })
shouldPanic("are not comparable", func() { value.Equal(value) })
// If one is non-comparable and the other is invalid, the expected result is always false.
if r := value.Equal(invalid); r != false {

View File

@ -3320,22 +3320,77 @@ func (v Value) Comparable() bool {
}
// Equal reports true if v is equal to u.
// For valid values, if one of them is non-comparable, and the other is comparable,
// Equal reports false; if v and u are both non-comparable, Equal will panic.
// For two invalid values, Equal will report true.
// For an interface value, Equal will compare the value within the interface.
// Otherwise, If the values have different types, Equal will report false.
// Otherwise, for arrays and structs Equal will compare each element in order,
// and report false if it finds non-equal elements.
// During all comparisons, if values of the same type are compared,
// and the type is not comparable, Equal will panic.
func (v Value) Equal(u Value) bool {
if v.Kind() == Interface {
v = v.Elem()
}
if u.Kind() == Interface {
u = u.Elem()
}
if !v.IsValid() || !u.IsValid() {
return v.IsValid() == u.IsValid()
}
if v.Comparable() || u.Comparable() {
return valueInterface(v, false) == valueInterface(u, false)
if v.Kind() != u.Kind() || v.Type() != u.Type() {
return false
}
if u.Kind() == Interface && v.kind() == Interface { // this case is for nil interface value
return v.Elem().Equal(u.Elem())
// Handle ach Kind directly rather than calling valueInterface
// to avoid allocating.
switch v.Kind() {
default:
panic("reflect.Value.Equal: invalid Kind")
case Bool:
return v.Bool() == u.Bool()
case Int, Int8, Int16, Int32, Int64:
return v.Int() == u.Int()
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return v.Uint() == u.Uint()
case Float32, Float64:
return v.Float() == u.Float()
case Complex64, Complex128:
return v.Complex() == u.Complex()
case String:
return v.String() == u.String()
case Chan, Pointer, UnsafePointer:
return v.Pointer() == u.Pointer()
case Array:
// u and v have the same type so they have the same length
vl := v.Len()
if vl == 0 {
// panic on [0]func()
if !v.Type().Elem().Comparable() {
break
}
return true
}
for i := 0; i < vl; i++ {
if !v.Index(i).Equal(u.Index(i)) {
return false
}
}
return true
case Struct:
// u and v have the same type so they have the same fields
nf := v.NumField()
for i := 0; i < nf; i++ {
if !v.Field(i).Equal(u.Field(i)) {
return false
}
}
return true
case Func, Map, Slice:
break
}
panic("reflect.Value.Equal using two non-comparable values")
panic("reflect.Value.Equal: values of type " + v.Type().String() + " are not comparable")
}
// convertOp returns the function to convert a value of type src