Skip to content

Commit

Permalink
fix: handle call ellipsis
Browse files Browse the repository at this point in the history
  • Loading branch information
nrwiersma committed Jun 3, 2020
1 parent 2de0c80 commit eb25c84
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 12 deletions.
20 changes: 20 additions & 0 deletions _test/variadic7.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import "fmt"

func main() {
var a, b string

pattern := "%s %s"
dest := []interface{}{&a, &b}

n, err := fmt.Sscanf("test1 test2", pattern, dest...)
if err != nil || n != len(dest) {
println("error")
return
}
println(a, b)
}

// Output:
// test1 test2
9 changes: 8 additions & 1 deletion interp/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const (
aBitNot
aBranch
aCall
aCallSlice
aCase
aCompositeLit
aConvert
Expand Down Expand Up @@ -258,6 +259,7 @@ var actions = [...]string{
aBitNot: "^",
aBranch: "branch",
aCall: "call",
aCallSlice: "callSlice",
aCase: "case",
aCompositeLit: "compositeLit",
aConvert: "convert",
Expand Down Expand Up @@ -549,7 +551,12 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
st.push(addChild(&root, anc, pos, kind, aNop), nod)

case *ast.CallExpr:
st.push(addChild(&root, anc, pos, callExpr, aCall), nod)
action := aCall
if a.Ellipsis != token.NoPos {
action = aCallSlice
}

st.push(addChild(&root, anc, pos, callExpr, action), nod)

case *ast.CaseClause:
st.push(addChild(&root, anc, pos, caseClause, aCase), nod)
Expand Down
10 changes: 7 additions & 3 deletions interp/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
// Propagate type
// TODO: Check that existing destination type matches source type
switch {
case n.action == aAssign && src.action == aCall && dest.typ.cat != interfaceT && !isRecursiveField(dest):
case n.action == aAssign && isCall(src) && dest.typ.cat != interfaceT && !isRecursiveField(dest):
// Call action may perform the assignment directly.
n.gen = nop
src.level = level
Expand Down Expand Up @@ -827,7 +827,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
typ := n.child[0].typ.rtype
numIn := len(n.child) - 1
tni := typ.NumIn()
if numIn == 1 && n.child[1].action == aCall {
if numIn == 1 && isCall(n.child[1]) {
numIn = n.child[1].typ.numOut()
}
if n.child[0].action == aGetMethod {
Expand Down Expand Up @@ -1224,7 +1224,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
case returnStmt:
if mustReturnValue(sc.def.child[2]) {
nret := len(n.child)
if nret == 1 && n.child[0].action == aCall {
if nret == 1 && isCall(n.child[0]) {
nret = n.child[0].child[0].typ.numOut()
}
if nret < sc.def.typ.numOut() {
Expand Down Expand Up @@ -2032,6 +2032,10 @@ func isMapEntry(n *node) bool {
return n.action == aGetIndex && isMap(n.child[0].typ)
}

func isCall(n *node) bool {
return n.action == aCall || n.action == aCallSlice
}

func isBinCall(n *node) bool {
return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func
}
Expand Down
30 changes: 22 additions & 8 deletions interp/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var builtin = [...]bltnGenerator{
aAndNotAssign: andNotAssign,
aBitNot: bitNot,
aCall: call,
aCallSlice: call,
aCase: _case,
aCompositeLit: arrayLit,
aDec: dec,
Expand Down Expand Up @@ -884,6 +885,12 @@ func callBin(n *node) {
}
}

// Determine if we should use `Call` or `CallSlice` on the function Value.
callFn := func(v reflect.Value, in []reflect.Value) []reflect.Value { return v.Call(in) }
if n.action == aCallSlice {
callFn = func(v reflect.Value, in []reflect.Value) []reflect.Value { return v.CallSlice(in) }
}

for i, c := range child {
defType := funcType.In(pindex(i, variadic))
switch {
Expand Down Expand Up @@ -919,6 +926,13 @@ func callBin(n *node) {
values = append(values, genFunctionWrapper(c))
case interfaceT:
values = append(values, genValueInterfaceValue(c))
case arrayT:
switch c.typ.val.cat {
case interfaceT:
values = append(values, genValueInterfaceArray(c))
default:
values = append(values, genInterfaceWrapper(c, defType))
}
default:
values = append(values, genInterfaceWrapper(c, defType))
}
Expand All @@ -945,7 +959,7 @@ func callBin(n *node) {
for i, v := range values {
in[i] = v(f)
}
go value(f).Call(in)
go callFn(value(f), in)
return tnext
}
case fnext != nil:
Expand All @@ -956,7 +970,7 @@ func callBin(n *node) {
for i, v := range values {
in[i] = v(f)
}
res := value(f).Call(in)
res := callFn(value(f), in)
b := res[0].Bool()
f.data[index].SetBool(b)
if b {
Expand All @@ -981,7 +995,7 @@ func callBin(n *node) {
for i, v := range values {
in[i] = v(f)
}
out := value(f).Call(in)
out := callFn(value(f), in)
for i, v := range rvalues {
if v != nil {
v(f).Set(out[i])
Expand All @@ -998,7 +1012,7 @@ func callBin(n *node) {
for i, v := range values {
in[i] = v(f)
}
out := value(f).Call(in)
out := callFn(value(f), in)
for i, v := range out {
f.data[b+i].Set(v)
}
Expand All @@ -1010,7 +1024,7 @@ func callBin(n *node) {
for i, v := range values {
in[i] = v(f)
}
out := value(f).Call(in)
out := callFn(value(f), in)
copy(f.data[n.findex:], out)
return tnext
}
Expand All @@ -1019,15 +1033,15 @@ func callBin(n *node) {
}

func getIndexBinMethod(n *node) {
//dest := genValue(n)
// dest := genValue(n)
i := n.findex
m := n.val.(int)
value := genValue(n.child[0])
next := getExec(n.tnext)

n.exec = func(f *frame) bltn {
// Can not use .Set() because dest type contains the receiver and source not
//dest(f).Set(value(f).Method(m))
// dest(f).Set(value(f).Method(m))
f.data[i] = value(f).Method(m)
return next
}
Expand Down Expand Up @@ -1623,7 +1637,7 @@ func _return(n *node) {
case 0:
n.exec = nil
case 1:
if child[0].kind == binaryExpr || child[0].action == aCall {
if child[0].kind == binaryExpr || isCall(child[0]) {
n.exec = nil
} else {
v := values[0]
Expand Down
13 changes: 13 additions & 0 deletions interp/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,19 @@ func genValueRangeArray(n *node) func(*frame) reflect.Value {
}
}

func genValueInterfaceArray(n *node) func(*frame) reflect.Value {
value := genValue(n)
return func(f *frame) reflect.Value {
vi := value(f).Interface().([]valueInterface)
v := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(vi), len(vi))
for i, vv := range vi {
v.Index(i).Set(vv.value)
}

return v
}
}

func genValueInterfacePtr(n *node) func(*frame) reflect.Value {
value := genValue(n)

Expand Down

0 comments on commit eb25c84

Please sign in to comment.