fix: improve handling of function return values

This commit is contained in:
Marc Vertes
2020-04-30 10:32:04 +02:00
committed by GitHub
parent 01e2e4600e
commit 1feece61ce
15 changed files with 231 additions and 33 deletions

14
_test/append0.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -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: ">",

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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)