]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile, reflect: use field pkgPath if needed
authorIan Lance Taylor <iant@golang.org>
Thu, 17 Nov 2016 00:13:22 +0000 (16:13 -0800)
committerIan Lance Taylor <iant@golang.org>
Thu, 17 Nov 2016 01:19:46 +0000 (01:19 +0000)
It's possible for the pkgPath of a field to be different than that of
the struct type as a whole. In that case, store the field's pkgPath in
the name field. Use the field's pkgPath when setting PkgPath and when
checking for type identity.

Fixes #17952.

Change-Id: Iebaf92f0054b11427c8f6e4158c3bebcfff06f45
Reviewed-on: https://go-review.googlesource.com/33333
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
src/cmd/compile/internal/gc/reflect.go
src/reflect/all_test.go
src/reflect/type.go

index 3554dc2e99f30b50717365cf7735a0b49237e1f5..ed1733ee2359e9f5a4d7b6da2f57aa44fdadf9ba 100644 (file)
@@ -494,26 +494,31 @@ func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
 }
 
 // isExportedField reports whether a struct field is exported.
-func isExportedField(ft *Field) bool {
+// It also returns the package to use for PkgPath for an unexported field.
+func isExportedField(ft *Field) (bool, *Pkg) {
        if ft.Sym != nil && ft.Embedded == 0 {
-               return exportname(ft.Sym.Name)
+               return exportname(ft.Sym.Name), ft.Sym.Pkg
        } else {
                if ft.Type.Sym != nil &&
                        (ft.Type.Sym.Pkg == builtinpkg || !exportname(ft.Type.Sym.Name)) {
-                       return false
+                       return false, ft.Type.Sym.Pkg
                } else {
-                       return true
+                       return true, nil
                }
        }
 }
 
 // dnameField dumps a reflect.name for a struct field.
-func dnameField(s *Sym, ot int, ft *Field) int {
+func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
        var name string
        if ft.Sym != nil && ft.Embedded == 0 {
                name = ft.Sym.Name
        }
-       nsym := dname(name, ft.Note, nil, isExportedField(ft))
+       isExported, fpkg := isExportedField(ft)
+       if isExported || fpkg == spkg {
+               fpkg = nil
+       }
+       nsym := dname(name, ft.Note, fpkg, isExported)
        return dsymptrLSym(Linksym(s), ot, nsym, 0)
 }
 
@@ -1332,7 +1337,7 @@ ok:
 
                for _, f := range t.Fields().Slice() {
                        // ../../../../runtime/type.go:/structField
-                       ot = dnameField(s, ot, f)
+                       ot = dnameField(s, ot, pkg, f)
                        ot = dsymptr(s, ot, dtypesym(f.Type), 0)
                        ot = duintptr(s, ot, uint64(f.Offset))
                }
index 7dfdfd870999ad0e54e195c93a0c25fcba4ebeb7..e26c20148f3e3b9f0cce80f022622fcdbcbe2a96 100644 (file)
@@ -2325,25 +2325,39 @@ func TestFieldPkgPath(t *testing.T) {
                unexported string
                OtherPkgFields
        }{})
-       for _, test := range []struct {
+
+       type pkgpathTest struct {
                index     []int
                pkgPath   string
                anonymous bool
-       }{
+       }
+
+       checkPkgPath := func(name string, s []pkgpathTest) {
+               for _, test := range s {
+                       f := typ.FieldByIndex(test.index)
+                       if got, want := f.PkgPath, test.pkgPath; got != want {
+                               t.Errorf("%s: Field(%d).PkgPath = %q, want %q", name, test.index, got, want)
+                       }
+                       if got, want := f.Anonymous, test.anonymous; got != want {
+                               t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
+                       }
+               }
+       }
+
+       checkPkgPath("testStruct", []pkgpathTest{
                {[]int{0}, "", false},             // Exported
                {[]int{1}, "reflect_test", false}, // unexported
                {[]int{2}, "", true},              // OtherPkgFields
                {[]int{2, 0}, "", false},          // OtherExported
                {[]int{2, 1}, "reflect", false},   // otherUnexported
-       } {
-               f := typ.FieldByIndex(test.index)
-               if got, want := f.PkgPath, test.pkgPath; got != want {
-                       t.Errorf("Field(%d).PkgPath = %q, want %q", test.index, got, want)
-               }
-               if got, want := f.Anonymous, test.anonymous; got != want {
-                       t.Errorf("Field(%d).Anonymous = %v, want %v", test.index, got, want)
-               }
-       }
+       })
+
+       type localOtherPkgFields OtherPkgFields
+       typ = TypeOf(localOtherPkgFields{})
+       checkPkgPath("localOtherPkgFields", []pkgpathTest{
+               {[]int{0}, "", false},        // OtherExported
+               {[]int{1}, "reflect", false}, // otherUnexported
+       })
 }
 
 func TestVariadicType(t *testing.T) {
index e04eff7931fc9152c01483fb3fe827d5cde3d638..28276a5ac0c01d543ff674f6747d5ca2b127725f 100644 (file)
@@ -1226,8 +1226,10 @@ func (t *structType) Field(i int) (f StructField) {
                f.Anonymous = true
        }
        if !p.name.isExported() {
-               // Fields never have an import path in their name.
-               f.PkgPath = t.pkgPath.name()
+               f.PkgPath = p.name.pkgPath()
+               if f.PkgPath == "" {
+                       f.PkgPath = t.pkgPath.name()
+               }
        }
        if tag := p.name.tag(); tag != "" {
                f.Tag = StructTag(tag)
@@ -1680,7 +1682,6 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
                if len(t.fields) != len(v.fields) {
                        return false
                }
-               allExported := true
                for i := range t.fields {
                        tf := &t.fields[i]
                        vf := &v.fields[i]
@@ -1696,15 +1697,19 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
                        if tf.offset != vf.offset {
                                return false
                        }
-                       allExported = allExported && tf.name.isExported()
-               }
-               if !allExported && t.pkgPath.name() != v.pkgPath.name() {
-                       // An unexported field of a struct is not
-                       // visible outside of the package that defines
-                       // it, so the package path is implicitly part
-                       // of the definition of any struct with an
-                       // unexported field.
-                       return false
+                       if !tf.name.isExported() {
+                               tp := tf.name.pkgPath()
+                               if tp == "" {
+                                       tp = t.pkgPath.name()
+                               }
+                               vp := vf.name.pkgPath()
+                               if vp == "" {
+                                       vp = v.pkgPath.name()
+                               }
+                               if tp != vp {
+                                       return false
+                               }
+                       }
                }
                return true
        }