compiler: correctly handle defer in functions without return values

Fix #2253.
This commit is contained in:
Roman Khimov 2021-11-12 19:30:50 +03:00
parent b31a8d750e
commit a91581fa16
2 changed files with 37 additions and 4 deletions

View file

@ -499,6 +499,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
// we should clean alt.stack manually. // we should clean alt.stack manually.
// This can be the case with void and named-return functions. // This can be the case with void and named-return functions.
if !isInit && !isDeploy && !lastStmtIsReturn(decl.Body) { if !isInit && !isDeploy && !lastStmtIsReturn(decl.Body) {
c.processDefers()
c.saveSequencePoint(decl.Body) c.saveSequencePoint(decl.Body)
emit.Opcodes(c.prog.BinWriter, opcode.RET) emit.Opcodes(c.prog.BinWriter, opcode.RET)
} }
@ -1329,10 +1330,13 @@ func (c *codegen) processDefers() {
c.emitStoreByIndex(varLocal, finalIndex) c.emitStoreByIndex(varLocal, finalIndex)
ast.Walk(c, stmt.expr) ast.Walk(c, stmt.expr)
if i == 0 { if i == 0 {
// After panic, default values must be returns, except for named returns, results := c.scope.decl.Type.Results
// which we don't support here for now. if results.NumFields() != 0 {
for i := len(c.scope.decl.Type.Results.List) - 1; i >= 0; i-- { // After panic, default values must be returns, except for named returns,
c.emitDefault(c.typeOf(c.scope.decl.Type.Results.List[i].Type)) // which we don't support here for now.
for i := len(results.List) - 1; i >= 0; i-- {
c.emitDefault(c.typeOf(results.List[i].Type))
}
} }
} }
emit.Jmp(c.prog.BinWriter, opcode.ENDTRYL, after) emit.Jmp(c.prog.BinWriter, opcode.ENDTRYL, after)

View file

@ -3,6 +3,8 @@ package compiler_test
import ( import (
"math/big" "math/big"
"testing" "testing"
"github.com/stretchr/testify/require"
) )
func TestDefer(t *testing.T) { func TestDefer(t *testing.T) {
@ -79,6 +81,33 @@ func TestDefer(t *testing.T) {
}` }`
eval(t, src, big.NewInt(13)) eval(t, src, big.NewInt(13))
}) })
t.Run("NoReturnReturn", func(t *testing.T) {
src := `package main
var i int
func Main() {
defer func() {
i++
}()
return
}`
vm := vmAndCompile(t, src)
err := vm.Run()
require.NoError(t, err)
require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items")
})
t.Run("NoReturnNoReturn", func(t *testing.T) {
src := `package main
var i int
func Main() {
defer func() {
i++
}()
}`
vm := vmAndCompile(t, src)
err := vm.Run()
require.NoError(t, err)
require.Equal(t, 0, vm.Estack().Len(), "stack contains unexpected items")
})
} }
func TestRecover(t *testing.T) { func TestRecover(t *testing.T) {