diff --git a/src/AstGen.zig b/src/AstGen.zig index 528ef930e62da54010692c01dcee4ec8eea7d6f7..0078057eefc2a1574c079971ac6afcad592cd863 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1940,6 +1940,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) .break_inline else .@"break"; + if (break_tag == .break_inline) { + _ = try parent_gz.addNode(.check_comptime_control_flow, node); + } _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); return Zir.Inst.Ref.unreachable_value; }, @@ -2473,6 +2476,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .repeat_inline, .panic, .panic_comptime, + .check_comptime_control_flow, => { noreturn_src_node = statement; break :b true; diff --git a/src/Module.zig b/src/Module.zig index 397134d9116f51dd363dd0c972feea044c3218fa..deff4620b9d6244bd0a86175344e9001e490276e 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2283,6 +2283,8 @@ pub const SrcLoc = struct { .@"while" => tree.whileFull(node).ast.cond_expr, .for_simple => tree.forSimple(node).ast.cond_expr, .@"for" => tree.forFull(node).ast.cond_expr, + .@"orelse" => node, + .@"catch" => node, else => unreachable, }; return nodeToSpan(tree, src_node); diff --git a/src/Sema.zig b/src/Sema.zig index ac9e24a9be60e091a98c5c41545e566d550e285a..5a70679b8d00630c10b6cbd9714748b1eb1bf8c4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1146,6 +1146,24 @@ fn analyzeBodyInner( i += 1; continue; }, + .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 msg = msg: { + const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } + i += 1; + continue; + }, // Special case instructions to handle comptime control flow. .@"break" => { diff --git a/src/Zir.zig b/src/Zir.zig index 6e9b133310884eec8927ed4673f2b89933505136..4540032605d750c296fcb50a589d15c80f099e05 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -280,6 +280,9 @@ pub const Inst = struct { /// break instruction in a block, and the target block is the parent. /// Uses the `break` union field. break_inline, + /// Checks that comptime control flow does not happen inside a runtime block. + /// Uses the `node` union field. + check_comptime_control_flow, /// Function call. /// Uses the `pl_node` union field with payload `Call`. /// AST node is the function call. @@ -1266,6 +1269,7 @@ pub const Inst = struct { .repeat_inline, .panic, .panic_comptime, + .check_comptime_control_flow, => true, }; } @@ -1315,6 +1319,7 @@ pub const Inst = struct { .set_runtime_safety, .memcpy, .memset, + .check_comptime_control_flow, => true, .param, @@ -1595,6 +1600,7 @@ pub const Inst = struct { .bool_br_or = .bool_br, .@"break" = .@"break", .break_inline = .@"break", + .check_comptime_control_flow = .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 de51c271c4d5695035c00b49a1df1de606ac7fc3..7723446f1c3d5171ea04b083d6f79d299c916868 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -409,6 +409,7 @@ 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/cases/compile_errors/comptime_continue_inside_runtime_catch.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_catch.zig new file mode 100644 index 0000000000000000000000000000000000000000..9e62420f1f3329118ca97597b4f0ee36eb26223b --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_catch.zig @@ -0,0 +1,16 @@ +export fn entry() void { + const ints = [_]u8{ 1, 2 }; + inline for (ints) |_| { + bad() catch continue; + } +} +fn bad() !void { + return error.Bad; +} + +// error +// backend=stage2 +// target=native +// +// :4:21: error: comptime control flow inside runtime block +// :4:15: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig new file mode 100644 index 0000000000000000000000000000000000000000..b2a7312c529301e7f6371d8350f8b9683defecbb --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: usize = undefined; + comptime var q = true; + inline while (q) { + if (p == 11) continue; + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :5:22: error: comptime control flow inside runtime block +// :5:15: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig new file mode 100644 index 0000000000000000000000000000000000000000..194274a1edf82f3a7a8edea31e242f2a1437a6ba --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: anyerror!i32 = undefined; + comptime var q = true; + inline while (q) { + if (p) |_| continue else |_| {} + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :5:20: error: comptime control flow inside runtime block +// :5:13: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig new file mode 100644 index 0000000000000000000000000000000000000000..965454ef03f0bf51b2e5cc8dea46486675aa612b --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: ?i32 = undefined; + comptime var q = true; + inline while (q) { + if (p) |_| continue; + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :5:20: error: comptime control flow inside runtime block +// :5:13: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig new file mode 100644 index 0000000000000000000000000000000000000000..56b65c1ab7c3c7868873b97dbbf513aa9c0cf95e --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig @@ -0,0 +1,16 @@ +export fn entry() void { + const ints = [_]u8{ 1, 2 }; + inline for (ints) |_| { + bad() orelse continue; + } +} +fn bad() ?void { + return null; +} + +// error +// backend=stage2 +// target=native +// +// :4:22: error: comptime control flow inside runtime block +// :4:15: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig new file mode 100644 index 0000000000000000000000000000000000000000..391ecbdf1a2bc48d09427ec4ca905180a8c07cc8 --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig @@ -0,0 +1,18 @@ +export fn entry() void { + var p: i32 = undefined; + comptime var q = true; + inline while (q) { + switch (p) { + 11 => continue, + else => {}, + } + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :6:19: error: comptime control flow inside runtime block +// :5:17: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig new file mode 100644 index 0000000000000000000000000000000000000000..54d62e6d371cf1ff3602b6d9e4f3a4e6d61fe28c --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p == 11) continue :outer; + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :5:25: error: comptime control flow inside runtime block +// :5:18: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig new file mode 100644 index 0000000000000000000000000000000000000000..0eef1c33745ec04619b57f79ecb0f15bd42c70f1 --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig @@ -0,0 +1,17 @@ +export fn entry() void { + var p: anyerror!usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p) |_| { + continue :outer; + } else |_| {} + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :6:13: error: comptime control flow inside runtime block +// :5:16: note: runtime control flow here diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig new file mode 100644 index 0000000000000000000000000000000000000000..e6753a5911e34415c90f42589bfcc9c19ae355eb --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: ?usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p) |_| continue :outer; + q = false; + } +} + +// error +// backend=stage2 +// target=native +// +// :5:23: error: comptime control flow inside runtime block +// :5:16: note: runtime control flow here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig deleted file mode 100644 index 7eefeb80b4a1f31c3855d85811752a4e970ee104..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig +++ /dev/null @@ -1,16 +0,0 @@ -export fn entry() void { - const ints = [_]u8{ 1, 2 }; - inline for (ints) |_| { - bad() catch continue; - } -} -fn bad() !void { - return error.Bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:21: error: comptime control flow inside runtime block -// tmp.zig:4:15: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig deleted file mode 100644 index 9ace5ddceb6513056031aa183fdcff5070c31245..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var p: usize = undefined; - comptime var q = true; - inline while (q) { - if (p == 11) continue; - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:22: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig deleted file mode 100644 index 554ba3c43e5ab55e5855acaf1a24269d87b85d77..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var p: anyerror!i32 = undefined; - comptime var q = true; - inline while (q) { - if (p) |_| continue else |_| {} - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:20: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig deleted file mode 100644 index 32c71e5c7751725196a0e809f43e369ff24dec41..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var p: ?i32 = undefined; - comptime var q = true; - inline while (q) { - if (p) |_| continue; - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:20: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig deleted file mode 100644 index d145897b4167ff3892fcd35e0e87100759bbdcc2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig +++ /dev/null @@ -1,18 +0,0 @@ -export fn entry() void { - var p: i32 = undefined; - comptime var q = true; - inline while (q) { - switch (p) { - 11 => continue, - else => {}, - } - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:19: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig deleted file mode 100644 index 8e57854728208344098e268d501647199e51106c..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var p: usize = undefined; - comptime var q = true; - outer: inline while (q) { - while (p == 11) continue :outer; - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:25: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig deleted file mode 100644 index 818455c3540bb7ae27e552703411a5ce023540d2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig +++ /dev/null @@ -1,17 +0,0 @@ -export fn entry() void { - var p: anyerror!usize = undefined; - comptime var q = true; - outer: inline while (q) { - while (p) |_| { - continue :outer; - } else |_| {} - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:13: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig deleted file mode 100644 index ed22cc2cacc6573104293ab240be3e39413303ed..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry() void { - var p: ?usize = undefined; - comptime var q = true; - outer: inline while (q) { - while (p) |_| continue :outer; - q = false; - } -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:23: error: comptime control flow inside runtime block -// tmp.zig:5:9: note: runtime block created here