1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker,cgen: unpack multi returns in return statements

This commit is contained in:
Emily Hudson 2020-05-27 13:47:38 +01:00 committed by GitHub
parent 72ed673566
commit 0b64e2190f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 3 deletions

View File

@ -1167,17 +1167,28 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
mut got_types := []table.Type{}
for expr in return_stmt.exprs {
typ := c.expr(expr)
got_types << typ
// Unpack multi return types
sym := c.table.get_type_symbol(typ)
if sym.kind == .multi_return {
for t in sym.mr_info().types {
got_types << t
}
} else {
got_types << typ
}
}
return_stmt.types = got_types
// allow `none` & `error (Option)` return types for function that returns optional
if exp_is_optional && got_types[0].idx() in [table.none_type_idx, c.table.type_idxs['Option']] {
return
}
if expected_types.len > 0 && expected_types.len != got_types.len {
// c.error('wrong number of return arguments:\n\texpected: $expected_table.str()\n\tgot: $got_types.str()', return_stmt.pos)
c.error('wrong number of return arguments', return_stmt.pos)
return
}
for i, exp_type in expected_types {
got_typ := got_types[i]
is_generic := exp_type == table.t_type

View File

@ -2191,6 +2191,19 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
}
}
[inline]
fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool {
match expr {
ast.CallExpr {
return g.table.get_type_symbol(it.return_type).kind == .multi_return
}
else {
return false
}
}
}
fn (mut g Gen) return_statement(node ast.Return) {
g.write('return ')
if g.fn_decl.name == 'main' {
@ -2226,18 +2239,57 @@ fn (mut g Gen) return_statement(node ast.Return) {
} else {
styp = g.typ(g.fn_decl.return_type)
}
// Use this to keep the tmp assignments in order
mut multi_unpack := ''
g.write('($styp){')
mut arg_idx := 0
for i, expr in node.exprs {
g.write('.arg$i=')
// Check if we are dealing with a multi return and handle it seperately
if g.expr_is_multi_return_call(expr) {
c := expr as ast.CallExpr
expr_sym := g.table.get_type_symbol(c.return_type)
// Create a tmp for this call
tmp := g.new_tmp_var()
s := g.go_before_stmt(0)
expr_styp := g.typ(c.return_type)
g.write('$expr_styp $tmp=')
g.expr(expr)
g.writeln(';')
multi_unpack += g.go_before_stmt(0)
g.write(s)
expr_types := expr_sym.mr_info().types
for j, _ in expr_types {
g.write('.arg$arg_idx=${tmp}.arg$j')
if j < expr_types.len || i < node.exprs.len - 1 {
g.write(',')
}
arg_idx++
}
continue
}
g.write('.arg$arg_idx=')
g.expr(expr)
arg_idx++
if i < node.exprs.len - 1 {
g.write(',')
}
}
g.write('}')
if fn_return_is_optional {
g.write(' }, sizeof($styp))')
}
// Make sure to add our unpacks
g.insert_before_stmt(multi_unpack)
} else if node.exprs.len >= 1 {
// normal return
return_sym := g.table.get_type_symbol(node.types[0])