]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: ignore struct tags when converting structs
authorRobert Griesemer <gri@golang.org>
Mon, 3 Oct 2016 18:40:43 +0000 (11:40 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 4 Oct 2016 17:10:47 +0000 (17:10 +0000)
Implementation of spec change https://golang.org/cl/24190/.

For #16085.

Change-Id: Id71ef29af5031b073e8be163f578d1bb768ff97a
Reviewed-on: https://go-review.googlesource.com/30169
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/gc/subr.go
test/convert2.go [new file with mode: 0644]

index 2c2e6ed1efbd6d547962eec2c9b9e79fc96f5e45..39951ac05a44d31e222c6a43f001f67f835d0c40 100644 (file)
@@ -644,7 +644,12 @@ func cplxsubtype(et EType) EType {
 // pointer (t1 == t2), so there's no chance of chasing cycles
 // ad infinitum, so no need for a depth counter.
 func eqtype(t1, t2 *Type) bool {
-       return eqtype1(t1, t2, nil)
+       return eqtype1(t1, t2, true, nil)
+}
+
+// eqtypeIgnoreTags is like eqtype but it ignores struct tags for struct identity.
+func eqtypeIgnoreTags(t1, t2 *Type) bool {
+       return eqtype1(t1, t2, false, nil)
 }
 
 type typePair struct {
@@ -652,7 +657,7 @@ type typePair struct {
        t2 *Type
 }
 
-func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
+func eqtype1(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
        if t1 == t2 {
                return true
        }
@@ -684,7 +689,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
                t1, i1 := iterFields(t1)
                t2, i2 := iterFields(t2)
                for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() {
-                       if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || t1.Note != t2.Note {
+                       if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, cmpTags, assumedEqual) || cmpTags && t1.Note != t2.Note {
                                return false
                        }
                }
@@ -703,7 +708,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
                        ta, ia := iterFields(f(t1))
                        tb, ib := iterFields(f(t2))
                        for ; ta != nil && tb != nil; ta, tb = ia.Next(), ib.Next() {
-                               if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, assumedEqual) {
+                               if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, cmpTags, assumedEqual) {
                                        return false
                                }
                        }
@@ -724,13 +729,13 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
                }
 
        case TMAP:
-               if !eqtype1(t1.Key(), t2.Key(), assumedEqual) {
+               if !eqtype1(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
                        return false
                }
-               return eqtype1(t1.Val(), t2.Val(), assumedEqual)
+               return eqtype1(t1.Val(), t2.Val(), cmpTags, assumedEqual)
        }
 
-       return eqtype1(t1.Elem(), t2.Elem(), assumedEqual)
+       return eqtype1(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
 }
 
 // Are t1 and t2 equal struct types when field names are ignored?
@@ -906,15 +911,15 @@ func convertop(src *Type, dst *Type, why *string) Op {
                *why = ""
        }
 
-       // 2. src and dst have identical underlying types.
-       if eqtype(src.Orig, dst.Orig) {
+       // 2. Ignoring struct tags, src and dst have identical underlying types.
+       if eqtypeIgnoreTags(src.Orig, dst.Orig) {
                return OCONVNOP
        }
 
-       // 3. src and dst are unnamed pointer types
-       // and their base types have identical underlying types.
+       // 3. src and dst are unnamed pointer types and, ignoring struct tags,
+       // their base types have identical underlying types.
        if src.IsPtr() && dst.IsPtr() && src.Sym == nil && dst.Sym == nil {
-               if eqtype(src.Elem().Orig, dst.Elem().Orig) {
+               if eqtypeIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
                        return OCONVNOP
                }
        }
diff --git a/test/convert2.go b/test/convert2.go
new file mode 100644 (file)
index 0000000..c500638
--- /dev/null
@@ -0,0 +1,315 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test various valid and invalid struct assignments and conversions.
+// Does not compile.
+
+package main
+
+type I interface {
+       m()
+}
+
+// conversions between structs
+
+func _() {
+       type S struct{}
+       type T struct{}
+       var s S
+       var t T
+       var u struct{}
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u
+       s = S(s)
+       s = S(t)
+       s = S(u)
+       t = u
+       t = T(u)
+}
+
+func _() {
+       type S struct{ x int }
+       type T struct {
+               x int "foo"
+       }
+       var s S
+       var t T
+       var u struct {
+               x int "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = S(s)
+       s = S(t)
+       s = S(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = T(u)
+}
+
+func _() {
+       type E struct{ x int }
+       type S struct{ x E }
+       type T struct {
+               x E "foo"
+       }
+       var s S
+       var t T
+       var u struct {
+               x E "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = S(s)
+       s = S(t)
+       s = S(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = T(u)
+}
+
+func _() {
+       type S struct {
+               x struct {
+                       x int "foo"
+               }
+       }
+       type T struct {
+               x struct {
+                       x int "bar"
+               } "foo"
+       }
+       var s S
+       var t T
+       var u struct {
+               x struct {
+                       x int "bar"
+               } "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = S(s)
+       s = S(t)
+       s = S(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = T(u)
+}
+
+func _() {
+       type E1 struct {
+               x int "foo"
+       }
+       type E2 struct {
+               x int "bar"
+       }
+       type S struct{ x E1 }
+       type T struct {
+               x E2 "foo"
+       }
+       var s S
+       var t T
+       var u struct {
+               x E2 "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = S(s)
+       s = S(t) // ERROR "cannot convert"
+       s = S(u) // ERROR "cannot convert"
+       t = u    // ERROR "cannot use .* in assignment"
+       t = T(u)
+}
+
+func _() {
+       type E struct{ x int }
+       type S struct {
+               f func(struct {
+                       x int "foo"
+               })
+       }
+       type T struct {
+               f func(struct {
+                       x int "bar"
+               })
+       }
+       var s S
+       var t T
+       var u struct{ f func(E) }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = S(s)
+       s = S(t)
+       s = S(u) // ERROR "cannot convert"
+       t = u    // ERROR "cannot use .* in assignment"
+       t = T(u) // ERROR "cannot convert"
+}
+
+// conversions between pointers to structs
+
+func _() {
+       type S struct{}
+       type T struct{}
+       var s *S
+       var t *T
+       var u *struct{}
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = (*T)(u)
+}
+
+func _() {
+       type S struct{ x int }
+       type T struct {
+               x int "foo"
+       }
+       var s *S
+       var t *T
+       var u *struct {
+               x int "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = (*T)(u)
+}
+
+func _() {
+       type E struct{ x int }
+       type S struct{ x E }
+       type T struct {
+               x E "foo"
+       }
+       var s *S
+       var t *T
+       var u *struct {
+               x E "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = (*T)(u)
+}
+
+func _() {
+       type S struct {
+               x struct {
+                       x int "foo"
+               }
+       }
+       type T struct {
+               x struct {
+                       x int "bar"
+               } "foo"
+       }
+       var s *S
+       var t *T
+       var u *struct {
+               x struct {
+                       x int "bar"
+               } "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u)
+       t = u // ERROR "cannot use .* in assignment"
+       t = (*T)(u)
+}
+
+func _() {
+       type E1 struct {
+               x int "foo"
+       }
+       type E2 struct {
+               x int "bar"
+       }
+       type S struct{ x E1 }
+       type T struct {
+               x E2 "foo"
+       }
+       var s *S
+       var t *T
+       var u *struct {
+               x E2 "bar"
+       }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t) // ERROR "cannot convert"
+       s = (*S)(u) // ERROR "cannot convert"
+       t = u       // ERROR "cannot use .* in assignment"
+       t = (*T)(u)
+}
+
+func _() {
+       type E struct{ x int }
+       type S struct {
+               f func(struct {
+                       x int "foo"
+               })
+       }
+       type T struct {
+               f func(struct {
+                       x int "bar"
+               })
+       }
+       var s *S
+       var t *T
+       var u *struct{ f func(E) }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u) // ERROR "cannot convert"
+       t = u       // ERROR "cannot use .* in assignment"
+       t = (*T)(u) // ERROR "cannot convert"
+}
+
+func _() {
+       type E struct{ x int }
+       type S struct {
+               f func(*struct {
+                       x int "foo"
+               })
+       }
+       type T struct {
+               f func(*struct {
+                       x int "bar"
+               })
+       }
+       var s *S
+       var t *T
+       var u *struct{ f func(E) }
+       s = s
+       s = t // ERROR "cannot use .* in assignment"
+       s = u // ERROR "cannot use .* in assignment"
+       s = (*S)(s)
+       s = (*S)(t)
+       s = (*S)(u) // ERROR "cannot convert"
+       t = u       // ERROR "cannot use .* in assignment"
+       t = (*T)(u) // ERROR "cannot convert"
+}