Skip to content

Commit 6a7ca4b

Browse files
authored
Merge pull request #23617 from mlugg/incr-fixes
incremental: fixes
2 parents ffd85ff + 8c9c24e commit 6a7ca4b

File tree

8 files changed

+462
-67
lines changed

8 files changed

+462
-67
lines changed

src/Compilation.zig

+84-39
Original file line numberDiff line numberDiff line change
@@ -2262,8 +2262,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
22622262
const pt: Zcu.PerThread = .activate(zcu, .main);
22632263
defer pt.deactivate();
22642264

2265-
zcu.compile_log_text.shrinkAndFree(gpa, 0);
2266-
22672265
zcu.skip_analysis_this_update = false;
22682266

22692267
// Make sure std.zig is inside the import_table. We unconditionally need
@@ -3323,30 +3321,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
33233321
err: *?Error,
33243322

33253323
const Error = @typeInfo(
3326-
@typeInfo(@TypeOf(Zcu.SrcLoc.span)).@"fn".return_type.?,
3324+
@typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?,
33273325
).error_union.error_set;
33283326

33293327
pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
3330-
if (ctx.err.*) |_| return lhs_index < rhs_index;
3331-
const lhs_src_loc = ctx.errors[lhs_index].src_loc.upgradeOrLost(ctx.zcu) orelse {
3332-
// LHS source location lost, so should never be referenced. Just sort it to the end.
3333-
return false;
3334-
};
3335-
const rhs_src_loc = ctx.errors[rhs_index].src_loc.upgradeOrLost(ctx.zcu) orelse {
3336-
// RHS source location lost, so should never be referenced. Just sort it to the end.
3337-
return true;
3338-
};
3339-
return if (lhs_src_loc.file_scope != rhs_src_loc.file_scope) std.mem.order(
3340-
u8,
3341-
lhs_src_loc.file_scope.sub_file_path,
3342-
rhs_src_loc.file_scope.sub_file_path,
3343-
).compare(.lt) else (lhs_src_loc.span(ctx.zcu.gpa) catch |e| {
3344-
ctx.err.* = e;
3345-
return lhs_index < rhs_index;
3346-
}).main < (rhs_src_loc.span(ctx.zcu.gpa) catch |e| {
3328+
if (ctx.err.* != null) return lhs_index < rhs_index;
3329+
return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu) catch |e| {
33473330
ctx.err.* = e;
33483331
return lhs_index < rhs_index;
3349-
}).main;
3332+
};
33503333
}
33513334
};
33523335

@@ -3450,28 +3433,76 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
34503433

34513434
try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
34523435

3453-
if (comp.zcu) |zcu| {
3454-
if (!zcu.skip_analysis_this_update and bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
3455-
const values = zcu.compile_log_sources.values();
3456-
// First one will be the error; subsequent ones will be notes.
3457-
const src_loc = values[0].src();
3458-
const err_msg: Zcu.ErrorMsg = .{
3459-
.src_loc = src_loc,
3460-
.msg = "found compile log statement",
3461-
.notes = try gpa.alloc(Zcu.ErrorMsg, zcu.compile_log_sources.count() - 1),
3462-
};
3463-
defer gpa.free(err_msg.notes);
3436+
const compile_log_text: []const u8 = compile_log_text: {
3437+
const zcu = comp.zcu orelse break :compile_log_text "";
3438+
if (zcu.skip_analysis_this_update) break :compile_log_text "";
3439+
if (zcu.compile_logs.count() == 0) break :compile_log_text "";
3440+
3441+
// If there are no other errors, we include a "found compile log statement" error.
3442+
// Otherwise, we just show the compile log output, with no error.
3443+
const include_compile_log_sources = bundle.root_list.items.len == 0;
3444+
3445+
const refs = try zcu.resolveReferences();
3446+
3447+
var messages: std.ArrayListUnmanaged(Zcu.ErrorMsg) = .empty;
3448+
defer messages.deinit(gpa);
3449+
for (zcu.compile_logs.keys(), zcu.compile_logs.values()) |logging_unit, compile_log| {
3450+
if (!refs.contains(logging_unit)) continue;
3451+
try messages.append(gpa, .{
3452+
.src_loc = compile_log.src(),
3453+
.msg = undefined, // populated later
3454+
.notes = &.{},
3455+
// We actually clear this later for most of these, but we populate
3456+
// this field for now to avoid having to allocate more data to track
3457+
// which compile log text this corresponds to.
3458+
.reference_trace_root = logging_unit.toOptional(),
3459+
});
3460+
}
34643461

3465-
for (values[1..], err_msg.notes) |src_info, *note| {
3466-
note.* = .{
3467-
.src_loc = src_info.src(),
3468-
.msg = "also here",
3462+
if (messages.items.len == 0) break :compile_log_text "";
3463+
3464+
// Okay, there *are* referenced compile logs. Sort them into a consistent order.
3465+
3466+
const SortContext = struct {
3467+
err: *?Error,
3468+
zcu: *Zcu,
3469+
const Error = @typeInfo(
3470+
@typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?,
3471+
).error_union.error_set;
3472+
fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
3473+
if (ctx.err.* != null) return false;
3474+
return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu) catch |e| {
3475+
ctx.err.* = e;
3476+
return false;
34693477
};
34703478
}
3479+
};
3480+
var sort_err: ?SortContext.Error = null;
3481+
std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .err = &sort_err, .zcu = zcu }), SortContext.lessThan);
3482+
if (sort_err) |e| return e;
3483+
3484+
var log_text: std.ArrayListUnmanaged(u8) = .empty;
3485+
defer log_text.deinit(gpa);
3486+
3487+
// Index 0 will be the root message; the rest will be notes.
3488+
// Only the actual message, i.e. index 0, will retain its reference trace.
3489+
try appendCompileLogLines(&log_text, zcu, messages.items[0].reference_trace_root.unwrap().?);
3490+
messages.items[0].notes = messages.items[1..];
3491+
messages.items[0].msg = "found compile log statement";
3492+
for (messages.items[1..]) |*note| {
3493+
try appendCompileLogLines(&log_text, zcu, note.reference_trace_root.unwrap().?);
3494+
note.reference_trace_root = .none; // notes don't have reference traces
3495+
note.msg = "also here";
3496+
}
34713497

3472-
try addModuleErrorMsg(zcu, &bundle, err_msg);
3498+
// We don't actually include the error here if `!include_compile_log_sources`.
3499+
// The sorting above was still necessary, though, to get `log_text` in the right order.
3500+
if (include_compile_log_sources) {
3501+
try addModuleErrorMsg(zcu, &bundle, messages.items[0]);
34733502
}
3474-
}
3503+
3504+
break :compile_log_text try log_text.toOwnedSlice(gpa);
3505+
};
34753506

34763507
// TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
34773508
// very common way for incremental compilation bugs to manifest, so let's always check it.
@@ -3497,10 +3528,24 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
34973528
}
34983529
};
34993530

3500-
const compile_log_text = if (comp.zcu) |m| m.compile_log_text.items else "";
35013531
return bundle.toOwnedBundle(compile_log_text);
35023532
}
35033533

3534+
/// Writes all compile log lines belonging to `logging_unit` into `log_text` using `zcu.gpa`.
3535+
fn appendCompileLogLines(log_text: *std.ArrayListUnmanaged(u8), zcu: *Zcu, logging_unit: InternPool.AnalUnit) Allocator.Error!void {
3536+
const gpa = zcu.gpa;
3537+
const ip = &zcu.intern_pool;
3538+
var opt_line_idx = zcu.compile_logs.get(logging_unit).?.first_line.toOptional();
3539+
while (opt_line_idx.unwrap()) |line_idx| {
3540+
const line = line_idx.get(zcu).*;
3541+
opt_line_idx = line.next;
3542+
const line_slice = line.data.toSlice(ip);
3543+
try log_text.ensureUnusedCapacity(gpa, line_slice.len + 1);
3544+
log_text.appendSliceAssumeCapacity(line_slice);
3545+
log_text.appendAssumeCapacity('\n');
3546+
}
3547+
}
3548+
35043549
fn anyErrors(comp: *Compilation) bool {
35053550
return (totalErrorCount(comp) catch return true) != 0;
35063551
}

src/Sema.zig

+34-8
Original file line numberDiff line numberDiff line change
@@ -5884,10 +5884,12 @@ fn zirCompileLog(
58845884
) CompileError!Air.Inst.Ref {
58855885
const pt = sema.pt;
58865886
const zcu = pt.zcu;
5887+
const gpa = zcu.gpa;
5888+
5889+
var buf: std.ArrayListUnmanaged(u8) = .empty;
5890+
defer buf.deinit(gpa);
58875891

5888-
var managed = zcu.compile_log_text.toManaged(sema.gpa);
5889-
defer pt.zcu.compile_log_text = managed.moveToUnmanaged();
5890-
const writer = managed.writer();
5892+
const writer = buf.writer(gpa);
58915893

58925894
const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
58935895
const src_node = extra.data.src_node;
@@ -5906,13 +5908,37 @@ fn zirCompileLog(
59065908
try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(pt)});
59075909
}
59085910
}
5909-
try writer.print("\n", .{});
59105911

5911-
const gop = try zcu.compile_log_sources.getOrPut(sema.gpa, sema.owner);
5912-
if (!gop.found_existing) gop.value_ptr.* = .{
5913-
.base_node_inst = block.src_base_inst,
5914-
.node_offset = src_node,
5912+
const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls);
5913+
5914+
const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: {
5915+
zcu.compile_log_lines.items[@intFromEnum(idx)] = .{
5916+
.next = .none,
5917+
.data = line_data,
5918+
};
5919+
break :idx idx;
5920+
} else idx: {
5921+
try zcu.compile_log_lines.append(gpa, .{
5922+
.next = .none,
5923+
.data = line_data,
5924+
});
5925+
break :idx @enumFromInt(zcu.compile_log_lines.items.len - 1);
59155926
};
5927+
5928+
const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner);
5929+
if (gop.found_existing) {
5930+
const prev_line = gop.value_ptr.last_line.get(zcu);
5931+
assert(prev_line.next == .none);
5932+
prev_line.next = line_idx.toOptional();
5933+
gop.value_ptr.last_line = line_idx;
5934+
} else {
5935+
gop.value_ptr.* = .{
5936+
.base_node_inst = block.src_base_inst,
5937+
.node_offset = src_node,
5938+
.first_line = line_idx,
5939+
.last_line = line_idx,
5940+
};
5941+
}
59165942
return .void_value;
59175943
}
59185944

src/Zcu.zig

+77-9
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,23 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp
130130
/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
131131
failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty,
132132
failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty,
133-
/// Keep track of one `@compileLog` callsite per `AnalUnit`.
134-
/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.
135-
compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
133+
/// Keep track of `@compileLog`s per `AnalUnit`.
134+
/// We track the source location of the first `@compileLog` call, and all logged lines as a linked list.
135+
/// The list is singly linked, but we do track its tail for fast appends (optimizing many logs in one unit).
136+
compile_logs: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct {
136137
base_node_inst: InternPool.TrackedInst.Index,
137138
node_offset: Ast.Node.Offset,
139+
first_line: CompileLogLine.Index,
140+
last_line: CompileLogLine.Index,
138141
pub fn src(self: @This()) LazySrcLoc {
139142
return .{
140143
.base_node_inst = self.base_node_inst,
141144
.offset = LazySrcLoc.Offset.nodeOffset(self.node_offset),
142145
};
143146
}
144-
}) = .{},
147+
}) = .empty,
148+
compile_log_lines: std.ArrayListUnmanaged(CompileLogLine) = .empty,
149+
free_compile_log_lines: std.ArrayListUnmanaged(CompileLogLine.Index) = .empty,
145150
/// Using a map here for consistency with the other fields here.
146151
/// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.
147152
failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty,
@@ -196,8 +201,6 @@ stage1_flags: packed struct {
196201
reserved: u2 = 0,
197202
} = .{},
198203

199-
compile_log_text: std.ArrayListUnmanaged(u8) = .empty,
200-
201204
test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
202205

203206
global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty,
@@ -547,6 +550,31 @@ pub const Export = struct {
547550
};
548551
};
549552

553+
pub const CompileLogLine = struct {
554+
next: Index.Optional,
555+
/// Does *not* include the trailing newline.
556+
data: InternPool.NullTerminatedString,
557+
pub const Index = enum(u32) {
558+
_,
559+
pub fn get(idx: Index, zcu: *Zcu) *CompileLogLine {
560+
return &zcu.compile_log_lines.items[@intFromEnum(idx)];
561+
}
562+
pub fn toOptional(idx: Index) Optional {
563+
return @enumFromInt(@intFromEnum(idx));
564+
}
565+
pub const Optional = enum(u32) {
566+
none = std.math.maxInt(u32),
567+
_,
568+
pub fn unwrap(opt: Optional) ?Index {
569+
return switch (opt) {
570+
.none => null,
571+
_ => @enumFromInt(@intFromEnum(opt)),
572+
};
573+
}
574+
};
575+
};
576+
};
577+
550578
pub const Reference = struct {
551579
/// The `AnalUnit` whose semantic analysis was triggered by this reference.
552580
referenced: AnalUnit,
@@ -2464,6 +2492,30 @@ pub const LazySrcLoc = struct {
24642492
.lazy = lazy.offset,
24652493
};
24662494
}
2495+
2496+
/// Used to sort error messages, so that they're printed in a consistent order.
2497+
/// If an error is returned, that error makes sorting impossible.
2498+
pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu) !bool {
2499+
const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse {
2500+
// LHS source location lost, so should never be referenced. Just sort it to the end.
2501+
return false;
2502+
};
2503+
const rhs_src = rhs_lazy.upgradeOrLost(zcu) orelse {
2504+
// RHS source location lost, so should never be referenced. Just sort it to the end.
2505+
return true;
2506+
};
2507+
if (lhs_src.file_scope != rhs_src.file_scope) {
2508+
return std.mem.order(
2509+
u8,
2510+
lhs_src.file_scope.sub_file_path,
2511+
rhs_src.file_scope.sub_file_path,
2512+
).compare(.lt);
2513+
}
2514+
2515+
const lhs_span = try lhs_src.span(zcu.gpa);
2516+
const rhs_span = try rhs_src.span(zcu.gpa);
2517+
return lhs_span.main < rhs_span.main;
2518+
}
24672519
};
24682520

24692521
pub const SemaError = error{ OutOfMemory, AnalysisFail };
@@ -2506,8 +2558,6 @@ pub fn deinit(zcu: *Zcu) void {
25062558
}
25072559
zcu.embed_table.deinit(gpa);
25082560

2509-
zcu.compile_log_text.deinit(gpa);
2510-
25112561
zcu.local_zir_cache.handle.close();
25122562
zcu.global_zir_cache.handle.close();
25132563

@@ -2535,7 +2585,9 @@ pub fn deinit(zcu: *Zcu) void {
25352585
}
25362586
zcu.cimport_errors.deinit(gpa);
25372587

2538-
zcu.compile_log_sources.deinit(gpa);
2588+
zcu.compile_logs.deinit(gpa);
2589+
zcu.compile_log_lines.deinit(gpa);
2590+
zcu.free_compile_log_lines.deinit(gpa);
25392591

25402592
zcu.all_exports.deinit(gpa);
25412593
zcu.free_exports.deinit(gpa);
@@ -3412,6 +3464,22 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
34123464
}
34133465
}
34143466

3467+
/// Delete all compile logs performed by this `AnalUnit`.
3468+
/// Re-analysis of the `AnalUnit` will cause logs to be rediscovered.
3469+
pub fn deleteUnitCompileLogs(zcu: *Zcu, anal_unit: AnalUnit) void {
3470+
const kv = zcu.compile_logs.fetchSwapRemove(anal_unit) orelse return;
3471+
const gpa = zcu.gpa;
3472+
var opt_line_idx = kv.value.first_line.toOptional();
3473+
while (opt_line_idx.unwrap()) |line_idx| {
3474+
zcu.free_compile_log_lines.append(gpa, line_idx) catch {
3475+
// This space will be reused eventually, so we need not propagate this error.
3476+
// Just leak it for now, and let GC reclaim it later on.
3477+
return;
3478+
};
3479+
opt_line_idx = line_idx.get(zcu).next;
3480+
}
3481+
}
3482+
34153483
pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void {
34163484
const gpa = zcu.gpa;
34173485

0 commit comments

Comments
 (0)