authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-09 21:36:08+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-15 00:50:18+03:00
log5e37da6ade7eb307d51c21a2dfcdbef23e9cbf08
treea4549f045d0c87d67519eb1045d3ddccf84d9751
parentbf4a3df9a961e18a258d94fa35b0c433424e4bbe

Sema: check_comptime_control_flow needs to check runtime_index


6 files changed, 72 insertions(+), 7 deletions(-)

src/AstGen.zig+1-1
......@@ -1981,7 +1981,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
19811981 else
19821982 .@"break";
19831983 if (break_tag == .break_inline) {
1984 _ = try parent_gz.addNode(.check_comptime_control_flow, node);
1984 _ = try parent_gz.addUnNode(.check_comptime_control_flow, Zir.indexToRef(continue_block), node);
19851985 }
19861986 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
19871987 return Zir.Inst.Ref.unreachable_value;
src/Sema.zig+20-3
......@@ -144,6 +144,7 @@ pub const Block = struct {
144144 /// Non zero if a non-inline loop or a runtime conditional have been encountered.
145145 /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index.
146146 runtime_index: Value.RuntimeIndex = .zero,
147 inline_block: Zir.Inst.Index = 0,
147148
148149 is_comptime: bool,
149150 is_typeof: bool = false,
......@@ -1157,9 +1158,20 @@ fn analyzeBodyInner(
11571158 },
11581159 .check_comptime_control_flow => {
11591160 if (!block.is_comptime) {
1160 if (block.runtime_cond orelse block.runtime_loop) |runtime_src| {
1161 const inst_data = sema.code.instructions.items(.data)[inst].node;
1162 const src = LazySrcLoc.nodeOffset(inst_data);
1161 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1162 const src = inst_data.src();
1163 const inline_block = Zir.refToIndex(inst_data.operand).?;
1164
1165 var check_block = block;
1166 const target_runtime_index = while (true) {
1167 if (check_block.inline_block == inline_block) {
1168 break check_block.runtime_index;
1169 }
1170 check_block = check_block.parent.?;
1171 } else unreachable;
1172
1173 if (@enumToInt(target_runtime_index) < @enumToInt(block.runtime_index)) {
1174 const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
11631175 const msg = msg: {
11641176 const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{});
11651177 errdefer msg.destroy(sema.gpa);
......@@ -1272,10 +1284,15 @@ fn analyzeBodyInner(
12721284 // current list of parameters and restore it later.
12731285 // Note: this probably needs to be resolved in a more general manner.
12741286 const prev_params = block.params;
1287 const prev_inline_block = block.inline_block;
1288 if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) {
1289 block.inline_block = inline_body[0];
1290 }
12751291 block.params = .{};
12761292 defer {
12771293 block.params.deinit(gpa);
12781294 block.params = prev_params;
1295 block.inline_block = prev_inline_block;
12791296 }
12801297 const opt_break_data = try sema.analyzeBodyBreak(block, inline_body);
12811298 // A runtime conditional branch that needs a post-hoc block to be
src/Zir.zig+2-2
......@@ -287,7 +287,7 @@ pub const Inst = struct {
287287 /// Uses the `break` union field.
288288 break_inline,
289289 /// Checks that comptime control flow does not happen inside a runtime block.
290 /// Uses the `node` union field.
290 /// Uses the `un_node` union field.
291291 check_comptime_control_flow,
292292 /// Function call.
293293 /// Uses the `pl_node` union field with payload `Call`.
......@@ -1600,7 +1600,7 @@ pub const Inst = struct {
16001600 .bool_br_or = .bool_br,
16011601 .@"break" = .@"break",
16021602 .break_inline = .@"break",
1603 .check_comptime_control_flow = .node,
1603 .check_comptime_control_flow = .un_node,
16041604 .call = .pl_node,
16051605 .cmp_lt = .pl_node,
16061606 .cmp_lte = .pl_node,
src/print_zir.zig+1-1
......@@ -232,6 +232,7 @@ const Writer = struct {
232232 .make_ptr_const,
233233 .validate_deref,
234234 .overflow_arithmetic_ptr,
235 .check_comptime_control_flow,
235236 => try self.writeUnNode(stream, inst),
236237
237238 .ref,
......@@ -406,7 +407,6 @@ const Writer = struct {
406407 .alloc_inferred_comptime_mut,
407408 .ret_ptr,
408409 .ret_type,
409 .check_comptime_control_flow,
410410 => try self.writeNode(stream, inst),
411411
412412 .error_value,
test/behavior/eval.zig+27
......@@ -1371,3 +1371,30 @@ test "break from inline loop depends on runtime condition" {
13711371 try expect(blk == 4);
13721372 }
13731373}
1374
1375test "inline for inside a runtime condition" {
1376 var a = false;
1377 if (a) {
1378 const arr = .{ 1, 2, 3 };
1379 inline for (arr) |val| {
1380 if (val < 3) continue;
1381 try expect(val == 3);
1382 }
1383 }
1384}
1385
1386test "continue in inline for inside a comptime switch" {
1387 const arr = .{ 1, 2, 3 };
1388 var count: u8 = 0;
1389 switch (arr[1]) {
1390 2 => {
1391 inline for (arr) |val| {
1392 if (val == 2) continue;
1393
1394 count += val;
1395 }
1396 },
1397 else => {},
1398 }
1399 try expect(count == 4);
1400}
test/cases/compile_errors/comptime_continue_to_outer_inline_loop.zig created+21
......@@ -0,0 +1,21 @@
1pub export fn entry() void {
2 var a = false;
3 const arr1 = .{ 1, 2, 3 };
4 loop: inline for (arr1) |val1| {
5 _ = val1;
6 if (a) {
7 const arr = .{ 1, 2, 3 };
8 inline for (arr) |val| {
9 if (val < 3) continue :loop;
10 if (val != 3) unreachable;
11 }
12 }
13 }
14}
15
16// error
17// backend=stage2
18// target=native
19//
20// :9:30: error: comptime control flow inside runtime block
21// :6:13: note: runtime control flow here