Skip to content

Commit c560e72

Browse files
authored
cgen: fix defer generation within comptime $if/$match/$for (#25677)
1 parent a8b0e1d commit c560e72

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
lines changed

vlib/v/ast/ast.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,7 @@ pub:
13501350
kind ComptimeForKind
13511351
pos token.Pos
13521352
typ_pos token.Pos
1353+
scope &Scope = unsafe { nil }
13531354
pub mut:
13541355
stmts []Stmt
13551356
typ Type

vlib/v/gen/c/comptime.v

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,10 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
382382
// => skip the #if defined ... #endif wrapper
383383
// and just generate the branch statements:
384384
g.indent--
385-
g.stmts(node.branches[0].stmts)
385+
branch := node.branches[0]
386+
g.stmts(branch.stmts)
386387
g.indent++
388+
g.write_defer_stmts(branch.scope, false, branch.pos)
387389
return
388390
}
389391
}
@@ -475,6 +477,7 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
475477
g.skip_stmt_pos = prev_skip_stmt_pos
476478
g.writeln2(';', '}')
477479
g.indent--
480+
g.write_defer_stmts(branch.scope, false, branch.pos)
478481
} else {
479482
g.indent++
480483
g.set_current_pos_as_last_stmt_pos()
@@ -493,6 +496,7 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
493496
g.skip_stmt_pos = prev_skip_stmt_pos
494497
g.writeln(';')
495498
g.indent--
499+
g.write_defer_stmts(branch.scope, false, branch.pos)
496500
}
497501
}
498502
} else {
@@ -505,6 +509,7 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
505509
g.stmts(branch.stmts)
506510
}
507511
if should_create_scope {
512+
g.write_defer_stmts(branch.scope, false, branch.pos)
508513
g.writeln('}')
509514
}
510515
}
@@ -613,14 +618,15 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
613618
g.writeln('/* \$for ${node.val_var} in ${sym.name}.${node.kind.str()} */ {')
614619
g.indent++
615620
mut i := 0
616-
621+
old_defer_stmts := g.defer_stmts
617622
if node.kind == .methods {
618623
methods := sym.get_methods()
619624
if methods.len > 0 {
620625
g.writeln('FunctionData ${node.val_var} = {0};')
621626
}
622627
typ_vweb_result := g.table.find_type('vweb.Result')
623628
for method in methods {
629+
g.defer_stmts = old_defer_stmts
624630
g.push_new_comptime_info()
625631
g.comptime.inside_comptime_for = true
626632
// filter vweb route methods (non-generic method)
@@ -690,6 +696,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
690696
g.type_resolver.update_ct_type('${node.val_var}.return_type', ret_typ)
691697
g.type_resolver.update_ct_type('${node.val_var}.typ', typ)
692698
g.stmts(node.stmts)
699+
g.write_defer_stmts(node.scope, false, node.pos)
693700
i++
694701
g.writeln('}')
695702
g.pop_comptime_info()
@@ -706,13 +713,14 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
706713
else {
707714
g.error('comptime field lookup is supported only for structs and interfaces, and ${sym.name} is neither',
708715
node.pos)
709-
[]ast.StructField{len: 0}
716+
[]ast.StructField{}
710717
}
711718
}
712719
if fields.len > 0 {
713720
g.writeln('\tFieldData ${node.val_var} = {0};')
714721
}
715722
for field in fields {
723+
g.defer_stmts = old_defer_stmts
716724
g.push_new_comptime_info()
717725
g.comptime.inside_comptime_for = true
718726
g.comptime.comptime_for_field_var = node.val_var
@@ -754,6 +762,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
754762
g.type_resolver.update_ct_type('${node.val_var}.typ', field.typ)
755763
g.type_resolver.update_ct_type('${node.val_var}.unaliased_typ', unaliased_styp)
756764
g.stmts(node.stmts)
765+
g.write_defer_stmts(node.scope, false, node.pos)
757766
i++
758767
g.writeln('}')
759768
g.pop_comptime_info()
@@ -766,6 +775,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
766775
g.writeln('\tEnumData ${node.val_var} = {0};')
767776
}
768777
for val in sym.info.vals {
778+
g.defer_stmts = old_defer_stmts
769779
g.push_new_comptime_info()
770780
g.comptime.inside_comptime_for = true
771781
g.comptime.comptime_for_enum_var = node.val_var
@@ -794,6 +804,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
794804
attrs.join(', ') + '}));\n')
795805
}
796806
g.stmts(node.stmts)
807+
g.write_defer_stmts(node.scope, false, node.pos)
797808
g.writeln('}')
798809
i++
799810
g.pop_comptime_info()
@@ -806,6 +817,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
806817
g.writeln('\tVAttribute ${node.val_var} = {0};')
807818

808819
for attr in attrs {
820+
g.defer_stmts = old_defer_stmts
809821
g.push_new_comptime_info()
810822
g.comptime.inside_comptime_for = true
811823
g.comptime.comptime_for_attr_var = node.val_var
@@ -816,6 +828,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
816828
g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");')
817829
g.writeln('\t${node.val_var}.kind = AttributeKind__${attr.kind};')
818830
g.stmts(node.stmts)
831+
g.write_defer_stmts(node.scope, false, node.pos)
819832
g.writeln('}')
820833
i++
821834
g.pop_comptime_info()
@@ -828,6 +841,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
828841
}
829842
g.comptime.inside_comptime_for = true
830843
for variant in sym.info.variants {
844+
g.defer_stmts = old_defer_stmts
831845
g.push_new_comptime_info()
832846
g.comptime.inside_comptime_for = true
833847
g.comptime.comptime_for_variant_var = node.val_var
@@ -836,6 +850,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
836850
g.writeln('/* variant ${i} : ${g.table.type_to_str(variant)} */ {')
837851
g.writeln('\t${node.val_var}.typ = ${int(variant)};\t// ')
838852
g.stmts(node.stmts)
853+
g.write_defer_stmts(node.scope, false, node.pos)
839854
g.writeln('}')
840855
i++
841856
g.pop_comptime_info()
@@ -848,6 +863,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
848863
}
849864
params := if func.is_method { func.params[1..] } else { func.params }
850865
for param in params {
866+
g.defer_stmts = old_defer_stmts
851867
g.push_new_comptime_info()
852868
g.comptime.inside_comptime_for = true
853869
g.comptime.comptime_for_method_param_var = node.val_var
@@ -857,11 +873,13 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
857873
g.writeln('\t${node.val_var}.typ = ${int(param.typ)};\t// ${g.table.type_to_str(param.typ)}')
858874
g.writeln('\t${node.val_var}.name = _S("${param.name}");')
859875
g.stmts(node.stmts)
876+
g.write_defer_stmts(node.scope, false, node.pos)
860877
g.writeln('}')
861878
i++
862879
g.pop_comptime_info()
863880
}
864881
}
882+
g.defer_stmts = old_defer_stmts
865883
g.indent--
866884
g.writeln('}// \$for')
867885
}
@@ -1028,7 +1046,9 @@ fn (mut g Gen) comptime_match(node ast.MatchExpr) {
10281046
g.stmt(last)
10291047
}
10301048
g.skip_stmt_pos = prev_skip_stmt_pos
1031-
g.writeln2(';', '}')
1049+
g.writeln(';')
1050+
g.write_defer_stmts(branch.scope, false, branch.pos)
1051+
g.writeln('}')
10321052
g.indent--
10331053
} else {
10341054
g.indent++
@@ -1047,20 +1067,21 @@ fn (mut g Gen) comptime_match(node ast.MatchExpr) {
10471067
}
10481068
g.skip_stmt_pos = prev_skip_stmt_pos
10491069
g.writeln(';')
1070+
g.write_defer_stmts(branch.scope, false, branch.pos)
10501071
g.indent--
10511072
}
10521073
} else if last is ast.Return {
10531074
if last.exprs.len > 0 {
10541075
g.write('${tmp_var} = ')
10551076
g.expr(last.exprs[0])
10561077
g.writeln(';')
1078+
g.write_defer_stmts(branch.scope, false, branch.pos)
10571079
}
10581080
}
10591081
}
1060-
} else {
1061-
if is_true.val || g.pref.output_cross_c {
1062-
g.stmts(branch.stmts)
1063-
}
1082+
} else if is_true.val || g.pref.output_cross_c {
1083+
g.stmts(branch.stmts)
1084+
g.write_defer_stmts(branch.scope, false, branch.pos)
10641085
}
10651086
}
10661087
g.writeln('#endif')

vlib/v/parser/comptime.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ fn (mut p Parser) comptime_for() ast.ComptimeFor {
486486
typ: typ
487487
expr: expr
488488
typ_pos: typ_pos
489+
scope: p.scope
489490
pos: spos.extend(p.tok.pos())
490491
}
491492
}

vlib/v/tests/defer/scoped_defer_test.v

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn test_if_expr_with_defer() {
5050
fn test_scoped_defer() {
5151
mut res := 0
5252

53-
defer(fn) {
53+
defer {
5454
res++
5555
assert res == 5
5656
}
@@ -69,3 +69,51 @@ fn test_scoped_defer() {
6969
} // <- Block 2 ends. Defer 3 executes. res = 3.
7070
} // <- Block 1 ends. Defer 2 executes. res = 4.
7171
} // <- 'test_scoped_defer' ends. Defer 1 executes. res = 5.
72+
73+
fn test_defer_with_comptime_if() {
74+
mut c := 0
75+
defer { assert c == 3 }
76+
defer { c++ }
77+
$if tinyc || gcc || clang || msvc || mingw {
78+
defer { c++ }
79+
c++
80+
} $else {
81+
c = 0
82+
}
83+
}
84+
85+
fn test_defer_with_comptime_match() {
86+
mut c := 0
87+
defer { assert c == 3 }
88+
defer { c++ }
89+
$match @CCOMPILER {
90+
'tinyc', 'gcc', 'clang', 'msvc', 'mingw' {
91+
defer { c++ }
92+
c++
93+
}
94+
$else {
95+
c = 0
96+
}
97+
}
98+
}
99+
100+
fn test_defer_with_comptime_for() {
101+
mut c := 0
102+
$for f in Data.fields {
103+
defer {
104+
if f.name == 'counter' {
105+
c++
106+
}
107+
}
108+
}
109+
assert c == 1
110+
111+
$for m in Data.methods {
112+
defer {
113+
if m.name in ['operation', 'run_operation'] {
114+
c++
115+
}
116+
}
117+
}
118+
assert c == 3
119+
}

0 commit comments

Comments
 (0)