feat: add support for named output variables (#113)

* feat: add support for named output variables

Function output parameters are located at the start of the function
frame, uninitialized, as they could be only set through return
statements.

Supporting named output variables requires to:

- identify and allocate symbols corresponding to output names:
  done at ident parsing, with the funcRet() helper,

- compute the location of output name in the frame:
  done with retRank() helper,

- initialize the frame entry with the zero value of symbol type,
  as the symbol can be accessed and written at multiple times,
  and return not setting the variable (opposite to unnamed).
  Done with frameType() helper, which now takes into account
  output parameters.

* refactor: simplify memory management

Perform function frame analysis at pre-order, instead of post-order, to
initialize memory frame, and track value types. Remove all operation
involving unitialized types. Frame memory layout is now build
incrementally, instead of having to perform dedicated tree walks on AST.
This commit is contained in:
Marc Vertes
2019-03-12 19:58:02 +01:00
committed by Ludovic Fernandez
parent 22a6d011f4
commit 6657e9a18b
12 changed files with 620 additions and 509 deletions

View File

@@ -77,16 +77,12 @@ func (interp *Interpreter) run(n *Node, cf *Frame) {
if cf == nil {
f = interp.Frame
} else {
f = &Frame{anc: cf, data: make([]reflect.Value, n.flen)}
f = &Frame{anc: cf, data: make([]reflect.Value, len(n.types))}
}
for i, t := range n.types {
// FIXME: nil types are forbidden and should be detected at compile time (CFG)
if t != nil && i < len(f.data) && !f.data[i].IsValid() {
f.data[i] = reflect.New(t).Elem()
}
f.data[i] = reflect.New(t).Elem()
}
//log.Println(n.index, "run", n.start.index)
runCfg(n.start, f)
}
@@ -117,7 +113,7 @@ func typeAssert(n *Node) {
switch {
case n.child[0].typ.cat == ValueT:
n.exec = func(f *Frame) Builtin {
f.data[i] = value(f).Elem()
f.data[i].Set(value(f).Elem())
return next
}
case n.child[1].typ.cat == InterfaceT:
@@ -129,7 +125,7 @@ func typeAssert(n *Node) {
default:
n.exec = func(f *Frame) Builtin {
v := value(f).Interface().(valueInterface)
f.data[i] = v.value
f.data[i].Set(v.value)
return next
}
}
@@ -205,30 +201,46 @@ func assignX(n *Node) {
}
}
// assignX2 implements multiple value assignment for expression where type is defined
func assignX2(n *Node) {
l := len(n.child) - 2
b := n.child[l].findex
s := n.child[:l]
next := getExec(n.tnext)
values := make([]func(*Frame) reflect.Value, l)
for i, c := range s {
values[i] = genValue(c)
}
n.exec = func(f *Frame) Builtin {
for i, value := range values {
if f.data[b+i].IsValid() {
value(f).Set(f.data[b+i])
}
}
return next
}
}
// assign implements single value assignment
func assign(n *Node) {
next := getExec(n.tnext)
value := genValue(n)
dest, src := n.child[0], n.lastChild()
var value1 func(*Frame) reflect.Value
if n.child[0].typ.cat == InterfaceT {
valueAddr := genValueAddr(n)
value1 := genValueInterface(n.lastChild())
n.exec = func(f *Frame) Builtin {
*(valueAddr(f)) = value1(f)
return next
}
} else {
value := genValue(n)
dest, src := n.child[0], n.lastChild()
var value1 func(*Frame) reflect.Value
if dest.typ.cat == ValueT && src.typ.cat == FuncT {
value1 = genNodeWrapper(src)
} else {
value1 = genValue(src)
}
n.exec = func(f *Frame) Builtin {
value(f).Set(value1(f))
return next
}
switch {
case n.child[0].typ.cat == InterfaceT:
value1 = genValueInterface(src)
case dest.typ.cat == ValueT && src.typ.cat == FuncT:
value1 = genNodeWrapper(src)
default:
value1 = genValue(src)
}
n.exec = func(f *Frame) Builtin {
value(f).Set(value1(f))
return next
}
}
@@ -322,11 +334,6 @@ func _println(n *Node) {
fmt.Printf(" ")
}
fmt.Printf("%v", value(f))
// Handle multiple results of a function call argument
for j := 1; j < child[i].fsize; j++ {
fmt.Printf(" %v", f.data[child[i].findex+j])
}
}
fmt.Println("")
return next
@@ -362,6 +369,7 @@ func genNodeWrapper(n *Node) func(*Frame) reflect.Value {
def := n.val.(*Node)
setExec(def.child[3].start)
start := def.child[3].start
numRet := len(def.typ.ret)
var receiver func(*Frame) reflect.Value
if n.recv != nil {
@@ -374,43 +382,32 @@ func genNodeWrapper(n *Node) func(*Frame) reflect.Value {
}
return reflect.MakeFunc(n.typ.TypeOf(), func(in []reflect.Value) []reflect.Value {
// Allocate and init local frame. All values to be settable and addressable.
frame := Frame{anc: f, data: make([]reflect.Value, def.flen)}
frame := Frame{anc: f, data: make([]reflect.Value, len(def.types))}
d := frame.data
for i, t := range def.types {
if t != nil {
frame.data[i] = reflect.New(t).Elem()
}
d[i] = reflect.New(t).Elem()
}
// Copy method receiver as first argument, if defined
i := 0
if receiver != nil {
frame.data[def.framepos[i]].Set(receiver(f))
i++
d[numRet].Set(receiver(f))
d = d[numRet+1:]
} else {
d = d[numRet:]
}
// Copy function input arguments in local frame
for _, arg := range in {
frame.data[def.framepos[i]].Set(arg)
i++
for i, arg := range in {
d[i].Set(arg)
}
// Interpreter code execution
runCfg(start, &frame)
// Return output values in a result vector
var result []reflect.Value
if len(def.child[2].child) > 1 {
if fieldList := def.child[2].child[1]; fieldList != nil {
result = make([]reflect.Value, len(fieldList.child))
for i, c := range fieldList.child {
if c.typ.cat == FuncT {
gv := genNodeWrapper(frame.data[i].Interface().(*Node))
result[i] = gv(f)
} else {
result[i] = frame.data[i]
}
}
result := frame.data[:numRet]
for i, r := range result {
if v, ok := r.Interface().(*Node); ok {
result[i] = genNodeWrapper(v)(f)
}
}
return result
@@ -473,6 +470,7 @@ func call(n *Node) {
values = append(values, nil)
method = true
}
numRet := len(n.child[0].typ.ret)
variadic := variadicPos(n)
child := n.child[1:]
tnext := getExec(n.tnext)
@@ -517,24 +515,21 @@ func call(n *Node) {
if def.frame != nil {
anc = def.frame
}
nf := Frame{anc: anc, data: make([]reflect.Value, def.flen)}
nf := Frame{anc: anc, data: make([]reflect.Value, len(def.types))}
var vararg reflect.Value
// Init local frame values
for i, t := range def.types {
if t != nil {
nf.data[i] = reflect.New(t).Elem()
}
nf.data[i] = reflect.New(t).Elem()
}
// Init variadic argument vector
if variadic >= 0 {
fi := def.framepos[variadic]
nf.data[fi] = reflect.New(reflect.SliceOf(def.types[fi])).Elem()
vararg = nf.data[fi]
vararg = nf.data[numRet+variadic]
}
// Copy input parameters from caller
dest := nf.data[numRet:]
for i, v := range values {
switch {
case method && i == 0:
@@ -552,24 +547,21 @@ func call(n *Node) {
} else {
src = v(f)
}
dest := nf.data[def.framepos[i]]
// Accommodate to receiver type
ks, kd := src.Kind(), dest.Kind()
if ks != kd {
d := dest[0]
if ks, kd := src.Kind(), d.Kind(); ks != kd {
if kd == reflect.Ptr {
dest.Set(src.Addr())
d.Set(src.Addr())
} else {
dest.Set(src.Elem())
d.Set(src.Elem())
}
} else {
dest.Set(src)
d.Set(src)
}
case variadic >= 0 && i >= variadic:
vararg.Set(reflect.Append(vararg, v(f)))
case len(def.typ.arg) > i && def.typ.arg[i].cat == InterfaceT:
nf.data[def.framepos[i]] = v(f)
default:
nf.data[def.framepos[i]].Set(v(f))
dest[i].Set(v(f))
}
}
@@ -588,7 +580,6 @@ func call(n *Node) {
return fnext
}
// Propagate return values to caller frame
//log.Println(n.index, "call rets:", ret, nf.data[:len(ret)])
for i, r := range ret {
f.data[r] = nf.data[i]
}
@@ -650,7 +641,6 @@ func callBin(n *Node) {
}
}
l := len(values)
fsize := n.child[0].fsize
switch {
case n.anc.kind == GoStmt:
@@ -670,8 +660,8 @@ func callBin(n *Node) {
for i, v := range values {
in[i] = v(f)
}
r := value(f).Call(in)
if r[0].Bool() {
res := value(f).Call(in)
if res[0].Bool() {
return tnext
}
return fnext
@@ -682,12 +672,7 @@ func callBin(n *Node) {
for i, v := range values {
in[i] = v(f)
}
//log.Println(n.index, "callbin", value(f).Type(), in)
r := value(f).Call(in)
//log.Println(n.index, "callBin, res:", r, fsize, n.findex, len(r), len(f.data))
for i := 0; i < fsize; i++ {
f.data[n.findex+i] = r[i]
}
copy(f.data[n.findex:], value(f).Call(in))
return tnext
}
}
@@ -769,16 +754,13 @@ func getIndexMap2(n *Node) {
func getFunc(n *Node) {
i := n.findex
next := getExec(n.tnext)
if len(n.types) == 0 {
n.types, _ = frameTypes(n, n.flen)
}
n.exec = func(f *Frame) Builtin {
frame := *f
node := *n
node.val = &node
node.frame = &frame
f.data[i] = reflect.ValueOf(&node)
f.data[i].Set(reflect.ValueOf(&node))
return next
}
}
@@ -1178,29 +1160,38 @@ func _case(n *Node) {
for i := range types {
types[i] = n.child[i].typ
}
value := genValue(sn.child[1].lastChild().child[0])
srcValue := genValue(sn.child[1].lastChild().child[0])
if len(sn.child[1].child) == 2 {
// assign in switch guard
vaddr := genValueAddr(sn.child[1].child[0])
destValue := genValue(n.lastChild().child[0])
switch len(types) {
case 0:
// default clause: assign var to interface value
n.exec = func(f *Frame) Builtin {
*(vaddr(f)) = value(f)
destValue(f).Set(srcValue(f))
return tnext
}
case 1:
// match against 1 type: assign var to concrete value
typ := types[0]
n.exec = func(f *Frame) Builtin {
if v := value(f); !v.IsValid() {
v := srcValue(f)
if !v.IsValid() {
// match zero value against nil
if typ.cat == NilT {
return tnext
}
return fnext
} else if vi := v.Interface().(valueInterface); vi.node.typ.id() == typ.id() {
*(vaddr(f)) = vi.value
}
vi := v.Interface().(valueInterface)
if vi.node == nil {
if typ.cat == NilT {
return tnext
}
return fnext
}
if vi.node.typ.id() == typ.id() {
destValue(f).Set(vi.value)
return tnext
}
return fnext
@@ -1208,10 +1199,11 @@ func _case(n *Node) {
default:
// match against multiple types: assign var to interface value
n.exec = func(f *Frame) Builtin {
vtyp := value(f).Interface().(valueInterface).node.typ
val := srcValue(f)
vid := val.Interface().(valueInterface).node.typ.id()
for _, typ := range types {
if vtyp.id() == typ.id() {
*(vaddr(f)) = value(f)
if vid == typ.id() {
destValue(f).Set(val)
return tnext
}
}
@@ -1224,7 +1216,7 @@ func _case(n *Node) {
n.exec = func(f *Frame) Builtin { return tnext }
} else {
n.exec = func(f *Frame) Builtin {
vtyp := value(f).Interface().(valueInterface).node.typ
vtyp := srcValue(f).Interface().(valueInterface).node.typ
for _, typ := range types {
if vtyp.id() == typ.id() {
return tnext