1
0
mirror of https://github.com/golang/go synced 2024-11-26 08:58:09 -07:00

reflect: add Value.{Comparable,Equal}

For #46746

Change-Id: I879124974cdb55932cd9d07d3b384d49d5059857
Reviewed-on: https://go-review.googlesource.com/c/go/+/423794
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
cuiweixie 2022-08-14 16:45:33 +08:00 committed by Gopher Robot
parent 897ad2fe90
commit acabf87127
3 changed files with 451 additions and 0 deletions

2
api/next/46746.txt Normal file
View File

@ -0,0 +1,2 @@
pkg reflect, method (Value) Comparable() bool #46746
pkg reflect, method (Value) Equal(Value) bool #46746

View File

@ -8214,3 +8214,366 @@ func TestValue_Len(t *testing.T) {
t.Errorf("error is %q, want %q", e, wantStr)
}
}
func TestValue_Comparable(t *testing.T) {
var a int
var s []int
var i interface{} = a
var iSlice interface{} = s
var iArrayFalse interface{} = [2]interface{}{1, map[int]int{}}
var iArrayTrue interface{} = [2]interface{}{1, struct{ I interface{} }{1}}
var testcases = []struct {
value Value
comparable bool
deref bool
}{
{
ValueOf(32),
true,
false,
},
{
ValueOf(int8(1)),
true,
false,
},
{
ValueOf(int16(1)),
true,
false,
},
{
ValueOf(int32(1)),
true,
false,
},
{
ValueOf(int64(1)),
true,
false,
},
{
ValueOf(uint8(1)),
true,
false,
},
{
ValueOf(uint16(1)),
true,
false,
},
{
ValueOf(uint32(1)),
true,
false,
},
{
ValueOf(uint64(1)),
true,
false,
},
{
ValueOf(float32(1)),
true,
false,
},
{
ValueOf(float64(1)),
true,
false,
},
{
ValueOf(complex(float32(1), float32(1))),
true,
false,
},
{
ValueOf(complex(float64(1), float64(1))),
true,
false,
},
{
ValueOf("abc"),
true,
false,
},
{
ValueOf(true),
true,
false,
},
{
ValueOf(map[int]int{}),
false,
false,
},
{
ValueOf([]int{}),
false,
false,
},
{
Value{},
false,
false,
},
{
ValueOf(&a),
true,
false,
},
{
ValueOf(&s),
true,
false,
},
{
ValueOf(&i),
true,
true,
},
{
ValueOf(&iSlice),
false,
true,
},
{
ValueOf([2]int{}),
true,
false,
},
{
ValueOf([2]map[int]int{}),
false,
false,
},
{
ValueOf([0]func(){}),
false,
false,
},
{
ValueOf([2]struct{ I interface{} }{{1}, {1}}),
true,
false,
},
{
ValueOf([2]struct{ I interface{} }{{[]int{}}, {1}}),
false,
false,
},
{
ValueOf([2]interface{}{1, struct{ I int }{1}}),
true,
false,
},
{
ValueOf([2]interface{}{[1]interface{}{map[int]int{}}, struct{ I int }{1}}),
false,
false,
},
{
ValueOf(&iArrayFalse),
false,
true,
},
{
ValueOf(&iArrayTrue),
true,
true,
},
}
for _, cas := range testcases {
v := cas.value
if cas.deref {
v = v.Elem()
}
got := v.Comparable()
if got != cas.comparable {
t.Errorf("%T.Comparable = %t, want %t", v, got, cas.comparable)
}
}
}
type ValueEqualTest struct {
v, u any
eq bool
vDeref, uDeref bool
}
var equalI interface{} = 1
var equalSlice interface{} = []int{1}
var nilInterface interface{}
var mapInterface interface{} = map[int]int{}
var valueEqualTests = []ValueEqualTest{
{
Value{}, Value{},
true,
false, false,
},
{
true, true,
true,
false, false,
},
{
1, 1,
true,
false, false,
},
{
int8(1), int8(1),
true,
false, false,
},
{
int16(1), int16(1),
true,
false, false,
},
{
int32(1), int32(1),
true,
false, false,
},
{
int64(1), int64(1),
true,
false, false,
},
{
uint(1), uint(1),
true,
false, false,
},
{
uint8(1), uint8(1),
true,
false, false,
},
{
uint16(1), uint16(1),
true,
false, false,
},
{
uint32(1), uint32(1),
true,
false, false,
},
{
uint64(1), uint64(1),
true,
false, false,
},
{
float32(1), float32(1),
true,
false, false,
},
{
float64(1), float64(1),
true,
false, false,
},
{
complex(1, 1), complex(1, 1),
true,
false, false,
},
{
complex128(1 + 1i), complex128(1 + 1i),
true,
false, false,
},
{
func() {}, nil,
false,
false, false,
},
{
&equalI, 1,
true,
true, false,
},
{
&equalSlice, []int{1},
false,
true, false,
},
{
map[int]int{}, map[int]int{},
false,
false, false,
},
{
(chan int)(nil), nil,
false,
false, false,
},
{
(chan int)(nil), (chan int)(nil),
true,
false, false,
},
{
&equalI, &equalI,
true,
false, false,
},
{
struct{ i int }{1}, struct{ i int }{1},
true,
false, false,
},
{
struct{ i int }{1}, struct{ i int }{2},
false,
false, false,
},
{
&nilInterface, &nilInterface,
true,
true, true,
},
{
1, ValueOf(struct{ i int }{1}).Field(0),
true,
false, false,
},
{
&mapInterface, &mapInterface,
false,
true, true,
},
}
func TestValue_Equal(t *testing.T) {
for _, test := range valueEqualTests {
var v, u Value
if vv, ok := test.v.(Value); ok {
v = vv
} else {
v = ValueOf(test.v)
}
if uu, ok := test.u.(Value); ok {
u = uu
} else {
u = ValueOf(test.u)
}
if test.vDeref {
v = v.Elem()
}
if test.uDeref {
u = u.Elem()
}
if r := v.Equal(u); r != test.eq {
t.Errorf("%s == %s got %t, want %t", v.Type(), u.Type(), r, test.eq)
}
}
}

View File

@ -3246,6 +3246,92 @@ func (v Value) CanConvert(t Type) bool {
return true
}
// Comparable reports whether the type of v is comparable.
// If the type of v is an interface, this checks the dynamic type.
// If this reports true then v.Interface() == x will not panic for any x.
func (v Value) Comparable() bool {
k := v.Kind()
switch k {
case Invalid:
return false
case Bool,
Int, Int8, Int16, Int32, Int64,
Uint, Uint8, Uint16, Uint32, Uint64,
Uintptr,
Float32, Float64, Complex64, Complex128,
Chan:
return true
case Array:
if v.Type().Len() == 0 {
return v.Type().Comparable()
}
switch v.Type().Elem().Kind() {
case Interface, Array, Struct:
for i := 0; i < v.Type().Len(); i++ {
if !v.Index(i).Comparable() {
return false
}
}
default:
return v.Index(0).Comparable()
}
return true
case Func:
return false
case Interface:
return v.Elem().Comparable()
case Map:
return false
case Pointer:
return true
case Slice:
return false
case String:
return true
case Struct:
for i := 0; i < v.NumField(); i++ {
if !v.Field(i).Comparable() {
return false
}
}
return true
case UnsafePointer:
return true
default:
return false
}
}
// Equal reports true if v is equal to u.
func (v Value) Equal(u Value) bool {
if !v.IsValid() || !u.IsValid() {
return v.IsValid() == u.IsValid()
}
if v.Comparable() || u.Comparable() {
return valueInterface(v, false) == valueInterface(u, false)
}
if u.Kind() == Interface && v.kind() == Interface { // this case is for nil interface value
return v.Elem().Equal(u.Elem())
}
return false
}
// convertOp returns the function to convert a value of type src
// to a value of type dst. If the conversion is illegal, convertOp returns nil.
func convertOp(dst, src *rtype) func(Value, Type) Value {