From: Russ Cox Date: Wed, 1 Jul 2009 23:45:09 +0000 (-0700) Subject: add test, fix bug: structs that differ in their X-Git-Tag: weekly.2009-11-06~1289 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a439f6622812865898d1a07a3ee66dc0cfda1cc0;p=gostls13.git add test, fix bug: structs that differ in their first field were not being handled correctly because the visited map did not include the type. R=r OCL=31006 CL=31006 --- diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 9cfc7e2688..fcbe473bef 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -420,6 +420,7 @@ var deepEqualTests = []DeepEqualTest { DeepEqualTest{ &[3]int{ 1, 2, 3 }, &[3]int{ 1, 2, 3 }, true }, DeepEqualTest{ Basic{ 1, 0.5 }, Basic{ 1, 0.5 }, true }, DeepEqualTest{ os.Error(nil), os.Error(nil), true }, + // Inequalities DeepEqualTest{ 1, 2, false }, DeepEqualTest{ int32(1), int32(2), false }, @@ -429,6 +430,8 @@ var deepEqualTests = []DeepEqualTest { DeepEqualTest{ make([]int, 10), make([]int, 11), false }, DeepEqualTest{ &[3]int{ 1, 2, 3 }, &[3]int{ 1, 2, 4 }, false }, DeepEqualTest{ Basic{ 1, 0.5 }, Basic{ 1, 0.6 }, false }, + DeepEqualTest{ Basic{ 1, 0 }, Basic{ 2, 0 }, false }, + // Mismatched types DeepEqualTest{ 1, 1.0, false }, DeepEqualTest{ int32(1), int64(1), false }, diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go index 0195a43a63..d4299edb57 100644 --- a/src/pkg/reflect/deepequal.go +++ b/src/pkg/reflect/deepequal.go @@ -8,33 +8,57 @@ package reflect import "reflect" +// During deepValueEqual, must keep track of checks that are +// in progress. The comparison algorithm assumes that all +// checks in progress are true when it reencounters them. +// Visited are stored in a map indexed by 17 * a1 + a2; +type visit struct { + a1 uintptr; + a2 uintptr; + typ Type; + next *visit; +} + // Tests for deep equality using reflected types. The map argument tracks // comparisons that have already been seen, which allows short circuiting on // recursive types. -func deepValueEqual(v1, v2 Value, visited map[Addr]Addr, depth int) bool { +func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) bool { if v1 == nil { return v2 == nil } if v2 == nil { return false } - if v1.Kind() != v2.Kind() { + if !equalType(v1.Type(), v2.Type()) { return false; } // if depth > 10 { panic("deepValueEqual") } // for debugging - // Short circuit if references are identical or already seen - addr1 := v1.Addr(); - addr2 := v2.Addr(); + addr1 := uintptr(v1.Addr()); + addr2 := uintptr(v2.Addr()); + if addr1 > addr2 { + // Canonicalize order to reduce number of entries in visited. + addr1, addr2 = addr2, addr1; + } + // Short circuit if references are identical ... if addr1 == addr2 { return true; } - if vaddr, ok := visited[addr1]; ok && vaddr == addr2 { - return true; + + // ... or already seen + h := 17 * addr1 + addr2; + seen, ok := visited[h]; + typ := v1.Type(); + for p := seen; p != nil; p = p.next { + if p.a1 == addr1 && p.a2 == addr2 && p.typ == typ { + return true; + } } - visited[addr1] = addr2; + + // Remember for later. + visited[h] = &visit{addr1, addr2, typ, seen}; switch v1.Kind() { case ArrayKind: @@ -91,5 +115,5 @@ func DeepEqual(a1, a2 interface{}) bool { if !equalType(v1.Type(), v2.Type()) { return false; } - return deepValueEqual(v1, v2, make(map[Addr]Addr), 0); + return deepValueEqual(v1, v2, make(map[uintptr]*visit), 0); } diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 61410af997..f59e3a2729 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -16,7 +16,7 @@ import ( type Addr unsafe.Pointer func equalType(a, b Type) bool { - return a.String() == b.String() + return a.Kind() == b.Kind() && a.String() == b.String() } // Value is the generic interface to reflection values. Once its Kind is known,