zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit c150a66bfe2010be86e38f40573ffc8bdf5c2326 (tree)
parent 7c832740e027f8f656e4d2d945b985196367c826
Author: David Rubin <sinon@vortan.dev>
Date:   Sat, 20 Jun 2026 16:08:28 -0500

Sema: fix inline for loop breaks

Diffstat:
Msrc/Air/print.zig | 20++++++++------------
Msrc/Sema.zig | 42++++++++++++++++++++++++++++++------------
Mtest/behavior/for.zig | 17+++++++++++++++++
3 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/src/Air/print.zig b/src/Air/print.zig @@ -73,23 +73,19 @@ pub fn writeInst( } pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - const comp = pt.zcu.comp; - const io = comp.io; - var buffer: [512]u8 = undefined; - const stderr = try io.lockStderr(&buffer, null); - defer io.unlockStderr(); + var buffer: [4096]u8 = undefined; + const stderr = std.debug.lockStderr(&buffer); + defer std.debug.unlockStderr(); const w = &stderr.file_writer.interface; - air.write(w, pt, liveness); + air.write(w, pt, liveness) catch return; } pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - const comp = pt.zcu.comp; - const io = comp.io; - var buffer: [512]u8 = undefined; - const stderr = try io.lockStderr(&buffer, null); - defer io.unlockStderr(); + var buffer: [4096]u8 = undefined; + const stderr = std.debug.lockStderr(&buffer); + defer std.debug.unlockStderr(); const w = &stderr.file_writer.interface; - air.writeInst(w, inst, pt, liveness); + air.writeInst(w, inst, pt, liveness) catch return; } const Writer = struct { diff --git a/src/Sema.zig b/src/Sema.zig @@ -5287,33 +5287,51 @@ fn resolveBlockBody( return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope); } else |err| switch (err) { error.ComptimeBreak => { + const break_inst = sema.comptime_break_inst; + const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; + const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; + const breaks_to_body = extra.block_inst == body_inst; + // Comptime control flow is happening, however child_block may still contain // runtime instructions which need to be copied to the parent block. if (need_debug_scope and child_block.instructions.items.len > 0) { - // We need a runtime block for scoping reasons. - _ = try child_block.addBr(merges.block_inst, .void_value); + // We need a runtime block for scoping reasons. The break + // operand may have been produced by a runtime instruction + // inside `child_blocks`. + const operand = sema.resolveInst(break_data.operand); + const operand_ty = sema.typeOf(operand); + _ = try child_block.addBr(merges.block_inst, operand); try parent_block.instructions.append(sema.gpa, merges.block_inst); try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".field_names.len + child_block.instructions.items.len); sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{ - .ty = .void_type, + .ty = Air.internedToRef(operand_ty.toIntern()), .payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = @intCast(child_block.instructions.items.len), }), } }; sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); + + // The block result now holds the operand value, so we remap + // the operand such that an enclosing scope which resolves + // it picks up the block result rather than the internal + // block instruction. + if (break_data.operand.toIndex()) |operand_zir| { + sema.inst_map.putAssumeCapacity(operand_zir, merges.block_inst.toRef()); + } + if (breaks_to_body) { + return merges.block_inst.toRef(); + } else { + return error.ComptimeBreak; + } } else { // We can copy instructions directly to the parent block. try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); - } - - const break_inst = sema.comptime_break_inst; - const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break"; - const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; - if (extra.block_inst == body_inst) { - return sema.resolveInst(break_data.operand); - } else { - return error.ComptimeBreak; + if (breaks_to_body) { + return sema.resolveInst(break_data.operand); + } else { + return error.ComptimeBreak; + } } }, else => |e| return e, diff --git a/test/behavior/for.zig b/test/behavior/for.zig @@ -547,3 +547,20 @@ test "labeled break from else" { try S.doTheTest(5); try comptime S.doTheTest(5); } + +test "value break from inline for" { + const S = struct { + fn doTheTest() !void { + const x = inline for (0..2) |_| { + if (true) { + var idx: u32 = 0; + idx += 1; + break idx; + } + }; + try expect(x == 1); + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +}