authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-02-02 03:19:23+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-02-04 18:38:39+00:00
log7f4bd247c7735ccf277c9bba42222e014cacf856
treeb47acb0631fc101995b03dd415efe587cec9738b
parenta1b607acb5c1e9dd03760efd7078185e7628b29d
signaturelock-open Commit is signed but in an unrecognized format.

compiler: re-introduce dependencies for incremental compilation

Sema now tracks dependencies appropriately. Early logic in Zcu for resolving outdated decls/functions is in place. The setup used does not support `usingnamespace`; compilations using this construct are not yet supported by this incremental compilation model.

5 files changed, 537 insertions(+), 57 deletions(-)

src/Compilation.zig+29-1
......@@ -2807,6 +2807,13 @@ const Header = extern struct {
28072807 limbs_len: u32,
28082808 string_bytes_len: u32,
28092809 tracked_insts_len: u32,
2810 src_hash_deps_len: u32,
2811 decl_val_deps_len: u32,
2812 namespace_deps_len: u32,
2813 namespace_name_deps_len: u32,
2814 first_dependency_len: u32,
2815 dep_entries_len: u32,
2816 free_dep_entries_len: u32,
28102817 },
28112818};
28122819
......@@ -2814,7 +2821,7 @@ const Header = extern struct {
28142821/// saved, such as the target and most CLI flags. A cache hit will only occur
28152822/// when subsequent compiler invocations use the same set of flags.
28162823pub fn saveState(comp: *Compilation) !void {
2817 var bufs_list: [7]std.os.iovec_const = undefined;
2824 var bufs_list: [19]std.os.iovec_const = undefined;
28182825 var bufs_len: usize = 0;
28192826
28202827 const lf = comp.bin_file orelse return;
......@@ -2828,6 +2835,13 @@ pub fn saveState(comp: *Compilation) !void {
28282835 .limbs_len = @intCast(ip.limbs.items.len),
28292836 .string_bytes_len = @intCast(ip.string_bytes.items.len),
28302837 .tracked_insts_len = @intCast(ip.tracked_insts.count()),
2838 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
2839 .decl_val_deps_len = @intCast(ip.decl_val_deps.count()),
2840 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
2841 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
2842 .first_dependency_len = @intCast(ip.first_dependency.count()),
2843 .dep_entries_len = @intCast(ip.dep_entries.items.len),
2844 .free_dep_entries_len = @intCast(ip.free_dep_entries.items.len),
28312845 },
28322846 };
28332847 addBuf(&bufs_list, &bufs_len, mem.asBytes(&header));
......@@ -2838,6 +2852,20 @@ pub fn saveState(comp: *Compilation) !void {
28382852 addBuf(&bufs_list, &bufs_len, ip.string_bytes.items);
28392853 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.tracked_insts.keys()));
28402854
2855 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.keys()));
2856 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.src_hash_deps.values()));
2857 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.decl_val_deps.keys()));
2858 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.decl_val_deps.values()));
2859 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.namespace_deps.keys()));
2860 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.namespace_deps.values()));
2861 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
2862 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.namespace_name_deps.values()));
2863
2864 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.first_dependency.keys()));
2865 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.first_dependency.values()));
2866 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.dep_entries.items));
2867 addBuf(&bufs_list, &bufs_len, mem.sliceAsBytes(ip.free_dep_entries.items));
2868
28412869 // TODO: compilation errors
28422870 // TODO: files
28432871 // TODO: namespaces
src/InternPool.zig+278-11
......@@ -58,6 +58,38 @@ string_table: std.HashMapUnmanaged(
5858/// persists across incremental updates.
5959tracked_insts: std.AutoArrayHashMapUnmanaged(TrackedInst, void) = .{},
6060
61/// Dependencies on the source code hash associated with a ZIR instruction.
62/// * For a `declaration`, this is the entire declaration body.
63/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
64/// * For a `func`, this is the source of the full function signature.
65/// These are also invalidated if tracking fails for this instruction.
66/// Value is index into `dep_entries` of the first dependency on this hash.
67src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) = .{},
68/// Dependencies on the value of a Decl.
69/// Value is index into `dep_entries` of the first dependency on this Decl value.
70decl_val_deps: std.AutoArrayHashMapUnmanaged(DeclIndex, DepEntry.Index) = .{},
71/// Dependencies on the full set of names in a ZIR namespace.
72/// Key refers to a `struct_decl`, `union_decl`, etc.
73/// Value is index into `dep_entries` of the first dependency on this namespace.
74namespace_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index) = .{},
75/// Dependencies on the (non-)existence of some name in a namespace.
76/// Value is index into `dep_entries` of the first dependency on this name.
77namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.Index) = .{},
78
79/// Given a `Depender`, points to an entry in `dep_entries` whose `depender`
80/// matches. The `next_dependee` field can be used to iterate all such entries
81/// and remove them from the corresponding lists.
82first_dependency: std.AutoArrayHashMapUnmanaged(Depender, DepEntry.Index) = .{},
83
84/// Stores dependency information. The hashmaps declared above are used to look
85/// up entries in this list as required. This is not stored in `extra` so that
86/// we can use `free_dep_entries` to track free indices, since dependencies are
87/// removed frequently.
88dep_entries: std.ArrayListUnmanaged(DepEntry) = .{},
89/// Stores unused indices in `dep_entries` which can be reused without a full
90/// garbage collection pass.
91free_dep_entries: std.ArrayListUnmanaged(DepEntry.Index) = .{},
92
6193pub const TrackedInst = extern struct {
6294 path_digest: Cache.BinDigest,
6395 inst: Zir.Inst.Index,
......@@ -70,6 +102,19 @@ pub const TrackedInst = extern struct {
70102 pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) Zir.Inst.Index {
71103 return ip.tracked_insts.keys()[@intFromEnum(i)].inst;
72104 }
105 pub fn toOptional(i: TrackedInst.Index) Optional {
106 return @enumFromInt(@intFromEnum(i));
107 }
108 pub const Optional = enum(u32) {
109 none = std.math.maxInt(u32),
110 _,
111 pub fn unwrap(opt: Optional) ?TrackedInst.Index {
112 return switch (opt) {
113 .none => null,
114 _ => @enumFromInt(@intFromEnum(opt)),
115 };
116 }
117 };
73118 };
74119};
75120
......@@ -82,6 +127,202 @@ pub fn trackZir(ip: *InternPool, gpa: Allocator, file: *Module.File, inst: Zir.I
82127 return @enumFromInt(gop.index);
83128}
84129
130/// Reperesents the "source" of a dependency edge, i.e. either a Decl or a
131/// runtime function (represented as an InternPool index).
132/// MSB is 0 for a Decl, 1 for a function.
133pub const Depender = enum(u32) {
134 _,
135 pub const Unwrapped = union(enum) {
136 decl: DeclIndex,
137 func: InternPool.Index,
138 };
139 pub fn unwrap(dep: Depender) Unwrapped {
140 const tag: u1 = @truncate(@intFromEnum(dep) >> 31);
141 const val: u31 = @truncate(@intFromEnum(dep));
142 return switch (tag) {
143 0 => .{ .decl = @enumFromInt(val) },
144 1 => .{ .func = @enumFromInt(val) },
145 };
146 }
147 pub fn wrap(raw: Unwrapped) Depender {
148 return @enumFromInt(switch (raw) {
149 .decl => |decl| @intFromEnum(decl),
150 .func => |func| (1 << 31) | @intFromEnum(func),
151 });
152 }
153 pub fn toOptional(dep: Depender) Optional {
154 return @enumFromInt(@intFromEnum(dep));
155 }
156 pub const Optional = enum(u32) {
157 none = std.math.maxInt(u32),
158 _,
159 pub fn unwrap(opt: Optional) ?Depender {
160 return switch (opt) {
161 .none => null,
162 _ => @enumFromInt(@intFromEnum(opt)),
163 };
164 }
165 };
166};
167
168pub const Dependee = union(enum) {
169 src_hash: TrackedInst.Index,
170 decl_val: DeclIndex,
171 namespace: TrackedInst.Index,
172 namespace_name: NamespaceNameKey,
173};
174
175pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: Depender) void {
176 var opt_idx = (ip.first_dependency.fetchSwapRemove(depender) orelse return).value.toOptional();
177
178 while (opt_idx.unwrap()) |idx| {
179 const dep = ip.dep_entries.items[@intFromEnum(idx)];
180 opt_idx = dep.next_dependee;
181
182 const prev_idx = dep.prev.unwrap() orelse {
183 // This entry is the start of a list in some `*_deps`.
184 // We cannot easily remove this mapping, so this must remain as a dummy entry.
185 ip.dep_entries.items[@intFromEnum(idx)].depender = .none;
186 continue;
187 };
188
189 ip.dep_entries.items[@intFromEnum(prev_idx)].next = dep.next;
190 if (dep.next.unwrap()) |next_idx| {
191 ip.dep_entries.items[@intFromEnum(next_idx)].prev = dep.prev;
192 }
193
194 ip.free_dep_entries.append(gpa, idx) catch {
195 // This memory will be reclaimed on the next garbage collection.
196 // Thus, we do not need to propagate this error.
197 };
198 }
199}
200
201pub const DependencyIterator = struct {
202 ip: *const InternPool,
203 next_entry: DepEntry.Index.Optional,
204 pub fn next(it: *DependencyIterator) ?Depender {
205 const idx = it.next_entry.unwrap() orelse return null;
206 const entry = it.ip.dep_entries.items[@intFromEnum(idx)];
207 it.next_entry = entry.next;
208 return entry.depender.unwrap().?;
209 }
210};
211
212pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
213 const first_entry = switch (dependee) {
214 .src_hash => |x| ip.src_hash_deps.get(x),
215 .decl_val => |x| ip.decl_val_deps.get(x),
216 .namespace => |x| ip.namespace_deps.get(x),
217 .namespace_name => |x| ip.namespace_name_deps.get(x),
218 } orelse return .{
219 .ip = ip,
220 .next_entry = .none,
221 };
222 if (ip.dep_entries.items[@intFromEnum(first_entry)].depender == .none) return .{
223 .ip = ip,
224 .next_entry = .none,
225 };
226 return .{
227 .ip = ip,
228 .next_entry = first_entry.toOptional(),
229 };
230}
231
232pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: Depender, dependee: Dependee) Allocator.Error!void {
233 const first_depender_dep: DepEntry.Index.Optional = if (ip.first_dependency.get(depender)) |idx| dep: {
234 // The entry already exists, so there is capacity to overwrite it later.
235 break :dep idx.toOptional();
236 } else none: {
237 // Ensure there is capacity available to add this dependency later.
238 try ip.first_dependency.ensureUnusedCapacity(gpa, 1);
239 break :none .none;
240 };
241
242 // We're very likely to need space for a new entry - reserve it now to avoid
243 // the need for error cleanup logic.
244 if (ip.free_dep_entries.items.len == 0) {
245 try ip.dep_entries.ensureUnusedCapacity(gpa, 1);
246 }
247
248 // This block should allocate an entry and prepend it to the relevant `*_deps` list.
249 // The `next` field should be correctly initialized; all other fields may be undefined.
250 const new_index: DepEntry.Index = switch (dependee) {
251 inline else => |dependee_payload, tag| new_index: {
252 const gop = try switch (tag) {
253 .src_hash => ip.src_hash_deps,
254 .decl_val => ip.decl_val_deps,
255 .namespace => ip.namespace_deps,
256 .namespace_name => ip.namespace_name_deps,
257 }.getOrPut(gpa, dependee_payload);
258
259 if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {
260 // Dummy entry, so we can reuse it rather than allocating a new one!
261 ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].next = .none;
262 break :new_index gop.value_ptr.*;
263 }
264
265 // Prepend a new dependency.
266 const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
267 break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
268 } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
269 ptr.next = if (gop.found_existing) gop.value_ptr.*.toOptional() else .none;
270 gop.value_ptr.* = new_index;
271 break :new_index new_index;
272 },
273 };
274
275 ip.dep_entries.items[@intFromEnum(new_index)].depender = depender.toOptional();
276 ip.dep_entries.items[@intFromEnum(new_index)].prev = .none;
277 ip.dep_entries.items[@intFromEnum(new_index)].next_dependee = first_depender_dep;
278 ip.first_dependency.putAssumeCapacity(depender, new_index);
279}
280
281/// String is the name whose existence the dependency is on.
282/// DepEntry.Index refers to the first such dependency.
283pub const NamespaceNameKey = struct {
284 /// The instruction (`struct_decl` etc) which owns the namespace in question.
285 namespace: TrackedInst.Index,
286 /// The name whose existence the dependency is on.
287 name: NullTerminatedString,
288};
289
290pub const DepEntry = extern struct {
291 /// If null, this is a dummy entry - all other fields are `undefined`. It is
292 /// the first and only entry in one of `intern_pool.*_deps`, and does not
293 /// appear in any list by `first_dependency`, but is not in
294 /// `free_dep_entries` since `*_deps` stores a reference to it.
295 depender: Depender.Optional,
296 /// Index into `dep_entries` forming a doubly linked list of all dependencies on this dependee.
297 /// Used to iterate all dependers for a given dependee during an update.
298 /// null if this is the end of the list.
299 next: DepEntry.Index.Optional,
300 /// The other link for `next`.
301 /// null if this is the start of the list.
302 prev: DepEntry.Index.Optional,
303 /// Index into `dep_entries` forming a singly linked list of dependencies *of* `depender`.
304 /// Used to efficiently remove all `DepEntry`s for a single `depender` when it is re-analyzed.
305 /// null if this is the end of the list.
306 next_dependee: DepEntry.Index.Optional,
307
308 pub const Index = enum(u32) {
309 _,
310 pub fn toOptional(dep: DepEntry.Index) Optional {
311 return @enumFromInt(@intFromEnum(dep));
312 }
313 pub const Optional = enum(u32) {
314 none = std.math.maxInt(u32),
315 _,
316 pub fn unwrap(opt: Optional) ?DepEntry.Index {
317 return switch (opt) {
318 .none => null,
319 _ => @enumFromInt(@intFromEnum(opt)),
320 };
321 }
322 };
323 };
324};
325
85326const FieldMap = std.ArrayHashMapUnmanaged(void, void, std.array_hash_map.AutoContext(void), false);
86327
87328const builtin = @import("builtin");
......@@ -428,6 +669,7 @@ pub const Key = union(enum) {
428669 decl: DeclIndex,
429670 /// Represents the declarations inside this opaque.
430671 namespace: NamespaceIndex,
672 zir_index: TrackedInst.Index.Optional,
431673 };
432674
433675 /// Although packed structs and non-packed structs are encoded differently,
......@@ -440,7 +682,7 @@ pub const Key = union(enum) {
440682 /// `none` when the struct has no declarations.
441683 namespace: OptionalNamespaceIndex,
442684 /// Index of the struct_decl ZIR instruction.
443 zir_index: TrackedInst.Index,
685 zir_index: TrackedInst.Index.Optional,
444686 layout: std.builtin.Type.ContainerLayout,
445687 field_names: NullTerminatedString.Slice,
446688 field_types: Index.Slice,
......@@ -684,7 +926,7 @@ pub const Key = union(enum) {
684926 }
685927
686928 /// Asserts the struct is not packed.
687 pub fn setZirIndex(s: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index) void {
929 pub fn setZirIndex(s: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void {
688930 assert(s.layout != .Packed);
689931 const field_index = std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?;
690932 ip.extra.items[s.extra_index + field_index] = @intFromEnum(new_zir_index);
......@@ -800,7 +1042,7 @@ pub const Key = union(enum) {
8001042 flags: Tag.TypeUnion.Flags,
8011043 /// The enum that provides the list of field names and values.
8021044 enum_tag_ty: Index,
803 zir_index: TrackedInst.Index,
1045 zir_index: TrackedInst.Index.Optional,
8041046
8051047 /// The returned pointer expires with any addition to the `InternPool`.
8061048 pub fn flagsPtr(self: @This(), ip: *const InternPool) *Tag.TypeUnion.Flags {
......@@ -889,6 +1131,7 @@ pub const Key = union(enum) {
8891131 /// This is ignored by `get` but will be provided by `indexToKey` when
8901132 /// a value map exists.
8911133 values_map: OptionalMapIndex = .none,
1134 zir_index: TrackedInst.Index.Optional,
8921135
8931136 pub const TagMode = enum {
8941137 /// The integer tag type was auto-numbered by zig.
......@@ -953,6 +1196,7 @@ pub const Key = union(enum) {
9531196 tag_mode: EnumType.TagMode,
9541197 /// This may be updated via `setTagType` later.
9551198 tag_ty: Index = .none,
1199 zir_index: TrackedInst.Index.Optional,
9561200
9571201 pub fn toEnumType(self: @This()) EnumType {
9581202 return .{
......@@ -962,6 +1206,7 @@ pub const Key = union(enum) {
9621206 .tag_mode = self.tag_mode,
9631207 .names = .{ .start = 0, .len = 0 },
9641208 .values = .{ .start = 0, .len = 0 },
1209 .zir_index = self.zir_index,
9651210 };
9661211 }
9671212
......@@ -1909,7 +2154,7 @@ pub const UnionType = struct {
19092154 /// If this slice has length 0 it means all elements are `none`.
19102155 field_aligns: Alignment.Slice,
19112156 /// Index of the union_decl ZIR instruction.
1912 zir_index: TrackedInst.Index,
2157 zir_index: TrackedInst.Index.Optional,
19132158 /// Index into extra array of the `flags` field.
19142159 flags_index: u32,
19152160 /// Copied from `enum_tag_ty`.
......@@ -2003,10 +2248,10 @@ pub const UnionType = struct {
20032248 }
20042249
20052250 /// This does not mutate the field of UnionType.
2006 pub fn setZirIndex(self: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index) void {
2251 pub fn setZirIndex(self: @This(), ip: *InternPool, new_zir_index: TrackedInst.Index.Optional) void {
20072252 const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
20082253 const zir_index_field_index = std.meta.fieldIndex(Tag.TypeUnion, "zir_index").?;
2009 const ptr: *TrackedInst.Index =
2254 const ptr: *TrackedInst.Index.Optional =
20102255 @ptrCast(&ip.extra.items[self.flags_index - flags_field_index + zir_index_field_index]);
20112256 ptr.* = new_zir_index;
20122257 }
......@@ -3099,7 +3344,7 @@ pub const Tag = enum(u8) {
30993344 namespace: NamespaceIndex,
31003345 /// The enum that provides the list of field names and values.
31013346 tag_ty: Index,
3102 zir_index: TrackedInst.Index,
3347 zir_index: TrackedInst.Index.Optional,
31033348
31043349 pub const Flags = packed struct(u32) {
31053350 runtime_tag: UnionType.RuntimeTag,
......@@ -3121,7 +3366,7 @@ pub const Tag = enum(u8) {
31213366 /// 2. init: Index for each fields_len // if tag is type_struct_packed_inits
31223367 pub const TypeStructPacked = struct {
31233368 decl: DeclIndex,
3124 zir_index: TrackedInst.Index,
3369 zir_index: TrackedInst.Index.Optional,
31253370 fields_len: u32,
31263371 namespace: OptionalNamespaceIndex,
31273372 backing_int_ty: Index,
......@@ -3168,7 +3413,7 @@ pub const Tag = enum(u8) {
31683413 /// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved
31693414 pub const TypeStruct = struct {
31703415 decl: DeclIndex,
3171 zir_index: TrackedInst.Index,
3416 zir_index: TrackedInst.Index.Optional,
31723417 fields_len: u32,
31733418 flags: Flags,
31743419 size: u32,
......@@ -3523,6 +3768,7 @@ pub const EnumExplicit = struct {
35233768 /// If this is `none`, it means the trailing tag values are absent because
35243769 /// they are auto-numbered.
35253770 values_map: OptionalMapIndex,
3771 zir_index: TrackedInst.Index.Optional,
35263772};
35273773
35283774/// Trailing:
......@@ -3538,6 +3784,7 @@ pub const EnumAuto = struct {
35383784 fields_len: u32,
35393785 /// Maps field names to declaration index.
35403786 names_map: MapIndex,
3787 zir_index: TrackedInst.Index.Optional,
35413788};
35423789
35433790pub const PackedU64 = packed struct(u64) {
......@@ -3759,6 +4006,16 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
37594006
37604007 ip.tracked_insts.deinit(gpa);
37614008
4009 ip.src_hash_deps.deinit(gpa);
4010 ip.decl_val_deps.deinit(gpa);
4011 ip.namespace_deps.deinit(gpa);
4012 ip.namespace_name_deps.deinit(gpa);
4013
4014 ip.first_dependency.deinit(gpa);
4015
4016 ip.dep_entries.deinit(gpa);
4017 ip.free_dep_entries.deinit(gpa);
4018
37624019 ip.* = undefined;
37634020}
37644021
......@@ -3885,6 +4142,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
38854142 .tag_mode = .auto,
38864143 .names_map = enum_auto.data.names_map.toOptional(),
38874144 .values_map = .none,
4145 .zir_index = enum_auto.data.zir_index,
38884146 } };
38894147 },
38904148 .type_enum_explicit => ip.indexToKeyEnum(data, .explicit),
......@@ -4493,6 +4751,7 @@ fn indexToKeyEnum(ip: *const InternPool, data: u32, tag_mode: Key.EnumType.TagMo
44934751 .tag_mode = tag_mode,
44944752 .names_map = enum_explicit.data.names_map.toOptional(),
44954753 .values_map = enum_explicit.data.values_map,
4754 .zir_index = enum_explicit.data.zir_index,
44964755 } };
44974756}
44984757
......@@ -5329,7 +5588,7 @@ pub const UnionTypeInit = struct {
53295588 flags: Tag.TypeUnion.Flags,
53305589 decl: DeclIndex,
53315590 namespace: NamespaceIndex,
5332 zir_index: TrackedInst.Index,
5591 zir_index: TrackedInst.Index.Optional,
53335592 fields_len: u32,
53345593 enum_tag_ty: Index,
53355594 /// May have length 0 which leaves the values unset until later.
......@@ -5401,7 +5660,7 @@ pub const StructTypeInit = struct {
54015660 decl: DeclIndex,
54025661 namespace: OptionalNamespaceIndex,
54035662 layout: std.builtin.Type.ContainerLayout,
5404 zir_index: TrackedInst.Index,
5663 zir_index: TrackedInst.Index.Optional,
54055664 fields_len: u32,
54065665 known_non_opv: bool,
54075666 requires_comptime: RequiresComptime,
......@@ -6264,6 +6523,7 @@ fn getIncompleteEnumAuto(
62646523 .int_tag_type = int_tag_type,
62656524 .names_map = names_map,
62666525 .fields_len = enum_type.fields_len,
6526 .zir_index = enum_type.zir_index,
62676527 });
62686528
62696529 ip.items.appendAssumeCapacity(.{
......@@ -6314,6 +6574,7 @@ fn getIncompleteEnumExplicit(
63146574 .fields_len = enum_type.fields_len,
63156575 .names_map = names_map,
63166576 .values_map = values_map,
6577 .zir_index = enum_type.zir_index,
63176578 });
63186579
63196580 ip.items.appendAssumeCapacity(.{
......@@ -6339,6 +6600,7 @@ pub const GetEnumInit = struct {
63396600 names: []const NullTerminatedString,
63406601 values: []const Index,
63416602 tag_mode: Key.EnumType.TagMode,
6603 zir_index: TrackedInst.Index.Optional,
63426604};
63436605
63446606pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Error!Index {
......@@ -6355,6 +6617,7 @@ pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Erro
63556617 .tag_mode = undefined,
63566618 .names_map = undefined,
63576619 .values_map = undefined,
6620 .zir_index = undefined,
63586621 },
63596622 }, adapter);
63606623 if (gop.found_existing) return @enumFromInt(gop.index);
......@@ -6380,6 +6643,7 @@ pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Erro
63806643 .int_tag_type = ini.tag_ty,
63816644 .names_map = names_map,
63826645 .fields_len = fields_len,
6646 .zir_index = ini.zir_index,
63836647 }),
63846648 });
63856649 ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
......@@ -6416,6 +6680,7 @@ pub fn finishGetEnum(
64166680 .fields_len = fields_len,
64176681 .names_map = names_map,
64186682 .values_map = values_map,
6683 .zir_index = ini.zir_index,
64196684 }),
64206685 });
64216686 ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
......@@ -6507,6 +6772,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
65076772 OptionalNullTerminatedString,
65086773 Tag.TypePointer.VectorIndex,
65096774 TrackedInst.Index,
6775 TrackedInst.Index.Optional,
65106776 => @intFromEnum(@field(extra, field.name)),
65116777
65126778 u32,
......@@ -6583,6 +6849,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
65836849 OptionalNullTerminatedString,
65846850 Tag.TypePointer.VectorIndex,
65856851 TrackedInst.Index,
6852 TrackedInst.Index.Optional,
65866853 => @enumFromInt(int32),
65876854
65886855 u32,
src/Module.zig+140-37
......@@ -149,6 +149,10 @@ error_limit: ErrorInt,
149149/// previous analysis.
150150generation: u32 = 0,
151151
152/// Value is the number of PO dependencies of this Depender.
153potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{},
154outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{},
155
152156stage1_flags: packed struct {
153157 have_winmain: bool = false,
154158 have_wwinmain: bool = false,
......@@ -680,14 +684,6 @@ pub const Decl = struct {
680684 return mod.namespacePtr(decl.src_namespace).file_scope;
681685 }
682686
683 pub fn removeDependant(decl: *Decl, other: Decl.Index) void {
684 assert(decl.dependants.swapRemove(other));
685 }
686
687 pub fn removeDependency(decl: *Decl, other: Decl.Index) void {
688 assert(decl.dependencies.swapRemove(other));
689 }
690
691687 pub fn getExternDecl(decl: Decl, mod: *Module) OptionalIndex {
692688 assert(decl.has_tv);
693689 return switch (mod.intern_pool.indexToKey(decl.val.toIntern())) {
......@@ -838,14 +834,6 @@ pub const File = struct {
838834 /// undefined until `zir_loaded == true`.
839835 path_digest: Cache.BinDigest = undefined,
840836
841 /// Used by change detection algorithm, after astgen, contains the
842 /// set of decls that existed in the previous ZIR but not in the new one.
843 deleted_decls: ArrayListUnmanaged(Decl.Index) = .{},
844 /// Used by change detection algorithm, after astgen, contains the
845 /// set of decls that existed both in the previous ZIR and in the new one,
846 /// but their source code has been modified.
847 outdated_decls: ArrayListUnmanaged(Decl.Index) = .{},
848
849837 /// The most recent successful ZIR for this file, with no errors.
850838 /// This is only populated when a previously successful ZIR
851839 /// newly introduces compile errors during an update. When ZIR is
......@@ -898,8 +886,6 @@ pub const File = struct {
898886 gpa.free(file.sub_file_path);
899887 file.unload(gpa);
900888 }
901 file.deleted_decls.deinit(gpa);
902 file.outdated_decls.deinit(gpa);
903889 file.references.deinit(gpa);
904890 if (file.root_decl.unwrap()) |root_decl| {
905891 mod.destroyDecl(root_decl);
......@@ -2498,6 +2484,8 @@ pub fn deinit(zcu: *Zcu) void {
24982484
24992485 zcu.global_error_set.deinit(gpa);
25002486
2487 zcu.potentially_outdated.deinit(gpa);
2488
25012489 zcu.test_functions.deinit(gpa);
25022490
25032491 for (zcu.global_assembly.values()) |s| {
......@@ -2856,27 +2844,18 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
28562844 }
28572845
28582846 if (file.prev_zir) |prev_zir| {
2859 // Iterate over all Namespace objects contained within this File, looking at the
2860 // previous and new ZIR together and update the references to point
2861 // to the new one. For example, Decl name, Decl zir_decl_index, and Namespace
2862 // decl_table keys need to get updated to point to the new memory, even if the
2863 // underlying source code is unchanged.
2864 // We do not need to hold any locks at this time because all the Decl and Namespace
2865 // objects being touched are specific to this File, and the only other concurrent
2866 // tasks are touching other File objects.
28672847 try updateZirRefs(mod, file, prev_zir.*);
2868 // At this point, `file.outdated_decls` and `file.deleted_decls` are populated,
2869 // and semantic analysis will deal with them properly.
28702848 // No need to keep previous ZIR.
28712849 prev_zir.deinit(gpa);
28722850 gpa.destroy(prev_zir);
28732851 file.prev_zir = null;
2874 } else if (file.root_decl.unwrap()) |root_decl| {
2875 // This is an update, but it is the first time the File has succeeded
2876 // ZIR. We must mark it outdated since we have already tried to
2877 // semantically analyze it.
2878 try file.outdated_decls.resize(gpa, 1);
2879 file.outdated_decls.items[0] = root_decl;
2852 }
2853
2854 if (file.root_decl.unwrap()) |root_decl| {
2855 // The root of this file must be re-analyzed, since the file has changed.
2856 comp.mutex.lock();
2857 defer comp.mutex.unlock();
2858 try mod.outdated.put(gpa, InternPool.Depender.wrap(.{ .decl = root_decl }), {});
28802859 }
28812860}
28822861
......@@ -2950,25 +2929,142 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File)
29502929 return zir;
29512930}
29522931
2932/// This is called from the AstGen thread pool, so must acquire
2933/// the Compilation mutex when acting on shared state.
29532934fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void {
29542935 const gpa = zcu.gpa;
2936 const new_zir = file.zir;
29552937
29562938 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
29572939 defer inst_map.deinit(gpa);
29582940
2959 try mapOldZirToNew(gpa, old_zir, file.zir, &inst_map);
2941 try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map);
2942
2943 const old_tag = old_zir.instructions.items(.tag);
2944 const old_data = old_zir.instructions.items(.data);
29602945
29612946 // TODO: this should be done after all AstGen workers complete, to avoid
29622947 // iterating over this full set for every updated file.
2963 for (zcu.intern_pool.tracked_insts.keys()) |*ti| {
2948 for (zcu.intern_pool.tracked_insts.keys(), 0..) |*ti, idx_raw| {
2949 const ti_idx: InternPool.TrackedInst.Index = @enumFromInt(idx_raw);
29642950 if (!std.mem.eql(u8, &ti.path_digest, &file.path_digest)) continue;
2951 const old_inst = ti.inst;
29652952 ti.inst = inst_map.get(ti.inst) orelse {
2966 // TODO: invalidate this `TrackedInst` via the dependency mechanism
2953 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
2954 zcu.comp.mutex.lock();
2955 defer zcu.comp.mutex.unlock();
2956 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx });
29672957 continue;
29682958 };
2959
2960 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
2961 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
2962 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
2963 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
2964 else => false,
2965 },
2966 else => false,
2967 };
2968 if (!has_namespace) continue;
2969
2970 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
2971 defer old_names.deinit(zcu.gpa);
2972 {
2973 var it = old_zir.declIterator(old_inst);
2974 while (it.next()) |decl_inst| {
2975 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
2976 switch (decl_name) {
2977 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
2978 _ => if (decl_name.isNamedTest(old_zir)) continue,
2979 }
2980 const name_zir = decl_name.toString(old_zir).?;
2981 const name_ip = try zcu.intern_pool.getOrPutString(
2982 zcu.gpa,
2983 old_zir.nullTerminatedString(name_zir),
2984 );
2985 try old_names.put(zcu.gpa, name_ip, {});
2986 }
2987 }
2988 var any_change = false;
2989 {
2990 var it = new_zir.declIterator(ti.inst);
2991 while (it.next()) |decl_inst| {
2992 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
2993 switch (decl_name) {
2994 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
2995 _ => if (decl_name.isNamedTest(old_zir)) continue,
2996 }
2997 const name_zir = decl_name.toString(old_zir).?;
2998 const name_ip = try zcu.intern_pool.getOrPutString(
2999 zcu.gpa,
3000 old_zir.nullTerminatedString(name_zir),
3001 );
3002 if (!old_names.swapRemove(name_ip)) continue;
3003 // Name added
3004 any_change = true;
3005 zcu.comp.mutex.lock();
3006 defer zcu.comp.mutex.unlock();
3007 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3008 .namespace = ti_idx,
3009 .name = name_ip,
3010 } });
3011 }
3012 }
3013 // The only elements remaining in `old_names` now are any names which were removed.
3014 for (old_names.keys()) |name_ip| {
3015 any_change = true;
3016 zcu.comp.mutex.lock();
3017 defer zcu.comp.mutex.unlock();
3018 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3019 .namespace = ti_idx,
3020 .name = name_ip,
3021 } });
3022 }
3023
3024 if (any_change) {
3025 zcu.comp.mutex.lock();
3026 defer zcu.comp.mutex.unlock();
3027 try zcu.markDependeeOutdated(.{ .namespace = ti_idx });
3028 }
3029 }
3030}
3031
3032pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3033 var it = zcu.intern_pool.dependencyIterator(dependee);
3034 while (it.next()) |depender| {
3035 if (zcu.outdated.contains(depender)) continue;
3036 const was_po = zcu.potentially_outdated.swapRemove(depender);
3037 try zcu.outdated.putNoClobber(zcu.gpa, depender, {});
3038 // If this is a Decl and was not previously PO, we must recursively
3039 // mark dependencies on its tyval as PO.
3040 if (was_po) switch (depender.unwrap()) {
3041 .decl => |decl_index| try zcu.markDeclDependenciesPotentiallyOutdated(decl_index),
3042 .func => {},
3043 };
29693044 }
29703045}
29713046
3047/// Given a Decl which is newly outdated or PO, mark all dependers which depend
3048/// on its tyval as PO.
3049fn markDeclDependenciesPotentiallyOutdated(zcu: *Zcu, decl_index: Decl.Index) !void {
3050 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });
3051 while (it.next()) |po| {
3052 if (zcu.potentially_outdated.getPtr(po)) |n| {
3053 // There is now one more PO dependency.
3054 n.* += 1;
3055 continue;
3056 }
3057 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
3058 // If this ia a Decl, we must recursively mark dependencies
3059 // on its tyval as PO.
3060 switch (po.unwrap()) {
3061 .decl => |po_decl| try zcu.markDeclDependenciesPotentiallyOutdated(po_decl),
3062 .func => {},
3063 }
3064 }
3065 // TODO: repeat the above for `decl_ty` dependencies when they are introduced
3066}
3067
29723068pub fn mapOldZirToNew(
29733069 gpa: Allocator,
29743070 old_zir: Zir,
......@@ -3535,6 +3631,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
35353631 break :blk .none;
35363632 };
35373633
3634 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
3635
35383636 decl.analysis = .in_progress;
35393637
35403638 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
......@@ -3564,6 +3662,9 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
35643662 };
35653663 defer sema.deinit();
35663664
3665 // Every Decl has a dependency on its own source.
3666 try sema.declareDependency(.{ .src_hash = try ip.trackZir(sema.gpa, decl.getFileScope(mod), decl.zir_decl_index.unwrap().?) });
3667
35673668 assert(!mod.declIsRoot(decl_index));
35683669
35693670 var block_scope: Sema.Block = .{
......@@ -4362,6 +4463,8 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
43624463 const decl_index = func.owner_decl;
43634464 const decl = mod.declPtr(decl_index);
43644465
4466 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
4467
43654468 var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
43664469 defer comptime_mutable_decls.deinit();
43674470
src/Sema.zig+78-8
......@@ -2748,7 +2748,7 @@ pub fn getStructType(
27482748 const ty = try ip.getStructType(gpa, .{
27492749 .decl = decl,
27502750 .namespace = namespace.toOptional(),
2751 .zir_index = tracked_inst,
2751 .zir_index = tracked_inst.toOptional(),
27522752 .layout = small.layout,
27532753 .known_non_opv = small.known_non_opv,
27542754 .is_tuple = small.is_tuple,
......@@ -2789,6 +2789,12 @@ fn zirStructDecl(
27892789 new_decl.owns_tv = true;
27902790 errdefer mod.abortAnonDecl(new_decl_index);
27912791
2792 try ip.addDependency(
2793 sema.gpa,
2794 InternPool.Depender.wrap(.{ .decl = new_decl_index }),
2795 .{ .src_hash = try ip.trackZir(sema.gpa, block.getFileScope(mod), inst) },
2796 );
2797
27922798 const new_namespace_index = try mod.createNamespace(.{
27932799 .parent = block.namespace.toOptional(),
27942800 .ty = undefined,
......@@ -2973,6 +2979,12 @@ fn zirEnumDecl(
29732979 new_decl.owns_tv = true;
29742980 errdefer if (!done) mod.abortAnonDecl(new_decl_index);
29752981
2982 try mod.intern_pool.addDependency(
2983 sema.gpa,
2984 InternPool.Depender.wrap(.{ .decl = new_decl_index }),
2985 .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
2986 );
2987
29762988 const new_namespace_index = try mod.createNamespace(.{
29772989 .parent = block.namespace.toOptional(),
29782990 .ty = undefined,
......@@ -3008,6 +3020,7 @@ fn zirEnumDecl(
30083020 .auto
30093021 else
30103022 .explicit,
3023 .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(),
30113024 });
30123025 if (sema.builtin_type_target_index != .none) {
30133026 mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index);
......@@ -3225,6 +3238,12 @@ fn zirUnionDecl(
32253238 new_decl.owns_tv = true;
32263239 errdefer mod.abortAnonDecl(new_decl_index);
32273240
3241 try mod.intern_pool.addDependency(
3242 sema.gpa,
3243 InternPool.Depender.wrap(.{ .decl = new_decl_index }),
3244 .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
3245 );
3246
32283247 const new_namespace_index = try mod.createNamespace(.{
32293248 .parent = block.namespace.toOptional(),
32303249 .ty = undefined,
......@@ -3254,7 +3273,7 @@ fn zirUnionDecl(
32543273 },
32553274 .decl = new_decl_index,
32563275 .namespace = new_namespace_index,
3257 .zir_index = try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst),
3276 .zir_index = (try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst)).toOptional(),
32583277 .fields_len = fields_len,
32593278 .enum_tag_ty = .none,
32603279 .field_types = &.{},
......@@ -3318,6 +3337,12 @@ fn zirOpaqueDecl(
33183337 new_decl.owns_tv = true;
33193338 errdefer mod.abortAnonDecl(new_decl_index);
33203339
3340 try mod.intern_pool.addDependency(
3341 sema.gpa,
3342 InternPool.Depender.wrap(.{ .decl = new_decl_index }),
3343 .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
3344 );
3345
33213346 const new_namespace_index = try mod.createNamespace(.{
33223347 .parent = block.namespace.toOptional(),
33233348 .ty = undefined,
......@@ -3329,6 +3354,7 @@ fn zirOpaqueDecl(
33293354 const opaque_ty = try mod.intern(.{ .opaque_type = .{
33303355 .decl = new_decl_index,
33313356 .namespace = new_namespace_index,
3357 .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(),
33323358 } });
33333359 // TODO: figure out InternPool removals for incremental compilation
33343360 //errdefer mod.intern_pool.remove(opaque_ty);
......@@ -7890,6 +7916,8 @@ fn instantiateGenericCall(
78907916 const generic_owner_func = mod.intern_pool.indexToKey(generic_owner).func;
78917917 const generic_owner_ty_info = mod.typeToFunc(Type.fromInterned(generic_owner_func.ty)).?;
78927918
7919 try sema.declareDependency(.{ .src_hash = generic_owner_func.zir_body_inst });
7920
78937921 // Even though there may already be a generic instantiation corresponding
78947922 // to this callsite, we must evaluate the expressions of the generic
78957923 // function signature with the values of the callsite plugged in.
......@@ -13594,6 +13622,12 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1359413622 });
1359513623
1359613624 try sema.checkNamespaceType(block, lhs_src, container_type);
13625 if (container_type.typeDeclInst(mod)) |type_decl_inst| {
13626 try sema.declareDependency(.{ .namespace_name = .{
13627 .namespace = type_decl_inst,
13628 .name = decl_name,
13629 } });
13630 }
1359713631
1359813632 const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse
1359913633 return .bool_false;
......@@ -17447,6 +17481,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1744717481 const type_info_ty = try sema.getBuiltinType("Type");
1744817482 const type_info_tag_ty = type_info_ty.unionTagType(mod).?;
1744917483
17484 if (ty.typeDeclInst(mod)) |type_decl_inst| {
17485 try sema.declareDependency(.{ .namespace = type_decl_inst });
17486 }
17487
1745017488 switch (ty.zigTypeTag(mod)) {
1745117489 .Type,
1745217490 .Void,
......@@ -21313,6 +21351,7 @@ fn zirReify(
2131321351 else
2131421352 .explicit,
2131521353 .tag_ty = int_tag_ty.toIntern(),
21354 .zir_index = .none,
2131621355 });
2131721356 // TODO: figure out InternPool removals for incremental compilation
2131821357 //errdefer ip.remove(incomplete_enum.index);
......@@ -21410,6 +21449,7 @@ fn zirReify(
2141021449 const opaque_ty = try mod.intern(.{ .opaque_type = .{
2141121450 .decl = new_decl_index,
2141221451 .namespace = new_namespace_index,
21452 .zir_index = .none,
2141321453 } });
2141421454 // TODO: figure out InternPool removals for incremental compilation
2141521455 //errdefer ip.remove(opaque_ty);
......@@ -21628,7 +21668,7 @@ fn zirReify(
2162821668 .namespace = new_namespace_index,
2162921669 .enum_tag_ty = enum_tag_ty,
2163021670 .fields_len = fields_len,
21631 .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), // TODO: should reified types be handled differently?
21671 .zir_index = .none,
2163221672 .flags = .{
2163321673 .layout = layout,
2163421674 .status = .have_field_types,
......@@ -21796,7 +21836,7 @@ fn reifyStruct(
2179621836 const ty = try ip.getStructType(gpa, .{
2179721837 .decl = new_decl_index,
2179821838 .namespace = .none,
21799 .zir_index = try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst), // TODO: should reified types be handled differently?
21839 .zir_index = .none,
2180021840 .layout = layout,
2180121841 .known_non_opv = false,
2180221842 .fields_len = fields_len,
......@@ -26416,6 +26456,7 @@ fn prepareSimplePanic(sema: *Sema, block: *Block) !void {
2641626456 // owns the function.
2641726457 try sema.ensureDeclAnalyzed(decl_index);
2641826458 const tv = try mod.declPtr(decl_index).typedValue();
26459 try sema.declareDependency(.{ .decl_val = decl_index });
2641926460 assert(tv.ty.zigTypeTag(mod) == .Fn);
2642026461 assert(try sema.fnHasRuntimeBits(tv.ty));
2642126462 const func_index = tv.val.toIntern();
......@@ -26837,6 +26878,13 @@ fn fieldVal(
2683726878 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
2683826879 const child_type = val.toType();
2683926880
26881 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
26882 try sema.declareDependency(.{ .namespace_name = .{
26883 .namespace = type_decl_inst,
26884 .name = field_name,
26885 } });
26886 }
26887
2684026888 switch (try child_type.zigTypeTagOrPoison(mod)) {
2684126889 .ErrorSet => {
2684226890 switch (ip.indexToKey(child_type.toIntern())) {
......@@ -27060,6 +27108,13 @@ fn fieldPtr(
2706027108 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
2706127109 const child_type = val.toType();
2706227110
27111 if (child_type.typeDeclInst(mod)) |type_decl_inst| {
27112 try sema.declareDependency(.{ .namespace_name = .{
27113 .namespace = type_decl_inst,
27114 .name = field_name,
27115 } });
27116 }
27117
2706327118 switch (child_type.zigTypeTag(mod)) {
2706427119 .ErrorSet => {
2706527120 switch (ip.indexToKey(child_type.toIntern())) {
......@@ -31129,6 +31184,7 @@ fn beginComptimePtrLoad(
3112931184 const is_mutable = ptr.addr == .mut_decl;
3113031185 const decl = mod.declPtr(decl_index);
3113131186 const decl_tv = try decl.typedValue();
31187 try sema.declareDependency(.{ .decl_val = decl_index });
3113231188 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
3113331189
3113431190 const layout_defined = decl.ty.hasWellDefinedLayout(mod);
......@@ -32382,6 +32438,8 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn
3238232438
3238332439 const decl = mod.declPtr(decl_index);
3238432440 const decl_tv = try decl.typedValue();
32441 // TODO: if this is a `decl_ref`, only depend on decl type
32442 try sema.declareDependency(.{ .decl_val = decl_index });
3238532443 const ptr_ty = try sema.ptrType(.{
3238632444 .child = decl_tv.ty.toIntern(),
3238732445 .flags = .{
......@@ -35678,7 +35736,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
3567835736 break :blk accumulator;
3567935737 };
3568035738
35681 const zir_index = struct_type.zir_index.resolve(ip);
35739 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);
3568235740 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
3568335741 assert(extended.opcode == .struct_decl);
3568435742 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
......@@ -36443,7 +36501,7 @@ fn semaStructFields(
3644336501 const decl = mod.declPtr(decl_index);
3644436502 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
3644536503 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
36446 const zir_index = struct_type.zir_index.resolve(ip);
36504 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);
3644736505
3644836506 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
3644936507
......@@ -36714,7 +36772,7 @@ fn semaStructFieldInits(
3671436772 const decl = mod.declPtr(decl_index);
3671536773 const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
3671636774 const zir = mod.namespacePtr(namespace_index).file_scope.zir;
36717 const zir_index = struct_type.zir_index.resolve(ip);
36775 const zir_index = struct_type.zir_index.unwrap().?.resolve(ip);
3671836776 const fields_len, const small, var extra_index = structZirInfo(zir, zir_index);
3671936777
3672036778 var comptime_mutable_decls = std.ArrayList(InternPool.DeclIndex).init(gpa);
......@@ -36863,7 +36921,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
3686336921 const ip = &mod.intern_pool;
3686436922 const decl_index = union_type.decl;
3686536923 const zir = mod.namespacePtr(union_type.namespace).file_scope.zir;
36866 const zir_index = union_type.zir_index.resolve(ip);
36924 const zir_index = union_type.zir_index.unwrap().?.resolve(ip);
3686736925 const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
3686836926 assert(extended.opcode == .union_decl);
3686936927 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
......@@ -37307,6 +37365,7 @@ fn generateUnionTagTypeNumbered(
3730737365 .names = enum_field_names,
3730837366 .values = enum_field_vals,
3730937367 .tag_mode = .explicit,
37368 .zir_index = .none,
3731037369 });
3731137370
3731237371 new_decl.ty = Type.type;
......@@ -37357,6 +37416,7 @@ fn generateUnionTagTypeSimple(
3735737416 .names = enum_field_names,
3735837417 .values = &.{},
3735937418 .tag_mode = .auto,
37419 .zir_index = .none,
3736037420 });
3736137421
3736237422 const new_decl = mod.declPtr(new_decl_index);
......@@ -38870,3 +38930,13 @@ fn ptrType(sema: *Sema, info: InternPool.Key.PtrType) CompileError!Type {
3887038930 }
3887138931 return sema.mod.ptrType(info);
3887238932}
38933
38934pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
38935 const depender = InternPool.Depender.wrap(
38936 if (sema.owner_func_index != .none)
38937 .{ .func = sema.owner_func_index }
38938 else
38939 .{ .decl = sema.owner_decl_index },
38940 );
38941 try sema.mod.intern_pool.addDependency(sema.gpa, depender, dependee);
38942}
src/type.zig+12
......@@ -4,6 +4,7 @@ const Value = @import("value.zig").Value;
44const assert = std.debug.assert;
55const Target = std.Target;
66const Module = @import("Module.zig");
7const Zcu = Module;
78const log = std.log.scoped(.Type);
89const target_util = @import("target.zig");
910const TypedValue = @import("TypedValue.zig");
......@@ -3228,6 +3229,17 @@ pub const Type = struct {
32283229 };
32293230 }
32303231
3232 pub fn typeDeclInst(ty: Type, zcu: *const Zcu) ?InternPool.TrackedInst.Index {
3233 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
3234 inline .struct_type,
3235 .union_type,
3236 .enum_type,
3237 .opaque_type,
3238 => |info| info.zir_index.unwrap(),
3239 else => null,
3240 };
3241 }
3242
32313243 pub const @"u1": Type = .{ .ip_index = .u1_type };
32323244 pub const @"u8": Type = .{ .ip_index = .u8_type };
32333245 pub const @"u16": Type = .{ .ip_index = .u16_type };