feature: handle nested recursion
* fix: copy the type in recursion
In more advanced recursive cases, setting the rtype to interface may interfear with typeing. To stop this from happening, instead of setting t.val.rtype to interface in the hope it will be set correctly later, a copy if the type is made, and the rtype of the copy is set to interface{}.
* fix: detect intermediate recursive structs
In the case of a nested recussion, each symbol can have
a different perspective on the recursion. In this case,
it is impossible to move from one struct to the next.
To keep the perspectives the same, any intermediate struct
that contains a recursion should also be set to interface{}.
so that all perspectives are the same.
* fix: handle arb recursion
* chore: refactor dref to be consistent
* fix: invalid recursive struct issue
* fix: handle checkptr issue
* fix: move unsafe into function to stop ptr check
* fix: handle deref in assign
This commit is contained in:
@@ -2142,7 +2142,17 @@ func isField(n *node) bool {
|
||||
}
|
||||
|
||||
func isRecursiveField(n *node) bool {
|
||||
return isField(n) && (n.typ.recursive || n.typ.cat == ptrT && n.typ.val.recursive)
|
||||
if !isField(n) {
|
||||
return false
|
||||
}
|
||||
t := n.typ
|
||||
for t != nil {
|
||||
if t.recursive {
|
||||
return true
|
||||
}
|
||||
t = t.val
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNewDefine returns true if node refers to a new definition.
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"go/constant"
|
||||
"log"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// bltn type defines functions which run at CFG execution.
|
||||
@@ -351,14 +352,16 @@ func convert(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
func isRecursiveStruct(t *itype, rtype reflect.Type) bool {
|
||||
func isRecursiveType(t *itype, rtype reflect.Type) bool {
|
||||
if t.cat == structT && rtype.Kind() == reflect.Interface {
|
||||
return true
|
||||
}
|
||||
if t.cat == ptrT && t.rtype != nil {
|
||||
return isRecursiveStruct(t.val, t.rtype.Elem())
|
||||
switch t.cat {
|
||||
case ptrT, arrayT, mapT:
|
||||
return isRecursiveType(t.val, t.val.rtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func assign(n *node) {
|
||||
@@ -387,8 +390,10 @@ func assign(n *node) {
|
||||
case src.kind == basicLit && src.val == nil:
|
||||
t := dest.typ.TypeOf()
|
||||
svalue[i] = func(*frame) reflect.Value { return reflect.New(t).Elem() }
|
||||
case isRecursiveStruct(dest.typ, dest.typ.rtype):
|
||||
svalue[i] = genValueInterfacePtr(src)
|
||||
case isRecursiveType(dest.typ, dest.typ.rtype):
|
||||
svalue[i] = genValueRecursiveInterface(src, dest.typ.rtype)
|
||||
case isRecursiveType(src.typ, src.typ.rtype):
|
||||
svalue[i] = genValueRecursiveInterfacePtrValue(src)
|
||||
case src.typ.untyped && isComplex(dest.typ.TypeOf()):
|
||||
svalue[i] = genValueComplex(src)
|
||||
case src.typ.untyped && !dest.typ.untyped:
|
||||
@@ -762,7 +767,7 @@ func call(n *node) {
|
||||
var values []func(*frame) reflect.Value
|
||||
if n.child[0].recv != nil {
|
||||
// Compute method receiver value.
|
||||
if isRecursiveStruct(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
if isRecursiveType(n.child[0].recv.node.typ, n.child[0].recv.node.typ.rtype) {
|
||||
values = append(values, genValueRecvInterfacePtr(n.child[0]))
|
||||
} else {
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
@@ -808,8 +813,8 @@ func call(n *node) {
|
||||
switch {
|
||||
case len(n.child[0].typ.arg) > i && n.child[0].typ.arg[i].cat == interfaceT:
|
||||
values = append(values, genValueInterface(c))
|
||||
case isRecursiveStruct(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueDerefInterfacePtr(c))
|
||||
case isRecursiveType(c.typ, c.typ.rtype):
|
||||
values = append(values, genValueRecursiveInterfacePtrValue(c))
|
||||
default:
|
||||
values = append(values, genValue(c))
|
||||
}
|
||||
@@ -1498,7 +1503,11 @@ func getIndexSeq(n *node) {
|
||||
if n.fnext != nil {
|
||||
fnext := getExec(n.fnext)
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
f.data[i] = v.FieldByIndex(index)
|
||||
if f.data[i].Bool() {
|
||||
return tnext
|
||||
}
|
||||
@@ -1506,17 +1515,29 @@ func getIndexSeq(n *node) {
|
||||
}
|
||||
} else {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[i] = value(f).FieldByIndex(index)
|
||||
v := value(f)
|
||||
if v.Type().Kind() == reflect.Interface && n.child[0].typ.recursive {
|
||||
v = writableDeref(v)
|
||||
}
|
||||
f.data[i] = v.FieldByIndex(index)
|
||||
return tnext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func writableDeref(v reflect.Value) reflect.Value {
|
||||
// Here we have an interface to a struct. Any attempt to dereference it will
|
||||
// make a copy of the struct. We need to get a Value to the actual struct.
|
||||
// TODO: using unsafe is a temporary measure. Rethink this.
|
||||
return reflect.NewAt(v.Elem().Type(), unsafe.Pointer(v.InterfaceData()[1])).Elem() //nolint:govet
|
||||
}
|
||||
|
||||
func getPtrIndexSeq(n *node) {
|
||||
index := n.val.([]int)
|
||||
tnext := getExec(n.tnext)
|
||||
var value func(*frame) reflect.Value
|
||||
if isRecursiveStruct(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
if isRecursiveType(n.child[0].typ, n.child[0].typ.rtype) {
|
||||
v := genValue(n.child[0])
|
||||
value = func(f *frame) reflect.Value { return v(f).Elem().Elem() }
|
||||
} else {
|
||||
@@ -2033,8 +2054,8 @@ func doCompositeSparse(n *node, hasType bool) {
|
||||
switch {
|
||||
case c1.typ.cat == funcT:
|
||||
values[field] = genFunctionWrapper(c1)
|
||||
case isRecursiveStruct(n.typ.field[field].typ, n.typ.field[field].typ.rtype):
|
||||
values[field] = genValueInterfacePtr(c1)
|
||||
case isRecursiveType(n.typ.field[field].typ, n.typ.field[field].typ.rtype):
|
||||
values[field] = genValueRecursiveInterface(c1, n.typ.field[field].typ.rtype)
|
||||
default:
|
||||
values[field] = genValue(c1)
|
||||
}
|
||||
@@ -2336,8 +2357,8 @@ func _append(n *node) {
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
values[i] = genValueInterface(arg)
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueInterfacePtr(arg)
|
||||
case isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
values[i] = genValueRecursiveInterface(arg, n.typ.val.rtype)
|
||||
case arg.typ.untyped:
|
||||
values[i] = genValueAs(arg, n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
@@ -2358,8 +2379,8 @@ func _append(n *node) {
|
||||
switch {
|
||||
case n.typ.val.cat == interfaceT:
|
||||
value0 = genValueInterface(n.child[2])
|
||||
case isRecursiveStruct(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueInterfacePtr(n.child[2])
|
||||
case isRecursiveType(n.typ.val, n.typ.val.rtype):
|
||||
value0 = genValueRecursiveInterface(n.child[2], n.typ.val.rtype)
|
||||
case n.child[2].typ.untyped:
|
||||
value0 = genValueAs(n.child[2], n.child[1].typ.TypeOf().Elem())
|
||||
default:
|
||||
|
||||
@@ -1241,6 +1241,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
// a node can still point to a previous copy.
|
||||
st.typ.recursive = st.typ.recursive || st.typ.isRecursive()
|
||||
recursive = st.typ.isRecursive()
|
||||
// It is possible that t.recursive is not inline with st.typ.recursive
|
||||
// which will break recursion detection. Set it here to make sure it
|
||||
// is correct.
|
||||
t.recursive = recursive
|
||||
}
|
||||
}
|
||||
if wrapRecursive && t.recursive {
|
||||
@@ -1252,9 +1256,11 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
|
||||
if defined[name] != nil && defined[name].rtype != nil {
|
||||
return defined[name].rtype
|
||||
}
|
||||
if t.val != nil && defined[t.val.path+"/"+t.val.name] != nil && t.val.rtype == nil {
|
||||
if t.val != nil && t.val.cat == structT && t.val.rtype == nil && hasRecursiveStruct(t.val, defined) {
|
||||
// Replace reference to self (direct or indirect) by an interface{} to handle
|
||||
// recursive types with reflect.
|
||||
typ := *t.val
|
||||
t.val = &typ
|
||||
t.val.rtype = interf
|
||||
recursive = true
|
||||
}
|
||||
@@ -1357,6 +1363,34 @@ func (t *itype) implements(it *itype) bool {
|
||||
return t.methods().contains(it.methods())
|
||||
}
|
||||
|
||||
// hasRecursiveStruct determines if a struct is a recursion or a recursion
|
||||
// intermediate. A recursion intermediate is a struct that contains a recursive
|
||||
// struct.
|
||||
func hasRecursiveStruct(t *itype, defined map[string]*itype) bool {
|
||||
if len(defined) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := t
|
||||
for typ != nil {
|
||||
if typ.cat != structT {
|
||||
typ = typ.val
|
||||
continue
|
||||
}
|
||||
|
||||
if defined[typ.path+"/"+typ.name] != nil {
|
||||
return true
|
||||
}
|
||||
for _, f := range typ.field {
|
||||
if hasRecursiveStruct(f.typ, defined) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
func catOf(t reflect.Type) tcat {
|
||||
|
||||
@@ -182,16 +182,6 @@ func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := reflect.New(interf).Elem()
|
||||
v.Set(value(f))
|
||||
return v.Addr()
|
||||
}
|
||||
}
|
||||
|
||||
func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
@@ -211,18 +201,6 @@ func genValueInterface(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueDerefInterfacePtr(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func zeroInterfaceValue() reflect.Value {
|
||||
n := &node{kind: basicLit, typ: &itype{cat: nilT, untyped: true}}
|
||||
v := reflect.New(interf).Elem()
|
||||
@@ -269,6 +247,63 @@ func genValueNode(n *node) func(*frame) reflect.Value {
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterface(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
vv := value(f)
|
||||
v := reflect.New(t).Elem()
|
||||
toRecursive(v, vv)
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func toRecursive(dest, src reflect.Value) {
|
||||
if !src.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch dest.Kind() {
|
||||
case reflect.Map:
|
||||
v := reflect.MakeMapWithSize(dest.Type(), src.Len())
|
||||
for _, kv := range src.MapKeys() {
|
||||
vv := reflect.New(dest.Type().Elem()).Elem()
|
||||
toRecursive(vv, src.MapIndex(kv))
|
||||
vv.SetMapIndex(kv, vv)
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Slice:
|
||||
l := src.Len()
|
||||
v := reflect.MakeSlice(dest.Type(), l, l)
|
||||
for i := 0; i < l; i++ {
|
||||
toRecursive(v.Index(i), src.Index(i))
|
||||
}
|
||||
dest.Set(v)
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(dest.Type().Elem()).Elem()
|
||||
s := src
|
||||
if s.Elem().Kind() != reflect.Struct { // In the case of *interface{}, we want *struct{}
|
||||
s = s.Elem()
|
||||
}
|
||||
toRecursive(v, s)
|
||||
dest.Set(v.Addr())
|
||||
default:
|
||||
dest.Set(src)
|
||||
}
|
||||
}
|
||||
|
||||
func genValueRecursiveInterfacePtrValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
return func(f *frame) reflect.Value {
|
||||
v := value(f)
|
||||
if v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return v.Elem().Elem()
|
||||
}
|
||||
}
|
||||
|
||||
func vInt(v reflect.Value) (i int64) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
|
||||
Reference in New Issue
Block a user