checker: handle multireturn fn calls as if/match last expressions

pull/4793/head
Tanel Liiv 2020-05-08 23:49:45 +03:00 committed by GitHub
parent b5bf0eeac5
commit 7815a5495c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 17 deletions

View File

@ -1099,32 +1099,38 @@ pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) {
pub fn (mut c Checker) assign_stmt(assign_stmt mut ast.AssignStmt) {
c.expected_type = table.none_type // TODO a hack to make `x := if ... work`
if assign_stmt.right[0] is ast.CallExpr {
call_expr := assign_stmt.right[0] as ast.CallExpr
right_first := assign_stmt.right[0]
mut right_len := assign_stmt.right.len
if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr {
right_type0 := c.expr(assign_stmt.right[0])
assign_stmt.right_types = [right_type0]
right_type_sym0 := c.table.get_type_symbol(right_type0)
mut right_len := if right_type0 == table.void_type { 0 } else { assign_stmt.right.len }
right_len = if right_type0 == table.void_type { 0 } else { right_len }
if right_type_sym0.kind == .multi_return {
assign_stmt.right_types = right_type_sym0.mr_info().types
right_len = assign_stmt.right_types.len
}
if assign_stmt.left.len != right_len {
if right_first is ast.CallExpr {
call_expr := assign_stmt.right[0] as ast.CallExpr
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${call_expr.name}()` returns $right_len value(s)',
assign_stmt.pos)
return
}
} else {
if assign_stmt.left.len != assign_stmt.right.len {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)',
assign_stmt.pos)
return
}
}
} else if assign_stmt.left.len != right_len {
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $assign_stmt.right.len value(s)',
assign_stmt.pos)
return
}
}
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
for i, _ in assign_stmt.left {
mut ident := assign_stmt.left[i]
if assign_stmt.right_types.len < assign_stmt.right.len {
if assign_stmt.right_types.len < right_len {
assign_stmt.right_types << c.expr(assign_stmt.right[i])
}
val_type := assign_stmt.right_types[i]
@ -1993,7 +1999,7 @@ pub fn (mut c Checker) if_expr(node mut ast.IfExpr) table.Type {
}
if node.has_else && node.is_expr {
last_branch := node.branches[node.branches.len - 1]
if last_branch.stmts.len > 0 {
if last_branch.stmts.len > 0 && node.branches[0].stmts.len > 0 {
match last_branch.stmts[last_branch.stmts.len - 1] {
ast.ExprStmt {
// type_sym := p.table.get_type_symbol(it.typ)
@ -2008,8 +2014,14 @@ pub fn (mut c Checker) if_expr(node mut ast.IfExpr) table.Type {
}
else {}
}
} else {
c.error('`if` expression needs returns in both branches', node.pos)
}
}
// won't yet work due to eg: if true { println('foo') }
/*if node.is_expr && !node.has_else {
c.error('`if` expression needs `else` clause. remove return values or add `else`', node.pos)
}*/
return table.bool_type
}

View File

@ -837,10 +837,18 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write('static ')
}
mut return_type := table.void_type
if assign_stmt.right[0] is ast.CallExpr {
it := assign_stmt.right[0] as ast.CallExpr
match assign_stmt.right[0] {
ast.CallExpr {
return_type = it.return_type
}
ast.IfExpr {
return_type = it.typ
}
ast.MatchExpr {
return_type = it.return_type
}
else {}
}
mut is_multi := false
// json_test failed w/o this check
if return_type != 0 {

View File

@ -0,0 +1,58 @@
fn multireturner(n int, s string) (int, string) {
return n + 1, s
}
fn test_assign_multireturn_expression() {
a, b := if true {
multireturner(13, 'awesome')
} else {
multireturner(-1, 'notawesome')
}
assert a == 14
assert b == 'awesome'
c, d := if false {
multireturner(-1, 'notawesome')
} else if true {
multireturner(17, 'awesomer')
} else {
multireturner(-1, 'notawesome')
}
assert c == 18
assert d == 'awesomer'
e, f := if false {
multireturner(-1, 'notawesome')
} else if false {
multireturner(-1, 'notawesome')
} else {
multireturner(17, 'awesomer')
}
assert e == 18
assert f == 'awesomer'
g, h := match true {
true { multireturner(0, 'good') }
false { multireturner(100, 'bad') }
else { multireturner(200, 'bad') }
}
assert g == 1
assert h == 'good'
i, j := match true {
false { multireturner(100, 'bad') }
else { multireturner(0, 'good') }
}
assert i == 1
assert j == 'good'
// TODO: returning non-function calls does not work yet due to parsing issues
/*
e, f := if true {
1, 'awesome'
} else {
0, 'bad'
}
*/
}