authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-06-29 20:00:11+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-07-04 21:01:41+01:00
log089bbd6588d82ccda0646e756006cf5787eadef2
treee6db5043b11058300c2537b30bae4fb5ddd4ef81
parent5f03c025058ddda09bfb3eac283bb88d30ad38cc
signaturelock-open Commit is signed but in an unrecognized format.

Zcu: rework reference traces

Previously, `reference_table` mapped from a `Decl` being referenced to the `Decl` that performed the reference. This is convenient for constructing error messages, but problematic for incremental compilation. This is because on an incremental update, we want to efficiently remove all references triggered by an `AnalUnit` which is being re-analyzed. For this reason, `reference_table` now maps the other way: from the `AnalUnit` *performing* the reference, to the `AnalUnit` whose analysis was triggered. As a general rule, any call to any of the following functions should be preceded by a call to `Sema.addReferenceEntry`: * `Zcu.ensureDeclAnalyzed` * `Sema.ensureDeclAnalyzed` * `Zcu.ensureFuncBodyAnalyzed` * `Zcu.ensureFuncBodyAnalysisQueued` This is not just important for error messages, but also more fundamentally for incremental compilation. When an incremental update occurs, we must determine whether any `AnalUnit` has become unreferenced: in this case, we should ignore its associated error messages, and perhaps even remove it from the binary. For this reason, we no longer store only one reference to every `AnalUnit`, but every reference. At the end of an update, `Zcu.resolveReferences` will construct the reverse mapping, and as such identify which `AnalUnit`s are still referenced. The current implementation doesn't quite do what we need for incremental compilation here, but the framework is in place. Note that `Zcu.resolveReferences` does constitute a non-trivial amount of work on every incremental update. However, for incremental compilation, this work -- which will effectively be a graph traversal over all `AnalUnit` references -- seems strictly necessary. At the moment, this work is only done if the `Zcu` has any errors, when collecting them into the final `ErrorBundle`. An unsolved problem here is how to represent inline function calls in the reference trace. If `foo` performs an inline call to `bar` which references `qux`, then ideally, `bar` would be shown on the reference trace between `foo` and `qux`, but this is not currently the case. The solution here is probably for `Zcu.Reference` to store information about the source locations of active inline calls betweeen the referencer and its reference.

3 files changed, 231 insertions(+), 181 deletions(-)

src/Compilation.zig+59-37
...@@ -31,6 +31,7 @@ const clangMain = @import("main.zig").clangMain;...@@ -31,6 +31,7 @@ const clangMain = @import("main.zig").clangMain;
31const Zcu = @import("Zcu.zig");31const Zcu = @import("Zcu.zig");
32/// Deprecated; use `Zcu`.32/// Deprecated; use `Zcu`.
33const Module = Zcu;33const Module = Zcu;
34const Sema = @import("Sema.zig");
34const InternPool = @import("InternPool.zig");35const InternPool = @import("InternPool.zig");
35const Cache = std.Build.Cache;36const Cache = std.Build.Cache;
36const c_codegen = @import("codegen/c.zig");37const c_codegen = @import("codegen/c.zig");
...@@ -2939,9 +2940,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -2939,9 +2940,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
2939 });2940 });
2940 }2941 }
2941 if (comp.module) |zcu| {2942 if (comp.module) |zcu| {
2943 var all_references = try zcu.resolveReferences();
2944 defer all_references.deinit(gpa);
2945
2942 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {2946 for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
2943 if (error_msg) |msg| {2947 if (error_msg) |msg| {
2944 try addModuleErrorMsg(zcu, &bundle, msg.*);2948 try addModuleErrorMsg(zcu, &bundle, msg.*, &all_references);
2945 } else {2949 } else {
2946 // Must be ZIR errors. Note that this may include AST errors.2950 // Must be ZIR errors. Note that this may include AST errors.
2947 // addZirErrorMessages asserts that the tree is loaded.2951 // addZirErrorMessages asserts that the tree is loaded.
...@@ -2950,7 +2954,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -2950,7 +2954,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
2950 }2954 }
2951 }2955 }
2952 for (zcu.failed_embed_files.values()) |error_msg| {2956 for (zcu.failed_embed_files.values()) |error_msg| {
2953 try addModuleErrorMsg(zcu, &bundle, error_msg.*);2957 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
2954 }2958 }
2955 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {2959 for (zcu.failed_analysis.keys(), zcu.failed_analysis.values()) |anal_unit, error_msg| {
2956 const decl_index = switch (anal_unit.unwrap()) {2960 const decl_index = switch (anal_unit.unwrap()) {
...@@ -2962,7 +2966,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -2962,7 +2966,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
2962 // We'll try again once parsing succeeds.2966 // We'll try again once parsing succeeds.
2963 if (!zcu.declFileScope(decl_index).okToReportErrors()) continue;2967 if (!zcu.declFileScope(decl_index).okToReportErrors()) continue;
29642968
2965 try addModuleErrorMsg(zcu, &bundle, error_msg.*);2969 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
2966 if (zcu.cimport_errors.get(anal_unit)) |errors| {2970 if (zcu.cimport_errors.get(anal_unit)) |errors| {
2967 for (errors.getMessages()) |err_msg_index| {2971 for (errors.getMessages()) |err_msg_index| {
2968 const err_msg = errors.getErrorMessage(err_msg_index);2972 const err_msg = errors.getErrorMessage(err_msg_index);
...@@ -2989,12 +2993,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -2989,12 +2993,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
2989 // Skip errors for Decls within files that had a parse failure.2993 // Skip errors for Decls within files that had a parse failure.
2990 // We'll try again once parsing succeeds.2994 // We'll try again once parsing succeeds.
2991 if (zcu.declFileScope(decl_index).okToReportErrors()) {2995 if (zcu.declFileScope(decl_index).okToReportErrors()) {
2992 try addModuleErrorMsg(zcu, &bundle, error_msg.*);2996 try addModuleErrorMsg(zcu, &bundle, error_msg.*, &all_references);
2993 }2997 }
2994 }2998 }
2995 }2999 }
2996 for (zcu.failed_exports.values()) |value| {3000 for (zcu.failed_exports.values()) |value| {
2997 try addModuleErrorMsg(zcu, &bundle, value.*);3001 try addModuleErrorMsg(zcu, &bundle, value.*, &all_references);
2998 }3002 }
29993003
3000 const actual_error_count = zcu.global_error_set.entries.len - 1;3004 const actual_error_count = zcu.global_error_set.entries.len - 1;
...@@ -3051,6 +3055,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3051,6 +3055,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
30513055
3052 if (comp.module) |zcu| {3056 if (comp.module) |zcu| {
3053 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {3057 if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
3058 var all_references = try zcu.resolveReferences();
3059 defer all_references.deinit(gpa);
3060
3054 const values = zcu.compile_log_sources.values();3061 const values = zcu.compile_log_sources.values();
3055 // First one will be the error; subsequent ones will be notes.3062 // First one will be the error; subsequent ones will be notes.
3056 const src_loc = values[0].src().upgrade(zcu);3063 const src_loc = values[0].src().upgrade(zcu);
...@@ -3068,7 +3075,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {...@@ -3068,7 +3075,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
3068 };3075 };
3069 }3076 }
30703077
3071 try addModuleErrorMsg(zcu, &bundle, err_msg);3078 try addModuleErrorMsg(zcu, &bundle, err_msg, &all_references);
3072 }3079 }
3073 }3080 }
30743081
...@@ -3124,7 +3131,12 @@ pub const ErrorNoteHashContext = struct {...@@ -3124,7 +3131,12 @@ pub const ErrorNoteHashContext = struct {
3124 }3131 }
3125};3132};
31263133
3127pub fn addModuleErrorMsg(mod: *Module, eb: *ErrorBundle.Wip, module_err_msg: Module.ErrorMsg) !void {3134pub fn addModuleErrorMsg(
3135 mod: *Module,
3136 eb: *ErrorBundle.Wip,
3137 module_err_msg: Module.ErrorMsg,
3138 all_references: *const std.AutoHashMapUnmanaged(InternPool.AnalUnit, Zcu.ResolvedReference),
3139) !void {
3128 const gpa = eb.gpa;3140 const gpa = eb.gpa;
3129 const ip = &mod.intern_pool;3141 const ip = &mod.intern_pool;
3130 const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| {3142 const err_source = module_err_msg.src_loc.file_scope.getSource(gpa) catch |err| {
...@@ -3145,39 +3157,49 @@ pub fn addModuleErrorMsg(mod: *Module, eb: *ErrorBundle.Wip, module_err_msg: Mod...@@ -3145,39 +3157,49 @@ pub fn addModuleErrorMsg(mod: *Module, eb: *ErrorBundle.Wip, module_err_msg: Mod
3145 var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{};3157 var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .{};
3146 defer ref_traces.deinit(gpa);3158 defer ref_traces.deinit(gpa);
31473159
3148 const remaining_references: ?u32 = remaining: {3160 if (module_err_msg.reference_trace_root.unwrap()) |rt_root| {
3149 if (mod.comp.reference_trace) |_| {3161 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .{};
3150 if (module_err_msg.hidden_references > 0) break :remaining module_err_msg.hidden_references;3162 defer seen.deinit(gpa);
3151 } else {3163
3152 if (module_err_msg.reference_trace.len > 0) break :remaining 0;3164 const max_references = mod.comp.reference_trace orelse Sema.default_reference_trace_len;
3165
3166 var referenced_by = rt_root;
3167 while (all_references.get(referenced_by)) |ref| {
3168 const gop = try seen.getOrPut(gpa, ref.referencer);
3169 if (gop.found_existing) break;
3170 if (ref_traces.items.len < max_references) {
3171 const src = ref.src.upgrade(mod);
3172 const source = try src.file_scope.getSource(gpa);
3173 const span = try src.span(gpa);
3174 const loc = std.zig.findLineColumn(source.bytes, span.main);
3175 const rt_file_path = try src.file_scope.fullPath(gpa);
3176 const name = switch (ref.referencer.unwrap()) {
3177 .decl => |d| mod.declPtr(d).name,
3178 .func => |f| mod.funcOwnerDeclPtr(f).name,
3179 };
3180 try ref_traces.append(gpa, .{
3181 .decl_name = try eb.addString(name.toSlice(ip)),
3182 .src_loc = try eb.addSourceLocation(.{
3183 .src_path = try eb.addString(rt_file_path),
3184 .span_start = span.start,
3185 .span_main = span.main,
3186 .span_end = span.end,
3187 .line = @intCast(loc.line),
3188 .column = @intCast(loc.column),
3189 .source_line = 0,
3190 }),
3191 });
3192 }
3193 referenced_by = ref.referencer;
3153 }3194 }
3154 break :remaining null;
3155 };
3156 try ref_traces.ensureTotalCapacityPrecise(gpa, module_err_msg.reference_trace.len +
3157 @intFromBool(remaining_references != null));
31583195
3159 for (module_err_msg.reference_trace) |module_reference| {3196 if (seen.count() > ref_traces.items.len) {
3160 const source = try module_reference.src_loc.file_scope.getSource(gpa);3197 try ref_traces.append(gpa, .{
3161 const span = try module_reference.src_loc.span(gpa);3198 .decl_name = @intCast(seen.count() - ref_traces.items.len),
3162 const loc = std.zig.findLineColumn(source.bytes, span.main);3199 .src_loc = .none,
3163 const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa);3200 });
3164 defer gpa.free(rt_file_path);3201 }
3165 ref_traces.appendAssumeCapacity(.{
3166 .decl_name = try eb.addString(module_reference.decl.toSlice(ip)),
3167 .src_loc = try eb.addSourceLocation(.{
3168 .src_path = try eb.addString(rt_file_path),
3169 .span_start = span.start,
3170 .span_main = span.main,
3171 .span_end = span.end,
3172 .line = @intCast(loc.line),
3173 .column = @intCast(loc.column),
3174 .source_line = 0,
3175 }),
3176 });
3177 }3202 }
3178 if (remaining_references) |remaining| ref_traces.appendAssumeCapacity(
3179 .{ .decl_name = remaining, .src_loc = .none },
3180 );
31813203
3182 const src_loc = try eb.addSourceLocation(.{3204 const src_loc = try eb.addSourceLocation(.{
3183 .src_path = try eb.addString(file_path),3205 .src_path = try eb.addString(file_path),
src/Sema.zig+72-95
...@@ -121,6 +121,11 @@ comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .{},...@@ -121,6 +121,11 @@ comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .{},
121/// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`.121/// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`.
122exports: std.ArrayListUnmanaged(Zcu.Export) = .{},122exports: std.ArrayListUnmanaged(Zcu.Export) = .{},
123123
124/// All references registered so far by this `Sema`. This is a temporary duplicate
125/// of data stored in `Zcu.all_references`. It exists to avoid adding references to
126/// a given `AnalUnit` multiple times.
127references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{},
128
124const MaybeComptimeAlloc = struct {129const MaybeComptimeAlloc = struct {
125 /// The runtime index of the `alloc` instruction.130 /// The runtime index of the `alloc` instruction.
126 runtime_index: Value.RuntimeIndex,131 runtime_index: Value.RuntimeIndex,
...@@ -2472,87 +2477,57 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.Error...@@ -2472,87 +2477,57 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.Error
2472 @setCold(true);2477 @setCold(true);
2473 const gpa = sema.gpa;2478 const gpa = sema.gpa;
2474 const mod = sema.mod;2479 const mod = sema.mod;
2480 const ip = &mod.intern_pool;
24752481
2476 ref: {2482 if (build_options.enable_debug_extensions and mod.comp.debug_compile_errors) {
2477 errdefer err_msg.destroy(gpa);2483 var all_references = mod.resolveReferences() catch @panic("out of memory");
2484 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2485 wip_errors.init(gpa) catch @panic("out of memory");
2486 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*, &all_references) catch unreachable;
2487 std.debug.print("compile error during Sema:\n", .{});
2488 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;
2489 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
2490 crash_report.compilerPanic("unexpected compile error occurred", null, null);
2491 }
24782492
2479 if (build_options.enable_debug_extensions and mod.comp.debug_compile_errors) {2493 if (block) |start_block| {
2480 var wip_errors: std.zig.ErrorBundle.Wip = undefined;2494 var block_it = start_block;
2481 wip_errors.init(gpa) catch unreachable;2495 while (block_it.inlining) |inlining| {
2482 Compilation.addModuleErrorMsg(mod, &wip_errors, err_msg.*) catch unreachable;2496 try sema.errNote(
2483 std.debug.print("compile error during Sema:\n", .{});2497 inlining.call_src,
2484 var error_bundle = wip_errors.toOwnedBundle("") catch unreachable;2498 err_msg,
2485 error_bundle.renderToStdErr(.{ .ttyconf = .no_color });2499 "called from here",
2486 crash_report.compilerPanic("unexpected compile error occurred", null, null);2500 .{},
2501 );
2502 block_it = inlining.call_block;
2487 }2503 }
2504 }
24882505
2489 try mod.failed_analysis.ensureUnusedCapacity(gpa, 1);2506 const use_ref_trace = if (mod.comp.reference_trace) |n| n > 0 else mod.failed_analysis.count() == 0;
2490 try mod.failed_files.ensureUnusedCapacity(gpa, 1);2507 if (use_ref_trace) {
24912508 err_msg.reference_trace_root = sema.ownerUnit().toOptional();
2492 if (block) |start_block| {2509 }
2493 var block_it = start_block;
2494 while (block_it.inlining) |inlining| {
2495 try sema.errNote(
2496 inlining.call_src,
2497 err_msg,
2498 "called from here",
2499 .{},
2500 );
2501 block_it = inlining.call_block;
2502 }
2503
2504 const max_references = refs: {
2505 if (mod.comp.reference_trace) |num| break :refs num;
2506 // Do not add multiple traces without explicit request.
2507 if (mod.failed_analysis.count() > 0) break :ref;
2508 break :refs default_reference_trace_len;
2509 };
25102510
2511 var referenced_by = if (sema.owner_func_index != .none)2511 const gop = try mod.failed_analysis.getOrPut(gpa, sema.ownerUnit());
2512 mod.funcOwnerDeclIndex(sema.owner_func_index)2512 if (gop.found_existing) {
2513 else2513 // If there are multiple errors for the same Decl, prefer the first one added.
2514 sema.owner_decl_index;2514 sema.err = null;
2515 var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(gpa);2515 err_msg.destroy(gpa);
2516 defer reference_stack.deinit();2516 } else {
25172517 sema.err = err_msg;
2518 // Avoid infinite loops.2518 gop.value_ptr.* = err_msg;
2519 var seen = std.AutoHashMap(InternPool.DeclIndex, void).init(gpa);
2520 defer seen.deinit();
2521
2522 while (mod.reference_table.get(referenced_by)) |ref| {
2523 const gop = try seen.getOrPut(ref.referencer);
2524 if (gop.found_existing) break;
2525 if (reference_stack.items.len < max_references) {
2526 const decl = mod.declPtr(ref.referencer);
2527 try reference_stack.append(.{
2528 .decl = decl.name,
2529 .src_loc = ref.src.upgrade(mod),
2530 });
2531 }
2532 referenced_by = ref.referencer;
2533 }
2534 err_msg.reference_trace = try reference_stack.toOwnedSlice();
2535 err_msg.hidden_references = @intCast(seen.count() -| max_references);
2536 }
2537 }2519 }
2538 const ip = &mod.intern_pool;2520
2539 if (sema.owner_func_index != .none) {2521 if (sema.owner_func_index != .none) {
2540 ip.funcAnalysis(sema.owner_func_index).state = .sema_failure;2522 ip.funcAnalysis(sema.owner_func_index).state = .sema_failure;
2541 } else {2523 } else {
2542 sema.owner_decl.analysis = .sema_failure;2524 sema.owner_decl.analysis = .sema_failure;
2543 }2525 }
2526
2544 if (sema.func_index != .none) {2527 if (sema.func_index != .none) {
2545 ip.funcAnalysis(sema.func_index).state = .sema_failure;2528 ip.funcAnalysis(sema.func_index).state = .sema_failure;
2546 }2529 }
2547 const gop = mod.failed_analysis.getOrPutAssumeCapacity(sema.ownerUnit());2530
2548 if (gop.found_existing) {
2549 // If there are multiple errors for the same Decl, prefer the first one added.
2550 sema.err = null;
2551 err_msg.destroy(gpa);
2552 } else {
2553 sema.err = err_msg;
2554 gop.value_ptr.* = err_msg;
2555 }
2556 return error.AnalysisFail;2531 return error.AnalysisFail;
2557}2532}
25582533
...@@ -4235,6 +4210,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -4235,6 +4210,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
4235 if (mod.intern_pool.isFuncBody(val)) {4210 if (mod.intern_pool.isFuncBody(val)) {
4236 const ty = Type.fromInterned(mod.intern_pool.typeOf(val));4211 const ty = Type.fromInterned(mod.intern_pool.typeOf(val));
4237 if (try sema.fnHasRuntimeBits(ty)) {4212 if (try sema.fnHasRuntimeBits(ty)) {
4213 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = val }));
4238 try mod.ensureFuncBodyAnalysisQueued(val);4214 try mod.ensureFuncBodyAnalysisQueued(val);
4239 }4215 }
4240 }4216 }
...@@ -6395,6 +6371,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -6395,6 +6371,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
6395 } else try sema.lookupIdentifier(block, operand_src, decl_name);6371 } else try sema.lookupIdentifier(block, operand_src, decl_name);
6396 const options = try sema.resolveExportOptions(block, options_src, extra.options);6372 const options = try sema.resolveExportOptions(block, options_src, extra.options);
6397 {6373 {
6374 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .decl = decl_index }));
6398 try sema.ensureDeclAnalyzed(decl_index);6375 try sema.ensureDeclAnalyzed(decl_index);
6399 const exported_decl = mod.declPtr(decl_index);6376 const exported_decl = mod.declPtr(decl_index);
6400 if (exported_decl.val.getFunction(mod)) |function| {6377 if (exported_decl.val.getFunction(mod)) |function| {
...@@ -6446,6 +6423,7 @@ pub fn analyzeExport(...@@ -6446,6 +6423,7 @@ pub fn analyzeExport(
6446 if (options.linkage == .internal)6423 if (options.linkage == .internal)
6447 return;6424 return;
64486425
6426 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .decl = exported_decl_index }));
6449 try sema.ensureDeclAnalyzed(exported_decl_index);6427 try sema.ensureDeclAnalyzed(exported_decl_index);
6450 const exported_decl = mod.declPtr(exported_decl_index);6428 const exported_decl = mod.declPtr(exported_decl_index);
6451 const export_ty = exported_decl.typeOf(mod);6429 const export_ty = exported_decl.typeOf(mod);
...@@ -6468,7 +6446,7 @@ pub fn analyzeExport(...@@ -6468,7 +6446,7 @@ pub fn analyzeExport(
6468 return sema.fail(block, src, "export target cannot be extern", .{});6446 return sema.fail(block, src, "export target cannot be extern", .{});
6469 }6447 }
64706448
6471 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);6449 try sema.maybeQueueFuncBodyAnalysis(src, exported_decl_index);
64726450
6473 try sema.exports.append(gpa, .{6451 try sema.exports.append(gpa, .{
6474 .opts = options,6452 .opts = options,
...@@ -6699,8 +6677,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -6699,8 +6677,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
6699 .no_embedded_nulls,6677 .no_embedded_nulls,
6700 );6678 );
6701 const decl_index = try sema.lookupIdentifier(block, src, decl_name);6679 const decl_index = try sema.lookupIdentifier(block, src, decl_name);
6702 try sema.addReferencedBy(src, decl_index);6680 return sema.analyzeDeclRef(src, decl_index);
6703 return sema.analyzeDeclRef(decl_index);
6704}6681}
67056682
6706fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6683fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -7903,6 +7880,7 @@ fn analyzeCall(...@@ -7903,6 +7880,7 @@ fn analyzeCall(
79037880
7904 if (try sema.resolveValue(func)) |func_val| {7881 if (try sema.resolveValue(func)) |func_val| {
7905 if (mod.intern_pool.isFuncBody(func_val.toIntern())) {7882 if (mod.intern_pool.isFuncBody(func_val.toIntern())) {
7883 try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = func_val.toIntern() }));
7906 try mod.ensureFuncBodyAnalysisQueued(func_val.toIntern());7884 try mod.ensureFuncBodyAnalysisQueued(func_val.toIntern());
7907 }7885 }
7908 }7886 }
...@@ -8339,8 +8317,6 @@ fn instantiateGenericCall(...@@ -8339,8 +8317,6 @@ fn instantiateGenericCall(
8339 const callee = mod.funcInfo(callee_index);8317 const callee = mod.funcInfo(callee_index);
8340 callee.branchQuota(ip).* = @max(callee.branchQuota(ip).*, sema.branch_quota);8318 callee.branchQuota(ip).* = @max(callee.branchQuota(ip).*, sema.branch_quota);
83418319
8342 try sema.addReferencedBy(call_src, callee.owner_decl);
8343
8344 // Make a runtime call to the new function, making sure to omit the comptime args.8320 // Make a runtime call to the new function, making sure to omit the comptime args.
8345 const func_ty = Type.fromInterned(callee.ty);8321 const func_ty = Type.fromInterned(callee.ty);
8346 const func_ty_info = mod.typeToFunc(func_ty).?;8322 const func_ty_info = mod.typeToFunc(func_ty).?;
...@@ -8366,6 +8342,7 @@ fn instantiateGenericCall(...@@ -8366,6 +8342,7 @@ fn instantiateGenericCall(
8366 ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true;8342 ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn = true;
8367 }8343 }
83688344
8345 try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = callee_index }));
8369 try mod.ensureFuncBodyAnalysisQueued(callee_index);8346 try mod.ensureFuncBodyAnalysisQueued(callee_index);
83708347
8371 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args.items.len);8348 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args.items.len);
...@@ -17479,7 +17456,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -17479,7 +17456,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
17479 .@"comptime" => |index| return Air.internedToRef(index),17456 .@"comptime" => |index| return Air.internedToRef(index),
17480 .runtime => |index| index,17457 .runtime => |index| index,
17481 .decl_val => |decl_index| return sema.analyzeDeclVal(block, src, decl_index),17458 .decl_val => |decl_index| return sema.analyzeDeclVal(block, src, decl_index),
17482 .decl_ref => |decl_index| return sema.analyzeDeclRef(decl_index),17459 .decl_ref => |decl_index| return sema.analyzeDeclRef(src, decl_index),
17483 };17460 };
1748417461
17485 // The comptime case is handled already above. Runtime case below.17462 // The comptime case is handled already above. Runtime case below.
...@@ -27673,7 +27650,6 @@ fn fieldCallBind(...@@ -27673,7 +27650,6 @@ fn fieldCallBind(
27673 const decl_idx = (try sema.namespaceLookup(block, src, namespace, field_name)) orelse27650 const decl_idx = (try sema.namespaceLookup(block, src, namespace, field_name)) orelse
27674 break :found_decl null;27651 break :found_decl null;
2767527652
27676 try sema.addReferencedBy(src, decl_idx);
27677 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);27653 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
27678 const decl_type = sema.typeOf(decl_val);27654 const decl_type = sema.typeOf(decl_val);
27679 if (mod.typeToFunc(decl_type)) |func_type| f: {27655 if (mod.typeToFunc(decl_type)) |func_type| f: {
...@@ -27829,8 +27805,7 @@ fn namespaceLookupRef(...@@ -27829,8 +27805,7 @@ fn namespaceLookupRef(
27829 decl_name: InternPool.NullTerminatedString,27805 decl_name: InternPool.NullTerminatedString,
27830) CompileError!?Air.Inst.Ref {27806) CompileError!?Air.Inst.Ref {
27831 const decl = (try sema.namespaceLookup(block, src, opt_namespace, decl_name)) orelse return null;27807 const decl = (try sema.namespaceLookup(block, src, opt_namespace, decl_name)) orelse return null;
27832 try sema.addReferencedBy(src, decl);27808 return try sema.analyzeDeclRef(src, decl);
27833 return try sema.analyzeDeclRef(decl);
27834}27809}
2783527810
27836fn namespaceLookupVal(27811fn namespaceLookupVal(
...@@ -28968,7 +28943,7 @@ fn coerceExtra(...@@ -28968,7 +28943,7 @@ fn coerceExtra(
28968 if (inst_ty.zigTypeTag(zcu) == .Fn) {28943 if (inst_ty.zigTypeTag(zcu) == .Fn) {
28969 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);28944 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
28970 const fn_decl = fn_val.pointerDecl(zcu).?;28945 const fn_decl = fn_val.pointerDecl(zcu).?;
28971 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);28946 const inst_as_ptr = try sema.analyzeDeclRef(inst_src, fn_decl);
28972 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);28947 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
28973 }28948 }
2897428949
...@@ -30521,7 +30496,7 @@ fn coerceVarArgParam(...@@ -30521,7 +30496,7 @@ fn coerceVarArgParam(
30521 .Fn => fn_ptr: {30496 .Fn => fn_ptr: {
30522 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);30497 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
30523 const fn_decl = fn_val.pointerDecl(mod).?;30498 const fn_decl = fn_val.pointerDecl(mod).?;
30524 break :fn_ptr try sema.analyzeDeclRef(fn_decl);30499 break :fn_ptr try sema.analyzeDeclRef(inst_src, fn_decl);
30525 },30500 },
30526 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),30501 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
30527 .Float => float: {30502 .Float => float: {
...@@ -31748,11 +31723,10 @@ fn analyzeDeclVal(...@@ -31748,11 +31723,10 @@ fn analyzeDeclVal(
31748 src: LazySrcLoc,31723 src: LazySrcLoc,
31749 decl_index: InternPool.DeclIndex,31724 decl_index: InternPool.DeclIndex,
31750) CompileError!Air.Inst.Ref {31725) CompileError!Air.Inst.Ref {
31751 try sema.addReferencedBy(src, decl_index);
31752 if (sema.decl_val_table.get(decl_index)) |result| {31726 if (sema.decl_val_table.get(decl_index)) |result| {
31753 return result;31727 return result;
31754 }31728 }
31755 const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);31729 const decl_ref = try sema.analyzeDeclRefInner(src, decl_index, false);
31756 const result = try sema.analyzeLoad(block, src, decl_ref, src);31730 const result = try sema.analyzeLoad(block, src, decl_ref, src);
31757 if (result.toInterned() != null) {31731 if (result.toInterned() != null) {
31758 if (!block.is_typeof) {31732 if (!block.is_typeof) {
...@@ -31762,18 +31736,18 @@ fn analyzeDeclVal(...@@ -31762,18 +31736,18 @@ fn analyzeDeclVal(
31762 return result;31736 return result;
31763}31737}
3176431738
31765fn addReferencedBy(31739fn addReferenceEntry(
31766 sema: *Sema,31740 sema: *Sema,
31767 src: LazySrcLoc,31741 src: LazySrcLoc,
31768 decl_index: InternPool.DeclIndex,31742 referenced_unit: AnalUnit,
31769) !void {31743) !void {
31770 if (sema.mod.comp.reference_trace == 0) return;31744 if (sema.mod.comp.reference_trace == 0) return;
31771 try sema.mod.reference_table.put(sema.gpa, decl_index, .{31745 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
31772 // TODO: this can make the reference trace suboptimal. This will be fixed31746 if (gop.found_existing) return;
31773 // once the reference table is reworked for incremental compilation.31747 // TODO: we need to figure out how to model inline calls here.
31774 .referencer = sema.owner_decl_index,31748 // They aren't references in the analysis sense, but ought to show up in the reference trace!
31775 .src = src,31749 // Would representing inline calls in the reference table cause excessive memory usage?
31776 });31750 try sema.mod.addUnitReference(sema.ownerUnit(), referenced_unit, src);
31777}31751}
3177831752
31779pub fn ensureDeclAnalyzed(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!void {31753pub fn ensureDeclAnalyzed(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!void {
...@@ -31823,16 +31797,17 @@ fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {...@@ -31823,16 +31797,17 @@ fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
31823 } })));31797 } })));
31824}31798}
3182531799
31826fn analyzeDeclRef(sema: *Sema, decl_index: InternPool.DeclIndex) CompileError!Air.Inst.Ref {31800fn analyzeDeclRef(sema: *Sema, src: LazySrcLoc, decl_index: InternPool.DeclIndex) CompileError!Air.Inst.Ref {
31827 return sema.analyzeDeclRefInner(decl_index, true);31801 return sema.analyzeDeclRefInner(src, decl_index, true);
31828}31802}
3182931803
31830/// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but31804/// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but
31831/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a31805/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
31832/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps31806/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps
31833/// this function with `analyze_fn_body` set to true.31807/// this function with `analyze_fn_body` set to true.
31834fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn_body: bool) CompileError!Air.Inst.Ref {31808fn analyzeDeclRefInner(sema: *Sema, src: LazySrcLoc, decl_index: InternPool.DeclIndex, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
31835 const mod = sema.mod;31809 const mod = sema.mod;
31810 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .decl = decl_index }));
31836 try sema.ensureDeclAnalyzed(decl_index);31811 try sema.ensureDeclAnalyzed(decl_index);
3183731812
31838 const decl_val = try mod.declPtr(decl_index).valueOrFail();31813 const decl_val = try mod.declPtr(decl_index).valueOrFail();
...@@ -31853,7 +31828,7 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn...@@ -31853,7 +31828,7 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn
31853 },31828 },
31854 });31829 });
31855 if (analyze_fn_body) {31830 if (analyze_fn_body) {
31856 try sema.maybeQueueFuncBodyAnalysis(decl_index);31831 try sema.maybeQueueFuncBodyAnalysis(src, decl_index);
31857 }31832 }
31858 return Air.internedToRef((try mod.intern(.{ .ptr = .{31833 return Air.internedToRef((try mod.intern(.{ .ptr = .{
31859 .ty = ptr_ty.toIntern(),31834 .ty = ptr_ty.toIntern(),
...@@ -31862,12 +31837,13 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn...@@ -31862,12 +31837,13 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn
31862 } })));31837 } })));
31863}31838}
3186431839
31865fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: InternPool.DeclIndex) !void {31840fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, decl_index: InternPool.DeclIndex) !void {
31866 const mod = sema.mod;31841 const mod = sema.mod;
31867 const decl = mod.declPtr(decl_index);31842 const decl = mod.declPtr(decl_index);
31868 const decl_val = try decl.valueOrFail();31843 const decl_val = try decl.valueOrFail();
31869 if (!mod.intern_pool.isFuncBody(decl_val.toIntern())) return;31844 if (!mod.intern_pool.isFuncBody(decl_val.toIntern())) return;
31870 if (!try sema.fnHasRuntimeBits(decl_val.typeOf(mod))) return;31845 if (!try sema.fnHasRuntimeBits(decl_val.typeOf(mod))) return;
31846 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = decl_val.toIntern() }));
31871 try mod.ensureFuncBodyAnalysisQueued(decl_val.toIntern());31847 try mod.ensureFuncBodyAnalysisQueued(decl_val.toIntern());
31872}31848}
3187331849
...@@ -31882,8 +31858,8 @@ fn analyzeRef(...@@ -31882,8 +31858,8 @@ fn analyzeRef(
3188231858
31883 if (try sema.resolveValue(operand)) |val| {31859 if (try sema.resolveValue(operand)) |val| {
31884 switch (mod.intern_pool.indexToKey(val.toIntern())) {31860 switch (mod.intern_pool.indexToKey(val.toIntern())) {
31885 .extern_func => |extern_func| return sema.analyzeDeclRef(extern_func.decl),31861 .extern_func => |extern_func| return sema.analyzeDeclRef(src, extern_func.decl),
31886 .func => |func| return sema.analyzeDeclRef(func.owner_decl),31862 .func => |func| return sema.analyzeDeclRef(src, func.owner_decl),
31887 else => return anonDeclRef(sema, val.toIntern()),31863 else => return anonDeclRef(sema, val.toIntern()),
31888 }31864 }
31889 }31865 }
...@@ -35834,6 +35810,7 @@ fn resolveInferredErrorSet(...@@ -35834,6 +35810,7 @@ fn resolveInferredErrorSet(
35834 }35810 }
35835 // In this case we are dealing with the actual InferredErrorSet object that35811 // In this case we are dealing with the actual InferredErrorSet object that
35836 // corresponds to the function, not one created to track an inline/comptime call.35812 // corresponds to the function, not one created to track an inline/comptime call.
35813 try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = func_index }));
35837 try sema.ensureFuncBodyAnalyzed(func_index);35814 try sema.ensureFuncBodyAnalyzed(func_index);
35838 }35815 }
3583935816
src/Zcu.zig+100-49
...@@ -179,10 +179,15 @@ test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},...@@ -179,10 +179,15 @@ test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
179/// TODO: the key here will be a `Cau.Index`.179/// TODO: the key here will be a `Cau.Index`.
180global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{},180global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{},
181181
182reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {182/// Key is the `AnalUnit` *performing* the reference. This representation allows
183 referencer: Decl.Index,183/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
184 src: LazySrcLoc,184/// Value is index into `all_reference` of the first reference triggered by the unit.
185}) = .{},185/// The `next` field on the `Reference` forms a linked list of all references
186/// triggered by the key `AnalUnit`.
187reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{},
188all_references: std.ArrayListUnmanaged(Reference) = .{},
189/// Freelist of indices in `all_references`.
190free_references: std.ArrayListUnmanaged(u32) = .{},
186191
187panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,192panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,
188/// The panic function body.193/// The panic function body.
...@@ -290,44 +295,14 @@ pub const Export = struct {...@@ -290,44 +295,14 @@ pub const Export = struct {
290 }295 }
291};296};
292297
293const ValueArena = struct {298pub const Reference = struct {
294 state: std.heap.ArenaAllocator.State,299 /// The `AnalUnit` whose semantic analysis was triggered by this reference.
295 state_acquired: ?*std.heap.ArenaAllocator.State = null,300 referenced: AnalUnit,
296301 /// Index into `all_references` of the next `Reference` triggered by the same `AnalUnit`.
297 /// If this ValueArena replaced an existing one during re-analysis, this is the previous instance302 /// `std.math.maxInt(u32)` is the sentinel.
298 prev: ?*ValueArena = null,303 next: u32,
299304 /// The source location of the reference.
300 /// Returns an allocator backed by either promoting `state`, or by the existing ArenaAllocator305 src: LazySrcLoc,
301 /// that has already promoted `state`. `out_arena_allocator` provides storage for the initial promotion,
302 /// and must live until the matching call to release().
303 pub fn acquire(self: *ValueArena, child_allocator: Allocator, out_arena_allocator: *std.heap.ArenaAllocator) Allocator {
304 if (self.state_acquired) |state_acquired| {
305 return @as(*std.heap.ArenaAllocator, @fieldParentPtr("state", state_acquired)).allocator();
306 }
307
308 out_arena_allocator.* = self.state.promote(child_allocator);
309 self.state_acquired = &out_arena_allocator.state;
310 return out_arena_allocator.allocator();
311 }
312
313 /// Releases the allocator acquired by `acquire. `arena_allocator` must match the one passed to `acquire`.
314 pub fn release(self: *ValueArena, arena_allocator: *std.heap.ArenaAllocator) void {
315 if (@as(*std.heap.ArenaAllocator, @fieldParentPtr("state", self.state_acquired.?)) == arena_allocator) {
316 self.state = self.state_acquired.?.*;
317 self.state_acquired = null;
318 }
319 }
320
321 pub fn deinit(self: ValueArena, child_allocator: Allocator) void {
322 assert(self.state_acquired == null);
323
324 const prev = self.prev;
325 self.state.promote(child_allocator).deinit();
326
327 if (prev) |p| {
328 p.deinit(child_allocator);
329 }
330 }
331};306};
332307
333pub const Decl = struct {308pub const Decl = struct {
...@@ -758,7 +733,7 @@ pub const File = struct {...@@ -758,7 +733,7 @@ pub const File = struct {
758 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.733 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
759 multi_pkg: bool = false,734 multi_pkg: bool = false,
760 /// List of references to this file, used for multi-package errors.735 /// List of references to this file, used for multi-package errors.
761 references: std.ArrayListUnmanaged(Reference) = .{},736 references: std.ArrayListUnmanaged(File.Reference) = .{},
762 /// The hash of the path to this file, used to store `InternPool.TrackedInst`.737 /// The hash of the path to this file, used to store `InternPool.TrackedInst`.
763 path_digest: Cache.BinDigest,738 path_digest: Cache.BinDigest,
764739
...@@ -925,7 +900,7 @@ pub const File = struct {...@@ -925,7 +900,7 @@ pub const File = struct {
925 }900 }
926901
927 /// Add a reference to this file during AstGen.902 /// Add a reference to this file during AstGen.
928 pub fn addReference(file: *File, mod: Module, ref: Reference) !void {903 pub fn addReference(file: *File, mod: Module, ref: File.Reference) !void {
929 // Don't add the same module root twice. Note that since we always add module roots at the904 // Don't add the same module root twice. Note that since we always add module roots at the
930 // front of the references array (see below), this loop is actually O(1) on valid code.905 // front of the references array (see below), this loop is actually O(1) on valid code.
931 if (ref == .root) {906 if (ref == .root) {
...@@ -1002,8 +977,7 @@ pub const ErrorMsg = struct {...@@ -1002,8 +977,7 @@ pub const ErrorMsg = struct {
1002 src_loc: SrcLoc,977 src_loc: SrcLoc,
1003 msg: []const u8,978 msg: []const u8,
1004 notes: []ErrorMsg = &.{},979 notes: []ErrorMsg = &.{},
1005 reference_trace: []Trace = &.{},980 reference_trace_root: AnalUnit.Optional = .none,
1006 hidden_references: u32 = 0,
1007981
1008 pub const Trace = struct {982 pub const Trace = struct {
1009 decl: InternPool.NullTerminatedString,983 decl: InternPool.NullTerminatedString,
...@@ -1048,7 +1022,6 @@ pub const ErrorMsg = struct {...@@ -1048,7 +1022,6 @@ pub const ErrorMsg = struct {
1048 }1022 }
1049 gpa.free(err_msg.notes);1023 gpa.free(err_msg.notes);
1050 gpa.free(err_msg.msg);1024 gpa.free(err_msg.msg);
1051 gpa.free(err_msg.reference_trace);
1052 err_msg.* = undefined;1025 err_msg.* = undefined;
1053 }1026 }
1054};1027};
...@@ -2520,6 +2493,8 @@ pub fn deinit(zcu: *Zcu) void {...@@ -2520,6 +2493,8 @@ pub fn deinit(zcu: *Zcu) void {
2520 zcu.global_assembly.deinit(gpa);2493 zcu.global_assembly.deinit(gpa);
25212494
2522 zcu.reference_table.deinit(gpa);2495 zcu.reference_table.deinit(gpa);
2496 zcu.all_references.deinit(gpa);
2497 zcu.free_references.deinit(gpa);
25232498
2524 {2499 {
2525 var it = zcu.intern_pool.allocated_namespaces.iterator(0);2500 var it = zcu.intern_pool.allocated_namespaces.iterator(0);
...@@ -3462,7 +3437,8 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -3462,7 +3437,8 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
3462 // The exports this Decl performs will be re-discovered, so we remove them here3437 // The exports this Decl performs will be re-discovered, so we remove them here
3463 // prior to re-analysis.3438 // prior to re-analysis.
3464 if (build_options.only_c) unreachable;3439 if (build_options.only_c) unreachable;
3465 mod.deleteUnitExports(AnalUnit.wrap(.{ .decl = decl_index }));3440 mod.deleteUnitExports(decl_as_depender);
3441 mod.deleteUnitReferences(decl_as_depender);
3466 }3442 }
34673443
3468 const sema_result: SemaDeclResult = blk: {3444 const sema_result: SemaDeclResult = blk: {
...@@ -3591,7 +3567,8 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.In...@@ -3591,7 +3567,8 @@ pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.In
3591 if (was_outdated) {3567 if (was_outdated) {
3592 if (build_options.only_c) unreachable;3568 if (build_options.only_c) unreachable;
3593 _ = zcu.outdated_ready.swapRemove(func_as_depender);3569 _ = zcu.outdated_ready.swapRemove(func_as_depender);
3594 zcu.deleteUnitExports(AnalUnit.wrap(.{ .func = func_index }));3570 zcu.deleteUnitExports(func_as_depender);
3571 zcu.deleteUnitReferences(func_as_depender);
3595 }3572 }
35963573
3597 switch (func.analysis(ip).state) {3574 switch (func.analysis(ip).state) {
...@@ -4967,6 +4944,47 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {...@@ -4967,6 +4944,47 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
4967 }4944 }
4968}4945}
49694946
4947/// Delete all references in `reference_table` which are caused by this `AnalUnit`.
4948/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated.
4949fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
4950 const gpa = zcu.gpa;
4951
4952 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;
4953 var idx = kv.value;
4954
4955 while (idx != std.math.maxInt(u32)) {
4956 zcu.free_references.append(gpa, idx) catch {
4957 // This space will be reused eventually, so we need not propagate this error.
4958 // Just leak it for now, and let GC reclaim it later on.
4959 return;
4960 };
4961 idx = zcu.all_references.items[idx].next;
4962 }
4963}
4964
4965pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void {
4966 const gpa = zcu.gpa;
4967
4968 try zcu.reference_table.ensureUnusedCapacity(gpa, 1);
4969
4970 const ref_idx = zcu.free_references.popOrNull() orelse idx: {
4971 _ = try zcu.all_references.addOne(gpa);
4972 break :idx zcu.all_references.items.len - 1;
4973 };
4974
4975 errdefer comptime unreachable;
4976
4977 const gop = zcu.reference_table.getOrPutAssumeCapacity(src_unit);
4978
4979 zcu.all_references.items[ref_idx] = .{
4980 .referenced = referenced_unit,
4981 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32),
4982 .src = ref_src,
4983 };
4984
4985 gop.value_ptr.* = @intCast(ref_idx);
4986}
4987
4970pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air {4988pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air {
4971 const tracy = trace(@src());4989 const tracy = trace(@src());
4972 defer tracy.end();4990 defer tracy.end();
...@@ -6447,3 +6465,36 @@ pub fn structPackedFieldBitOffset(...@@ -6447,3 +6465,36 @@ pub fn structPackedFieldBitOffset(
6447 }6465 }
6448 unreachable; // index out of bounds6466 unreachable; // index out of bounds
6449}6467}
6468
6469pub const ResolvedReference = struct {
6470 referencer: AnalUnit,
6471 src: LazySrcLoc,
6472};
6473
6474/// Returns a mapping from an `AnalUnit` to where it is referenced.
6475/// TODO: in future, this must be adapted to traverse from roots of analysis. That way, we can
6476/// use the returned map to determine which units have become unreferenced in an incremental update.
6477pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) {
6478 const gpa = zcu.gpa;
6479
6480 var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{};
6481 errdefer result.deinit(gpa);
6482
6483 // This is not a sufficient size, but a lower bound.
6484 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
6485
6486 for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| {
6487 assert(first_ref_idx != std.math.maxInt(u32));
6488 var ref_idx = first_ref_idx;
6489 while (ref_idx != std.math.maxInt(u32)) {
6490 const ref = zcu.all_references.items[ref_idx];
6491 const gop = try result.getOrPut(gpa, ref.referenced);
6492 if (!gop.found_existing) {
6493 gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src };
6494 }
6495 ref_idx = ref.next;
6496 }
6497 }
6498
6499 return result;
6500}