interp: fix use of function as field of a recursive struct.

The logic of patching reflect struct representation has been fixed in presence of function fields and to better handle indirect recursivity (struct recursive through nested struct).

Fixes #1519.
This commit is contained in:
Marc Vertes
2023-03-21 11:50:05 +01:00
committed by GitHub
parent c473dceda8
commit c4a297cbdc
4 changed files with 92 additions and 19 deletions

View File

@@ -1958,11 +1958,6 @@ var (
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
)
type fieldRebuild struct {
typ *itype
idx int
}
type refTypeContext struct {
defined map[string]*itype
@@ -1970,7 +1965,7 @@ type refTypeContext struct {
// type name (as key) is used as a field of another (or possibly the same) struct
// type. Each of these fields will then live as an unsafe2.dummy type until the
// whole recursion is fully resolved, and the type is fixed.
refs map[string][]fieldRebuild
refs map[string][]*itype
// When we detect for the first time that we are in a recursive type (thanks to
// defined), we keep track of the first occurrence of the type where the recursion
@@ -2043,7 +2038,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
if ctx == nil {
ctx = &refTypeContext{
defined: map[string]*itype{},
refs: map[string][]fieldRebuild{},
refs: map[string][]*itype{},
}
}
if t.incomplete || t.cat == nilT {
@@ -2068,14 +2063,14 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
// The recursion has not been fully resolved yet.
// To indicate that a rebuild is needed on the englobing struct,
// return a dummy field type and create an entry with an empty fieldRebuild.
// return a dummy field type and create an empty entry.
flds := ctx.refs[name]
ctx.rect = dt
// We know we are used as a field by someone, but we don't know by who
// at this point in the code, so we just mark it as an empty fieldRebuild for now.
// We'll complete the fieldRebuild in the caller.
ctx.refs[name] = append(flds, fieldRebuild{})
// at this point in the code, so we just mark it as an empty *itype for now.
// We'll complete the *itype in the caller.
ctx.refs[name] = append(flds, (*itype)(nil))
return unsafe2.DummyType
}
if isGeneric(t) {
@@ -2132,7 +2127,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
}
ctx.slevel++
var fields []reflect.StructField
for i, f := range t.field {
for _, f := range t.field {
field := reflect.StructField{
Name: exportName(f.name), Type: f.typ.refType(ctx),
Tag: reflect.StructTag(f.tag), Anonymous: f.embed,
@@ -2141,14 +2136,18 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
// Find any nil type refs that indicates a rebuild is needed on this field.
for _, flds := range ctx.refs {
for j, fld := range flds {
if fld.typ == nil {
flds[j] = fieldRebuild{typ: t, idx: i}
if fld == nil {
flds[j] = t
}
}
}
}
ctx.slevel--
fieldFix := []int{} // Slice of field indices to fix for recursivity.
type fixStructField struct {
name string
index int
}
fieldFix := []fixStructField{} // Slice of field indices to fix for recursivity.
t.rtype = reflect.StructOf(fields)
if ctx.isComplete() {
for _, s := range ctx.defined {
@@ -2157,8 +2156,12 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
if name == s.path+"/"+s.name {
fieldFix = append(fieldFix, i)
fieldFix = append(fieldFix, fixStructField{s.name, i})
}
continue
}
if f.Type.Kind() == reflect.Func && strings.Contains(f.Type.String(), "unsafe2.dummy") {
fieldFix = append(fieldFix, fixStructField{s.name, i})
}
}
}
@@ -2173,9 +2176,11 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
// and we need both the loop above, around all the struct fields, and the loop
// below, around the ctx.refs.
for _, f := range ctx.refs[name] {
for _, index := range fieldFix {
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
for _, ff := range fieldFix {
if ff.name == f.name {
ftyp := f.field[ff.index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
unsafe2.SetFieldType(f.rtype, ff.index, ftyp)
}
}
}
default: