fix: improve handling of function return values
This commit is contained in:
14
_test/append0.go
Normal file
14
_test/append0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a []int, b int) interface{} { return append(a, b) }
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
r := f(a, 3)
|
||||
fmt.Println(r.([]int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
13
_test/cap0.go
Normal file
13
_test/cap0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f(a []int) interface{} {
|
||||
return cap(a)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
println(f(a).(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
13
_test/complex4.go
Normal file
13
_test/complex4.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a, b float64) interface{} { return complex(a, b) }
|
||||
|
||||
func main() {
|
||||
a := f(3, 2)
|
||||
fmt.Println(a.(complex128))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// (3+2i)
|
||||
16
_test/copy2.go
Normal file
16
_test/copy2.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(a, b []int) interface{} { return copy(a, b) }
|
||||
|
||||
func main() {
|
||||
a := []int{10, 20, 30}
|
||||
b := [4]int{}
|
||||
c := b[:]
|
||||
r := f(c, a)
|
||||
fmt.Println(r.(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
13
_test/fun14.go
Normal file
13
_test/fun14.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f() (bool, int) { return true, 2 }
|
||||
|
||||
func g() (bool, int) { return f() }
|
||||
|
||||
func main() {
|
||||
b, i := g()
|
||||
println(b, i)
|
||||
}
|
||||
|
||||
// Output
|
||||
// true 2
|
||||
14
_test/imag0.go
Normal file
14
_test/imag0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(c complex128) interface{} { return imag(c) }
|
||||
|
||||
func main() {
|
||||
c := complex(3, 2)
|
||||
a := f(c)
|
||||
fmt.Println(a.(float64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
13
_test/len0.go
Normal file
13
_test/len0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f(a []int) interface{} {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := []int{1, 2}
|
||||
println(f(a).(int))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
13
_test/make0.go
Normal file
13
_test/make0.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func f() interface{} {
|
||||
return make([]int, 2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := f()
|
||||
println(len(a.([]int)))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
15
_test/make1.go
Normal file
15
_test/make1.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f() interface{} {
|
||||
return make(map[int]int)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, ok := f().(map[int]int)
|
||||
fmt.Println(a, ok)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// map[] true
|
||||
10
_test/new2.go
Normal file
10
_test/new2.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func f() interface{} {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := f()
|
||||
println(*(a.(*int)))
|
||||
}
|
||||
14
_test/real0.go
Normal file
14
_test/real0.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func f(c complex128) interface{} { return real(c) }
|
||||
|
||||
func main() {
|
||||
c := complex(3, 2)
|
||||
a := f(c)
|
||||
fmt.Println(a.(float64))
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
@@ -199,6 +199,7 @@ const (
|
||||
aCall
|
||||
aCase
|
||||
aCompositeLit
|
||||
aConvert
|
||||
aDec
|
||||
aEqual
|
||||
aGreater
|
||||
@@ -257,6 +258,7 @@ var actions = [...]string{
|
||||
aCall: "call",
|
||||
aCase: "case",
|
||||
aCompositeLit: "compositeLit",
|
||||
aConvert: "convert",
|
||||
aDec: "--",
|
||||
aEqual: "==",
|
||||
aGreater: ">",
|
||||
|
||||
@@ -565,7 +565,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
case defineXStmt:
|
||||
wireChild(n)
|
||||
if sc.def == nil {
|
||||
// in global scope, type definition already handled by GTA
|
||||
// In global scope, type definition already handled by GTA.
|
||||
break
|
||||
}
|
||||
err = compDefineX(sc, n)
|
||||
@@ -575,8 +575,8 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
nilSym := interp.universe.sym["nil"]
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
t0, t1 := c0.typ.TypeOf(), c1.typ.TypeOf()
|
||||
// Shift operator type is inherited from first parameter only
|
||||
// All other binary operators require both parameter types to be the same
|
||||
// Shift operator type is inherited from first parameter only.
|
||||
// All other binary operators require both parameter types to be the same.
|
||||
if !isShiftNode(n) && !c0.typ.untyped && !c1.typ.untyped && !c0.typ.equals(c1.typ) {
|
||||
err = n.cfgErrorf("mismatched types %s and %s", c0.typ.id(), c1.typ.id())
|
||||
break
|
||||
@@ -631,22 +631,29 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
return
|
||||
}
|
||||
}
|
||||
n.typ.TypeOf() // init reflect type
|
||||
constOp[n.action](n)
|
||||
n.typ.TypeOf() // Force compute of reflection type.
|
||||
constOp[n.action](n) // Compute a constant result now rather than during exec.
|
||||
}
|
||||
switch {
|
||||
case n.rval.IsValid():
|
||||
// This operation involved constants, and the result is already computed
|
||||
// by constOp and available in n.rval. Nothing else to do at execution.
|
||||
n.gen = nop
|
||||
n.findex = -1
|
||||
case n.anc.kind == assignStmt && n.anc.action == aAssign:
|
||||
// To avoid a copy in frame, if the result is to be assigned, store it directly
|
||||
// at the frame location of destination.
|
||||
dest := n.anc.child[childPos(n)-n.anc.nright]
|
||||
n.typ = dest.typ
|
||||
n.findex = dest.findex
|
||||
case n.anc.kind == returnStmt:
|
||||
// To avoid a copy in frame, if the result is to be returned, store it directly
|
||||
// at the frame location reserved for output arguments.
|
||||
pos := childPos(n)
|
||||
n.typ = sc.def.typ.ret[pos]
|
||||
n.findex = pos
|
||||
default:
|
||||
// Allocate a new location in frame, and store the result here.
|
||||
if n.typ == nil {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
@@ -754,14 +761,18 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if n.typ, err = nodeType(interp, sc, n); err != nil {
|
||||
return
|
||||
}
|
||||
if n.typ.cat == builtinT {
|
||||
switch {
|
||||
case n.typ.cat == builtinT:
|
||||
n.findex = -1
|
||||
n.val = nil
|
||||
} else {
|
||||
case n.anc.kind == returnStmt:
|
||||
// Store result directly to frame output location, to avoid a frame copy.
|
||||
n.findex = 0
|
||||
default:
|
||||
n.findex = sc.add(n.typ)
|
||||
}
|
||||
if op, ok := constBltn[n.child[0].ident]; ok && n.anc.action != aAssign {
|
||||
op(n) // pre-compute non-assigned constant builtin calls
|
||||
op(n) // pre-compute non-assigned constant :
|
||||
}
|
||||
|
||||
case n.child[0].isType(sc):
|
||||
@@ -769,6 +780,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
if isInt(n.child[0].typ.TypeOf()) && n.child[1].kind == basicLit && isFloat(n.child[1].typ.TypeOf()) {
|
||||
err = n.cfgErrorf("truncated to integer")
|
||||
}
|
||||
n.action = aConvert
|
||||
if isInterface(n.child[0].typ) && !n.child[1].isNil() {
|
||||
// Convert to interface: just check that all required methods are defined by concrete type.
|
||||
c0, c1 := n.child[0], n.child[1]
|
||||
@@ -807,7 +819,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
}
|
||||
default:
|
||||
if n.child[0].action == aGetFunc {
|
||||
// allocate frame entry for anonymous function
|
||||
// Allocate a frame entry to store the anonymous function definition.
|
||||
sc.add(n.child[0].typ)
|
||||
}
|
||||
if typ := n.child[0].typ; len(typ.ret) > 0 {
|
||||
|
||||
@@ -616,11 +616,11 @@ func call(n *node) {
|
||||
value := genValue(n.child[0])
|
||||
var values []func(*frame) reflect.Value
|
||||
if n.child[0].recv != nil {
|
||||
// Compute method receiver value
|
||||
// Compute method receiver value.
|
||||
values = append(values, genValueRecv(n.child[0]))
|
||||
method = true
|
||||
} else if n.child[0].action == aMethod {
|
||||
// add a place holder for interface method receiver
|
||||
// Add a place holder for interface method receiver.
|
||||
values = append(values, nil)
|
||||
method = true
|
||||
}
|
||||
@@ -630,18 +630,18 @@ func call(n *node) {
|
||||
tnext := getExec(n.tnext)
|
||||
fnext := getExec(n.fnext)
|
||||
|
||||
// compute input argument value functions
|
||||
// Compute input argument value functions.
|
||||
for i, c := range child {
|
||||
switch {
|
||||
case isBinCall(c):
|
||||
// Handle nested function calls: pass returned values as arguments
|
||||
// Handle nested function calls: pass returned values as arguments.
|
||||
numOut := c.child[0].typ.rtype.NumOut()
|
||||
for j := 0; j < numOut; j++ {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
}
|
||||
case isRegularCall(c):
|
||||
// Arguments are return values of a nested function call
|
||||
// Arguments are return values of a nested function call.
|
||||
for j := range c.child[0].typ.ret {
|
||||
ind := c.findex + j
|
||||
values = append(values, func(f *frame) reflect.Value { return f.data[ind] })
|
||||
@@ -664,6 +664,7 @@ func call(n *node) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compute output argument value functions.
|
||||
rtypes := n.child[0].typ.ret
|
||||
rvalues := make([]func(*frame) reflect.Value, len(rtypes))
|
||||
switch n.anc.kind {
|
||||
@@ -679,7 +680,14 @@ func call(n *node) {
|
||||
rvalues[i] = genValue(c)
|
||||
}
|
||||
}
|
||||
case returnStmt:
|
||||
// Function call from a return statement: forward return values (always at frame start).
|
||||
for i := range rtypes {
|
||||
j := i
|
||||
rvalues[i] = func(f *frame) reflect.Value { return f.data[j] }
|
||||
}
|
||||
default:
|
||||
// Multiple return values frame index are indexed from the node frame index.
|
||||
for i := range rtypes {
|
||||
j := n.findex + i
|
||||
rvalues[i] = func(f *frame) reflect.Value { return f.data[j] }
|
||||
@@ -927,8 +935,8 @@ func callBin(n *node) {
|
||||
default:
|
||||
switch n.anc.action {
|
||||
case aAssign, aAssignX:
|
||||
// The function call is part of an assign expression, we write results direcly
|
||||
// to assigned location, to avoid an additional assign operation.
|
||||
// The function call is part of an assign expression, store results direcly
|
||||
// to assigned location, to avoid an additional frame copy.
|
||||
rvalues := make([]func(*frame) reflect.Value, funcType.NumOut())
|
||||
for i := range rvalues {
|
||||
c := n.anc.child[i]
|
||||
@@ -949,6 +957,20 @@ func callBin(n *node) {
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
case aReturn:
|
||||
// The function call is part of a return statement, store output results
|
||||
// directly in the frame location of outputs of the current function.
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
for i, v := range values {
|
||||
in[i] = v(f)
|
||||
}
|
||||
out := value(f).Call(in)
|
||||
for i, v := range out {
|
||||
f.data[i].Set(v)
|
||||
}
|
||||
return tnext
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
in := make([]reflect.Value, l)
|
||||
@@ -1533,7 +1555,6 @@ func branch(n *node) {
|
||||
|
||||
func _return(n *node) {
|
||||
child := n.child
|
||||
next := getExec(n.tnext)
|
||||
def := n.val.(*node)
|
||||
values := make([]func(*frame) reflect.Value, len(child))
|
||||
for i, c := range child {
|
||||
@@ -1561,15 +1582,15 @@ func _return(n *node) {
|
||||
|
||||
switch len(child) {
|
||||
case 0:
|
||||
n.exec = func(f *frame) bltn { return next }
|
||||
n.exec = nil
|
||||
case 1:
|
||||
if child[0].kind == binaryExpr {
|
||||
n.exec = func(f *frame) bltn { return next }
|
||||
if child[0].kind == binaryExpr || child[0].action == aCall {
|
||||
n.exec = nil
|
||||
} else {
|
||||
v := values[0]
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[0].Set(v(f))
|
||||
return next
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
@@ -1577,14 +1598,14 @@ func _return(n *node) {
|
||||
n.exec = func(f *frame) bltn {
|
||||
f.data[0].Set(v0(f))
|
||||
f.data[1].Set(v1(f))
|
||||
return next
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
n.exec = func(f *frame) bltn {
|
||||
for i, value := range values {
|
||||
f.data[i].Set(value(f))
|
||||
}
|
||||
return next
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2073,7 +2094,7 @@ func _case(n *node) {
|
||||
}
|
||||
|
||||
func appendSlice(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, n.typ.rtype)
|
||||
next := getExec(n.tnext)
|
||||
value := genValue(n.child[1])
|
||||
value0 := genValue(n.child[2])
|
||||
@@ -2098,7 +2119,7 @@ func _append(n *node) {
|
||||
appendSlice(n)
|
||||
return
|
||||
}
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, n.typ.rtype)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
@@ -2148,7 +2169,7 @@ func _append(n *node) {
|
||||
}
|
||||
|
||||
func _cap(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
@@ -2159,7 +2180,7 @@ func _cap(n *node) {
|
||||
}
|
||||
|
||||
func _copy(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||
value0 := genValueArray(n.child[1])
|
||||
value1 := genValue(n.child[2])
|
||||
next := getExec(n.tnext)
|
||||
@@ -2181,7 +2202,7 @@ func _close(n *node) {
|
||||
}
|
||||
|
||||
func _complex(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(complex(0, 0)))
|
||||
c1, c2 := n.child[1], n.child[2]
|
||||
convertLiteralValue(c1, floatType)
|
||||
convertLiteralValue(c2, floatType)
|
||||
@@ -2204,7 +2225,7 @@ func _complex(n *node) {
|
||||
}
|
||||
|
||||
func _imag(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(float64(0)))
|
||||
convertLiteralValue(n.child[1], complexType)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
@@ -2216,7 +2237,7 @@ func _imag(n *node) {
|
||||
}
|
||||
|
||||
func _real(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(float64(0)))
|
||||
convertLiteralValue(n.child[1], complexType)
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
@@ -2240,7 +2261,7 @@ func _delete(n *node) {
|
||||
}
|
||||
|
||||
func _len(n *node) {
|
||||
dest := genValue(n)
|
||||
dest := genValueOutput(n, reflect.TypeOf(int(0)))
|
||||
value := genValue(n.child[1])
|
||||
next := getExec(n.tnext)
|
||||
|
||||
@@ -2251,9 +2272,9 @@ func _len(n *node) {
|
||||
}
|
||||
|
||||
func _new(n *node) {
|
||||
dest := genValue(n)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.child[1].typ.TypeOf()
|
||||
dest := genValueOutput(n, reflect.PtrTo(typ))
|
||||
|
||||
n.exec = func(f *frame) bltn {
|
||||
dest(f).Set(reflect.New(typ))
|
||||
@@ -2263,9 +2284,9 @@ func _new(n *node) {
|
||||
|
||||
// _make allocates and initializes a slice, a map or a chan.
|
||||
func _make(n *node) {
|
||||
dest := genValue(n)
|
||||
next := getExec(n.tnext)
|
||||
typ := n.child[1].typ.frameType()
|
||||
dest := genValueOutput(n, typ)
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
|
||||
@@ -182,6 +182,21 @@ func zeroInterfaceValue() reflect.Value {
|
||||
return reflect.ValueOf(valueInterface{n, v})
|
||||
}
|
||||
|
||||
func genValueOutput(n *node, t reflect.Type) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
if n.anc.kind == returnStmt && n.anc.val.(*node).typ.ret[0].cat == interfaceT {
|
||||
// The result of the builtin has to be returned as an interface type.
|
||||
// Wrap it in a valueInterface and return the dereferenced value.
|
||||
return func(f *frame) reflect.Value {
|
||||
d := value(f)
|
||||
v := reflect.New(t).Elem()
|
||||
d.Set(reflect.ValueOf(valueInterface{n, v}))
|
||||
return v
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func genValueInterfaceValue(n *node) func(*frame) reflect.Value {
|
||||
value := genValue(n)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user