From 0b64e2190ff5afc141991f9f8004f0b813a0121b Mon Sep 17 00:00:00 2001 From: Emily Hudson Date: Wed, 27 May 2020 13:47:38 +0100 Subject: [PATCH] checker,cgen: unpack multi returns in return statements --- vlib/v/checker/checker.v | 15 +++++++++-- vlib/v/gen/cgen.v | 54 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 524af0d416..3d61ee1a54 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 657fb6d1f8..1c3f84dc72 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -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])