authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-26 10:51:37+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-09-26 18:58:16+02:00
logd9c2b8859181cdf81732de50adf9fdc85fa47f3f
tree70d122aa82a56f923987f5435f925d0c79d8254f
parent62f13ef4efe5aab5030ea56442b552ad0c5fec42

incremental: don't map ZIR instructions when the tag changes

I don't think this technically used to matter for instructions other than `struct_decl`/`union_decl`/`enum_decl`/`opaque_decl` (which were already handled correctly), but it now does because of the recent improvements to DWARF's line number tracking. Resolves: https://codeberg.org/ziglang/zig/issues/36963

2 files changed, 51 insertions(+), 4 deletions(-)

src/Zcu.zig+32-4
...@@ -3509,9 +3509,23 @@ pub fn mapOldZirToNew(...@@ -3509,9 +3509,23 @@ pub fn mapOldZirToNew(
3509 for (3509 for (
3510 old_contents.other.items[0..num_other],3510 old_contents.other.items[0..num_other],
3511 new_contents.other.items[0..num_other],3511 new_contents.other.items[0..num_other],
3512 ) |old_inst, new_inst| {3512 ) |old_inst_index, new_inst_index| {
3513 // These instructions don't have declarations, so we just modify `inst_map` directly.3513 // These instructions don't have declarations, so we just modify `inst_map` directly.
3514 inst_map.putAssumeCapacity(old_inst, new_inst);3514
3515 // But first: a mapping must not change an instruction's tag, so ignore any
3516 // candidates which would.
3517 const old_inst = old_zir.instructions.get(@backingInt(old_inst_index));
3518 const new_inst = new_zir.instructions.get(@backingInt(new_inst_index));
3519 if (old_inst.tag != new_inst.tag) {
3520 continue;
3521 }
3522 if (old_inst.tag == .extended and
3523 old_inst.data.extended.opcode != new_inst.data.extended.opcode)
3524 {
3525 continue;
3526 }
3527
3528 inst_map.putAssumeCapacity(old_inst_index, new_inst_index);
3515 }3529 }
3516 }3530 }
35173531
...@@ -3602,9 +3616,23 @@ pub fn mapOldZirToNew(...@@ -3602,9 +3616,23 @@ pub fn mapOldZirToNew(
3602 for (3616 for (
3603 old_contents.other.items[0..num_other],3617 old_contents.other.items[0..num_other],
3604 new_contents.other.items[0..num_other],3618 new_contents.other.items[0..num_other],
3605 ) |old_inst, new_inst| {3619 ) |old_inst_index, new_inst_index| {
3606 // These instructions don't have declarations, so we just modify `inst_map` directly.3620 // These instructions don't have declarations, so we just modify `inst_map` directly.
3607 inst_map.putAssumeCapacity(old_inst, new_inst);3621
3622 // But first: a mapping must not change an instruction's tag, so ignore any
3623 // candidates which would.
3624 const old_inst = old_zir.instructions.get(@backingInt(old_inst_index));
3625 const new_inst = new_zir.instructions.get(@backingInt(new_inst_index));
3626 if (old_inst.tag != new_inst.tag) {
3627 continue;
3628 }
3629 if (old_inst.tag == .extended and
3630 old_inst.data.extended.opcode != new_inst.data.extended.opcode)
3631 {
3632 continue;
3633 }
3634
3635 inst_map.putAssumeCapacity(old_inst_index, new_inst_index);
3608 }3636 }
36093637
3610 if (old_contents.func_decl) |old_func_inst| {3638 if (old_contents.func_decl) |old_func_inst| {
test/incremental/struct_reification_becomes_anon_init created+19
...@@ -0,0 +1,19 @@
1#update=initial version
2#file=main.zig
3const Foo = @Struct(.auto, null, &.{"reified_field"}, &.{void}, &.{.{}});
4pub fn main(init: std.process.Init) !void {
5 const field_name = @typeInfo(Foo).@"struct".field_names[0];
6 try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n");
7}
8const std = @import("std");
9#expect_stdout="reified_field\n"
10
11#update=change to `@TypeOf` anonymous init
12#file=main.zig
13const Foo = @TypeOf(.{ .anon_field = 123 });
14pub fn main(init: std.process.Init) !void {
15 const field_name = @typeInfo(Foo).@"struct".field_names[0];
16 try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n");
17}
18const std = @import("std");
19#expect_stdout="anon_field\n"