diff --git a/_test/variadic7.go b/_test/variadic7.go new file mode 100644 index 00000000..7d20fd87 --- /dev/null +++ b/_test/variadic7.go @@ -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 diff --git a/interp/ast.go b/interp/ast.go index 0ad884fe..d680ef55 100644 --- a/interp/ast.go +++ b/interp/ast.go @@ -197,6 +197,7 @@ const ( aBitNot aBranch aCall + aCallSlice aCase aCompositeLit aConvert @@ -258,6 +259,7 @@ var actions = [...]string{ aBitNot: "^", aBranch: "branch", aCall: "call", + aCallSlice: "callSlice", aCase: "case", aCompositeLit: "compositeLit", aConvert: "convert", @@ -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) diff --git a/interp/cfg.go b/interp/cfg.go index b792dfac..63e345e7 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -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 @@ -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 { @@ -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() { @@ -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 } diff --git a/interp/run.go b/interp/run.go index efe1f806..5cc4470e 100644 --- a/interp/run.go +++ b/interp/run.go @@ -26,6 +26,7 @@ var builtin = [...]bltnGenerator{ aAndNotAssign: andNotAssign, aBitNot: bitNot, aCall: call, + aCallSlice: call, aCase: _case, aCompositeLit: arrayLit, aDec: dec, @@ -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 { @@ -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)) } @@ -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: @@ -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 { @@ -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]) @@ -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) } @@ -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 } @@ -1019,7 +1033,7 @@ 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]) @@ -1027,7 +1041,7 @@ func getIndexBinMethod(n *node) { 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 } @@ -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] diff --git a/interp/value.go b/interp/value.go index 34590a30..086104ad 100644 --- a/interp/value.go +++ b/interp/value.go @@ -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)