Commit 011cb642 authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/compile, reflect: use field pkgPath if needed

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: 's avatarDavid Crawshaw <crawshaw@golang.org>
parent fbf92436
...@@ -494,26 +494,31 @@ func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int { ...@@ -494,26 +494,31 @@ func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
} }
// isExportedField reports whether a struct field is exported. // 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 { if ft.Sym != nil && ft.Embedded == 0 {
return exportname(ft.Sym.Name) return exportname(ft.Sym.Name), ft.Sym.Pkg
} else { } else {
if ft.Type.Sym != nil && if ft.Type.Sym != nil &&
(ft.Type.Sym.Pkg == builtinpkg || !exportname(ft.Type.Sym.Name)) { (ft.Type.Sym.Pkg == builtinpkg || !exportname(ft.Type.Sym.Name)) {
return false return false, ft.Type.Sym.Pkg
} else { } else {
return true return true, nil
} }
} }
} }
// dnameField dumps a reflect.name for a struct field. // 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 var name string
if ft.Sym != nil && ft.Embedded == 0 { if ft.Sym != nil && ft.Embedded == 0 {
name = ft.Sym.Name 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) return dsymptrLSym(Linksym(s), ot, nsym, 0)
} }
...@@ -1332,7 +1337,7 @@ ok: ...@@ -1332,7 +1337,7 @@ ok:
for _, f := range t.Fields().Slice() { for _, f := range t.Fields().Slice() {
// ../../../../runtime/type.go:/structField // ../../../../runtime/type.go:/structField
ot = dnameField(s, ot, f) ot = dnameField(s, ot, pkg, f)
ot = dsymptr(s, ot, dtypesym(f.Type), 0) ot = dsymptr(s, ot, dtypesym(f.Type), 0)
ot = duintptr(s, ot, uint64(f.Offset)) ot = duintptr(s, ot, uint64(f.Offset))
} }
......
...@@ -2325,25 +2325,39 @@ func TestFieldPkgPath(t *testing.T) { ...@@ -2325,25 +2325,39 @@ func TestFieldPkgPath(t *testing.T) {
unexported string unexported string
OtherPkgFields OtherPkgFields
}{}) }{})
for _, test := range []struct {
type pkgpathTest struct {
index []int index []int
pkgPath string pkgPath string
anonymous bool anonymous bool
}{ }
{[]int{0}, "", false}, // Exported
{[]int{1}, "reflect_test", false}, // unexported checkPkgPath := func(name string, s []pkgpathTest) {
{[]int{2}, "", true}, // OtherPkgFields for _, test := range s {
{[]int{2, 0}, "", false}, // OtherExported
{[]int{2, 1}, "reflect", false}, // otherUnexported
} {
f := typ.FieldByIndex(test.index) f := typ.FieldByIndex(test.index)
if got, want := f.PkgPath, test.pkgPath; got != want { if got, want := f.PkgPath, test.pkgPath; got != want {
t.Errorf("Field(%d).PkgPath = %q, want %q", test.index, 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 { if got, want := f.Anonymous, test.anonymous; got != want {
t.Errorf("Field(%d).Anonymous = %v, want %v", test.index, 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
})
type localOtherPkgFields OtherPkgFields
typ = TypeOf(localOtherPkgFields{})
checkPkgPath("localOtherPkgFields", []pkgpathTest{
{[]int{0}, "", false}, // OtherExported
{[]int{1}, "reflect", false}, // otherUnexported
})
} }
func TestVariadicType(t *testing.T) { func TestVariadicType(t *testing.T) {
......
...@@ -1226,9 +1226,11 @@ func (t *structType) Field(i int) (f StructField) { ...@@ -1226,9 +1226,11 @@ func (t *structType) Field(i int) (f StructField) {
f.Anonymous = true f.Anonymous = true
} }
if !p.name.isExported() { if !p.name.isExported() {
// Fields never have an import path in their name. f.PkgPath = p.name.pkgPath()
if f.PkgPath == "" {
f.PkgPath = t.pkgPath.name() f.PkgPath = t.pkgPath.name()
} }
}
if tag := p.name.tag(); tag != "" { if tag := p.name.tag(); tag != "" {
f.Tag = StructTag(tag) f.Tag = StructTag(tag)
} }
...@@ -1680,7 +1682,6 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { ...@@ -1680,7 +1682,6 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
if len(t.fields) != len(v.fields) { if len(t.fields) != len(v.fields) {
return false return false
} }
allExported := true
for i := range t.fields { for i := range t.fields {
tf := &t.fields[i] tf := &t.fields[i]
vf := &v.fields[i] vf := &v.fields[i]
...@@ -1696,16 +1697,20 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { ...@@ -1696,16 +1697,20 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
if tf.offset != vf.offset { if tf.offset != vf.offset {
return false return false
} }
allExported = allExported && tf.name.isExported() 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 !allExported && t.pkgPath.name() != v.pkgPath.name() { if tp != vp {
// 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 return false
} }
}
}
return true return true
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment