| author | |
| committer | |
| log | 6faa4cc7e60c2ecd26759878a6f9e277d69a4968 |
| tree | fb54ba56449d8e78535a9459a4de0ec0e8d01d7c |
| parent | b65865b027f5531408654eae82cec05468b2c082 |
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 | 2264 | } |
| 2265 | 2265 | } |
| 2266 | 2266 | |
| 2267 | zcu.analysis_roots.resize(0) catch unreachable; | |
| 2268 | ||
| 2267 | 2269 | try comp.queueJob(.{ .analyze_mod = std_mod }); |
| 2270 | zcu.analysis_roots.appendAssumeCapacity(std_mod); | |
| 2271 | ||
| 2268 | 2272 | if (comp.config.is_test) { |
| 2269 | 2273 | try comp.queueJob(.{ .analyze_mod = zcu.main_mod }); |
| 2274 | zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod); | |
| 2270 | 2275 | } |
| 2271 | 2276 | |
| 2272 | 2277 | if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { |
| 2273 | 2278 | try comp.queueJob(.{ .analyze_mod = compiler_rt_mod }); |
| 2279 | zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod); | |
| 2274 | 2280 | } |
| 2275 | 2281 | } |
| 2276 | 2282 | |
| ... | ... | @@ -3059,6 +3065,9 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3059 | 3065 | if (comp.module) |zcu| { |
| 3060 | 3066 | const ip = &zcu.intern_pool; |
| 3061 | 3067 | |
| 3068 | var all_references = try zcu.resolveReferences(); | |
| 3069 | defer all_references.deinit(zcu.gpa); | |
| 3070 | ||
| 3062 | 3071 | total += zcu.failed_exports.count(); |
| 3063 | 3072 | total += zcu.failed_embed_files.count(); |
| 3064 | 3073 | |
| ... | ... | @@ -3079,6 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { |
| 3079 | 3088 | // the previous parse success, including compile errors, but we cannot |
| 3080 | 3089 | // emit them until the file succeeds parsing. |
| 3081 | 3090 | for (zcu.failed_analysis.keys()) |anal_unit| { |
| 3091 | if (!all_references.contains(anal_unit)) continue; | |
| 3082 | 3092 | const file_index = switch (anal_unit.unwrap()) { |
| 3083 | 3093 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| 3084 | 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 | 3126 | } |
| 3117 | 3127 | } |
| 3118 | 3128 | |
| 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 | 3129 | return @intCast(total); |
| 3126 | 3130 | } |
| 3127 | 3131 | |
| ... | ... | @@ -3167,12 +3171,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3167 | 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 | 3178 | if (comp.module) |zcu| { |
| 3171 | 3179 | const ip = &zcu.intern_pool; |
| 3172 | 3180 | |
| 3173 | var all_references = try zcu.resolveReferences(); | |
| 3174 | defer all_references.deinit(gpa); | |
| 3175 | ||
| 3176 | 3181 | for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| { |
| 3177 | 3182 | if (error_msg) |msg| { |
| 3178 | 3183 | try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references); |
| ... | ... | @@ -3220,6 +3225,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3220 | 3225 | if (err) |e| return e; |
| 3221 | 3226 | } |
| 3222 | 3227 | for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| { |
| 3228 | if (!all_references.contains(anal_unit)) continue; | |
| 3229 | ||
| 3223 | 3230 | const file_index = switch (anal_unit.unwrap()) { |
| 3224 | 3231 | .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, |
| 3225 | 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 | 3320 | |
| 3314 | 3321 | if (comp.module) |zcu| { |
| 3315 | 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 | 3323 | const values = zcu.compile_log_sources.values(); |
| 3320 | 3324 | // First one will be the error; subsequent ones will be notes. |
| 3321 | 3325 | const src_loc = values[0].src(); |
| ... | ... | @@ -3339,6 +3343,17 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3339 | 3343 | |
| 3340 | 3344 | assert(comp.totalErrorCount() == bundle.root_list.items.len); |
| 3341 | 3345 | |
| 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 | 3357 | const compile_log_text = if (comp.module) |m| m.compile_log_text.items else ""; |
| 3343 | 3358 | return bundle.toOwnedBundle(compile_log_text); |
| 3344 | 3359 | } |
| ... | ... | @@ -3393,7 +3408,7 @@ pub fn addModuleErrorMsg( |
| 3393 | 3408 | mod: *Zcu, |
| 3394 | 3409 | eb: *ErrorBundle.Wip, |
| 3395 | 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 | 3412 | ) !void { |
| 3398 | 3413 | const gpa = eb.gpa; |
| 3399 | 3414 | const ip = &mod.intern_pool; |
| ... | ... | @@ -3423,7 +3438,8 @@ pub fn addModuleErrorMsg( |
| 3423 | 3438 | const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len; |
| 3424 | 3439 | |
| 3425 | 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 | 3443 | const gop = try seen.getOrPut(gpa, ref.referencer); |
| 3428 | 3444 | if (gop.found_existing) break; |
| 3429 | 3445 | if (ref_traces.items.len < max_references) { |
src/Sema.zig+63-13| ... | ... | @@ -110,6 +110,7 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{}, |
| 110 | 110 | /// of data stored in `Zcu.all_references`. It exists to avoid adding references to |
| 111 | 111 | /// a given `AnalUnit` multiple times. |
| 112 | 112 | references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, |
| 113 | type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, | |
| 113 | 114 | |
| 114 | 115 | const MaybeComptimeAlloc = struct { |
| 115 | 116 | /// The runtime index of the `alloc` instruction. |
| ... | ... | @@ -877,6 +878,7 @@ pub fn deinit(sema: *Sema) void { |
| 877 | 878 | sema.comptime_allocs.deinit(gpa); |
| 878 | 879 | sema.exports.deinit(gpa); |
| 879 | 880 | sema.references.deinit(gpa); |
| 881 | sema.type_references.deinit(gpa); | |
| 880 | 882 | sema.* = undefined; |
| 881 | 883 | } |
| 882 | 884 | |
| ... | ... | @@ -2809,7 +2811,11 @@ fn zirStructDecl( |
| 2809 | 2811 | }; |
| 2810 | 2812 | const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) { |
| 2811 | 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 | 2819 | break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip; |
| 2814 | 2820 | }, |
| 2815 | 2821 | .wip => |wip| wip, |
| ... | ... | @@ -2850,8 +2856,8 @@ fn zirStructDecl( |
| 2850 | 2856 | if (block.ownerModule().strip) break :codegen_type; |
| 2851 | 2857 | try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 2852 | 2858 | } |
| 2853 | try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); | |
| 2854 | 2859 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 2860 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 2855 | 2861 | return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); |
| 2856 | 2862 | } |
| 2857 | 2863 | |
| ... | ... | @@ -3031,7 +3037,11 @@ fn zirEnumDecl( |
| 3031 | 3037 | }; |
| 3032 | 3038 | const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) { |
| 3033 | 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 | 3045 | break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip; |
| 3036 | 3046 | }, |
| 3037 | 3047 | .wip => |wip| wip, |
| ... | ... | @@ -3071,8 +3081,8 @@ fn zirEnumDecl( |
| 3071 | 3081 | |
| 3072 | 3082 | try pt.scanNamespace(new_namespace_index, decls); |
| 3073 | 3083 | |
| 3074 | try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); | |
| 3075 | 3084 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 3085 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 3076 | 3086 | |
| 3077 | 3087 | // We've finished the initial construction of this type, and are about to perform analysis. |
| 3078 | 3088 | // Set the Cau and namespace appropriately, and don't destroy anything on failure. |
| ... | ... | @@ -3297,7 +3307,11 @@ fn zirUnionDecl( |
| 3297 | 3307 | }; |
| 3298 | 3308 | const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) { |
| 3299 | 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 | 3315 | break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip; |
| 3302 | 3316 | }, |
| 3303 | 3317 | .wip => |wip| wip, |
| ... | ... | @@ -3338,8 +3352,8 @@ fn zirUnionDecl( |
| 3338 | 3352 | if (block.ownerModule().strip) break :codegen_type; |
| 3339 | 3353 | try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3340 | 3354 | } |
| 3341 | try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); | |
| 3342 | 3355 | try sema.declareDependency(.{ .interned = wip_ty.index }); |
| 3356 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 3343 | 3357 | return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); |
| 3344 | 3358 | } |
| 3345 | 3359 | |
| ... | ... | @@ -3388,7 +3402,10 @@ fn zirOpaqueDecl( |
| 3388 | 3402 | // No `wrapWipTy` needed as no std.builtin types are opaque. |
| 3389 | 3403 | const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { |
| 3390 | 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 | 3409 | .wip => |wip| wip, |
| 3393 | 3410 | }; |
| 3394 | 3411 | errdefer wip_ty.cancel(ip, pt.tid); |
| ... | ... | @@ -3416,6 +3433,7 @@ fn zirOpaqueDecl( |
| 3416 | 3433 | if (block.ownerModule().strip) break :codegen_type; |
| 3417 | 3434 | try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); |
| 3418 | 3435 | } |
| 3436 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 3419 | 3437 | return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index)); |
| 3420 | 3438 | } |
| 3421 | 3439 | |
| ... | ... | @@ -21820,7 +21838,10 @@ fn zirReify( |
| 21820 | 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 | 21845 | .wip => |wip| wip, |
| 21825 | 21846 | }; |
| 21826 | 21847 | errdefer wip_ty.cancel(ip, pt.tid); |
| ... | ... | @@ -21839,6 +21860,7 @@ fn zirReify( |
| 21839 | 21860 | .file_scope = block.getFileScopeIndex(mod), |
| 21840 | 21861 | }); |
| 21841 | 21862 | |
| 21863 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 21842 | 21864 | return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index)); |
| 21843 | 21865 | }, |
| 21844 | 21866 | .Union => { |
| ... | ... | @@ -22020,7 +22042,11 @@ fn reifyEnum( |
| 22020 | 22042 | } }, |
| 22021 | 22043 | })) { |
| 22022 | 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 | 22051 | errdefer wip_ty.cancel(ip, pt.tid); |
| 22026 | 22052 | |
| ... | ... | @@ -22044,6 +22070,8 @@ fn reifyEnum( |
| 22044 | 22070 | |
| 22045 | 22071 | const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index); |
| 22046 | 22072 | |
| 22073 | try sema.declareDependency(.{ .interned = wip_ty.index }); | |
| 22074 | try sema.addTypeReferenceEntry(src, wip_ty.index); | |
| 22047 | 22075 | wip_ty.prepare(ip, new_cau_index, new_namespace_index); |
| 22048 | 22076 | wip_ty.setTagTy(ip, tag_ty.toIntern()); |
| 22049 | 22077 | |
| ... | ... | @@ -22182,7 +22210,11 @@ fn reifyUnion( |
| 22182 | 22210 | } }, |
| 22183 | 22211 | })) { |
| 22184 | 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 | 22219 | errdefer wip_ty.cancel(ip, pt.tid); |
| 22188 | 22220 | |
| ... | ... | @@ -22347,7 +22379,8 @@ fn reifyUnion( |
| 22347 | 22379 | if (block.ownerModule().strip) break :codegen_type; |
| 22348 | 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 | 22384 | return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); |
| 22352 | 22385 | } |
| 22353 | 22386 | |
| ... | ... | @@ -22447,7 +22480,11 @@ fn reifyStruct( |
| 22447 | 22480 | } }, |
| 22448 | 22481 | })) { |
| 22449 | 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 | 22489 | errdefer wip_ty.cancel(ip, pt.tid); |
| 22453 | 22490 | |
| ... | ... | @@ -22625,7 +22662,8 @@ fn reifyStruct( |
| 22625 | 22662 | if (block.ownerModule().strip) break :codegen_type; |
| 22626 | 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 | 22667 | return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); |
| 22630 | 22668 | } |
| 22631 | 22669 | |
| ... | ... | @@ -32231,6 +32269,18 @@ fn addReferenceEntry( |
| 32231 | 32269 | try zcu.addUnitReference(sema.owner, referenced_unit, src); |
| 32232 | 32270 | } |
| 32233 | 32271 | |
| 32272 | fn 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 | ||
| 32234 | 32284 | pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void { |
| 32235 | 32285 | const pt = sema.pt; |
| 32236 | 32286 | const zcu = pt.zcu; |
src/Zcu.zig+237-23| ... | ... | @@ -168,6 +168,10 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, |
| 168 | 168 | /// it as outdated. |
| 169 | 169 | retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{}, |
| 170 | 170 | |
| 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. | |
| 173 | analysis_roots: std.BoundedArray(*Package.Module, 3) = .{}, | |
| 174 | ||
| 171 | 175 | stage1_flags: packed struct { |
| 172 | 176 | have_winmain: bool = false, |
| 173 | 177 | have_wwinmain: bool = false, |
| ... | ... | @@ -186,7 +190,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{} |
| 186 | 190 | |
| 187 | 191 | /// Key is the `AnalUnit` *performing* the reference. This representation allows |
| 188 | 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 | 194 | /// The `next` field on the `Reference` forms a linked list of all references |
| 191 | 195 | /// triggered by the key `AnalUnit`. |
| 192 | 196 | reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, |
| ... | ... | @@ -194,6 +198,16 @@ all_references: std.ArrayListUnmanaged(Reference) = .{}, |
| 194 | 198 | /// Freelist of indices in `all_references`. |
| 195 | 199 | free_references: std.ArrayListUnmanaged(u32) = .{}, |
| 196 | 200 | |
| 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`. | |
| 206 | type_reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, | |
| 207 | all_type_references: std.ArrayListUnmanaged(TypeReference) = .{}, | |
| 208 | /// Freelist of indices in `all_type_references`. | |
| 209 | free_type_references: std.ArrayListUnmanaged(u32) = .{}, | |
| 210 | ||
| 197 | 211 | panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len, |
| 198 | 212 | /// The panic function body. |
| 199 | 213 | panic_func_index: InternPool.Index = .none, |
| ... | ... | @@ -302,6 +316,16 @@ pub const Reference = struct { |
| 302 | 316 | src: LazySrcLoc, |
| 303 | 317 | }; |
| 304 | 318 | |
| 319 | pub 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 | 329 | /// The container that structs, enums, unions, and opaques have. |
| 306 | 330 | pub const Namespace = struct { |
| 307 | 331 | parent: OptionalIndex, |
| ... | ... | @@ -2155,6 +2179,10 @@ pub fn deinit(zcu: *Zcu) void { |
| 2155 | 2179 | zcu.all_references.deinit(gpa); |
| 2156 | 2180 | zcu.free_references.deinit(gpa); |
| 2157 | 2181 | |
| 2182 | zcu.type_reference_table.deinit(gpa); | |
| 2183 | zcu.all_type_references.deinit(gpa); | |
| 2184 | zcu.free_type_references.deinit(gpa); | |
| 2185 | ||
| 2158 | 2186 | zcu.intern_pool.deinit(gpa); |
| 2159 | 2187 | } |
| 2160 | 2188 | |
| ... | ... | @@ -2660,16 +2688,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 2660 | 2688 | pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 2661 | 2689 | const gpa = zcu.gpa; |
| 2662 | 2690 | |
| 2663 | const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return; | |
| 2664 | var idx = kv.value; | |
| 2691 | unit_refs: { | |
| 2692 | const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return; | |
| 2693 | var idx = kv.value; | |
| 2665 | 2694 | |
| 2666 | while (idx != std.math.maxInt(u32)) { | |
| 2667 | zcu.free_references.append(gpa, idx) catch { | |
| 2668 | // 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. | |
| 2670 | return; | |
| 2671 | }; | |
| 2672 | idx = zcu.all_references.items[idx].next; | |
| 2695 | while (idx != std.math.maxInt(u32)) { | |
| 2696 | zcu.free_references.append(gpa, idx) catch { | |
| 2697 | // This space will be reused eventually, so we need not propagate this error. | |
| 2698 | // Just leak it for now, and let GC reclaim it later on. | |
| 2699 | break :unit_refs; | |
| 2700 | }; | |
| 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 | } |
| 2675 | 2719 | |
| ... | ... | @@ -2696,6 +2740,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit |
| 2696 | 2740 | gop.value_ptr.* = @intCast(ref_idx); |
| 2697 | 2741 | } |
| 2698 | 2742 | |
| 2743 | pub 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 | ||
| 2699 | 2766 | pub fn errorSetBits(mod: *Zcu) u16 { |
| 2700 | 2767 | if (mod.error_limit == 0) return 0; |
| 2701 | 2768 | return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1; |
| ... | ... | @@ -2990,28 +3057,175 @@ pub const ResolvedReference = struct { |
| 2990 | 3057 | }; |
| 2991 | 3058 | |
| 2992 | 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 can | |
| 2994 | /// use the returned map to determine which units have become unreferenced in an incremental update. | |
| 2995 | pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) { | |
| 3060 | /// If the value is `null`, the `AnalUnit` is a root of analysis. | |
| 3061 | /// If an `AnalUnit` is not in the returned map, it is unreferenced. | |
| 3062 | pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) { | |
| 2996 | 3063 | const gpa = zcu.gpa; |
| 3064 | const comp = zcu.comp; | |
| 3065 | const ip = &zcu.intern_pool; | |
| 2997 | 3066 | |
| 2998 | var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{}; | |
| 3067 | var result: std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{}; | |
| 2999 | 3068 | errdefer result.deinit(gpa); |
| 3000 | 3069 | |
| 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 | 3079 | // This is not a sufficient size, but a lower bound. |
| 3002 | 3080 | try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); |
| 3003 | 3081 | |
| 3004 | for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| { | |
| 3005 | assert(first_ref_idx != std.math.maxInt(u32)); | |
| 3006 | var ref_idx = first_ref_idx; | |
| 3007 | while (ref_idx != std.math.maxInt(u32)) { | |
| 3008 | const ref = zcu.all_references.items[ref_idx]; | |
| 3009 | const gop = try result.getOrPut(gpa, ref.referenced); | |
| 3010 | if (!gop.found_existing) { | |
| 3011 | gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src }; | |
| 3082 | try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len); | |
| 3083 | for (zcu.analysis_roots.slice()) |mod| { | |
| 3084 | // Logic ripped from `Zcu.PerThread.importPkg`. | |
| 3085 | // TODO: this is silly, `Module` should just store a reference to its root `File`. | |
| 3086 | const resolved_path = try std.fs.path.resolve(gpa, &.{ | |
| 3087 | mod.root.root_dir.path orelse ".", | |
| 3088 | mod.root.sub_path, | |
| 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 | } |
| 3016 | 3230 | |
| 3017 | 3231 | return result; |