| author | |
| committer | |
| log | 5e37da6ade7eb307d51c21a2dfcdbef23e9cbf08 |
| tree | a4549f045d0c87d67519eb1045d3ddccf84d9751 |
| parent | bf4a3df9a961e18a258d94fa35b0c433424e4bbe |
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) | ... | @@ -1981,7 +1981,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) |
| 1981 | else | 1981 | else |
| 1982 | .@"break"; | 1982 | .@"break"; |
| 1983 | if (break_tag == .break_inline) { | 1983 | 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); |
| 1985 | } | 1985 | } |
| 1986 | _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); | 1986 | _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); |
| 1987 | return Zir.Inst.Ref.unreachable_value; | 1987 | return Zir.Inst.Ref.unreachable_value; |
src/Sema.zig+20-3| ... | @@ -144,6 +144,7 @@ pub const Block = struct { | ... | @@ -144,6 +144,7 @@ pub const Block = struct { |
| 144 | /// Non zero if a non-inline loop or a runtime conditional have been encountered. | 144 | /// Non zero if a non-inline loop or a runtime conditional have been encountered. |
| 145 | /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. | 145 | /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. |
| 146 | runtime_index: Value.RuntimeIndex = .zero, | 146 | runtime_index: Value.RuntimeIndex = .zero, |
| 147 | inline_block: Zir.Inst.Index = 0, | ||
| 147 | 148 | ||
| 148 | is_comptime: bool, | 149 | is_comptime: bool, |
| 149 | is_typeof: bool = false, | 150 | is_typeof: bool = false, |
| ... | @@ -1157,9 +1158,20 @@ fn analyzeBodyInner( | ... | @@ -1157,9 +1158,20 @@ fn analyzeBodyInner( |
| 1157 | }, | 1158 | }, |
| 1158 | .check_comptime_control_flow => { | 1159 | .check_comptime_control_flow => { |
| 1159 | if (!block.is_comptime) { | 1160 | 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].un_node; |
| 1161 | const inst_data = sema.code.instructions.items(.data)[inst].node; | 1162 | const src = inst_data.src(); |
| 1162 | const src = LazySrcLoc.nodeOffset(inst_data); | 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.?; | ||
| 1163 | const msg = msg: { | 1175 | const msg = msg: { |
| 1164 | const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); | 1176 | const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{}); |
| 1165 | errdefer msg.destroy(sema.gpa); | 1177 | errdefer msg.destroy(sema.gpa); |
| ... | @@ -1272,10 +1284,15 @@ fn analyzeBodyInner( | ... | @@ -1272,10 +1284,15 @@ fn analyzeBodyInner( |
| 1272 | // current list of parameters and restore it later. | 1284 | // current list of parameters and restore it later. |
| 1273 | // Note: this probably needs to be resolved in a more general manner. | 1285 | // Note: this probably needs to be resolved in a more general manner. |
| 1274 | const prev_params = block.params; | 1286 | 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 | } | ||
| 1275 | block.params = .{}; | 1291 | block.params = .{}; |
| 1276 | defer { | 1292 | defer { |
| 1277 | block.params.deinit(gpa); | 1293 | block.params.deinit(gpa); |
| 1278 | block.params = prev_params; | 1294 | block.params = prev_params; |
| 1295 | block.inline_block = prev_inline_block; | ||
| 1279 | } | 1296 | } |
| 1280 | const opt_break_data = try sema.analyzeBodyBreak(block, inline_body); | 1297 | const opt_break_data = try sema.analyzeBodyBreak(block, inline_body); |
| 1281 | // A runtime conditional branch that needs a post-hoc block to be | 1298 | // 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 { | ... | @@ -287,7 +287,7 @@ pub const Inst = struct { |
| 287 | /// Uses the `break` union field. | 287 | /// Uses the `break` union field. |
| 288 | break_inline, | 288 | break_inline, |
| 289 | /// Checks that comptime control flow does not happen inside a runtime block. | 289 | /// 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. |
| 291 | check_comptime_control_flow, | 291 | check_comptime_control_flow, |
| 292 | /// Function call. | 292 | /// Function call. |
| 293 | /// Uses the `pl_node` union field with payload `Call`. | 293 | /// Uses the `pl_node` union field with payload `Call`. |
| ... | @@ -1600,7 +1600,7 @@ pub const Inst = struct { | ... | @@ -1600,7 +1600,7 @@ pub const Inst = struct { |
| 1600 | .bool_br_or = .bool_br, | 1600 | .bool_br_or = .bool_br, |
| 1601 | .@"break" = .@"break", | 1601 | .@"break" = .@"break", |
| 1602 | .break_inline = .@"break", | 1602 | .break_inline = .@"break", |
| 1603 | .check_comptime_control_flow = .node, | 1603 | .check_comptime_control_flow = .un_node, |
| 1604 | .call = .pl_node, | 1604 | .call = .pl_node, |
| 1605 | .cmp_lt = .pl_node, | 1605 | .cmp_lt = .pl_node, |
| 1606 | .cmp_lte = .pl_node, | 1606 | .cmp_lte = .pl_node, |
src/print_zir.zig+1-1| ... | @@ -232,6 +232,7 @@ const Writer = struct { | ... | @@ -232,6 +232,7 @@ const Writer = struct { |
| 232 | .make_ptr_const, | 232 | .make_ptr_const, |
| 233 | .validate_deref, | 233 | .validate_deref, |
| 234 | .overflow_arithmetic_ptr, | 234 | .overflow_arithmetic_ptr, |
| 235 | .check_comptime_control_flow, | ||
| 235 | => try self.writeUnNode(stream, inst), | 236 | => try self.writeUnNode(stream, inst), |
| 236 | 237 | ||
| 237 | .ref, | 238 | .ref, |
| ... | @@ -406,7 +407,6 @@ const Writer = struct { | ... | @@ -406,7 +407,6 @@ const Writer = struct { |
| 406 | .alloc_inferred_comptime_mut, | 407 | .alloc_inferred_comptime_mut, |
| 407 | .ret_ptr, | 408 | .ret_ptr, |
| 408 | .ret_type, | 409 | .ret_type, |
| 409 | .check_comptime_control_flow, | ||
| 410 | => try self.writeNode(stream, inst), | 410 | => try self.writeNode(stream, inst), |
| 411 | 411 | ||
| 412 | .error_value, | 412 | .error_value, |
test/behavior/eval.zig+27| ... | @@ -1371,3 +1371,30 @@ test "break from inline loop depends on runtime condition" { | ... | @@ -1371,3 +1371,30 @@ test "break from inline loop depends on runtime condition" { |
| 1371 | try expect(blk == 4); | 1371 | try expect(blk == 4); |
| 1372 | } | 1372 | } |
| 1373 | } | 1373 | } |
| 1374 | |||
| 1375 | test "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 | |||
| 1386 | test "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 @@ | ||
| 1 | pub 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 | ||