interp: improve handling of interface values

In selector resolution, struct field matching now precedes
method matching. Before struct field matching could be skipped
in case of a matching method, which is incorrect, as demontrated
by _test/issue-1156.go.

Field lookup has been fixed to operate on recursive structures.

Concrete type values are derived when filling a receiver for
interface methods.

LookupBinField has been fixed to skip non struct values.

LookupMethod has been fixed to iterate on interface values as
well as concrete type values.

Fixes #1156.
This commit is contained in:
Marc Vertes
2021-06-24 16:20:13 +02:00
committed by GitHub
parent 1df5dc2e93
commit e32b2ab6bd
4 changed files with 197 additions and 140 deletions

View File

@@ -1254,24 +1254,36 @@ func (t *itype) fieldSeq(seq []int) *itype {
// lookupField returns a list of indices, i.e. a path to access a field in a struct object.
func (t *itype) lookupField(name string) []int {
switch t.cat {
case aliasT, ptrT:
return t.val.lookupField(name)
}
if fi := t.fieldIndex(name); fi >= 0 {
return []int{fi}
}
seen := map[*itype]bool{}
var lookup func(*itype) []int
for i, f := range t.field {
switch f.typ.cat {
case ptrT, structT, interfaceT, aliasT:
if index2 := f.typ.lookupField(name); len(index2) > 0 {
return append([]int{i}, index2...)
lookup = func(typ *itype) []int {
if seen[typ] {
return nil
}
seen[typ] = true
switch typ.cat {
case aliasT, ptrT:
return lookup(typ.val)
}
if fi := typ.fieldIndex(name); fi >= 0 {
return []int{fi}
}
for i, f := range typ.field {
switch f.typ.cat {
case ptrT, structT, interfaceT, aliasT:
if index2 := lookup(f.typ); len(index2) > 0 {
return append([]int{i}, index2...)
}
}
}
return nil
}
return nil
return lookup(t)
}
// lookupBinField returns a structfield and a path to access an embedded binary field in a struct object.
@@ -1282,10 +1294,13 @@ func (t *itype) lookupBinField(name string) (s reflect.StructField, index []int,
if !isStruct(t) {
return
}
rt := t.rtype
if t.cat == valueT && rt.Kind() == reflect.Ptr {
rt := t.TypeOf()
for t.cat == valueT && rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() != reflect.Struct {
return
}
s, ok = rt.FieldByName(name)
if !ok {
for i, f := range t.field {
@@ -1350,7 +1365,7 @@ func (t *itype) lookupMethod(name string) (*node, []int) {
}
}
}
if t.cat == aliasT {
if t.cat == aliasT || isInterfaceSrc(t) && t.val != nil {
return t.val.lookupMethod(name)
}
}