Commit 43c70943 authored by Russ Cox's avatar Russ Cox

[dev.typealias] reflect: fix StructOf use of StructField to match StructField docs

The runtime internal structField interprets name=="" as meaning anonymous,
but the exported reflect.StructField has always set Name, even for anonymous
fields, and also set Anonymous=true.

The initial implementation of StructOf confused the internal and public
meanings of the StructField, expecting the runtime representation of
anonymous fields instead of the exported reflect API representation.
It also did not document this fact, so that users had no way to know how
to create an anonymous field.

This CL changes StructOf to use the previously documented interpretation
of reflect.StructField instead of an undocumented one.

The implementation of StructOf also, in some cases, allowed creating
structs with unexported fields (if you knew how to ask) but set the
PkgPath incorrectly on those fields. Rather than try to fix that, this CL
changes StructOf to reject attempts to create unexported fields.
(I think that may be the right design choice, not just a temporary limitation.
In any event, it's not the topic for today's work.)

For #17766.
Fixes #18780.

Change-Id: I585a4e324dc5a90551f49d21ae04d2de9ea04b6c
Reviewed-on: https://go-review.googlesource.com/35731
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 9657e0b0
...@@ -3689,7 +3689,7 @@ func checkSameType(t *testing.T, x, y interface{}) { ...@@ -3689,7 +3689,7 @@ func checkSameType(t *testing.T, x, y interface{}) {
func TestArrayOf(t *testing.T) { func TestArrayOf(t *testing.T) {
// check construction and use of type not in binary // check construction and use of type not in binary
for _, table := range []struct { tests := []struct {
n int n int
value func(i int) interface{} value func(i int) interface{}
comparable bool comparable bool
...@@ -3767,7 +3767,9 @@ func TestArrayOf(t *testing.T) { ...@@ -3767,7 +3767,9 @@ func TestArrayOf(t *testing.T) {
comparable: true, comparable: true,
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]", want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
}, },
} { }
for _, table := range tests {
at := ArrayOf(table.n, TypeOf(table.value(0))) at := ArrayOf(table.n, TypeOf(table.value(0)))
v := New(at).Elem() v := New(at).Elem()
vok := New(at).Elem() vok := New(at).Elem()
...@@ -4133,50 +4135,58 @@ func TestStructOfExportRules(t *testing.T) { ...@@ -4133,50 +4135,58 @@ func TestStructOfExportRules(t *testing.T) {
f() f()
} }
for i, test := range []struct { tests := []struct {
field StructField field StructField
mustPanic bool mustPanic bool
exported bool exported bool
}{ }{
{ {
field: StructField{Name: "", Type: TypeOf(S1{})}, field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{})},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "", Type: TypeOf((*S1)(nil))}, field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil))},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "", Type: TypeOf(s2{})}, field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{})},
mustPanic: false, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "", Type: TypeOf((*s2)(nil))}, field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))},
mustPanic: false, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"}, field: StructField{Name: "Name", Type: nil, PkgPath: ""},
mustPanic: true, mustPanic: true,
exported: true,
}, },
{ {
field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"}, field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: ""},
mustPanic: true,
},
{
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{}), PkgPath: "other/pkg"},
mustPanic: true,
},
{
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
mustPanic: true,
},
{
field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{}), PkgPath: "other/pkg"},
mustPanic: true, mustPanic: true,
exported: true,
}, },
{ {
field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"}, field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
mustPanic: true,
},
{
field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "S", Type: TypeOf(S1{})}, field: StructField{Name: "S", Type: TypeOf(S1{})},
...@@ -4184,81 +4194,68 @@ func TestStructOfExportRules(t *testing.T) { ...@@ -4184,81 +4194,68 @@ func TestStructOfExportRules(t *testing.T) {
exported: true, exported: true,
}, },
{ {
field: StructField{Name: "S", Type: TypeOf((*S1)(nil))}, field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "S", Type: TypeOf(s2{})}, field: StructField{Name: "S", Type: TypeOf(s2{})},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "S", Type: TypeOf((*s2)(nil))}, field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf(S1{})}, field: StructField{Name: "s", Type: TypeOf(S1{})},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf((*S1)(nil))}, field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf(s2{})}, field: StructField{Name: "s", Type: TypeOf(s2{})},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf((*s2)(nil))}, field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
mustPanic: true, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"}, field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path mustPanic: true, // TODO(sbinet): creating a name with a package path
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"}, field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path mustPanic: true, // TODO(sbinet): creating a name with a package path
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"}, field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path mustPanic: true, // TODO(sbinet): creating a name with a package path
exported: false,
}, },
{ {
field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path mustPanic: true, // TODO(sbinet): creating a name with a package path
exported: false,
}, },
{ {
field: StructField{Name: "", Type: TypeOf(ΦType{})}, field: StructField{Name: "", Type: TypeOf(ΦType{})},
mustPanic: false, mustPanic: true,
exported: true,
}, },
{ {
field: StructField{Name: "", Type: TypeOf(φType{})}, field: StructField{Name: "", Type: TypeOf(φType{})},
mustPanic: false, mustPanic: true,
exported: false,
}, },
{ {
field: StructField{Name: "Φ", Type: TypeOf(0)}, field: StructField{Name: "Φ", Type: TypeOf(0)},
mustPanic: false, exported: true,
exported: true,
}, },
{ {
field: StructField{Name: "φ", Type: TypeOf(0)}, field: StructField{Name: "φ", Type: TypeOf(0)},
mustPanic: false, exported: false,
exported: false,
}, },
} { }
for i, test := range tests {
testPanic(i, test.mustPanic, func() { testPanic(i, test.mustPanic, func() {
typ := StructOf([]StructField{test.field}) typ := StructOf([]StructField{test.field})
if typ == nil { if typ == nil {
...@@ -4346,7 +4343,7 @@ func TestStructOfGenericAlg(t *testing.T) { ...@@ -4346,7 +4343,7 @@ func TestStructOfGenericAlg(t *testing.T) {
{Name: "S1", Type: st1}, {Name: "S1", Type: st1},
}) })
for _, table := range []struct { tests := []struct {
rt Type rt Type
idx []int idx []int
}{ }{
...@@ -4427,7 +4424,9 @@ func TestStructOfGenericAlg(t *testing.T) { ...@@ -4427,7 +4424,9 @@ func TestStructOfGenericAlg(t *testing.T) {
), ),
idx: []int{2}, idx: []int{2},
}, },
} { }
for _, table := range tests {
v1 := New(table.rt).Elem() v1 := New(table.rt).Elem()
v2 := New(table.rt).Elem() v2 := New(table.rt).Elem()
...@@ -4529,18 +4528,21 @@ func TestStructOfWithInterface(t *testing.T) { ...@@ -4529,18 +4528,21 @@ func TestStructOfWithInterface(t *testing.T) {
type Iface interface { type Iface interface {
Get() int Get() int
} }
for i, table := range []struct { tests := []struct {
name string
typ Type typ Type
val Value val Value
impl bool impl bool
}{ }{
{ {
name: "StructI",
typ: TypeOf(StructI(want)), typ: TypeOf(StructI(want)),
val: ValueOf(StructI(want)), val: ValueOf(StructI(want)),
impl: true, impl: true,
}, },
{ {
typ: PtrTo(TypeOf(StructI(want))), name: "StructI",
typ: PtrTo(TypeOf(StructI(want))),
val: ValueOf(func() interface{} { val: ValueOf(func() interface{} {
v := StructI(want) v := StructI(want)
return &v return &v
...@@ -4548,7 +4550,8 @@ func TestStructOfWithInterface(t *testing.T) { ...@@ -4548,7 +4550,8 @@ func TestStructOfWithInterface(t *testing.T) {
impl: true, impl: true,
}, },
{ {
typ: PtrTo(TypeOf(StructIPtr(want))), name: "StructIPtr",
typ: PtrTo(TypeOf(StructIPtr(want))),
val: ValueOf(func() interface{} { val: ValueOf(func() interface{} {
v := StructIPtr(want) v := StructIPtr(want)
return &v return &v
...@@ -4556,6 +4559,7 @@ func TestStructOfWithInterface(t *testing.T) { ...@@ -4556,6 +4559,7 @@ func TestStructOfWithInterface(t *testing.T) {
impl: true, impl: true,
}, },
{ {
name: "StructIPtr",
typ: TypeOf(StructIPtr(want)), typ: TypeOf(StructIPtr(want)),
val: ValueOf(StructIPtr(want)), val: ValueOf(StructIPtr(want)),
impl: false, impl: false,
...@@ -4565,13 +4569,16 @@ func TestStructOfWithInterface(t *testing.T) { ...@@ -4565,13 +4569,16 @@ func TestStructOfWithInterface(t *testing.T) {
// val: ValueOf(StructI(want)), // val: ValueOf(StructI(want)),
// impl: true, // impl: true,
// }, // },
} { }
for i, table := range tests {
rt := StructOf( rt := StructOf(
[]StructField{ []StructField{
{ {
Name: "", Name: table.name,
PkgPath: "", Anonymous: true,
Type: table.typ, PkgPath: "",
Type: table.typ,
}, },
}, },
) )
...@@ -5951,6 +5958,7 @@ func TestSwapper(t *testing.T) { ...@@ -5951,6 +5958,7 @@ func TestSwapper(t *testing.T) {
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}}, want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
}, },
} }
for i, tt := range tests { for i, tt := range tests {
inStr := fmt.Sprint(tt.in) inStr := fmt.Sprint(tt.in)
Swapper(tt.in)(tt.i, tt.j) Swapper(tt.in)(tt.i, tt.j)
......
...@@ -2403,6 +2403,9 @@ func StructOf(fields []StructField) Type { ...@@ -2403,6 +2403,9 @@ func StructOf(fields []StructField) Type {
lastzero := uintptr(0) lastzero := uintptr(0)
repr = append(repr, "struct {"...) repr = append(repr, "struct {"...)
for i, field := range fields { for i, field := range fields {
if field.Name == "" {
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name")
}
if field.Type == nil { if field.Type == nil {
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
} }
...@@ -2794,23 +2797,25 @@ func StructOf(fields []StructField) Type { ...@@ -2794,23 +2797,25 @@ func StructOf(fields []StructField) Type {
} }
func runtimeStructField(field StructField) structField { func runtimeStructField(field StructField) structField {
exported := field.PkgPath == "" if field.PkgPath != "" {
if field.Name == "" { panic("reflect.StructOf: StructOf does not allow unexported fields")
t := field.Type.(*rtype) }
if t.Kind() == Ptr {
t = t.Elem().(*rtype) // Best-effort check for misuse.
} // Since PkgPath is empty, not much harm done if Unicode lowercase slips through.
exported = t.nameOff(t.str).isExported() c := field.Name[0]
} else if exported { if 'a' <= c && c <= 'z' || c == '_' {
b0 := field.Name[0] panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath")
if ('a' <= b0 && b0 <= 'z') || b0 == '_' { }
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but has no PkgPath")
} name := field.Name
if field.Anonymous {
name = ""
} }
_ = resolveReflectType(field.Type.common()) resolveReflectType(field.Type.common()) // install in runtime
return structField{ return structField{
name: newName(field.Name, string(field.Tag), field.PkgPath, exported), name: newName(name, string(field.Tag), "", true),
typ: field.Type.common(), typ: field.Type.common(),
offset: 0, offset: 0,
} }
......
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