authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-12 11:02:18+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-08-17 18:50:10-04:00
log6faa4cc7e60c2ecd26759878a6f9e277d69a4968
treefb54ba56449d8e78535a9459a4de0ec0e8d01d7c
parentb65865b027f5531408654eae82cec05468b2c082

Zcu: construct full reference graph

This commit updates `Zcu.resolveReferences` to traverse the graph of `AnalUnit` references (starting from the 1-3 roots of analysis) in order to determine which `AnalUnit`s are referenced in an update. Errors for unreferenced entities are omitted from the error bundle. However, note that unreferenced `Nav`s are not removed from the binary.

3 files changed, 330 insertions(+), 50 deletions(-)

src/Compilation.zig+30-14
...@@ -2264,13 +2264,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {...@@ -2264,13 +2264,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
2264 }2264 }
2265 }2265 }
22662266
2267 zcu.analysis_roots.resize(0) catch unreachable;
2268
2267 try comp.queueJob(.{ .analyze_mod = std_mod });2269 try comp.queueJob(.{ .analyze_mod = std_mod });
2270 zcu.analysis_roots.appendAssumeCapacity(std_mod);
2271
2268 if (comp.config.is_test) {2272 if (comp.config.is_test) {
2269 try comp.queueJob(.{ .analyze_mod = zcu.main_mod });2273 try comp.queueJob(.{ .analyze_mod = zcu.main_mod });
2274 zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod);
2270 }2275 }
22712276
2272 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {2277 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2273 try comp.queueJob(.{ .analyze_mod = compiler_rt_mod });2278 try comp.queueJob(.{ .analyze_mod = compiler_rt_mod });
2279 zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod);
2274 }2280 }
2275 }2281 }
22762282
...@@ -3059,6 +3065,9 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3059,6 +3065,9 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3059 if (comp.module) |zcu| {3065 if (comp.module) |zcu| {
3060 const ip = &zcu.intern_pool;3066 const ip = &zcu.intern_pool;
30613067
3068 var all_references = try zcu.resolveReferences();
3069 defer all_references.deinit(zcu.gpa);
3070
3062 total += zcu.failed_exports.count();3071 total += zcu.failed_exports.count();
3063 total += zcu.failed_embed_files.count();3072 total += zcu.failed_embed_files.count();
30643073
...@@ -3079,6 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3079,6 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3079 // the previous parse success, including compile errors, but we cannot3088 // the previous parse success, including compile errors, but we cannot
3080 // emit them until the file succeeds parsing.3089 // emit them until the file succeeds parsing.
3081 for (zcu.failed_analysis.keys()) |anal_unit| {3090 for (zcu.failed_analysis.keys()) |anal_unit| {
3091 if (!all_references.contains(anal_unit)) continue;
3082 const file_index = switch (anal_unit.unwrap()) {3092 const file_index = switch (anal_unit.unwrap()) {
3083 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,3093 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
3084 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,3094 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
...@@ -3116,12 +3126,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {...@@ -3116,12 +3126,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
3116 }3126 }
3117 }3127 }
31183128
3119 if (comp.module) |zcu| {
3120 if (total == 0 and zcu.transitive_failed_analysis.count() > 0) {
3121 @panic("Transitive analysis errors, but none actually emitted");
3122 }
3123 }
3124
3125 return @intCast(total);3129 return @intCast(total);
3126}3130}
31273131
...@@ -3167,12 +3171,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3167,12 +3171,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3167 .msg = try bundle.addString("memory allocation failure"),3171 .msg = try bundle.addString("memory allocation failure"),
3168 });3172 });
3169 }3173 }
3174
3175 var all_references = if (comp.module) |zcu| try zcu.resolveReferences() else undefined;
3176 defer if (comp.module != null) all_references.deinit(gpa);
3177
3170 if (comp.module) |zcu| {3178 if (comp.module) |zcu| {
3171 const ip = &zcu.intern_pool;3179 const ip = &zcu.intern_pool;
31723180
3173 var all_references = try zcu.resolveReferences();
3174 defer all_references.deinit(gpa);
3175
3176 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {3181 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
3177 if (error_msg) |msg| {3182 if (error_msg) |msg| {
3178 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);3183 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);
...@@ -3220,6 +3225,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3220,6 +3225,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3220 if (err) |e| return e;3225 if (err) |e| return e;
3221 }3226 }
3222 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {3227 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {
3228 if (!all_references.contains(anal_unit)) continue;
3229
3223 const file_index = switch (anal_unit.unwrap()) {3230 const file_index = switch (anal_unit.unwrap()) {
3224 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,3231 .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
3225 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,3232 .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
...@@ -3313,9 +3320,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3313,9 +3320,6 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
33133320
3314 if (comp.module) |zcu| {3321 if (comp.module) |zcu| {
3315 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3322 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
3316 var all_references = try zcu.resolveReferences();
3317 defer all_references.deinit(gpa);
3318
3319 const values = zcu.compile_log_sources.values();3323 const values = zcu.compile_log_sources.values();
3320 // First one will be the error; subsequent ones will be notes.3324 // First one will be the error; subsequent ones will be notes.
3321 const src_loc = values[0].src();3325 const src_loc = values[0].src();
...@@ -3339,6 +3343,17 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3339,6 +3343,17 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
33393343
3340 assert(comp.totalErrorCount() == bundle.root_list.items.len);3344 assert(comp.totalErrorCount() == bundle.root_list.items.len);
33413345
3346 if (comp.module) |zcu| {
3347 if (bundle.root_list.items.len == 0) {
3348 const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
3349 if (all_references.contains(failed_unit)) break true;
3350 } else false;
3351 if (should_have_error) {
3352 @panic("referenced transitive analysis errors, but none actually emitted");
3353 }
3354 }
3355 }
3356
3342 const compile_log_text = if (comp.module) |m| m.compile_log_text.items else "";3357 const compile_log_text = if (comp.module) |m| m.compile_log_text.items else "";
3343 return bundle.toOwnedBundle(compile_log_text);3358 return bundle.toOwnedBundle(compile_log_text);
3344}3359}
...@@ -3393,7 +3408,7 @@ pub fn addModuleErrorMsg(...@@ -3393,7 +3408,7 @@ pub fn addModuleErrorMsg(
3393 mod: *Zcu,3408 mod: *Zcu,
3394 eb: *ErrorBundle.Wip,3409 eb: *ErrorBundle.Wip,
3395 module_err_msg: Zcu.ErrorMsg,3410 module_err_msg: Zcu.ErrorMsg,
3396 all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, Zcu.ResolvedReference),3411 all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference),
3397) !void {3412) !void {
3398 const gpa = eb.gpa;3413 const gpa = eb.gpa;
3399 const ip = &mod.intern_pool;3414 const ip = &mod.intern_pool;
...@@ -3423,7 +3438,8 @@ pub fn addModuleErrorMsg(...@@ -3423,7 +3438,8 @@ pub fn addModuleErrorMsg(
3423 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;3438 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;
34243439
3425 var referenced_by = rt_root;3440 var referenced_by = rt_root;
3426 while (all_references.get(referenced_by)) |ref| {3441 while (all_references.get(referenced_by)) |maybe_ref| {
3442 const ref = maybe_ref orelse break;
3427 const gop = try seen.getOrPut(gpa, ref.referencer);3443 const gop = try seen.getOrPut(gpa, ref.referencer);
3428 if (gop.found_existing) break;3444 if (gop.found_existing) break;
3429 if (ref_traces.items.len < max_references) {3445 if (ref_traces.items.len < max_references) {
src/Sema.zig+63-13
...@@ -110,6 +110,7 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},...@@ -110,6 +110,7 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{},
110/// of data stored in `Zcu.all_references`. It exists to avoid adding references to110/// of data stored in `Zcu.all_references`. It exists to avoid adding references to
111/// a given `AnalUnit` multiple times.111/// a given `AnalUnit` multiple times.
112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},112references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
113type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{},
113114
114const MaybeComptimeAlloc = struct {115const MaybeComptimeAlloc = struct {
115 /// The runtime index of the `alloc` instruction.116 /// The runtime index of the `alloc` instruction.
...@@ -877,6 +878,7 @@ pub fn deinit(sema: *Sema) void {...@@ -877,6 +878,7 @@ pub fn deinit(sema: *Sema) void {
877 sema.comptime_allocs.deinit(gpa);878 sema.comptime_allocs.deinit(gpa);
878 sema.exports.deinit(gpa);879 sema.exports.deinit(gpa);
879 sema.references.deinit(gpa);880 sema.references.deinit(gpa);
881 sema.type_references.deinit(gpa);
880 sema.* = undefined;882 sema.* = undefined;
881}883}
882884
...@@ -2809,7 +2811,11 @@ fn zirStructDecl(...@@ -2809,7 +2811,11 @@ fn zirStructDecl(
2809 };2811 };
2810 const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) {2812 const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) {
2811 .existing => |ty| wip: {2813 .existing => |ty| wip: {
2812 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);2814 if (!try sema.maybeRemoveOutdatedType(ty)) {
2815 try sema.declareDependency(.{ .interned = ty });
2816 try sema.addTypeReferenceEntry(src, ty);
2817 return Air.internedToRef(ty);
2818 }
2813 break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip;2819 break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip;
2814 },2820 },
2815 .wip => |wip| wip,2821 .wip => |wip| wip,
...@@ -2850,8 +2856,8 @@ fn zirStructDecl(...@@ -2850,8 +2856,8 @@ fn zirStructDecl(
2850 if (block.ownerModule().strip) break :codegen_type;2856 if (block.ownerModule().strip) break :codegen_type;
2851 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });2857 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
2852 }2858 }
2853 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
2854 try sema.declareDependency(.{ .interned = wip_ty.index });2859 try sema.declareDependency(.{ .interned = wip_ty.index });
2860 try sema.addTypeReferenceEntry(src, wip_ty.index);
2855 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));2861 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
2856}2862}
28572863
...@@ -3031,7 +3037,11 @@ fn zirEnumDecl(...@@ -3031,7 +3037,11 @@ fn zirEnumDecl(
3031 };3037 };
3032 const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) {3038 const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) {
3033 .existing => |ty| wip: {3039 .existing => |ty| wip: {
3034 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);3040 if (!try sema.maybeRemoveOutdatedType(ty)) {
3041 try sema.declareDependency(.{ .interned = ty });
3042 try sema.addTypeReferenceEntry(src, ty);
3043 return Air.internedToRef(ty);
3044 }
3035 break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip;3045 break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip;
3036 },3046 },
3037 .wip => |wip| wip,3047 .wip => |wip| wip,
...@@ -3071,8 +3081,8 @@ fn zirEnumDecl(...@@ -3071,8 +3081,8 @@ fn zirEnumDecl(
30713081
3072 try pt.scanNamespace(new_namespace_index, decls);3082 try pt.scanNamespace(new_namespace_index, decls);
30733083
3074 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
3075 try sema.declareDependency(.{ .interned = wip_ty.index });3084 try sema.declareDependency(.{ .interned = wip_ty.index });
3085 try sema.addTypeReferenceEntry(src, wip_ty.index);
30763086
3077 // We've finished the initial construction of this type, and are about to perform analysis.3087 // We've finished the initial construction of this type, and are about to perform analysis.
3078 // Set the Cau and namespace appropriately, and don't destroy anything on failure.3088 // Set the Cau and namespace appropriately, and don't destroy anything on failure.
...@@ -3297,7 +3307,11 @@ fn zirUnionDecl(...@@ -3297,7 +3307,11 @@ fn zirUnionDecl(
3297 };3307 };
3298 const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) {3308 const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) {
3299 .existing => |ty| wip: {3309 .existing => |ty| wip: {
3300 if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty);3310 if (!try sema.maybeRemoveOutdatedType(ty)) {
3311 try sema.declareDependency(.{ .interned = ty });
3312 try sema.addTypeReferenceEntry(src, ty);
3313 return Air.internedToRef(ty);
3314 }
3301 break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip;3315 break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip;
3302 },3316 },
3303 .wip => |wip| wip,3317 .wip => |wip| wip,
...@@ -3338,8 +3352,8 @@ fn zirUnionDecl(...@@ -3338,8 +3352,8 @@ fn zirUnionDecl(
3338 if (block.ownerModule().strip) break :codegen_type;3352 if (block.ownerModule().strip) break :codegen_type;
3339 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });3353 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
3340 }3354 }
3341 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));
3342 try sema.declareDependency(.{ .interned = wip_ty.index });3355 try sema.declareDependency(.{ .interned = wip_ty.index });
3356 try sema.addTypeReferenceEntry(src, wip_ty.index);
3343 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));3357 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
3344}3358}
33453359
...@@ -3388,7 +3402,10 @@ fn zirOpaqueDecl(...@@ -3388,7 +3402,10 @@ fn zirOpaqueDecl(
3388 // No `wrapWipTy` needed as no std.builtin types are opaque.3402 // No `wrapWipTy` needed as no std.builtin types are opaque.
3389 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {3403 const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
3390 // No `maybeRemoveOutdatedType` as opaque types are never outdated.3404 // No `maybeRemoveOutdatedType` as opaque types are never outdated.
3391 .existing => |ty| return Air.internedToRef(ty),3405 .existing => |ty| {
3406 try sema.addTypeReferenceEntry(src, ty);
3407 return Air.internedToRef(ty);
3408 },
3392 .wip => |wip| wip,3409 .wip => |wip| wip,
3393 };3410 };
3394 errdefer wip_ty.cancel(ip, pt.tid);3411 errdefer wip_ty.cancel(ip, pt.tid);
...@@ -3416,6 +3433,7 @@ fn zirOpaqueDecl(...@@ -3416,6 +3433,7 @@ fn zirOpaqueDecl(
3416 if (block.ownerModule().strip) break :codegen_type;3433 if (block.ownerModule().strip) break :codegen_type;
3417 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });3434 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
3418 }3435 }
3436 try sema.addTypeReferenceEntry(src, wip_ty.index);
3419 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));3437 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
3420}3438}
34213439
...@@ -21820,7 +21838,10 @@ fn zirReify(...@@ -21820,7 +21838,10 @@ fn zirReify(
21820 .zir_index = try block.trackZir(inst),21838 .zir_index = try block.trackZir(inst),
21821 } },21839 } },
21822 })) {21840 })) {
21823 .existing => |ty| return Air.internedToRef(ty),21841 .existing => |ty| {
21842 try sema.addTypeReferenceEntry(src, ty);
21843 return Air.internedToRef(ty);
21844 },
21824 .wip => |wip| wip,21845 .wip => |wip| wip,
21825 };21846 };
21826 errdefer wip_ty.cancel(ip, pt.tid);21847 errdefer wip_ty.cancel(ip, pt.tid);
...@@ -21839,6 +21860,7 @@ fn zirReify(...@@ -21839,6 +21860,7 @@ fn zirReify(
21839 .file_scope = block.getFileScopeIndex(mod),21860 .file_scope = block.getFileScopeIndex(mod),
21840 });21861 });
2184121862
21863 try sema.addTypeReferenceEntry(src, wip_ty.index);
21842 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));21864 return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
21843 },21865 },
21844 .Union => {21866 .Union => {
...@@ -22020,7 +22042,11 @@ fn reifyEnum(...@@ -22020,7 +22042,11 @@ fn reifyEnum(
22020 } },22042 } },
22021 })) {22043 })) {
22022 .wip => |wip| wip,22044 .wip => |wip| wip,
22023 .existing => |ty| return Air.internedToRef(ty),22045 .existing => |ty| {
22046 try sema.declareDependency(.{ .interned = ty });
22047 try sema.addTypeReferenceEntry(src, ty);
22048 return Air.internedToRef(ty);
22049 },
22024 };22050 };
22025 errdefer wip_ty.cancel(ip, pt.tid);22051 errdefer wip_ty.cancel(ip, pt.tid);
2202622052
...@@ -22044,6 +22070,8 @@ fn reifyEnum(...@@ -22044,6 +22070,8 @@ fn reifyEnum(
2204422070
22045 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);22071 const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
2204622072
22073 try sema.declareDependency(.{ .interned = wip_ty.index });
22074 try sema.addTypeReferenceEntry(src, wip_ty.index);
22047 wip_ty.prepare(ip, new_cau_index, new_namespace_index);22075 wip_ty.prepare(ip, new_cau_index, new_namespace_index);
22048 wip_ty.setTagTy(ip, tag_ty.toIntern());22076 wip_ty.setTagTy(ip, tag_ty.toIntern());
2204922077
...@@ -22182,7 +22210,11 @@ fn reifyUnion(...@@ -22182,7 +22210,11 @@ fn reifyUnion(
22182 } },22210 } },
22183 })) {22211 })) {
22184 .wip => |wip| wip,22212 .wip => |wip| wip,
22185 .existing => |ty| return Air.internedToRef(ty),22213 .existing => |ty| {
22214 try sema.declareDependency(.{ .interned = ty });
22215 try sema.addTypeReferenceEntry(src, ty);
22216 return Air.internedToRef(ty);
22217 },
22186 };22218 };
22187 errdefer wip_ty.cancel(ip, pt.tid);22219 errdefer wip_ty.cancel(ip, pt.tid);
2218822220
...@@ -22347,7 +22379,8 @@ fn reifyUnion(...@@ -22347,7 +22379,8 @@ fn reifyUnion(
22347 if (block.ownerModule().strip) break :codegen_type;22379 if (block.ownerModule().strip) break :codegen_type;
22348 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });22380 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
22349 }22381 }
22350 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));22382 try sema.declareDependency(.{ .interned = wip_ty.index });
22383 try sema.addTypeReferenceEntry(src, wip_ty.index);
22351 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));22384 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
22352}22385}
2235322386
...@@ -22447,7 +22480,11 @@ fn reifyStruct(...@@ -22447,7 +22480,11 @@ fn reifyStruct(
22447 } },22480 } },
22448 })) {22481 })) {
22449 .wip => |wip| wip,22482 .wip => |wip| wip,
22450 .existing => |ty| return Air.internedToRef(ty),22483 .existing => |ty| {
22484 try sema.declareDependency(.{ .interned = ty });
22485 try sema.addTypeReferenceEntry(src, ty);
22486 return Air.internedToRef(ty);
22487 },
22451 };22488 };
22452 errdefer wip_ty.cancel(ip, pt.tid);22489 errdefer wip_ty.cancel(ip, pt.tid);
2245322490
...@@ -22625,7 +22662,8 @@ fn reifyStruct(...@@ -22625,7 +22662,8 @@ fn reifyStruct(
22625 if (block.ownerModule().strip) break :codegen_type;22662 if (block.ownerModule().strip) break :codegen_type;
22626 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });22663 try mod.comp.queueJob(.{ .codegen_type = wip_ty.index });
22627 }22664 }
22628 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index }));22665 try sema.declareDependency(.{ .interned = wip_ty.index });
22666 try sema.addTypeReferenceEntry(src, wip_ty.index);
22629 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));22667 return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
22630}22668}
2263122669
...@@ -32231,6 +32269,18 @@ fn addReferenceEntry(...@@ -32231,6 +32269,18 @@ fn addReferenceEntry(
32231 try zcu.addUnitReference(sema.owner, referenced_unit, src);32269 try zcu.addUnitReference(sema.owner, referenced_unit, src);
32232}32270}
3223332271
32272fn addTypeReferenceEntry(
32273 sema: *Sema,
32274 src: LazySrcLoc,
32275 referenced_type: InternPool.Index,
32276) !void {
32277 const zcu = sema.pt.zcu;
32278 if (zcu.comp.reference_trace == 0) return;
32279 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
32280 if (gop.found_existing) return;
32281 try zcu.addTypeReference(sema.owner, referenced_type, src);
32282}
32283
32234pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {32284pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {
32235 const pt = sema.pt;32285 const pt = sema.pt;
32236 const zcu = pt.zcu;32286 const zcu = pt.zcu;
src/Zcu.zig+237-23
...@@ -168,6 +168,10 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},...@@ -168,6 +168,10 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
168/// it as outdated.168/// it as outdated.
169retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{},169retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{},
170170
171/// These are the modules which we initially queue for analysis in `Compilation.update`.
172/// `resolveReferences` will use these as the root of its reachability traversal.
173analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
174
171stage1_flags: packed struct {175stage1_flags: packed struct {
172 have_winmain: bool = false,176 have_winmain: bool = false,
173 have_wwinmain: bool = false,177 have_wwinmain: bool = false,
...@@ -186,7 +190,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{}...@@ -186,7 +190,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{}
186190
187/// Key is the `AnalUnit` *performing* the reference. This representation allows191/// Key is the `AnalUnit` *performing* the reference. This representation allows
188/// incremental updates to quickly delete references caused by a specific `AnalUnit`.192/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
189/// Value is index into `all_reference` of the first reference triggered by the unit.193/// Value is index into `all_references` of the first reference triggered by the unit.
190/// The `next` field on the `Reference` forms a linked list of all references194/// The `next` field on the `Reference` forms a linked list of all references
191/// triggered by the key `AnalUnit`.195/// triggered by the key `AnalUnit`.
192reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},196reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
...@@ -194,6 +198,16 @@ all_references: std.ArrayListUnmanaged(Reference) = .{},...@@ -194,6 +198,16 @@ all_references: std.ArrayListUnmanaged(Reference) = .{},
194/// Freelist of indices in `all_references`.198/// Freelist of indices in `all_references`.
195free_references: std.ArrayListUnmanaged(u32) = .{},199free_references: std.ArrayListUnmanaged(u32) = .{},
196200
201/// Key is the `AnalUnit` *performing* the reference. This representation allows
202/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
203/// Value is index into `all_type_reference` of the first reference triggered by the unit.
204/// The `next` field on the `TypeReference` forms a linked list of all type references
205/// triggered by the key `AnalUnit`.
206type_reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
207all_type_references: std.ArrayListUnmanaged(TypeReference) = .{},
208/// Freelist of indices in `all_type_references`.
209free_type_references: std.ArrayListUnmanaged(u32) = .{},
210
197panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,211panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len,
198/// The panic function body.212/// The panic function body.
199panic_func_index: InternPool.Index = .none,213panic_func_index: InternPool.Index = .none,
...@@ -302,6 +316,16 @@ pub const Reference = struct {...@@ -302,6 +316,16 @@ pub const Reference = struct {
302 src: LazySrcLoc,316 src: LazySrcLoc,
303};317};
304318
319pub const TypeReference = struct {
320 /// The container type which was referenced.
321 referenced: InternPool.Index,
322 /// Index into `all_type_references` of the next `TypeReference` triggered by the same `AnalUnit`.
323 /// `std.math.maxInt(u32)` is the sentinel.
324 next: u32,
325 /// The source location of the reference.
326 src: LazySrcLoc,
327};
328
305/// The container that structs, enums, unions, and opaques have.329/// The container that structs, enums, unions, and opaques have.
306pub const Namespace = struct {330pub const Namespace = struct {
307 parent: OptionalIndex,331 parent: OptionalIndex,
...@@ -2155,6 +2179,10 @@ pub fn deinit(zcu: *Zcu) void {...@@ -2155,6 +2179,10 @@ pub fn deinit(zcu: *Zcu) void {
2155 zcu.all_references.deinit(gpa);2179 zcu.all_references.deinit(gpa);
2156 zcu.free_references.deinit(gpa);2180 zcu.free_references.deinit(gpa);
21572181
2182 zcu.type_reference_table.deinit(gpa);
2183 zcu.all_type_references.deinit(gpa);
2184 zcu.free_type_references.deinit(gpa);
2185
2158 zcu.intern_pool.deinit(gpa);2186 zcu.intern_pool.deinit(gpa);
2159}2187}
21602188
...@@ -2660,16 +2688,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -2660,16 +2688,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
2660pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {2688pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
2661 const gpa = zcu.gpa;2689 const gpa = zcu.gpa;
26622690
2663 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;2691 unit_refs: {
2664 var idx = kv.value;2692 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;
2693 var idx = kv.value;
26652694
2666 while (idx != std.math.maxInt(u32)) {2695 while (idx != std.math.maxInt(u32)) {
2667 zcu.free_references.append(gpa, idx) catch {2696 zcu.free_references.append(gpa, idx) catch {
2668 // This space will be reused eventually, so we need not propagate this error.2697 // This space will be reused eventually, so we need not propagate this error.
2669 // Just leak it for now, and let GC reclaim it later on.2698 // Just leak it for now, and let GC reclaim it later on.
2670 return;2699 break :unit_refs;
2671 };2700 };
2672 idx = zcu.all_references.items[idx].next;2701 idx = zcu.all_references.items[idx].next;
2702 }
2703 }
2704
2705 type_refs: {
2706 const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse return;
2707 var idx = kv.value;
2708
2709 while (idx != std.math.maxInt(u32)) {
2710 zcu.free_type_references.append(gpa, idx) catch {
2711 // This space will be reused eventually, so we need not propagate this error.
2712 // Just leak it for now, and let GC reclaim it later on.
2713 break :type_refs;
2714 };
2715 idx = zcu.all_type_references.items[idx].next;
2716 }
2673 }2717 }
2674}2718}
26752719
...@@ -2696,6 +2740,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit...@@ -2696,6 +2740,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit
2696 gop.value_ptr.* = @intCast(ref_idx);2740 gop.value_ptr.* = @intCast(ref_idx);
2697}2741}
26982742
2743pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPool.Index, ref_src: LazySrcLoc) Allocator.Error!void {
2744 const gpa = zcu.gpa;
2745
2746 try zcu.type_reference_table.ensureUnusedCapacity(gpa, 1);
2747
2748 const ref_idx = zcu.free_type_references.popOrNull() orelse idx: {
2749 _ = try zcu.all_type_references.addOne(gpa);
2750 break :idx zcu.all_type_references.items.len - 1;
2751 };
2752
2753 errdefer comptime unreachable;
2754
2755 const gop = zcu.type_reference_table.getOrPutAssumeCapacity(src_unit);
2756
2757 zcu.all_type_references.items[ref_idx] = .{
2758 .referenced = referenced_type,
2759 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32),
2760 .src = ref_src,
2761 };
2762
2763 gop.value_ptr.* = @intCast(ref_idx);
2764}
2765
2699pub fn errorSetBits(mod: *Zcu) u16 {2766pub fn errorSetBits(mod: *Zcu) u16 {
2700 if (mod.error_limit == 0) return 0;2767 if (mod.error_limit == 0) return 0;
2701 return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1;2768 return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1;
...@@ -2990,28 +3057,175 @@ pub const ResolvedReference = struct {...@@ -2990,28 +3057,175 @@ pub const ResolvedReference = struct {
2990};3057};
29913058
2992/// Returns a mapping from an `AnalUnit` to where it is referenced.3059/// Returns a mapping from an `AnalUnit` to where it is referenced.
2993/// TODO: in future, this must be adapted to traverse from roots of analysis. That way, we can3060/// If the value is `null`, the `AnalUnit` is a root of analysis.
2994/// use the returned map to determine which units have become unreferenced in an incremental update.3061/// If an `AnalUnit` is not in the returned map, it is unreferenced.
2995pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) {3062pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
2996 const gpa = zcu.gpa;3063 const gpa = zcu.gpa;
3064 const comp = zcu.comp;
3065 const ip = &zcu.intern_pool;
29973066
2998 var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{};3067 var result: std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{};
2999 errdefer result.deinit(gpa);3068 errdefer result.deinit(gpa);
30003069
3070 var checked_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
3071 var type_queue: std.AutoArrayHashMapUnmanaged(InternPool.Index, ?ResolvedReference) = .{};
3072 var unit_queue: std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{};
3073 defer {
3074 checked_types.deinit(gpa);
3075 type_queue.deinit(gpa);
3076 unit_queue.deinit(gpa);
3077 }
3078
3001 // This is not a sufficient size, but a lower bound.3079 // This is not a sufficient size, but a lower bound.
3002 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));3080 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
30033081
3004 for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| {3082 try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len);
3005 assert(first_ref_idx != std.math.maxInt(u32));3083 for (zcu.analysis_roots.slice()) |mod| {
3006 var ref_idx = first_ref_idx;3084 // Logic ripped from `Zcu.PerThread.importPkg`.
3007 while (ref_idx != std.math.maxInt(u32)) {3085 // TODO: this is silly, `Module` should just store a reference to its root `File`.
3008 const ref = zcu.all_references.items[ref_idx];3086 const resolved_path = try std.fs.path.resolve(gpa, &.{
3009 const gop = try result.getOrPut(gpa, ref.referenced);3087 mod.root.root_dir.path orelse ".",
3010 if (!gop.found_existing) {3088 mod.root.sub_path,
3011 gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src };3089 mod.root_src_path,
3090 });
3091 defer gpa.free(resolved_path);
3092 const file = zcu.import_table.get(resolved_path).?;
3093 if (zcu.fileByIndex(file).status != .success_zir) continue;
3094 const root_ty = zcu.fileRootType(file);
3095 if (root_ty == .none) continue;
3096 type_queue.putAssumeCapacityNoClobber(root_ty, null);
3097 }
3098
3099 while (true) {
3100 if (type_queue.popOrNull()) |kv| {
3101 const ty = kv.key;
3102 const referencer = kv.value;
3103 try checked_types.putNoClobber(gpa, ty, {});
3104
3105 // If this type has a `Cau` for resolution, it's automatically referenced.
3106 const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
3107 .struct_type => ip.loadStructType(ty).cau,
3108 .union_type => ip.loadUnionType(ty).cau.toOptional(),
3109 .enum_type => ip.loadEnumType(ty).cau,
3110 .opaque_type => .none,
3111 else => unreachable,
3112 };
3113 if (resolution_cau.unwrap()) |cau| {
3114 // this should only be referenced by the type
3115 const unit = AnalUnit.wrap(.{ .cau = cau });
3116 assert(!result.contains(unit));
3117 try unit_queue.putNoClobber(gpa, unit, referencer);
3118 }
3119
3120 // If this is a union with a generated tag, its tag type is automatically referenced.
3121 // We don't add this reference for non-generated tags, as those will already be referenced via the union's `Cau`, with a better source location.
3122 if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
3123 const tag_ty = union_obj.enum_tag_ty;
3124 if (tag_ty != .none) {
3125 if (ip.indexToKey(tag_ty).enum_type == .generated_tag) {
3126 if (!checked_types.contains(tag_ty)) {
3127 try type_queue.put(gpa, tag_ty, referencer);
3128 }
3129 }
3130 }
3131 }
3132
3133 // Queue any decls within this type which would be automatically analyzed.
3134 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
3135 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap() orelse continue;
3136 for (zcu.namespacePtr(ns).other_decls.items) |cau| {
3137 // These are `comptime` and `test` declarations.
3138 // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
3139 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3140 const file = zcu.fileByIndex(inst_info.file);
3141 const zir = file.zir;
3142 const declaration = zir.getDeclaration(inst_info.inst)[0];
3143 const want_analysis = switch (declaration.name) {
3144 .@"usingnamespace" => unreachable,
3145 .@"comptime" => true,
3146 else => a: {
3147 if (!comp.config.is_test) break :a false;
3148 if (file.mod != zcu.main_mod) break :a false;
3149 if (declaration.name.isNamedTest(zir) or declaration.name == .decltest) {
3150 const nav = ip.getCau(cau).owner.unwrap().nav;
3151 const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
3152 for (comp.test_filters) |test_filter| {
3153 if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
3154 } else break :a false;
3155 }
3156 break :a true;
3157 },
3158 };
3159 if (want_analysis) {
3160 const unit = AnalUnit.wrap(.{ .cau = cau });
3161 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3162 }
3163 }
3164 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
3165 // These are named declarations. They are analyzed only if marked `export`.
3166 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3167 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3168 const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0];
3169 if (declaration.flags.is_export) {
3170 const unit = AnalUnit.wrap(.{ .cau = cau });
3171 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3172 }
3173 }
3174 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
3175 // These are named declarations. They are analyzed only if marked `export`.
3176 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3177 const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
3178 const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0];
3179 if (declaration.flags.is_export) {
3180 const unit = AnalUnit.wrap(.{ .cau = cau });
3181 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3182 }
3183 }
3184 // Incremental compilation does not support `usingnamespace`.
3185 // These are only included to keep good reference traces in non-incremental updates.
3186 for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
3187 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3188 const unit = AnalUnit.wrap(.{ .cau = cau });
3189 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3012 }3190 }
3013 ref_idx = ref.next;3191 for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
3192 const cau = ip.getNav(nav).analysis_owner.unwrap().?;
3193 const unit = AnalUnit.wrap(.{ .cau = cau });
3194 if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
3195 }
3196 continue;
3197 }
3198 if (unit_queue.popOrNull()) |kv| {
3199 const unit = kv.key;
3200 try result.putNoClobber(gpa, unit, kv.value);
3201
3202 if (zcu.reference_table.get(unit)) |first_ref_idx| {
3203 assert(first_ref_idx != std.math.maxInt(u32));
3204 var ref_idx = first_ref_idx;
3205 while (ref_idx != std.math.maxInt(u32)) {
3206 const ref = zcu.all_references.items[ref_idx];
3207 if (!result.contains(ref.referenced)) try unit_queue.put(gpa, ref.referenced, .{
3208 .referencer = unit,
3209 .src = ref.src,
3210 });
3211 ref_idx = ref.next;
3212 }
3213 }
3214 if (zcu.type_reference_table.get(unit)) |first_ref_idx| {
3215 assert(first_ref_idx != std.math.maxInt(u32));
3216 var ref_idx = first_ref_idx;
3217 while (ref_idx != std.math.maxInt(u32)) {
3218 const ref = zcu.all_type_references.items[ref_idx];
3219 if (!checked_types.contains(ref.referenced)) try type_queue.put(gpa, ref.referenced, .{
3220 .referencer = unit,
3221 .src = ref.src,
3222 });
3223 ref_idx = ref.next;
3224 }
3225 }
3226 continue;
3014 }3227 }
3228 break;
3015 }3229 }
30163230
3017 return result;3231 return result;