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:
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();
+}