Skip to content

Commit a343f40

Browse files
ianlancetaylorgopherbot
authored andcommitted
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 <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent a749850 commit a343f40

File tree

2 files changed

+64
-9
lines changed

2 files changed

+64
-9
lines changed

src/reflect/all_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8335,7 +8335,7 @@ func TestValue_EqualNonComparable(t *testing.T) {
83358335
}
83368336
for _, value := range values {
83378337
// Panic when reflect.Value.Equal using two valid non-comparable values.
8338-
shouldPanic("reflect.Value.Equal using two non-comparable values", func() { value.Equal(value) })
8338+
shouldPanic("are not comparable", func() { value.Equal(value) })
83398339

83408340
// If one is non-comparable and the other is invalid, the expected result is always false.
83418341
if r := value.Equal(invalid); r != false {

src/reflect/value.go

+63-8
Original file line numberDiff line numberDiff line change
@@ -3320,22 +3320,77 @@ func (v Value) Comparable() bool {
33203320
}
33213321

33223322
// Equal reports true if v is equal to u.
3323-
// For valid values, if one of them is non-comparable, and the other is comparable,
3324-
// Equal reports false; if v and u are both non-comparable, Equal will panic.
3323+
// For two invalid values, Equal will report true.
3324+
// For an interface value, Equal will compare the value within the interface.
3325+
// Otherwise, If the values have different types, Equal will report false.
3326+
// Otherwise, for arrays and structs Equal will compare each element in order,
3327+
// and report false if it finds non-equal elements.
3328+
// During all comparisons, if values of the same type are compared,
3329+
// and the type is not comparable, Equal will panic.
33253330
func (v Value) Equal(u Value) bool {
3331+
if v.Kind() == Interface {
3332+
v = v.Elem()
3333+
}
3334+
if u.Kind() == Interface {
3335+
u = u.Elem()
3336+
}
3337+
33263338
if !v.IsValid() || !u.IsValid() {
33273339
return v.IsValid() == u.IsValid()
33283340
}
33293341

3330-
if v.Comparable() || u.Comparable() {
3331-
return valueInterface(v, false) == valueInterface(u, false)
3342+
if v.Kind() != u.Kind() || v.Type() != u.Type() {
3343+
return false
33323344
}
33333345

3334-
if u.Kind() == Interface && v.kind() == Interface { // this case is for nil interface value
3335-
return v.Elem().Equal(u.Elem())
3346+
// Handle ach Kind directly rather than calling valueInterface
3347+
// to avoid allocating.
3348+
switch v.Kind() {
3349+
default:
3350+
panic("reflect.Value.Equal: invalid Kind")
3351+
case Bool:
3352+
return v.Bool() == u.Bool()
3353+
case Int, Int8, Int16, Int32, Int64:
3354+
return v.Int() == u.Int()
3355+
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
3356+
return v.Uint() == u.Uint()
3357+
case Float32, Float64:
3358+
return v.Float() == u.Float()
3359+
case Complex64, Complex128:
3360+
return v.Complex() == u.Complex()
3361+
case String:
3362+
return v.String() == u.String()
3363+
case Chan, Pointer, UnsafePointer:
3364+
return v.Pointer() == u.Pointer()
3365+
case Array:
3366+
// u and v have the same type so they have the same length
3367+
vl := v.Len()
3368+
if vl == 0 {
3369+
// panic on [0]func()
3370+
if !v.Type().Elem().Comparable() {
3371+
break
3372+
}
3373+
return true
3374+
}
3375+
for i := 0; i < vl; i++ {
3376+
if !v.Index(i).Equal(u.Index(i)) {
3377+
return false
3378+
}
3379+
}
3380+
return true
3381+
case Struct:
3382+
// u and v have the same type so they have the same fields
3383+
nf := v.NumField()
3384+
for i := 0; i < nf; i++ {
3385+
if !v.Field(i).Equal(u.Field(i)) {
3386+
return false
3387+
}
3388+
}
3389+
return true
3390+
case Func, Map, Slice:
3391+
break
33363392
}
3337-
3338-
panic("reflect.Value.Equal using two non-comparable values")
3393+
panic("reflect.Value.Equal: values of type " + v.Type().String() + " are not comparable")
33393394
}
33403395

33413396
// convertOp returns the function to convert a value of type src

0 commit comments

Comments
 (0)