authorgravatar for sinon@vortan.devDavid Rubin <sinon@vortan.dev> 2026-06-20 16:08:28-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-26 23:29:08+02:00
logc150a66bfe2010be86e38f40573ffc8bdf5c2326
tree69e4e861bfedd6d2b41e1ebfc51ecccd00fd09d3
parent7c832740e027f8f656e4d2d945b985196367c826

Sema: fix inline for loop breaks


3 files changed, 55 insertions(+), 24 deletions(-)

src/Air/print.zig+8-12
......@@ -73,23 +73,19 @@ pub fn writeInst(
7373}
7474
7575pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
76 const comp = pt.zcu.comp;
77 const io = comp.io;
78 var buffer: [512]u8 = undefined;
79 const stderr = try io.lockStderr(&buffer, null);
80 defer io.unlockStderr();
76 var buffer: [4096]u8 = undefined;
77 const stderr = std.debug.lockStderr(&buffer);
78 defer std.debug.unlockStderr();
8179 const w = &stderr.file_writer.interface;
82 air.write(w, pt, liveness);
80 air.write(w, pt, liveness) catch return;
8381}
8482
8583pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
86 const comp = pt.zcu.comp;
87 const io = comp.io;
88 var buffer: [512]u8 = undefined;
89 const stderr = try io.lockStderr(&buffer, null);
90 defer io.unlockStderr();
84 var buffer: [4096]u8 = undefined;
85 const stderr = std.debug.lockStderr(&buffer);
86 defer std.debug.unlockStderr();
9187 const w = &stderr.file_writer.interface;
92 air.writeInst(w, inst, pt, liveness);
88 air.writeInst(w, inst, pt, liveness) catch return;
9389}
9490
9591const Writer = struct {
src/Sema.zig+30-12
......@@ -5287,33 +5287,51 @@ fn resolveBlockBody(
52875287 return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope);
52885288 } else |err| switch (err) {
52895289 error.ComptimeBreak => {
5290 const break_inst = sema.comptime_break_inst;
5291 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
5292 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
5293 const breaks_to_body = extra.block_inst == body_inst;
5294
52905295 // Comptime control flow is happening, however child_block may still contain
52915296 // runtime instructions which need to be copied to the parent block.
52925297 if (need_debug_scope and child_block.instructions.items.len > 0) {
5293 // We need a runtime block for scoping reasons.
5294 _ = try child_block.addBr(merges.block_inst, .void_value);
5298 // We need a runtime block for scoping reasons. The break
5299 // operand may have been produced by a runtime instruction
5300 // inside `child_blocks`.
5301 const operand = sema.resolveInst(break_data.operand);
5302 const operand_ty = sema.typeOf(operand);
5303 _ = try child_block.addBr(merges.block_inst, operand);
52955304 try parent_block.instructions.append(sema.gpa, merges.block_inst);
52965305 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".field_names.len +
52975306 child_block.instructions.items.len);
52985307 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
5299 .ty = .void_type,
5308 .ty = Air.internedToRef(operand_ty.toIntern()),
53005309 .payload = sema.addExtraAssumeCapacity(Air.Block{
53015310 .body_len = @intCast(child_block.instructions.items.len),
53025311 }),
53035312 } };
53045313 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
5314
5315 // The block result now holds the operand value, so we remap
5316 // the operand such that an enclosing scope which resolves
5317 // it picks up the block result rather than the internal
5318 // block instruction.
5319 if (break_data.operand.toIndex()) |operand_zir| {
5320 sema.inst_map.putAssumeCapacity(operand_zir, merges.block_inst.toRef());
5321 }
5322 if (breaks_to_body) {
5323 return merges.block_inst.toRef();
5324 } else {
5325 return error.ComptimeBreak;
5326 }
53055327 } else {
53065328 // We can copy instructions directly to the parent block.
53075329 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
5308 }
5309
5310 const break_inst = sema.comptime_break_inst;
5311 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
5312 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
5313 if (extra.block_inst == body_inst) {
5314 return sema.resolveInst(break_data.operand);
5315 } else {
5316 return error.ComptimeBreak;
5330 if (breaks_to_body) {
5331 return sema.resolveInst(break_data.operand);
5332 } else {
5333 return error.ComptimeBreak;
5334 }
53175335 }
53185336 },
53195337 else => |e| return e,
test/behavior/for.zig+17
......@@ -547,3 +547,20 @@ test "labeled break from else" {
547547 try S.doTheTest(5);
548548 try comptime S.doTheTest(5);
549549}
550
551test "value break from inline for" {
552 const S = struct {
553 fn doTheTest() !void {
554 const x = inline for (0..2) |_| {
555 if (true) {
556 var idx: u32 = 0;
557 idx += 1;
558 break idx;
559 }
560 };
561 try expect(x == 1);
562 }
563 };
564 try S.doTheTest();
565 try comptime S.doTheTest();
566}