mirror of https://github.com/golang/go.git
go/ssa/interp: Adds reflect.DeepEqual model for interp testing
Implements reflect.DeepEqual() for testing interp packages. Adds test that exercises this. Fixes a nil pointer dereference in ext۰reflect۰Value۰Elem in interp. Updates golang/go#48525 Change-Id: Ib32e96d37aa5c85cefe59e9514d4e162d7f7011e Reviewed-on: https://go-review.googlesource.com/c/tools/+/391314 Reviewed-by: Robert Findley <rfindley@google.com> Run-TryBot: Tim King <taking@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Trust: Tim King <taking@google.com>
This commit is contained in:
parent
e342718dfc
commit
761e51fea3
|
|
@ -111,6 +111,7 @@ var testdataTests = []string{
|
|||
"complit.go",
|
||||
"convert.go",
|
||||
"coverage.go",
|
||||
"deepequal.go",
|
||||
"defer.go",
|
||||
"fieldprom.go",
|
||||
"ifaceconv.go",
|
||||
|
|
|
|||
|
|
@ -407,7 +407,11 @@ func ext۰reflect۰Value۰Elem(fr *frame, args []value) value {
|
|||
case iface:
|
||||
return makeReflectValue(x.t, x.v)
|
||||
case *value:
|
||||
return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), *x)
|
||||
var v value
|
||||
if x != nil {
|
||||
v = *x
|
||||
}
|
||||
return makeReflectValue(rV2T(args[0]).t.Underlying().(*types.Pointer).Elem(), v)
|
||||
default:
|
||||
panic(fmt.Sprintf("reflect.(Value).Elem(%T)", x))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
// This interpreter test is designed to test the test copy of DeepEqual.
|
||||
//
|
||||
// Validate this file with 'go run' after editing.
|
||||
|
||||
package main
|
||||
|
||||
import "reflect"
|
||||
|
||||
func assert(cond bool) {
|
||||
if !cond {
|
||||
panic("failed")
|
||||
}
|
||||
}
|
||||
|
||||
type X int
|
||||
type Y struct {
|
||||
y *Y
|
||||
z [3]int
|
||||
}
|
||||
|
||||
var (
|
||||
a = []int{0, 1, 2, 3}
|
||||
b = []X{0, 1, 2, 3}
|
||||
c = map[int]string{0: "zero", 1: "one"}
|
||||
d = map[X]string{0: "zero", 1: "one"}
|
||||
e = &Y{}
|
||||
f = (*Y)(nil)
|
||||
g = &Y{y: e}
|
||||
h *Y
|
||||
)
|
||||
|
||||
func init() {
|
||||
h = &Y{} // h->h
|
||||
h.y = h
|
||||
}
|
||||
|
||||
func main() {
|
||||
assert(reflect.DeepEqual(nil, nil))
|
||||
assert(reflect.DeepEqual((*int)(nil), (*int)(nil)))
|
||||
assert(!reflect.DeepEqual(nil, (*int)(nil)))
|
||||
|
||||
assert(reflect.DeepEqual(0, 0))
|
||||
assert(!reflect.DeepEqual(0, int64(0)))
|
||||
|
||||
assert(!reflect.DeepEqual("", 0))
|
||||
|
||||
assert(reflect.DeepEqual(a, []int{0, 1, 2, 3}))
|
||||
assert(!reflect.DeepEqual(a, []int{0, 1, 2}))
|
||||
assert(!reflect.DeepEqual(a, []int{0, 1, 0, 3}))
|
||||
|
||||
assert(reflect.DeepEqual(b, []X{0, 1, 2, 3}))
|
||||
assert(!reflect.DeepEqual(b, []X{0, 1, 0, 3}))
|
||||
|
||||
assert(reflect.DeepEqual(c, map[int]string{0: "zero", 1: "one"}))
|
||||
assert(!reflect.DeepEqual(c, map[int]string{0: "zero", 1: "one", 2: "two"}))
|
||||
assert(!reflect.DeepEqual(c, map[int]string{1: "one", 2: "two"}))
|
||||
assert(!reflect.DeepEqual(c, map[int]string{1: "one"}))
|
||||
|
||||
assert(reflect.DeepEqual(d, map[X]string{0: "zero", 1: "one"}))
|
||||
assert(!reflect.DeepEqual(d, map[int]string{0: "zero", 1: "one"}))
|
||||
|
||||
assert(reflect.DeepEqual(e, &Y{}))
|
||||
assert(reflect.DeepEqual(e, &Y{z: [3]int{0, 0, 0}}))
|
||||
assert(!reflect.DeepEqual(e, &Y{z: [3]int{0, 1, 0}}))
|
||||
|
||||
assert(reflect.DeepEqual(f, (*Y)(nil)))
|
||||
assert(!reflect.DeepEqual(f, nil))
|
||||
|
||||
// eq_h -> eq_h. Pointer structure and elements are equal so DeepEqual.
|
||||
eq_h := &Y{}
|
||||
eq_h.y = eq_h
|
||||
assert(reflect.DeepEqual(h, eq_h))
|
||||
|
||||
// deepeq_h->h->h. Pointed to elem of (deepeq_h, h) are (h,h). (h,h) are deep equal so h and deepeq_h are DeepEqual.
|
||||
deepeq_h := &Y{}
|
||||
deepeq_h.y = h
|
||||
assert(reflect.DeepEqual(h, deepeq_h))
|
||||
|
||||
distinct := []interface{}{a, b, c, d, e, f, g, h}
|
||||
for x := range distinct {
|
||||
for y := range distinct {
|
||||
assert((x == y) == reflect.DeepEqual(distinct[x], distinct[y]))
|
||||
}
|
||||
}
|
||||
|
||||
// anonymous struct types.
|
||||
assert(reflect.DeepEqual(struct{}{}, struct{}{}))
|
||||
assert(reflect.DeepEqual(struct{ x int }{1}, struct{ x int }{1}))
|
||||
assert(!reflect.DeepEqual(struct{ x int }{}, struct{ x int }{5}))
|
||||
assert(!reflect.DeepEqual(struct{ x, y int }{0, 1}, struct{ x int }{0}))
|
||||
assert(reflect.DeepEqual(struct{ x, y int }{2, 3}, struct{ x, y int }{2, 3}))
|
||||
assert(!reflect.DeepEqual(struct{ x, y int }{4, 5}, struct{ x, y int }{4, 6}))
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package reflect
|
||||
|
||||
// Not an actual implementation of DeepEqual. This is a model that supports
|
||||
// the bare minimum needed to get through testing interp.
|
||||
//
|
||||
// Does not handle cycles.
|
||||
//
|
||||
// Note: unclear if reflect.go can support this.
|
||||
func DeepEqual(x, y interface{}) bool {
|
||||
if x == nil || y == nil {
|
||||
return x == y
|
||||
}
|
||||
v1 := ValueOf(x)
|
||||
v2 := ValueOf(y)
|
||||
|
||||
return deepValueEqual(v1, v2, make(map[visit]bool))
|
||||
}
|
||||
|
||||
// Key for the visitedMap in deepValueEqual.
|
||||
type visit struct {
|
||||
a1, a2 uintptr
|
||||
typ Type
|
||||
}
|
||||
|
||||
func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Short circuit on reference types that can lead to cycles in comparison.
|
||||
switch v1.Kind() {
|
||||
case Pointer, Map, Slice, Interface:
|
||||
k := visit{v1.Pointer(), v2.Pointer(), v1.Type()} // Not safe for moving GC.
|
||||
if visited[k] {
|
||||
// The comparison algorithm assumes that all checks in progress are true when it reencounters them.
|
||||
return true
|
||||
}
|
||||
visited[k] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case Array:
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Slice:
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Interface:
|
||||
if v1.IsNil() || v2.IsNil() {
|
||||
return v1.IsNil() == v2.IsNil()
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
case Ptr:
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
case Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Map:
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
val1 := v1.MapIndex(k)
|
||||
val2 := v2.MapIndex(k)
|
||||
if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Func:
|
||||
return v1.IsNil() && v2.IsNil()
|
||||
default:
|
||||
// Normal equality suffices
|
||||
return v1.Interface() == v2.Interface() // try interface comparison as a fallback.
|
||||
}
|
||||
}
|
||||
|
|
@ -11,9 +11,20 @@ type Value struct {
|
|||
|
||||
func (Value) String() string
|
||||
|
||||
func (Value) Elem() string
|
||||
func (Value) Elem() Value
|
||||
func (Value) Kind() Kind
|
||||
func (Value) Int() int64
|
||||
func (Value) IsValid() bool
|
||||
func (Value) IsNil() bool
|
||||
func (Value) Len() int
|
||||
func (Value) Pointer() uintptr
|
||||
func (Value) Index(i int) Value
|
||||
func (Value) Type() Type
|
||||
func (Value) Field(int) Value
|
||||
func (Value) MapIndex(Value) Value
|
||||
func (Value) MapKeys() []Value
|
||||
func (Value) NumField() int
|
||||
func (Value) Interface() interface{}
|
||||
|
||||
func SliceOf(Type) Type
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue