| ... | @@ -39,6 +39,9 @@ func: ?*Module.Fn, | ... | @@ -39,6 +39,9 @@ func: ?*Module.Fn, |
| 39 | fn_ret_ty: Type, | 39 | fn_ret_ty: Type, |
| 40 | branch_quota: u32 = 1000, | 40 | branch_quota: u32 = 1000, |
| 41 | branch_count: u32 = 0, | 41 | branch_count: u32 = 0, |
| | 42 | /// Populated when returning `error.ComptimeBreak`. Used to communicate the |
| | 43 | /// break instruction up the stack to find the corresponding Block. |
| | 44 | comptime_break_inst: Zir.Inst.Index = undefined, |
| 42 | /// This field is updated when a new source location becomes active, so that | 45 | /// This field is updated when a new source location becomes active, so that |
| 43 | /// instructions which do not have explicitly mapped source locations still have | 46 | /// instructions which do not have explicitly mapped source locations still have |
| 44 | /// access to the source location set by the previous instruction which did | 47 | /// access to the source location set by the previous instruction which did |
| ... | @@ -486,8 +489,31 @@ pub fn deinit(sema: *Sema) void { | ... | @@ -486,8 +489,31 @@ pub fn deinit(sema: *Sema) void { |
| 486 | /// has no peers. | 489 | /// has no peers. |
| 487 | fn resolveBody(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) CompileError!Air.Inst.Ref { | 490 | fn resolveBody(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 488 | const break_inst = try sema.analyzeBody(block, body); | 491 | const break_inst = try sema.analyzeBody(block, body); |
| 489 | const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand; | 492 | const break_data = sema.code.instructions.items(.data)[break_inst].@"break"; |
| 490 | return sema.resolveInst(operand_ref); | 493 | // For comptime control flow, we need to detect when `analyzeBody` reports |
| | 494 | // that we need to break from an outer block. In such case we |
| | 495 | // use Zig's error mechanism to send control flow up the stack until |
| | 496 | // we find the corresponding block to this break. |
| | 497 | if (block.is_comptime) { |
| | 498 | if (block.label) |label| { |
| | 499 | if (label.zir_block != break_data.block_inst) { |
| | 500 | sema.comptime_break_inst = break_inst; |
| | 501 | return error.ComptimeBreak; |
| | 502 | } |
| | 503 | } |
| | 504 | } |
| | 505 | return sema.resolveInst(break_data.operand); |
| | 506 | } |
| | 507 | |
| | 508 | pub fn analyzeBody( |
| | 509 | sema: *Sema, |
| | 510 | block: *Block, |
| | 511 | body: []const Zir.Inst.Index, |
| | 512 | ) CompileError!Zir.Inst.Index { |
| | 513 | return sema.analyzeBodyInner(block, body) catch |err| switch (err) { |
| | 514 | error.ComptimeBreak => sema.comptime_break_inst, |
| | 515 | else => |e| return e, |
| | 516 | }; |
| 491 | } | 517 | } |
| 492 | | 518 | |
| 493 | /// ZIR instructions which are always `noreturn` return this. This matches the | 519 | /// ZIR instructions which are always `noreturn` return this. This matches the |
| ... | @@ -505,7 +531,7 @@ const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefin | ... | @@ -505,7 +531,7 @@ const always_noreturn: CompileError!Zir.Inst.Index = @as(Zir.Inst.Index, undefin |
| 505 | /// instruction. In this case, the `Zir.Inst.Index` part of the return value will be | 531 | /// instruction. In this case, the `Zir.Inst.Index` part of the return value will be |
| 506 | /// the break instruction. This communicates both which block the break applies to, as | 532 | /// the break instruction. This communicates both which block the break applies to, as |
| 507 | /// well as the operand. No block scope needs to be created for this strategy. | 533 | /// well as the operand. No block scope needs to be created for this strategy. |
| 508 | pub fn analyzeBody( | 534 | fn analyzeBodyInner( |
| 509 | sema: *Sema, | 535 | sema: *Sema, |
| 510 | block: *Block, | 536 | block: *Block, |
| 511 | body: []const Zir.Inst.Index, | 537 | body: []const Zir.Inst.Index, |
| ... | @@ -541,6 +567,9 @@ pub fn analyzeBody( | ... | @@ -541,6 +567,9 @@ pub fn analyzeBody( |
| 541 | const result = while (true) { | 567 | const result = while (true) { |
| 542 | crash_info.setBodyIndex(i); | 568 | crash_info.setBodyIndex(i); |
| 543 | const inst = body[i]; | 569 | const inst = body[i]; |
| | 570 | std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ |
| | 571 | block.src_decl.src_namespace.file_scope.sub_file_path, inst, |
| | 572 | }); |
| 544 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { | 573 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { |
| 545 | // zig fmt: off | 574 | // zig fmt: off |
| 546 | .alloc => try sema.zirAlloc(block, inst), | 575 | .alloc => try sema.zirAlloc(block, inst), |
| ... | @@ -4319,6 +4348,7 @@ fn analyzeCall( | ... | @@ -4319,6 +4348,7 @@ fn analyzeCall( |
| 4319 | const result = result: { | 4348 | const result = result: { |
| 4320 | _ = sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) { | 4349 | _ = sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) { |
| 4321 | error.ComptimeReturn => break :result inlining.comptime_result, | 4350 | error.ComptimeReturn => break :result inlining.comptime_result, |
| | 4351 | error.ComptimeBreak => unreachable, // Can't break through a fn call. |
| 4322 | else => |e| return e, | 4352 | else => |e| return e, |
| 4323 | }; | 4353 | }; |
| 4324 | break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); | 4354 | break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); |