incremental: fix tracking of nested container declarations

...but not in the way you'd expect. We were actually tracking them in
cases where we shouldn't have been! We cannot track a declaration if its
parent namespace has been lost, because that will cause analysis
failures immediately, but if we excluded a type from the mapping due to
a major change (such as a struct turning into a union, or a field being
added), we were still including any trackable instructions inside the
container's field expressions (e.g. struct field type expressions). This
meant we were tracking a type declaration while losing tracking on its
parent namespace, with predictably disastrous results.

Oh, also, tracking for opaque types was just totally wrong (I think this
was a typo from a while back). We could map it to things other than
opaque declarations, and we never mapped declarations inside opaques.
So, uh, I fixed that too.
This commit is contained in:
Matthew Lugg
2026-04-15 23:55:52 +01:00
parent 794edc81d0
commit 6608b65100
4 changed files with 162 additions and 91 deletions
@@ -0,0 +1,25 @@
#update=initial version
#file=main.zig
pub fn main() void {
_ = @as(S, undefined);
}
// To reproduce the original bug, the inner struct must perform a namespace lookup
// or a scope lookup when resolving its field type.
const SomeType = u8;
const S = struct {
foo: struct { inner: SomeType },
};
#expect_stdout=""
#update=add field to outer struct, change decl used by inner struct
#file=main.zig
pub fn main() void {
_ = @as(S, undefined);
}
// To reproduce the original bug, the inner struct must perform a namespace lookup
// or a scope lookup when resolving its field type.
const SomeType = u16;
const S = struct {
foo: struct { inner: SomeType },
bar: u32,
};
#expect_stdout=""
+41
View File
@@ -0,0 +1,41 @@
// TODO: it'd be great if we could actually check that no analysis happened!
#update=initial version
#file=main.zig
pub fn main() void {
const ptr: *const O = @ptrFromInt(0x1000);
_ = ptr;
}
const S = struct { foo: u32, nested: struct { x: u16 } };
const U = union(enum) { a, b, c: S };
const E = enum(u8) { a = @typeInfo(U).@"union".fields.len, b = 0, c };
const O = opaque {
comptime {
_ = @as(S, undefined);
_ = @as(U, undefined);
_ = @as(E, undefined);
const Wrapper = struct { val: S };
const wrapper: Wrapper = .{ .val = .{ .foo = 123, .nested = .{ .x = 456 } } };
_ = wrapper;
}
};
#expect_stdout=""
#update=do literally nothing
#file=main.zig
pub fn main() void {
const ptr: *const O = @ptrFromInt(0x1000);
_ = ptr;
}
const S = struct { foo: u32, nested: struct { x: u16 } };
const U = union(enum) { a, b, c: S };
const E = enum(u8) { a = @typeInfo(U).@"union".fields.len, b = 0, c };
const O = opaque {
comptime {
_ = @as(S, undefined);
_ = @as(U, undefined);
_ = @as(E, undefined);
const Wrapper = struct { val: S };
const wrapper: Wrapper = .{ .val = .{ .foo = 123, .nested = .{ .x = 456 } } };
_ = wrapper;
}
};
#expect_stdout=""