Merge pull request #574 from nspcc-dev/feature/multireturn

compiler: implement multiple return support, closes #562.
This commit is contained in:
Roman Khimov 2019-12-25 08:46:38 +03:00 committed by GitHub
commit 9cc0fca9d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 8 deletions

View file

@ -232,6 +232,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil return nil
case *ast.AssignStmt: case *ast.AssignStmt:
multiRet := len(n.Rhs) != len(n.Lhs)
for i := 0; i < len(n.Lhs); i++ { for i := 0; i < len(n.Lhs); i++ {
switch t := n.Lhs[i].(type) { switch t := n.Lhs[i].(type) {
case *ast.Ident: case *ast.Ident:
@ -243,7 +245,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
l := c.scope.loadLocal(t.Name) l := c.scope.loadLocal(t.Name)
c.emitStoreLocal(l) c.emitStoreLocal(l)
default: default:
ast.Walk(c, n.Rhs[i]) if i == 0 || !multiRet {
ast.Walk(c, n.Rhs[i])
}
l := c.scope.loadLocal(t.Name) l := c.scope.loadLocal(t.Name)
c.emitStoreLocal(l) c.emitStoreLocal(l)
} }
@ -291,16 +296,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil return nil
case *ast.ReturnStmt: case *ast.ReturnStmt:
if len(n.Results) > 1 {
c.prog.Err = fmt.Errorf("multiple returns not supported")
return nil
}
l := c.newLabel() l := c.newLabel()
c.setLabel(l) c.setLabel(l)
if len(n.Results) > 0 { // first result should be on top of the stack
ast.Walk(c, n.Results[0]) for i := len(n.Results) - 1; i >= 0; i-- {
ast.Walk(c, n.Results[i])
} }
emitOpcode(c.prog.BinWriter, opcode.FROMALTSTACK) emitOpcode(c.prog.BinWriter, opcode.FROMALTSTACK)

View file

@ -0,0 +1,71 @@
package compiler_test
import (
"math/big"
"testing"
)
func TestMultipleReturn1(t *testing.T) {
src := `
package hello
func two() (int, int) {
return 5, 9
}
func Main() int {
a, _ := two()
return a
}
`
eval(t, src, big.NewInt(5))
}
func TestMultipleReturn2(t *testing.T) {
src := `
package hello
func two() (int, int) {
return 5, 9
}
func Main() int {
_, b := two()
return b
}
`
eval(t, src, big.NewInt(9))
}
func TestMultipleReturnWithArg(t *testing.T) {
src := `
package hello
func inc2(a int) (int, int) {
return a+1, a+2
}
func Main() int {
a, b := 3, 9
a, b = inc2(a)
return a+b
}
`
eval(t, src, big.NewInt(9))
}
func TestSingleReturn(t *testing.T) {
src := `
package hello
func inc(k int) int {
return k+1
}
func Main() int {
a, b := inc(3), inc(4)
return a+b
}
`
eval(t, src, big.NewInt(9))
}