]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: ignore struct tags when converting structs
authorRobert Griesemer <gri@golang.org>
Mon, 3 Oct 2016 19:13:22 +0000 (12:13 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 4 Oct 2016 17:15:59 +0000 (17:15 +0000)
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 <crawshaw@golang.org>
src/reflect/all_test.go
src/reflect/type.go
src/reflect/value.go

index 1f2c7527846cf04f64881855820b97d9e14643e3..9f8fd0df1975783102dadd266d63b36da869d92d 100644 (file)
@@ -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))},
index a3329e01c6ed279ebfd914ae62f02a2dedf868e7..49da4ef34b39e5286d22bef938c0b94933fa1796 100644 (file)
@@ -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.
index e6b846e5d1003886ae9847693025ea9ce122c974..f9080abff4eb5a0dd76a8cfb933a6073c034a568 100644 (file)
@@ -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
        }