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 {
}
// 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))
}
......
......@@ -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) {
......
......@@ -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
}
......
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