diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-08-12 11:02:18 +0100 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2024-08-17 18:50:10 -0400 |
| commit | 6faa4cc7e60c2ecd26759878a6f9e277d69a4968 (patch) | |
| tree | fb54ba56449d8e78535a9459a4de0ec0e8d01d7c /src | |
| parent | b65865b027f5531408654eae82cec05468b2c082 (diff) | |
| download | zig-6faa4cc7e60c2ecd26759878a6f9e277d69a4968.tar.gz zig-6faa4cc7e60c2ecd26759878a6f9e277d69a4968.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 44 | ||||
| -rw-r--r-- | src/Sema.zig | 76 | ||||
| -rw-r--r-- | src/Zcu.zig | 260 |
3 files changed, 330 insertions, 50 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index fd55db0139..f2d61db0fd 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2264,13 +2264,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } + zcu.analysis_roots.resize(0) catch unreachable; + try comp.queueJob(.{ .analyze_mod = std_mod }); + zcu.analysis_roots.appendAssumeCapacity(std_mod); + if (comp.config.is_test) { try comp.queueJob(.{ .analyze_mod = zcu.main_mod }); + zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod); } if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { try comp.queueJob(.{ .analyze_mod = compiler_rt_mod }); + zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod); } } @@ -3059,6 +3065,9 @@ pub fn totalErrorCount(comp: *Compilation) u32 { if (comp.module) |zcu| { const ip = &zcu.intern_pool; + var all_references = try zcu.resolveReferences(); + defer all_references.deinit(zcu.gpa); + total += zcu.failed_exports.count(); total += zcu.failed_embed_files.count(); @@ -3079,6 +3088,7 @@ pub fn totalErrorCount(comp: *Compilation) u32 { // the previous parse success, including compile errors, but we cannot // emit them until the file succeeds parsing. for (zcu.failed_analysis.keys()) |anal_unit| { + if (!all_references.contains(anal_unit)) continue; const file_index = switch (anal_unit.unwrap()) { .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, .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 { } } - if (comp.module) |zcu| { - if (total == 0 and zcu.transitive_failed_analysis.count() > 0) { - @panic("Transitive analysis errors, but none actually emitted"); - } - } - return @intCast(total); } @@ -3167,12 +3171,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { .msg = try bundle.addString("memory allocation failure"), }); } + + var all_references = if (comp.module) |zcu| try zcu.resolveReferences() else undefined; + defer if (comp.module != null) all_references.deinit(gpa); + if (comp.module) |zcu| { const ip = &zcu.intern_pool; - var all_references = try zcu.resolveReferences(); - defer all_references.deinit(gpa); - for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| { if (error_msg) |msg| { try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references); @@ -3220,6 +3225,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { if (err) |e| return e; } for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| { + if (!all_references.contains(anal_unit)) continue; + const file_index = switch (anal_unit.unwrap()) { .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope, .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 { if (comp.module) |zcu| { if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { - var all_references = try zcu.resolveReferences(); - defer all_references.deinit(gpa); - const values = zcu.compile_log_sources.values(); // First one will be the error; subsequent ones will be notes. const src_loc = values[0].src(); @@ -3339,6 +3343,17 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { assert(comp.totalErrorCount() == bundle.root_list.items.len); + if (comp.module) |zcu| { + if (bundle.root_list.items.len == 0) { + const should_have_error = for (zcu.transitive_failed_analysis.keys()) |failed_unit| { + if (all_references.contains(failed_unit)) break true; + } else false; + if (should_have_error) { + @panic("referenced transitive analysis errors, but none actually emitted"); + } + } + } + const compile_log_text = if (comp.module) |m| m.compile_log_text.items else ""; return bundle.toOwnedBundle(compile_log_text); } @@ -3393,7 +3408,7 @@ pub fn addModuleErrorMsg( mod: *Zcu, eb: *ErrorBundle.Wip, module_err_msg: Zcu.ErrorMsg, - all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, Zcu.ResolvedReference), + all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, ?Zcu.ResolvedReference), ) !void { const gpa = eb.gpa; const ip = &mod.intern_pool; @@ -3423,7 +3438,8 @@ pub fn addModuleErrorMsg( const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len; var referenced_by = rt_root; - while (all_references.get(referenced_by)) |ref| { + while (all_references.get(referenced_by)) |maybe_ref| { + const ref = maybe_ref orelse break; const gop = try seen.getOrPut(gpa, ref.referencer); if (gop.found_existing) break; if (ref_traces.items.len < max_references) { diff --git a/src/Sema.zig b/src/Sema.zig index 84999c8a86..1a74b88e05 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -110,6 +110,7 @@ exports: std.ArrayListUnmanaged(Zcu.Export) = .{}, /// of data stored in `Zcu.all_references`. It exists to avoid adding references to /// a given `AnalUnit` multiple times. references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, +type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, const MaybeComptimeAlloc = struct { /// The runtime index of the `alloc` instruction. @@ -877,6 +878,7 @@ pub fn deinit(sema: *Sema) void { sema.comptime_allocs.deinit(gpa); sema.exports.deinit(gpa); sema.references.deinit(gpa); + sema.type_references.deinit(gpa); sema.* = undefined; } @@ -2809,7 +2811,11 @@ fn zirStructDecl( }; const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init)) { .existing => |ty| wip: { - if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty); + if (!try sema.maybeRemoveOutdatedType(ty)) { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + } break :wip (try ip.getStructType(gpa, pt.tid, struct_init)).wip; }, .wip => |wip| wip, @@ -2850,8 +2856,8 @@ fn zirStructDecl( if (block.ownerModule().strip) break :codegen_type; try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); } - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); } @@ -3031,7 +3037,11 @@ fn zirEnumDecl( }; const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init)) { .existing => |ty| wip: { - if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty); + if (!try sema.maybeRemoveOutdatedType(ty)) { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + } break :wip (try ip.getEnumType(gpa, pt.tid, enum_init)).wip; }, .wip => |wip| wip, @@ -3071,8 +3081,8 @@ fn zirEnumDecl( try pt.scanNamespace(new_namespace_index, decls); - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); // We've finished the initial construction of this type, and are about to perform analysis. // Set the Cau and namespace appropriately, and don't destroy anything on failure. @@ -3297,7 +3307,11 @@ fn zirUnionDecl( }; const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init)) { .existing => |ty| wip: { - if (!try sema.maybeRemoveOutdatedType(ty)) return Air.internedToRef(ty); + if (!try sema.maybeRemoveOutdatedType(ty)) { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + } break :wip (try ip.getUnionType(gpa, pt.tid, union_init)).wip; }, .wip => |wip| wip, @@ -3338,8 +3352,8 @@ fn zirUnionDecl( if (block.ownerModule().strip) break :codegen_type; try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); } - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); } @@ -3388,7 +3402,10 @@ fn zirOpaqueDecl( // No `wrapWipTy` needed as no std.builtin types are opaque. const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) { // No `maybeRemoveOutdatedType` as opaque types are never outdated. - .existing => |ty| return Air.internedToRef(ty), + .existing => |ty| { + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + }, .wip => |wip| wip, }; errdefer wip_ty.cancel(ip, pt.tid); @@ -3416,6 +3433,7 @@ fn zirOpaqueDecl( if (block.ownerModule().strip) break :codegen_type; try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); } + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index)); } @@ -21820,7 +21838,10 @@ fn zirReify( .zir_index = try block.trackZir(inst), } }, })) { - .existing => |ty| return Air.internedToRef(ty), + .existing => |ty| { + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + }, .wip => |wip| wip, }; errdefer wip_ty.cancel(ip, pt.tid); @@ -21839,6 +21860,7 @@ fn zirReify( .file_scope = block.getFileScopeIndex(mod), }); + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index)); }, .Union => { @@ -22020,7 +22042,11 @@ fn reifyEnum( } }, })) { .wip => |wip| wip, - .existing => |ty| return Air.internedToRef(ty), + .existing => |ty| { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + }, }; errdefer wip_ty.cancel(ip, pt.tid); @@ -22044,6 +22070,8 @@ fn reifyEnum( const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index); + try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); wip_ty.prepare(ip, new_cau_index, new_namespace_index); wip_ty.setTagTy(ip, tag_ty.toIntern()); @@ -22182,7 +22210,11 @@ fn reifyUnion( } }, })) { .wip => |wip| wip, - .existing => |ty| return Air.internedToRef(ty), + .existing => |ty| { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + }, }; errdefer wip_ty.cancel(ip, pt.tid); @@ -22347,7 +22379,8 @@ fn reifyUnion( if (block.ownerModule().strip) break :codegen_type; try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); } - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); + try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); } @@ -22447,7 +22480,11 @@ fn reifyStruct( } }, })) { .wip => |wip| wip, - .existing => |ty| return Air.internedToRef(ty), + .existing => |ty| { + try sema.declareDependency(.{ .interned = ty }); + try sema.addTypeReferenceEntry(src, ty); + return Air.internedToRef(ty); + }, }; errdefer wip_ty.cancel(ip, pt.tid); @@ -22625,7 +22662,8 @@ fn reifyStruct( if (block.ownerModule().strip) break :codegen_type; try mod.comp.queueJob(.{ .codegen_type = wip_ty.index }); } - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .cau = new_cau_index })); + try sema.declareDependency(.{ .interned = wip_ty.index }); + try sema.addTypeReferenceEntry(src, wip_ty.index); return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); } @@ -32231,6 +32269,18 @@ fn addReferenceEntry( try zcu.addUnitReference(sema.owner, referenced_unit, src); } +fn addTypeReferenceEntry( + sema: *Sema, + src: LazySrcLoc, + referenced_type: InternPool.Index, +) !void { + const zcu = sema.pt.zcu; + if (zcu.comp.reference_trace == 0) return; + const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type); + if (gop.found_existing) return; + try zcu.addTypeReference(sema.owner, referenced_type, src); +} + pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; diff --git a/src/Zcu.zig b/src/Zcu.zig index d0889ea62c..2ce001b92a 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -168,6 +168,10 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, /// it as outdated. retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{}, +/// These are the modules which we initially queue for analysis in `Compilation.update`. +/// `resolveReferences` will use these as the root of its reachability traversal. +analysis_roots: std.BoundedArray(*Package.Module, 3) = .{}, + stage1_flags: packed struct { have_winmain: bool = false, have_wwinmain: bool = false, @@ -186,7 +190,7 @@ global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .{} /// Key is the `AnalUnit` *performing* the reference. This representation allows /// incremental updates to quickly delete references caused by a specific `AnalUnit`. -/// Value is index into `all_reference` of the first reference triggered by the unit. +/// Value is index into `all_references` of the first reference triggered by the unit. /// The `next` field on the `Reference` forms a linked list of all references /// triggered by the key `AnalUnit`. reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, @@ -194,6 +198,16 @@ all_references: std.ArrayListUnmanaged(Reference) = .{}, /// Freelist of indices in `all_references`. free_references: std.ArrayListUnmanaged(u32) = .{}, +/// Key is the `AnalUnit` *performing* the reference. This representation allows +/// incremental updates to quickly delete references caused by a specific `AnalUnit`. +/// Value is index into `all_type_reference` of the first reference triggered by the unit. +/// The `next` field on the `TypeReference` forms a linked list of all type references +/// triggered by the key `AnalUnit`. +type_reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, +all_type_references: std.ArrayListUnmanaged(TypeReference) = .{}, +/// Freelist of indices in `all_type_references`. +free_type_references: std.ArrayListUnmanaged(u32) = .{}, + panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId.len, /// The panic function body. panic_func_index: InternPool.Index = .none, @@ -302,6 +316,16 @@ pub const Reference = struct { src: LazySrcLoc, }; +pub const TypeReference = struct { + /// The container type which was referenced. + referenced: InternPool.Index, + /// Index into `all_type_references` of the next `TypeReference` triggered by the same `AnalUnit`. + /// `std.math.maxInt(u32)` is the sentinel. + next: u32, + /// The source location of the reference. + src: LazySrcLoc, +}; + /// The container that structs, enums, unions, and opaques have. pub const Namespace = struct { parent: OptionalIndex, @@ -2155,6 +2179,10 @@ pub fn deinit(zcu: *Zcu) void { zcu.all_references.deinit(gpa); zcu.free_references.deinit(gpa); + zcu.type_reference_table.deinit(gpa); + zcu.all_type_references.deinit(gpa); + zcu.free_type_references.deinit(gpa); + zcu.intern_pool.deinit(gpa); } @@ -2660,16 +2688,32 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { const gpa = zcu.gpa; - const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return; - var idx = kv.value; + unit_refs: { + const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return; + var idx = kv.value; - while (idx != std.math.maxInt(u32)) { - zcu.free_references.append(gpa, idx) catch { - // This space will be reused eventually, so we need not propagate this error. - // Just leak it for now, and let GC reclaim it later on. - return; - }; - idx = zcu.all_references.items[idx].next; + while (idx != std.math.maxInt(u32)) { + zcu.free_references.append(gpa, idx) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break :unit_refs; + }; + idx = zcu.all_references.items[idx].next; + } + } + + type_refs: { + const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse return; + var idx = kv.value; + + while (idx != std.math.maxInt(u32)) { + zcu.free_type_references.append(gpa, idx) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break :type_refs; + }; + idx = zcu.all_type_references.items[idx].next; + } } } @@ -2696,6 +2740,29 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit gop.value_ptr.* = @intCast(ref_idx); } +pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPool.Index, ref_src: LazySrcLoc) Allocator.Error!void { + const gpa = zcu.gpa; + + try zcu.type_reference_table.ensureUnusedCapacity(gpa, 1); + + const ref_idx = zcu.free_type_references.popOrNull() orelse idx: { + _ = try zcu.all_type_references.addOne(gpa); + break :idx zcu.all_type_references.items.len - 1; + }; + + errdefer comptime unreachable; + + const gop = zcu.type_reference_table.getOrPutAssumeCapacity(src_unit); + + zcu.all_type_references.items[ref_idx] = .{ + .referenced = referenced_type, + .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32), + .src = ref_src, + }; + + gop.value_ptr.* = @intCast(ref_idx); +} + pub fn errorSetBits(mod: *Zcu) u16 { if (mod.error_limit == 0) return 0; return @as(u16, std.math.log2_int(ErrorInt, mod.error_limit)) + 1; @@ -2990,28 +3057,175 @@ pub const ResolvedReference = struct { }; /// Returns a mapping from an `AnalUnit` to where it is referenced. -/// TODO: in future, this must be adapted to traverse from roots of analysis. That way, we can -/// use the returned map to determine which units have become unreferenced in an incremental update. -pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) { +/// If the value is `null`, the `AnalUnit` is a root of analysis. +/// If an `AnalUnit` is not in the returned map, it is unreferenced. +pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) { const gpa = zcu.gpa; + const comp = zcu.comp; + const ip = &zcu.intern_pool; - var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{}; + var result: std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{}; errdefer result.deinit(gpa); + var checked_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; + var type_queue: std.AutoArrayHashMapUnmanaged(InternPool.Index, ?ResolvedReference) = .{}; + var unit_queue: std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) = .{}; + defer { + checked_types.deinit(gpa); + type_queue.deinit(gpa); + unit_queue.deinit(gpa); + } + // This is not a sufficient size, but a lower bound. try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); - for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| { - assert(first_ref_idx != std.math.maxInt(u32)); - var ref_idx = first_ref_idx; - while (ref_idx != std.math.maxInt(u32)) { - const ref = zcu.all_references.items[ref_idx]; - const gop = try result.getOrPut(gpa, ref.referenced); - if (!gop.found_existing) { - gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src }; + try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len); + for (zcu.analysis_roots.slice()) |mod| { + // Logic ripped from `Zcu.PerThread.importPkg`. + // TODO: this is silly, `Module` should just store a reference to its root `File`. + const resolved_path = try std.fs.path.resolve(gpa, &.{ + mod.root.root_dir.path orelse ".", + mod.root.sub_path, + mod.root_src_path, + }); + defer gpa.free(resolved_path); + const file = zcu.import_table.get(resolved_path).?; + if (zcu.fileByIndex(file).status != .success_zir) continue; + const root_ty = zcu.fileRootType(file); + if (root_ty == .none) continue; + type_queue.putAssumeCapacityNoClobber(root_ty, null); + } + + while (true) { + if (type_queue.popOrNull()) |kv| { + const ty = kv.key; + const referencer = kv.value; + try checked_types.putNoClobber(gpa, ty, {}); + + // If this type has a `Cau` for resolution, it's automatically referenced. + const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) { + .struct_type => ip.loadStructType(ty).cau, + .union_type => ip.loadUnionType(ty).cau.toOptional(), + .enum_type => ip.loadEnumType(ty).cau, + .opaque_type => .none, + else => unreachable, + }; + if (resolution_cau.unwrap()) |cau| { + // this should only be referenced by the type + const unit = AnalUnit.wrap(.{ .cau = cau }); + assert(!result.contains(unit)); + try unit_queue.putNoClobber(gpa, unit, referencer); + } + + // If this is a union with a generated tag, its tag type is automatically referenced. + // 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. + if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| { + const tag_ty = union_obj.enum_tag_ty; + if (tag_ty != .none) { + if (ip.indexToKey(tag_ty).enum_type == .generated_tag) { + if (!checked_types.contains(tag_ty)) { + try type_queue.put(gpa, tag_ty, referencer); + } + } + } + } + + // Queue any decls within this type which would be automatically analyzed. + // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`. + const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap() orelse continue; + for (zcu.namespacePtr(ns).other_decls.items) |cau| { + // These are `comptime` and `test` declarations. + // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter. + const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue; + const file = zcu.fileByIndex(inst_info.file); + const zir = file.zir; + const declaration = zir.getDeclaration(inst_info.inst)[0]; + const want_analysis = switch (declaration.name) { + .@"usingnamespace" => unreachable, + .@"comptime" => true, + else => a: { + if (!comp.config.is_test) break :a false; + if (file.mod != zcu.main_mod) break :a false; + if (declaration.name.isNamedTest(zir) or declaration.name == .decltest) { + const nav = ip.getCau(cau).owner.unwrap().nav; + const fqn_slice = ip.getNav(nav).fqn.toSlice(ip); + for (comp.test_filters) |test_filter| { + if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break; + } else break :a false; + } + break :a true; + }, + }; + if (want_analysis) { + const unit = AnalUnit.wrap(.{ .cau = cau }); + if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); + } + } + for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| { + // These are named declarations. They are analyzed only if marked `export`. + const cau = ip.getNav(nav).analysis_owner.unwrap().?; + const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue; + const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0]; + if (declaration.flags.is_export) { + const unit = AnalUnit.wrap(.{ .cau = cau }); + if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); + } + } + for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| { + // These are named declarations. They are analyzed only if marked `export`. + const cau = ip.getNav(nav).analysis_owner.unwrap().?; + const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue; + const declaration = zcu.fileByIndex(inst_info.file).zir.getDeclaration(inst_info.inst)[0]; + if (declaration.flags.is_export) { + const unit = AnalUnit.wrap(.{ .cau = cau }); + if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); + } + } + // Incremental compilation does not support `usingnamespace`. + // These are only included to keep good reference traces in non-incremental updates. + for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| { + const cau = ip.getNav(nav).analysis_owner.unwrap().?; + const unit = AnalUnit.wrap(.{ .cau = cau }); + if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); } - ref_idx = ref.next; + for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| { + const cau = ip.getNav(nav).analysis_owner.unwrap().?; + const unit = AnalUnit.wrap(.{ .cau = cau }); + if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer); + } + continue; + } + if (unit_queue.popOrNull()) |kv| { + const unit = kv.key; + try result.putNoClobber(gpa, unit, kv.value); + + if (zcu.reference_table.get(unit)) |first_ref_idx| { + assert(first_ref_idx != std.math.maxInt(u32)); + var ref_idx = first_ref_idx; + while (ref_idx != std.math.maxInt(u32)) { + const ref = zcu.all_references.items[ref_idx]; + if (!result.contains(ref.referenced)) try unit_queue.put(gpa, ref.referenced, .{ + .referencer = unit, + .src = ref.src, + }); + ref_idx = ref.next; + } + } + if (zcu.type_reference_table.get(unit)) |first_ref_idx| { + assert(first_ref_idx != std.math.maxInt(u32)); + var ref_idx = first_ref_idx; + while (ref_idx != std.math.maxInt(u32)) { + const ref = zcu.all_type_references.items[ref_idx]; + if (!checked_types.contains(ref.referenced)) try type_queue.put(gpa, ref.referenced, .{ + .referencer = unit, + .src = ref.src, + }); + ref_idx = ref.next; + } + } + continue; } + break; } return result; |
