Skip to content

Commit ca1f675

Browse files
authored
ast, checker, cgen: implement if guard with multi return optional (#13273)
1 parent fe77e64 commit ca1f675

File tree

13 files changed

+202
-43
lines changed

13 files changed

+202
-43
lines changed

cmd/tools/vast/vast.v

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1613,9 +1613,17 @@ fn (t Tree) par_expr(node ast.ParExpr) &Node {
16131613
fn (t Tree) if_guard_expr(node ast.IfGuardExpr) &Node {
16141614
mut obj := new_object()
16151615
obj.add_terse('ast_type', t.string_node('IfGuardExpr'))
1616-
obj.add_terse('var_name', t.string_node(node.var_name))
1616+
obj.add_terse('vars', t.array_node_if_guard_var(node.vars))
16171617
obj.add_terse('expr', t.expr(node.expr))
16181618
obj.add_terse('expr_type', t.type_node(node.expr_type))
1619+
return obj
1620+
}
1621+
1622+
fn (t Tree) if_guard_var(node ast.IfGuardVar) &Node {
1623+
mut obj := new_object()
1624+
obj.add_terse('ast_type', t.string_node('IfGuardVar'))
1625+
obj.add_terse('name', t.string_node(node.name))
1626+
obj.add_terse('is_mut', t.bool_node(node.is_mut))
16191627
obj.add('pos', t.position(node.pos))
16201628
return obj
16211629
}
@@ -2224,6 +2232,14 @@ fn (t Tree) array_node_struct_init_field(nodes []ast.StructInitField) &Node {
22242232
return arr
22252233
}
22262234

2235+
fn (t Tree) array_node_if_guard_var(nodes []ast.IfGuardVar) &Node {
2236+
mut arr := new_array()
2237+
for node in nodes {
2238+
arr.add_item(t.if_guard_var(node))
2239+
}
2240+
return arr
2241+
}
2242+
22272243
fn (t Tree) array_node_struct_init_embed(nodes []ast.StructInitEmbed) &Node {
22282244
mut arr := new_array()
22292245
for node in nodes {

vlib/v/ast/ast.v

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,12 +1417,17 @@ pub mut:
14171417
is_used bool // asserts are used in _test.v files, as well as in non -prod builds of all files
14181418
}
14191419

1420-
// `if [x := opt()] {`
1420+
pub struct IfGuardVar {
1421+
pub mut:
1422+
name string
1423+
is_mut bool
1424+
pos token.Position
1425+
}
1426+
1427+
// `if x := opt() {`
14211428
pub struct IfGuardExpr {
14221429
pub:
1423-
var_name string
1424-
is_mut bool
1425-
pos token.Position
1430+
vars []IfGuardVar
14261431
pub mut:
14271432
expr Expr
14281433
expr_type Type

vlib/v/ast/str.v

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,14 @@ pub fn (x Expr) str() string {
456456
} + ')'
457457
}
458458
IfGuardExpr {
459-
return x.var_name + ' := ' + x.expr.str()
459+
mut s := ''
460+
for i, var in x.vars {
461+
s += var.name
462+
if i != x.vars.len - 1 {
463+
s += ', '
464+
}
465+
}
466+
return s + ' := ' + x.expr.str()
460467
}
461468
StructInit {
462469
sname := global_table.sym(x.typ).name

vlib/v/checker/checker.v

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2962,7 +2962,19 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
29622962
if mut obj.expr is ast.IfGuardExpr {
29632963
// new variable from if guard shouldn't have the optional flag for further use
29642964
// a temp variable will be generated which unwraps it
2965-
typ = obj.expr.expr_type.clear_flag(.optional)
2965+
sym := c.table.sym(obj.expr.expr_type)
2966+
if sym.kind == .multi_return {
2967+
mr_info := sym.info as ast.MultiReturn
2968+
if mr_info.types.len == obj.expr.vars.len {
2969+
for vi, var in obj.expr.vars {
2970+
if var.name == node.name {
2971+
typ = mr_info.types[vi]
2972+
}
2973+
}
2974+
}
2975+
} else {
2976+
typ = obj.expr.expr_type.clear_flag(.optional)
2977+
}
29662978
} else {
29672979
typ = c.expr(obj.expr)
29682980
}

vlib/v/checker/if.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
206206
st.check_c_expr() or { c.error('`if` expression branch has $err.msg', st.pos) }
207207
}
208208
}
209+
if mut branch.cond is ast.IfGuardExpr {
210+
sym := c.table.sym(branch.cond.expr_type)
211+
if sym.kind == .multi_return {
212+
mr_info := sym.info as ast.MultiReturn
213+
if branch.cond.vars.len != mr_info.types.len {
214+
c.error('if guard expects $mr_info.types.len variables, but got $branch.cond.vars.len',
215+
branch.pos)
216+
} else {
217+
for vi, var in branch.cond.vars {
218+
branch.scope.update_var_type(var.name, mr_info.types[vi])
219+
}
220+
}
221+
}
222+
}
209223
// Also check for returns inside a comp.if's statements, even if its contents aren't parsed
210224
if has_return := c.has_return(branch.stmts) {
211225
if has_return {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
vlib/v/checker/tests/if_guard_variables_err.vv:6:2: error: if guard expects 3 variables, but got 1
2+
4 |
3+
5 | fn main() {
4+
6 | if r1 := create() {
5+
| ~~~~~~~~~~~~~~~~~
6+
7 | println(r1)
7+
8 | }
8+
vlib/v/checker/tests/if_guard_variables_err.vv:10:2: error: if guard expects 3 variables, but got 4
9+
8 | }
10+
9 |
11+
10 | if r1, r2, r3, r4 := create() {
12+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
11 | println(r1)
14+
12 | println(r2)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn create() ?(int, string, bool) {
2+
return 5, 'aa', true
3+
}
4+
5+
fn main() {
6+
if r1 := create() {
7+
println(r1)
8+
}
9+
10+
if r1, r2, r3, r4 := create() {
11+
println(r1)
12+
println(r2)
13+
println(r3)
14+
println(r4)
15+
}
16+
}

vlib/v/fmt/fmt.v

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,10 +1889,16 @@ fn branch_is_single_line(b ast.IfBranch) bool {
18891889
}
18901890

18911891
pub fn (mut f Fmt) if_guard_expr(node ast.IfGuardExpr) {
1892-
if node.is_mut {
1893-
f.write('mut ')
1892+
for i, var in node.vars {
1893+
if var.is_mut {
1894+
f.write('mut ')
1895+
}
1896+
f.write(var.name)
1897+
if i != node.vars.len - 1 {
1898+
f.write(', ')
1899+
}
18941900
}
1895-
f.write(node.var_name + ' := ')
1901+
f.write(' := ')
18961902
f.expr(node.expr)
18971903
}
18981904

vlib/v/gen/c/cgen.v

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4677,13 +4677,13 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
46774677
g.expr(branch.cond.expr)
46784678
g.writeln(', ${var_name}.state == 0) {')
46794679
}
4680-
if short_opt || branch.cond.var_name != '_' {
4680+
if short_opt || branch.cond.vars[0].name != '_' {
46814681
base_type := g.base_type(branch.cond.expr_type)
46824682
if short_opt {
4683-
cond_var_name := if branch.cond.var_name == '_' {
4683+
cond_var_name := if branch.cond.vars[0].name == '_' {
46844684
'_dummy_${g.tmp_count + 1}'
46854685
} else {
4686-
branch.cond.var_name
4686+
branch.cond.vars[0].name
46874687
}
46884688
g.write('\t$base_type $cond_var_name = ')
46894689
g.expr(branch.cond.expr)
@@ -4692,15 +4692,33 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
46924692
mut is_auto_heap := false
46934693
if branch.stmts.len > 0 {
46944694
scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).position().pos)
4695-
if v := scope.find_var(branch.cond.var_name) {
4695+
if v := scope.find_var(branch.cond.vars[0].name) {
46964696
is_auto_heap = v.is_auto_heap
46974697
}
46984698
}
4699-
left_var_name := c_name(branch.cond.var_name)
4700-
if is_auto_heap {
4701-
g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
4702-
} else {
4703-
g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
4699+
if branch.cond.vars.len == 1 {
4700+
left_var_name := c_name(branch.cond.vars[0].name)
4701+
if is_auto_heap {
4702+
g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
4703+
} else {
4704+
g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
4705+
}
4706+
} else if branch.cond.vars.len > 1 {
4707+
for vi, var in branch.cond.vars {
4708+
left_var_name := c_name(var.name)
4709+
sym := g.table.sym(branch.cond.expr_type)
4710+
if sym.kind == .multi_return {
4711+
mr_info := sym.info as ast.MultiReturn
4712+
if mr_info.types.len == branch.cond.vars.len {
4713+
var_typ := g.typ(mr_info.types[vi])
4714+
if is_auto_heap {
4715+
g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
4716+
} else {
4717+
g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
4718+
}
4719+
}
4720+
}
4721+
}
47044722
}
47054723
}
47064724
}

vlib/v/gen/js/js.v

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,18 +2833,18 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
28332833
g.expr(branch.cond.expr)
28342834
g.writeln(', ${var_name}.state == 0) {')
28352835
}
2836-
if short_opt || branch.cond.var_name != '_' {
2836+
if short_opt || branch.cond.vars[0].name != '_' {
28372837
if short_opt {
2838-
cond_var_name := if branch.cond.var_name == '_' {
2838+
cond_var_name := if branch.cond.vars[0].name == '_' {
28392839
'_dummy_${g.tmp_count + 1}'
28402840
} else {
2841-
branch.cond.var_name
2841+
branch.cond.vars[0].name
28422842
}
28432843
g.write('\tlet $cond_var_name = ')
28442844
g.expr(branch.cond.expr)
28452845
g.writeln(';')
28462846
} else {
2847-
g.writeln('\tlet $branch.cond.var_name = ${var_name}.data;')
2847+
g.writeln('\tlet $branch.cond.vars[0].name = ${var_name}.data;')
28482848
}
28492849
}
28502850
}

0 commit comments

Comments
 (0)