From bf4a3df9a961e18a258d94fa35b0c433424e4bbe Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 9 Sep 2022 16:56:59 +0300 Subject: Sema: allow runtime break from inline loop Closes #12787 --- test/behavior/eval.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'test/behavior/eval.zig') diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 47d2e4374e..dbbdbf2df3 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1337,3 +1337,37 @@ test "lazy value is resolved as slice operand" { try expect(@ptrToInt(ptr1) == @ptrToInt(ptr2)); try expect(ptr1.len == ptr2.len); } + +test "break from inline loop depends on runtime condition" { + const S = struct { + fn foo(a: u8) bool { + return a == 4; + } + }; + const arr = [_]u8{ 1, 2, 3, 4 }; + { + const blk = blk: { + inline for (arr) |val| { + if (S.foo(val)) { + break :blk val; + } + } + return error.TestFailed; + }; + try expect(blk == 4); + } + + { + comptime var i = 0; + const blk = blk: { + inline while (i < arr.len) : (i += 1) { + const val = arr[i]; + if (S.foo(val)) { + break :blk val; + } + } + return error.TestFailed; + }; + try expect(blk == 4); + } +} -- cgit v1.2.3 From 5e37da6ade7eb307d51c21a2dfcdbef23e9cbf08 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 9 Sep 2022 21:36:08 +0300 Subject: Sema: check_comptime_control_flow needs to check runtime_index --- src/AstGen.zig | 2 +- src/Sema.zig | 23 +++++++++++++++--- src/Zir.zig | 4 ++-- src/print_zir.zig | 2 +- test/behavior/eval.zig | 27 ++++++++++++++++++++++ .../comptime_continue_to_outer_inline_loop.zig | 21 +++++++++++++++++ 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 test/cases/compile_errors/comptime_continue_to_outer_inline_loop.zig (limited to 'test/behavior/eval.zig') diff --git a/src/AstGen.zig b/src/AstGen.zig index 476605601b..132525e4d4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1981,7 +1981,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) else .@"break"; if (break_tag == .break_inline) { - _ = try parent_gz.addNode(.check_comptime_control_flow, node); + _ = try parent_gz.addUnNode(.check_comptime_control_flow, Zir.indexToRef(continue_block), node); } _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); return Zir.Inst.Ref.unreachable_value; diff --git a/src/Sema.zig b/src/Sema.zig index 71dcfef513..2cec686989 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -144,6 +144,7 @@ pub const Block = struct { /// Non zero if a non-inline loop or a runtime conditional have been encountered. /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. runtime_index: Value.RuntimeIndex = .zero, + inline_block: Zir.Inst.Index = 0, is_comptime: bool, is_typeof: bool = false, @@ -1157,9 +1158,20 @@ fn analyzeBodyInner( }, .check_comptime_control_flow => { if (!block.is_comptime) { - if (block.runtime_cond orelse block.runtime_loop) |runtime_src| { - const inst_data = sema.code.instructions.items(.data)[inst].node; - const src = LazySrcLoc.nodeOffset(inst_data); + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const inline_block = Zir.refToIndex(inst_data.operand).?; + + var check_block = block; + const target_runtime_index = while (true) { + if (check_block.inline_block == inline_block) { + break check_block.runtime_index; + } + check_block = check_block.parent.?; + } else unreachable; + + if (@enumToInt(target_runtime_index) < @enumToInt(block.runtime_index)) { + const runtime_src = block.runtime_cond orelse block.runtime_loop.?; const msg = msg: { const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); errdefer msg.destroy(sema.gpa); @@ -1272,10 +1284,15 @@ fn analyzeBodyInner( // current list of parameters and restore it later. // Note: this probably needs to be resolved in a more general manner. const prev_params = block.params; + const prev_inline_block = block.inline_block; + if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) { + block.inline_block = inline_body[0]; + } block.params = .{}; defer { block.params.deinit(gpa); block.params = prev_params; + block.inline_block = prev_inline_block; } const opt_break_data = try sema.analyzeBodyBreak(block, inline_body); // A runtime conditional branch that needs a post-hoc block to be diff --git a/src/Zir.zig b/src/Zir.zig index 167a55d19e..4953e575f9 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -287,7 +287,7 @@ pub const Inst = struct { /// Uses the `break` union field. break_inline, /// Checks that comptime control flow does not happen inside a runtime block. - /// Uses the `node` union field. + /// Uses the `un_node` union field. check_comptime_control_flow, /// Function call. /// Uses the `pl_node` union field with payload `Call`. @@ -1600,7 +1600,7 @@ pub const Inst = struct { .bool_br_or = .bool_br, .@"break" = .@"break", .break_inline = .@"break", - .check_comptime_control_flow = .node, + .check_comptime_control_flow = .un_node, .call = .pl_node, .cmp_lt = .pl_node, .cmp_lte = .pl_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index fb1d2960c4..9e8a3f1481 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -232,6 +232,7 @@ const Writer = struct { .make_ptr_const, .validate_deref, .overflow_arithmetic_ptr, + .check_comptime_control_flow, => try self.writeUnNode(stream, inst), .ref, @@ -406,7 +407,6 @@ const Writer = struct { .alloc_inferred_comptime_mut, .ret_ptr, .ret_type, - .check_comptime_control_flow, => try self.writeNode(stream, inst), .error_value, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index dbbdbf2df3..849e0ca6cc 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1371,3 +1371,30 @@ test "break from inline loop depends on runtime condition" { try expect(blk == 4); } } + +test "inline for inside a runtime condition" { + var a = false; + if (a) { + const arr = .{ 1, 2, 3 }; + inline for (arr) |val| { + if (val < 3) continue; + try expect(val == 3); + } + } +} + +test "continue in inline for inside a comptime switch" { + const arr = .{ 1, 2, 3 }; + var count: u8 = 0; + switch (arr[1]) { + 2 => { + inline for (arr) |val| { + if (val == 2) continue; + + count += val; + } + }, + else => {}, + } + try expect(count == 4); +} diff --git a/test/cases/compile_errors/comptime_continue_to_outer_inline_loop.zig b/test/cases/compile_errors/comptime_continue_to_outer_inline_loop.zig new file mode 100644 index 0000000000..5e6a321db2 --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_to_outer_inline_loop.zig @@ -0,0 +1,21 @@ +pub export fn entry() void { + var a = false; + const arr1 = .{ 1, 2, 3 }; + loop: inline for (arr1) |val1| { + _ = val1; + if (a) { + const arr = .{ 1, 2, 3 }; + inline for (arr) |val| { + if (val < 3) continue :loop; + if (val != 3) unreachable; + } + } + } +} + +// error +// backend=stage2 +// target=native +// +// :9:30: error: comptime control flow inside runtime block +// :6:13: note: runtime control flow here -- cgit v1.2.3