From ddb77100a670d491d7553cc1beed630fc994a7f6 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 3 Oct 2016 12:13:22 -0700 Subject: [PATCH] reflect: ignore struct tags when converting structs Implementation of spec change https://golang.org/cl/24190/. For #16085. Change-Id: Ib7cb513354269282dfad663c7d2c6e624149f3cd Reviewed-on: https://go-review.googlesource.com/30191 Reviewed-by: David Crawshaw --- src/reflect/all_test.go | 32 ++++++++++++++++++++++++++++ src/reflect/type.go | 46 ++++++++++++++++++++++++++--------------- src/reflect/value.go | 4 ++-- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 1f2c752784..9f8fd0df19 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -3098,6 +3098,9 @@ func ReadWriterV(x io.ReadWriter) Value { } type Empty struct{} +type MyStruct struct { + x int "tag" +} type MyString string type MyBytes []byte type MyRunes []int32 @@ -3409,6 +3412,35 @@ var convertTests = []struct { {V((func())(nil)), V(MyFunc(nil))}, {V((MyFunc)(nil)), V((func())(nil))}, + // structs with different tags + {V(struct { + x int "foo" + }{}), V(struct { + x int "bar" + }{})}, + + {V(struct { + x int "bar" + }{}), V(struct { + x int "foo" + }{})}, + + {V(MyStruct{}), V(struct { + x int "foo" + }{})}, + + {V(struct { + x int "foo" + }{}), V(MyStruct{})}, + + {V(MyStruct{}), V(struct { + x int "bar" + }{})}, + + {V(struct { + x int "bar" + }{}), V(MyStruct{})}, + // can convert *byte and *MyByte {V((*byte)(nil)), V((*MyByte)(nil))}, {V((*MyByte)(nil)), V((*byte)(nil))}, diff --git a/src/reflect/type.go b/src/reflect/type.go index a3329e01c6..49da4ef34b 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -1584,10 +1584,22 @@ func directlyAssignable(T, V *rtype) bool { } // x's type T and V must have identical underlying types. - return haveIdenticalUnderlyingType(T, V) + return haveIdenticalUnderlyingType(T, V, true) } -func haveIdenticalUnderlyingType(T, V *rtype) bool { +func haveIdenticalType(T, V Type, cmpTags bool) bool { + if cmpTags { + return T == V + } + + if T.Name() != V.Name() || T.Kind() != V.Kind() { + return false + } + + return haveIdenticalUnderlyingType(T.common(), V.common(), false) +} + +func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if T == V { return true } @@ -1606,18 +1618,18 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { // Composite types. switch kind { case Array: - return T.Elem() == V.Elem() && T.Len() == V.Len() + return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) case Chan: // Special case: // x is a bidirectional channel value, T is a channel type, // and x's type V and T have identical element types. - if V.ChanDir() == BothDir && T.Elem() == V.Elem() { + if V.ChanDir() == BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) { return true } // Otherwise continue test for identical underlying type. - return V.ChanDir() == T.ChanDir() && T.Elem() == V.Elem() + return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) case Func: t := (*funcType)(unsafe.Pointer(T)) @@ -1626,12 +1638,12 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { return false } for i := 0; i < t.NumIn(); i++ { - if t.In(i) != v.In(i) { + if !haveIdenticalType(t.In(i), v.In(i), cmpTags) { return false } } for i := 0; i < t.NumOut(); i++ { - if t.Out(i) != v.Out(i) { + if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) { return false } } @@ -1648,10 +1660,10 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { return false case Map: - return T.Key() == V.Key() && T.Elem() == V.Elem() + return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) case Ptr, Slice: - return T.Elem() == V.Elem() + return haveIdenticalType(T.Elem(), V.Elem(), cmpTags) case Struct: t := (*structType)(unsafe.Pointer(T)) @@ -1665,10 +1677,10 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { if tf.name.name() != vf.name.name() { return false } - if tf.typ != vf.typ { + if !haveIdenticalType(tf.typ, vf.typ, cmpTags) { return false } - if tf.name.tag() != vf.name.tag() { + if cmpTags && tf.name.tag() != vf.name.tag() { return false } if tf.offset != vf.offset { @@ -2024,7 +2036,7 @@ func FuncOf(in, out []Type, variadic bool) Type { // Look in cache. funcLookupCache.RLock() for _, t := range funcLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&ft.rtype, t) { + if haveIdenticalUnderlyingType(&ft.rtype, t, true) { funcLookupCache.RUnlock() return t } @@ -2038,7 +2050,7 @@ func FuncOf(in, out []Type, variadic bool) Type { funcLookupCache.m = make(map[uint32][]*rtype) } for _, t := range funcLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&ft.rtype, t) { + if haveIdenticalUnderlyingType(&ft.rtype, t, true) { return t } } @@ -2046,7 +2058,7 @@ func FuncOf(in, out []Type, variadic bool) Type { // Look in known types for the same string representation. str := funcStr(ft) for _, tt := range typesByString(str) { - if haveIdenticalUnderlyingType(&ft.rtype, tt) { + if haveIdenticalUnderlyingType(&ft.rtype, tt, true) { funcLookupCache.m[hash] = append(funcLookupCache.m[hash], tt) return tt } @@ -2599,7 +2611,7 @@ func StructOf(fields []StructField) Type { structLookupCache.RLock() for _, st := range structLookupCache.m[hash] { t := st.common() - if haveIdenticalUnderlyingType(&typ.rtype, t) { + if haveIdenticalUnderlyingType(&typ.rtype, t, true) { structLookupCache.RUnlock() return t } @@ -2616,14 +2628,14 @@ func StructOf(fields []StructField) Type { } for _, st := range structLookupCache.m[hash] { t := st.common() - if haveIdenticalUnderlyingType(&typ.rtype, t) { + if haveIdenticalUnderlyingType(&typ.rtype, t, true) { return t } } // Look in known types. for _, t := range typesByString(str) { - if haveIdenticalUnderlyingType(&typ.rtype, t) { + if haveIdenticalUnderlyingType(&typ.rtype, t, true) { // even if 't' wasn't a structType with methods, we should be ok // as the 'u uncommonType' field won't be accessed except when // tflag&tflagUncommon is set. diff --git a/src/reflect/value.go b/src/reflect/value.go index e6b846e5d1..f9080abff4 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -2239,14 +2239,14 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { } // dst and src have same underlying type. - if haveIdenticalUnderlyingType(dst, src) { + if haveIdenticalUnderlyingType(dst, src, false) { return cvtDirect } // dst and src are unnamed pointer types with same underlying base type. if dst.Kind() == Ptr && dst.Name() == "" && src.Kind() == Ptr && src.Name() == "" && - haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common()) { + haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) { return cvtDirect } -- 2.48.1