From: cuiweixie Date: Sun, 14 Aug 2022 08:45:33 +0000 (+0800) Subject: reflect: add Value.{Comparable,Equal} X-Git-Tag: go1.20rc1~1378 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=acabf87127fab93513df46811d5f473ef72905b1;p=gostls13.git reflect: add Value.{Comparable,Equal} For #46746 Change-Id: I879124974cdb55932cd9d07d3b384d49d5059857 Reviewed-on: https://go-review.googlesource.com/c/go/+/423794 Reviewed-by: Cherry Mui Run-TryBot: Dmitri Shuralyov TryBot-Result: Gopher Robot Reviewed-by: Dmitri Shuralyov Auto-Submit: Dmitri Shuralyov Reviewed-by: Ian Lance Taylor --- diff --git a/api/next/46746.txt b/api/next/46746.txt new file mode 100644 index 0000000000..ae07682b34 --- /dev/null +++ b/api/next/46746.txt @@ -0,0 +1,2 @@ +pkg reflect, method (Value) Comparable() bool #46746 +pkg reflect, method (Value) Equal(Value) bool #46746 \ No newline at end of file diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index fe62407412..0398a5099d 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -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) + } + } +} diff --git a/src/reflect/value.go b/src/reflect/value.go index 6ab6cc8b23..9c8b82c41e 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -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 {