Commit 87d5f6b9 authored by Ian Lance Taylor's avatar Ian Lance Taylor

reflect: make StructOf panic for methods that don't work

When StructOf is used with an anonymous field that has methods, and
that anonymous field is not the first field, the methods we generate
are incorrect because they do not offset to the field as required.
If we encounter that case, panic rather than doing the wrong thing.

Fixes #20824
Updates #15924

Change-Id: I3b0901ddbc6d58af5f7e84660b5e3085a431035d
Reviewed-on: https://go-review.googlesource.com/47035
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 792f9c9a
...@@ -4686,41 +4686,67 @@ func TestStructOfWithInterface(t *testing.T) { ...@@ -4686,41 +4686,67 @@ func TestStructOfWithInterface(t *testing.T) {
} }
for i, table := range tests { for i, table := range tests {
rt := StructOf( for j := 0; j < 2; j++ {
[]StructField{ var fields []StructField
{ if j == 1 {
Name: table.name, fields = append(fields, StructField{
Anonymous: true, Name: "Dummy",
PkgPath: "", PkgPath: "",
Type: table.typ, Type: TypeOf(int(0)),
}, })
}, }
) fields = append(fields, StructField{
rv := New(rt).Elem() Name: table.name,
rv.Field(0).Set(table.val) Anonymous: true,
PkgPath: "",
Type: table.typ,
})
if _, ok := rv.Interface().(Iface); ok != table.impl { // We currently do not correctly implement methods
if table.impl { // for anonymous fields other than the first.
t.Errorf("test-%d: type=%v fails to implement Iface.\n", i, table.typ) // Therefore, for now, we expect those methods
} else { // to not exist. See issues 15924 and 20824.
t.Errorf("test-%d: type=%v should NOT implement Iface\n", i, table.typ) // When those issues are fixed, this test of panic
// should be removed.
if j == 1 && table.impl {
func() {
defer func() {
if err := recover(); err == nil {
t.Errorf("test-%d-%d did not panic", i, j)
}
}()
_ = StructOf(fields)
}()
continue
} }
continue
}
if !table.impl { rt := StructOf(fields)
continue rv := New(rt).Elem()
} rv.Field(j).Set(table.val)
v := rv.Interface().(Iface).Get() if _, ok := rv.Interface().(Iface); ok != table.impl {
if v != want { if table.impl {
t.Errorf("test-%d: x.Get()=%v. want=%v\n", i, v, want) t.Errorf("test-%d-%d: type=%v fails to implement Iface.\n", i, j, table.typ)
} } else {
t.Errorf("test-%d-%d: type=%v should NOT implement Iface\n", i, j, table.typ)
}
continue
}
fct := rv.MethodByName("Get") if !table.impl {
out := fct.Call(nil) continue
if !DeepEqual(out[0].Interface(), want) { }
t.Errorf("test-%d: x.Get()=%v. want=%v\n", i, out[0].Interface(), want)
v := rv.Interface().(Iface).Get()
if v != want {
t.Errorf("test-%d-%d: x.Get()=%v. want=%v\n", i, j, v, want)
}
fct := rv.MethodByName("Get")
out := fct.Call(nil)
if !DeepEqual(out[0].Interface(), want) {
t.Errorf("test-%d-%d: x.Get()=%v. want=%v\n", i, j, out[0].Interface(), want)
}
} }
} }
} }
......
...@@ -2434,7 +2434,7 @@ func StructOf(fields []StructField) Type { ...@@ -2434,7 +2434,7 @@ func StructOf(fields []StructField) Type {
ift := (*interfaceType)(unsafe.Pointer(ft)) ift := (*interfaceType)(unsafe.Pointer(ft))
for im, m := range ift.methods { for im, m := range ift.methods {
if ift.nameOff(m.name).pkgPath() != "" { if ift.nameOff(m.name).pkgPath() != "" {
// TODO(sbinet) // TODO(sbinet). Issue 15924.
panic("reflect: embedded interface with unexported method(s) not implemented") panic("reflect: embedded interface with unexported method(s) not implemented")
} }
...@@ -2492,10 +2492,15 @@ func StructOf(fields []StructField) Type { ...@@ -2492,10 +2492,15 @@ func StructOf(fields []StructField) Type {
case Ptr: case Ptr:
ptr := (*ptrType)(unsafe.Pointer(ft)) ptr := (*ptrType)(unsafe.Pointer(ft))
if unt := ptr.uncommon(); unt != nil { if unt := ptr.uncommon(); unt != nil {
if i > 0 && unt.mcount > 0 {
// Issue 15924.
panic("reflect: embedded type with methods not implemented if type is not first field")
}
for _, m := range unt.methods() { for _, m := range unt.methods() {
mname := ptr.nameOff(m.name) mname := ptr.nameOff(m.name)
if mname.pkgPath() != "" { if mname.pkgPath() != "" {
// TODO(sbinet) // TODO(sbinet).
// Issue 15924.
panic("reflect: embedded interface with unexported method(s) not implemented") panic("reflect: embedded interface with unexported method(s) not implemented")
} }
methods = append(methods, method{ methods = append(methods, method{
...@@ -2511,6 +2516,7 @@ func StructOf(fields []StructField) Type { ...@@ -2511,6 +2516,7 @@ func StructOf(fields []StructField) Type {
mname := ptr.nameOff(m.name) mname := ptr.nameOff(m.name)
if mname.pkgPath() != "" { if mname.pkgPath() != "" {
// TODO(sbinet) // TODO(sbinet)
// Issue 15924.
panic("reflect: embedded interface with unexported method(s) not implemented") panic("reflect: embedded interface with unexported method(s) not implemented")
} }
methods = append(methods, method{ methods = append(methods, method{
...@@ -2523,10 +2529,15 @@ func StructOf(fields []StructField) Type { ...@@ -2523,10 +2529,15 @@ func StructOf(fields []StructField) Type {
} }
default: default:
if unt := ft.uncommon(); unt != nil { if unt := ft.uncommon(); unt != nil {
if i > 0 && unt.mcount > 0 {
// Issue 15924.
panic("reflect: embedded type with methods not implemented if type is not first field")
}
for _, m := range unt.methods() { for _, m := range unt.methods() {
mname := ft.nameOff(m.name) mname := ft.nameOff(m.name)
if mname.pkgPath() != "" { if mname.pkgPath() != "" {
// TODO(sbinet) // TODO(sbinet)
// Issue 15924.
panic("reflect: embedded interface with unexported method(s) not implemented") panic("reflect: embedded interface with unexported method(s) not implemented")
} }
methods = append(methods, method{ methods = append(methods, method{
......
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