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:
16
_test/recurse1.go
Normal file
16
_test/recurse1.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type F func(a *A)
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
Name string
|
||||||
|
F
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &A{"Test", func(a *A) { println("in f", a.Name) }}
|
||||||
|
a.F(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// in f Test
|
||||||
27
_test/recurse2.go
Normal file
27
_test/recurse2.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type F func(a *A)
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B string
|
||||||
|
D
|
||||||
|
f F
|
||||||
|
}
|
||||||
|
|
||||||
|
type D struct {
|
||||||
|
*A
|
||||||
|
E *A
|
||||||
|
}
|
||||||
|
|
||||||
|
func f1(a *A) { println("in f1", a.B) }
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &A{B: "b", f: f1}
|
||||||
|
a.D = D{E: a}
|
||||||
|
println(a.D.E.B)
|
||||||
|
a.f(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// b
|
||||||
|
// in f1 b
|
||||||
25
_test/recurse3.go
Normal file
25
_test/recurse3.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type F func(a *A)
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
B string
|
||||||
|
D
|
||||||
|
}
|
||||||
|
|
||||||
|
type D struct {
|
||||||
|
*A
|
||||||
|
E *A
|
||||||
|
f F
|
||||||
|
}
|
||||||
|
|
||||||
|
func f1(a *A) { println("in f1", a.B) }
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := &A{B: "b"}
|
||||||
|
a.D = D{f: f1}
|
||||||
|
a.f(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// in f1 b
|
||||||
@@ -1958,11 +1958,6 @@ var (
|
|||||||
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
constVal = reflect.TypeOf((*constant.Value)(nil)).Elem()
|
||||||
)
|
)
|
||||||
|
|
||||||
type fieldRebuild struct {
|
|
||||||
typ *itype
|
|
||||||
idx int
|
|
||||||
}
|
|
||||||
|
|
||||||
type refTypeContext struct {
|
type refTypeContext struct {
|
||||||
defined map[string]*itype
|
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 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
|
// 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.
|
// 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
|
// 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
|
// 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 {
|
if ctx == nil {
|
||||||
ctx = &refTypeContext{
|
ctx = &refTypeContext{
|
||||||
defined: map[string]*itype{},
|
defined: map[string]*itype{},
|
||||||
refs: map[string][]fieldRebuild{},
|
refs: map[string][]*itype{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.incomplete || t.cat == nilT {
|
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.
|
// The recursion has not been fully resolved yet.
|
||||||
// To indicate that a rebuild is needed on the englobing struct,
|
// 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]
|
flds := ctx.refs[name]
|
||||||
ctx.rect = dt
|
ctx.rect = dt
|
||||||
|
|
||||||
// We know we are used as a field by someone, but we don't know by who
|
// 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.
|
// at this point in the code, so we just mark it as an empty *itype for now.
|
||||||
// We'll complete the fieldRebuild in the caller.
|
// We'll complete the *itype in the caller.
|
||||||
ctx.refs[name] = append(flds, fieldRebuild{})
|
ctx.refs[name] = append(flds, (*itype)(nil))
|
||||||
return unsafe2.DummyType
|
return unsafe2.DummyType
|
||||||
}
|
}
|
||||||
if isGeneric(t) {
|
if isGeneric(t) {
|
||||||
@@ -2132,7 +2127,7 @@ func (t *itype) refType(ctx *refTypeContext) reflect.Type {
|
|||||||
}
|
}
|
||||||
ctx.slevel++
|
ctx.slevel++
|
||||||
var fields []reflect.StructField
|
var fields []reflect.StructField
|
||||||
for i, f := range t.field {
|
for _, f := range t.field {
|
||||||
field := reflect.StructField{
|
field := reflect.StructField{
|
||||||
Name: exportName(f.name), Type: f.typ.refType(ctx),
|
Name: exportName(f.name), Type: f.typ.refType(ctx),
|
||||||
Tag: reflect.StructTag(f.tag), Anonymous: f.embed,
|
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.
|
// Find any nil type refs that indicates a rebuild is needed on this field.
|
||||||
for _, flds := range ctx.refs {
|
for _, flds := range ctx.refs {
|
||||||
for j, fld := range flds {
|
for j, fld := range flds {
|
||||||
if fld.typ == nil {
|
if fld == nil {
|
||||||
flds[j] = fieldRebuild{typ: t, idx: i}
|
flds[j] = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.slevel--
|
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)
|
t.rtype = reflect.StructOf(fields)
|
||||||
if ctx.isComplete() {
|
if ctx.isComplete() {
|
||||||
for _, s := range ctx.defined {
|
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") {
|
if strings.HasSuffix(f.Type.String(), "unsafe2.dummy") {
|
||||||
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
|
unsafe2.SetFieldType(s.rtype, i, ctx.rect.fixDummy(s.rtype.Field(i).Type))
|
||||||
if name == s.path+"/"+s.name {
|
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
|
// and we need both the loop above, around all the struct fields, and the loop
|
||||||
// below, around the ctx.refs.
|
// below, around the ctx.refs.
|
||||||
for _, f := range ctx.refs[name] {
|
for _, f := range ctx.refs[name] {
|
||||||
for _, index := range fieldFix {
|
for _, ff := range fieldFix {
|
||||||
ftyp := f.typ.field[index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
if ff.name == f.name {
|
||||||
unsafe2.SetFieldType(f.typ.rtype, index, ftyp)
|
ftyp := f.field[ff.index].typ.refType(&refTypeContext{defined: ctx.defined, rebuilding: true})
|
||||||
|
unsafe2.SetFieldType(f.rtype, ff.index, ftyp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user