checker,cgen: unpack multi returns in return statements

pull/5075/head
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{} mut got_types := []table.Type{}
for expr in return_stmt.exprs { for expr in return_stmt.exprs {
typ := c.expr(expr) typ := c.expr(expr)
// 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 got_types << typ
} }
}
return_stmt.types = got_types return_stmt.types = got_types
// allow `none` & `error (Option)` return types for function that returns optional // 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']] { if exp_is_optional && got_types[0].idx() in [table.none_type_idx, c.table.type_idxs['Option']] {
return return
} }
if expected_types.len > 0 && expected_types.len != got_types.len { 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) c.error('wrong number of return arguments', return_stmt.pos)
return
} }
for i, exp_type in expected_types { for i, exp_type in expected_types {
got_typ := got_types[i] got_typ := got_types[i]
is_generic := exp_type == table.t_type 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) { fn (mut g Gen) return_statement(node ast.Return) {
g.write('return ') g.write('return ')
if g.fn_decl.name == 'main' { if g.fn_decl.name == 'main' {
@ -2226,18 +2239,57 @@ fn (mut g Gen) return_statement(node ast.Return) {
} else { } else {
styp = g.typ(g.fn_decl.return_type) styp = g.typ(g.fn_decl.return_type)
} }
// Use this to keep the tmp assignments in order
mut multi_unpack := ''
g.write('($styp){') g.write('($styp){')
mut arg_idx := 0
for i, expr in node.exprs { 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.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 { if i < node.exprs.len - 1 {
g.write(',') g.write(',')
} }
} }
g.write('}') g.write('}')
if fn_return_is_optional { if fn_return_is_optional {
g.write(' }, sizeof($styp))') g.write(' }, sizeof($styp))')
} }
// Make sure to add our unpacks
g.insert_before_stmt(multi_unpack)
} else if node.exprs.len >= 1 { } else if node.exprs.len >= 1 {
// normal return // normal return
return_sym := g.table.get_type_symbol(node.types[0]) return_sym := g.table.get_type_symbol(node.types[0])