From d9c2b8859181cdf81732de50adf9fdc85fa47f3f Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sat, 26 Sep 2026 10:51:37 +0100 Subject: [PATCH] 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 --- src/Zcu.zig | 36 ++++++++++++++++--- .../struct_reification_becomes_anon_init | 19 ++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 test/incremental/struct_reification_becomes_anon_init diff --git a/src/Zcu.zig b/src/Zcu.zig index 56118073660df5a55f77517a459fe5f666c1d442..81684fad8e4d22bd26de82f1b6680a213ad4d609 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3509,9 +3509,23 @@ pub fn mapOldZirToNew( for ( old_contents.other.items[0..num_other], new_contents.other.items[0..num_other], - ) |old_inst, new_inst| { + ) |old_inst_index, new_inst_index| { // These instructions don't have declarations, so we just modify `inst_map` directly. - inst_map.putAssumeCapacity(old_inst, new_inst); + + // But first: a mapping must not change an instruction's tag, so ignore any + // candidates which would. + const old_inst = old_zir.instructions.get(@backingInt(old_inst_index)); + const new_inst = new_zir.instructions.get(@backingInt(new_inst_index)); + if (old_inst.tag != new_inst.tag) { + continue; + } + if (old_inst.tag == .extended and + old_inst.data.extended.opcode != new_inst.data.extended.opcode) + { + continue; + } + + inst_map.putAssumeCapacity(old_inst_index, new_inst_index); } } @@ -3602,9 +3616,23 @@ pub fn mapOldZirToNew( for ( old_contents.other.items[0..num_other], new_contents.other.items[0..num_other], - ) |old_inst, new_inst| { + ) |old_inst_index, new_inst_index| { // These instructions don't have declarations, so we just modify `inst_map` directly. - inst_map.putAssumeCapacity(old_inst, new_inst); + + // But first: a mapping must not change an instruction's tag, so ignore any + // candidates which would. + const old_inst = old_zir.instructions.get(@backingInt(old_inst_index)); + const new_inst = new_zir.instructions.get(@backingInt(new_inst_index)); + if (old_inst.tag != new_inst.tag) { + continue; + } + if (old_inst.tag == .extended and + old_inst.data.extended.opcode != new_inst.data.extended.opcode) + { + continue; + } + + inst_map.putAssumeCapacity(old_inst_index, new_inst_index); } if (old_contents.func_decl) |old_func_inst| { diff --git a/test/incremental/struct_reification_becomes_anon_init b/test/incremental/struct_reification_becomes_anon_init new file mode 100644 index 0000000000000000000000000000000000000000..7099685ba2c2e464607039c5eb461fa500422d69 --- /dev/null +++ b/test/incremental/struct_reification_becomes_anon_init @@ -0,0 +1,19 @@ +#update=initial version +#file=main.zig +const Foo = @Struct(.auto, null, &.{"reified_field"}, &.{void}, &.{.{}}); +pub fn main(init: std.process.Init) !void { + const field_name = @typeInfo(Foo).@"struct".field_names[0]; + try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); +} +const std = @import("std"); +#expect_stdout="reified_field\n" + +#update=change to `@TypeOf` anonymous init +#file=main.zig +const Foo = @TypeOf(.{ .anon_field = 123 }); +pub fn main(init: std.process.Init) !void { + const field_name = @typeInfo(Foo).@"struct".field_names[0]; + try std.Io.File.stdout().writeStreamingAll(init.io, field_name ++ "\n"); +} +const std = @import("std"); +#expect_stdout="anon_field\n" -- 2.54.0