compiler: Initialize named returns to default values

Make them behave as locals. We must initialize them at the start
because the default value could also be used inside the function body.

Signed-off-by: Evgenii Stratonikov <fyfyrchik@runbox.com>
This commit is contained in:
Evgenii Stratonikov 2024-04-04 17:51:03 +03:00
parent f2bb4d455b
commit 78cefca5c9
2 changed files with 31 additions and 1 deletions

View file

@ -522,6 +522,19 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
}
}
// Emit defaults for named returns.
if decl.Type.Results.NumFields() != 0 {
for _, arg := range decl.Type.Results.List {
for _, id := range arg.Names {
if id.Name != "_" {
i := c.scope.newLocal(id.Name)
c.emitDefault(c.typeOf(arg.Type))
c.emitStoreByIndex(varLocal, i)
}
}
}
}
ast.Walk(c, decl.Body)
// If we have reached the end of the function without encountering `return` statement,
@ -763,7 +776,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
for i := len(results.List) - 1; i >= 0; i-- {
names := results.List[i].Names
for j := len(names) - 1; j >= 0; j-- {
c.emitLoadVar("", names[j].Name)
if names[j].Name == "_" {
c.emitDefault(c.typeOf(results.List[i].Type))
} else {
c.emitLoadVar("", names[j].Name)
}
}
}
}

View file

@ -120,6 +120,19 @@ func TestNamedReturn(t *testing.T) {
t.Run("AnotherVariable", runCase("b, c", big.NewInt(5)))
}
func TestNamedReturnDefault(t *testing.T) {
src := `package foo
func Main() int {
a, b, c := f()
return a + b + c
}
func f() (_ int, b int, c int) {
b += 1
return
}`
eval(t, src, big.NewInt(1))
}
func TestTypeAssertReturn(t *testing.T) {
src := `
package main