| author | |
| committer | |
| log | 8c9c24e09b8a1e1ea92a16edd654a4b27e9ecf94 |
| tree | ca9b15c23345a9ed82e0d8f25b28510583cf1b8e |
| parent | 6561a98a61bb54c9d6c868f788da3eaa6f48d2c3 |
| signature |
Compile log output is now separated based on the `AnalUnit` which
perfomred the `@compileLog` call, so that we can omit the output for
unreferenced ("dead") units. The units are also sorted when collecting
the `ErrorBundle`, so that compile logs are always printed in a
consistent order, like compile errors are. This is important not only
for incremental compilation, but also for parallel analysis.
Resolves: #236097 files changed, 272 insertions(+), 64 deletions(-)
src/Compilation.zig+84-39| ... | @@ -2262,8 +2262,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { | ... | @@ -2262,8 +2262,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2262 | const pt: Zcu.PerThread = .activate(zcu, .main); | 2262 | const pt: Zcu.PerThread = .activate(zcu, .main); |
| 2263 | defer pt.deactivate(); | 2263 | defer pt.deactivate(); |
| 2264 | 2264 | ||
| 2265 | zcu.compile_log_text.shrinkAndFree(gpa, 0); | ||
| 2266 | |||
| 2267 | zcu.skip_analysis_this_update = false; | 2265 | zcu.skip_analysis_this_update = false; |
| 2268 | 2266 | ||
| 2269 | // Make sure std.zig is inside the import_table. We unconditionally need | 2267 | // Make sure std.zig is inside the import_table. We unconditionally need |
| ... | @@ -3323,30 +3321,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3323,30 +3321,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3323 | err: *?Error, | 3321 | err: *?Error, |
| 3324 | 3322 | ||
| 3325 | const Error = @typeInfo( | 3323 | const Error = @typeInfo( |
| 3326 | @typeInfo(@TypeOf(Zcu.SrcLoc.span)).@"fn".return_type.?, | 3324 | @typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?, |
| 3327 | ).error_union.error_set; | 3325 | ).error_union.error_set; |
| 3328 | 3326 | ||
| 3329 | pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { | 3327 | pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { |
| 3330 | if (ctx.err.*) |_| return lhs_index < rhs_index; | 3328 | if (ctx.err.* != null) return lhs_index < rhs_index; |
| 3331 | const lhs_src_loc = ctx.errors[lhs_index].src_loc.upgradeOrLost(ctx.zcu) orelse { | 3329 | return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu) catch |e| { |
| 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| { | ||
| 3347 | ctx.err.* = e; | 3330 | ctx.err.* = e; |
| 3348 | return lhs_index < rhs_index; | 3331 | return lhs_index < rhs_index; |
| 3349 | }).main; | 3332 | }; |
| 3350 | } | 3333 | } |
| 3351 | }; | 3334 | }; |
| 3352 | 3335 | ||
| ... | @@ -3450,28 +3433,76 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3450,28 +3433,76 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3450 | 3433 | ||
| 3451 | try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file); | 3434 | try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file); |
| 3452 | 3435 | ||
| 3453 | if (comp.zcu) |zcu| { | 3436 | const compile_log_text: []const u8 = compile_log_text: { |
| 3454 | if (!zcu.skip_analysis_this_update and bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) { | 3437 | const zcu = comp.zcu orelse break :compile_log_text ""; |
| 3455 | const values = zcu.compile_log_sources.values(); | 3438 | if (zcu.skip_analysis_this_update) break :compile_log_text ""; |
| 3456 | // First one will be the error; subsequent ones will be notes. | 3439 | if (zcu.compile_logs.count() == 0) break :compile_log_text ""; |
| 3457 | const src_loc = values[0].src(); | 3440 | |
| 3458 | const err_msg: Zcu.ErrorMsg = .{ | 3441 | // If there are no other errors, we include a "found compile log statement" error. |
| 3459 | .src_loc = src_loc, | 3442 | // Otherwise, we just show the compile log output, with no error. |
| 3460 | .msg = "found compile log statement", | 3443 | const include_compile_log_sources = bundle.root_list.items.len == 0; |
| 3461 | .notes = try gpa.alloc(Zcu.ErrorMsg, zcu.compile_log_sources.count() - 1), | 3444 | |
| 3462 | }; | 3445 | const refs = try zcu.resolveReferences(); |
| 3463 | defer gpa.free(err_msg.notes); | 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 | } | ||
| 3464 | 3461 | ||
| 3465 | for (values[1..], err_msg.notes) |src_info, *note| { | 3462 | if (messages.items.len == 0) break :compile_log_text ""; |
| 3466 | note.* = .{ | 3463 | |
| 3467 | .src_loc = src_info.src(), | 3464 | // Okay, there *are* referenced compile logs. Sort them into a consistent order. |
| 3468 | .msg = "also here", | 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; | ||
| 3469 | }; | 3477 | }; |
| 3470 | } | 3478 | } |
| 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 | } | ||
| 3471 | 3497 | ||
| 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]); | ||
| 3473 | } | 3502 | } |
| 3474 | } | 3503 | |
| 3504 | break :compile_log_text try log_text.toOwnedSlice(gpa); | ||
| 3505 | }; | ||
| 3475 | 3506 | ||
| 3476 | // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a | 3507 | // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a |
| 3477 | // very common way for incremental compilation bugs to manifest, so let's always check it. | 3508 | // 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 { | ... | @@ -3497,10 +3528,24 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3497 | } | 3528 | } |
| 3498 | }; | 3529 | }; |
| 3499 | 3530 | ||
| 3500 | const compile_log_text = if (comp.zcu) |m| m.compile_log_text.items else ""; | ||
| 3501 | return bundle.toOwnedBundle(compile_log_text); | 3531 | return bundle.toOwnedBundle(compile_log_text); |
| 3502 | } | 3532 | } |
| 3503 | 3533 | ||
| 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 | |||
| 3504 | fn anyErrors(comp: *Compilation) bool { | 3549 | fn anyErrors(comp: *Compilation) bool { |
| 3505 | return (totalErrorCount(comp) catch return true) != 0; | 3550 | return (totalErrorCount(comp) catch return true) != 0; |
| 3506 | } | 3551 | } |
src/Sema.zig+34-8| ... | @@ -5884,10 +5884,12 @@ fn zirCompileLog( | ... | @@ -5884,10 +5884,12 @@ fn zirCompileLog( |
| 5884 | ) CompileError!Air.Inst.Ref { | 5884 | ) CompileError!Air.Inst.Ref { |
| 5885 | const pt = sema.pt; | 5885 | const pt = sema.pt; |
| 5886 | const zcu = pt.zcu; | 5886 | const zcu = pt.zcu; |
| 5887 | const gpa = zcu.gpa; | ||
| 5888 | |||
| 5889 | var buf: std.ArrayListUnmanaged(u8) = .empty; | ||
| 5890 | defer buf.deinit(gpa); | ||
| 5887 | 5891 | ||
| 5888 | var managed = zcu.compile_log_text.toManaged(sema.gpa); | 5892 | const writer = buf.writer(gpa); |
| 5889 | defer pt.zcu.compile_log_text = managed.moveToUnmanaged(); | ||
| 5890 | const writer = managed.writer(); | ||
| 5891 | 5893 | ||
| 5892 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); | 5894 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); |
| 5893 | const src_node = extra.data.src_node; | 5895 | const src_node = extra.data.src_node; |
| ... | @@ -5906,13 +5908,37 @@ fn zirCompileLog( | ... | @@ -5906,13 +5908,37 @@ fn zirCompileLog( |
| 5906 | try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(pt)}); | 5908 | try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(pt)}); |
| 5907 | } | 5909 | } |
| 5908 | } | 5910 | } |
| 5909 | try writer.print("\n", .{}); | ||
| 5910 | 5911 | ||
| 5911 | const gop = try zcu.compile_log_sources.getOrPut(sema.gpa, sema.owner); | 5912 | const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls); |
| 5912 | if (!gop.found_existing) gop.value_ptr.* = .{ | 5913 | |
| 5913 | .base_node_inst = block.src_base_inst, | 5914 | const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: { |
| 5914 | .node_offset = src_node, | 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); | ||
| 5915 | }; | 5926 | }; |
| 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 | } | ||
| 5916 | return .void_value; | 5942 | return .void_value; |
| 5917 | } | 5943 | } |
| 5918 | 5944 |
src/Zcu.zig+77-9| ... | @@ -130,18 +130,23 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp | ... | @@ -130,18 +130,23 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp |
| 130 | /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. | 130 | /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. |
| 131 | failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, | 131 | failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty, |
| 132 | failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, | 132 | failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty, |
| 133 | /// Keep track of one `@compileLog` callsite per `AnalUnit`. | 133 | /// Keep track of `@compileLog`s per `AnalUnit`. |
| 134 | /// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`. | 134 | /// We track the source location of the first `@compileLog` call, and all logged lines as a linked list. |
| 135 | compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { | 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 { | ||
| 136 | base_node_inst: InternPool.TrackedInst.Index, | 137 | base_node_inst: InternPool.TrackedInst.Index, |
| 137 | node_offset: Ast.Node.Offset, | 138 | node_offset: Ast.Node.Offset, |
| 139 | first_line: CompileLogLine.Index, | ||
| 140 | last_line: CompileLogLine.Index, | ||
| 138 | pub fn src(self: @This()) LazySrcLoc { | 141 | pub fn src(self: @This()) LazySrcLoc { |
| 139 | return .{ | 142 | return .{ |
| 140 | .base_node_inst = self.base_node_inst, | 143 | .base_node_inst = self.base_node_inst, |
| 141 | .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset), | 144 | .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset), |
| 142 | }; | 145 | }; |
| 143 | } | 146 | } |
| 144 | }) = .{}, | 147 | }) = .empty, |
| 148 | compile_log_lines: std.ArrayListUnmanaged(CompileLogLine) = .empty, | ||
| 149 | free_compile_log_lines: std.ArrayListUnmanaged(CompileLogLine.Index) = .empty, | ||
| 145 | /// Using a map here for consistency with the other fields here. | 150 | /// Using a map here for consistency with the other fields here. |
| 146 | /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. | 151 | /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. |
| 147 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, | 152 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .empty, |
| ... | @@ -196,8 +201,6 @@ stage1_flags: packed struct { | ... | @@ -196,8 +201,6 @@ stage1_flags: packed struct { |
| 196 | reserved: u2 = 0, | 201 | reserved: u2 = 0, |
| 197 | } = .{}, | 202 | } = .{}, |
| 198 | 203 | ||
| 199 | compile_log_text: std.ArrayListUnmanaged(u8) = .empty, | ||
| 200 | |||
| 201 | test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, | 204 | test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty, |
| 202 | 205 | ||
| 203 | global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty, | 206 | global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty, |
| ... | @@ -547,6 +550,31 @@ pub const Export = struct { | ... | @@ -547,6 +550,31 @@ pub const Export = struct { |
| 547 | }; | 550 | }; |
| 548 | }; | 551 | }; |
| 549 | 552 | ||
| 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 | |||
| 550 | pub const Reference = struct { | 578 | pub const Reference = struct { |
| 551 | /// The `AnalUnit` whose semantic analysis was triggered by this reference. | 579 | /// The `AnalUnit` whose semantic analysis was triggered by this reference. |
| 552 | referenced: AnalUnit, | 580 | referenced: AnalUnit, |
| ... | @@ -2464,6 +2492,30 @@ pub const LazySrcLoc = struct { | ... | @@ -2464,6 +2492,30 @@ pub const LazySrcLoc = struct { |
| 2464 | .lazy = lazy.offset, | 2492 | .lazy = lazy.offset, |
| 2465 | }; | 2493 | }; |
| 2466 | } | 2494 | } |
| 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 | } | ||
| 2467 | }; | 2519 | }; |
| 2468 | 2520 | ||
| 2469 | pub const SemaError = error{ OutOfMemory, AnalysisFail }; | 2521 | pub const SemaError = error{ OutOfMemory, AnalysisFail }; |
| ... | @@ -2506,8 +2558,6 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2506,8 +2558,6 @@ pub fn deinit(zcu: *Zcu) void { |
| 2506 | } | 2558 | } |
| 2507 | zcu.embed_table.deinit(gpa); | 2559 | zcu.embed_table.deinit(gpa); |
| 2508 | 2560 | ||
| 2509 | zcu.compile_log_text.deinit(gpa); | ||
| 2510 | |||
| 2511 | zcu.local_zir_cache.handle.close(); | 2561 | zcu.local_zir_cache.handle.close(); |
| 2512 | zcu.global_zir_cache.handle.close(); | 2562 | zcu.global_zir_cache.handle.close(); |
| 2513 | 2563 | ||
| ... | @@ -2535,7 +2585,9 @@ pub fn deinit(zcu: *Zcu) void { | ... | @@ -2535,7 +2585,9 @@ pub fn deinit(zcu: *Zcu) void { |
| 2535 | } | 2585 | } |
| 2536 | zcu.cimport_errors.deinit(gpa); | 2586 | zcu.cimport_errors.deinit(gpa); |
| 2537 | 2587 | ||
| 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); | ||
| 2539 | 2591 | ||
| 2540 | zcu.all_exports.deinit(gpa); | 2592 | zcu.all_exports.deinit(gpa); |
| 2541 | zcu.free_exports.deinit(gpa); | 2593 | zcu.free_exports.deinit(gpa); |
| ... | @@ -3412,6 +3464,22 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { | ... | @@ -3412,6 +3464,22 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { |
| 3412 | } | 3464 | } |
| 3413 | } | 3465 | } |
| 3414 | 3466 | ||
| 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 | |||
| 3415 | pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void { | 3483 | pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void { |
| 3416 | const gpa = zcu.gpa; | 3484 | const gpa = zcu.gpa; |
| 3417 | 3485 |
src/Zcu/PerThread.zig+6| ... | @@ -599,6 +599,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized | ... | @@ -599,6 +599,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized |
| 599 | _ = zcu.outdated_ready.swapRemove(unit); | 599 | _ = zcu.outdated_ready.swapRemove(unit); |
| 600 | // No need for `deleteUnitExports` because we never export anything. | 600 | // No need for `deleteUnitExports` because we never export anything. |
| 601 | zcu.deleteUnitReferences(unit); | 601 | zcu.deleteUnitReferences(unit); |
| 602 | zcu.deleteUnitCompileLogs(unit); | ||
| 602 | if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { | 603 | if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { |
| 603 | kv.value.destroy(gpa); | 604 | kv.value.destroy(gpa); |
| 604 | } | 605 | } |
| ... | @@ -749,6 +750,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU | ... | @@ -749,6 +750,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU |
| 749 | if (dev.env.supports(.incremental)) { | 750 | if (dev.env.supports(.incremental)) { |
| 750 | zcu.deleteUnitExports(anal_unit); | 751 | zcu.deleteUnitExports(anal_unit); |
| 751 | zcu.deleteUnitReferences(anal_unit); | 752 | zcu.deleteUnitReferences(anal_unit); |
| 753 | zcu.deleteUnitCompileLogs(anal_unit); | ||
| 752 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | 754 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { |
| 753 | kv.value.destroy(gpa); | 755 | kv.value.destroy(gpa); |
| 754 | } | 756 | } |
| ... | @@ -921,6 +923,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu | ... | @@ -921,6 +923,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu |
| 921 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 923 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 922 | zcu.deleteUnitExports(anal_unit); | 924 | zcu.deleteUnitExports(anal_unit); |
| 923 | zcu.deleteUnitReferences(anal_unit); | 925 | zcu.deleteUnitReferences(anal_unit); |
| 926 | zcu.deleteUnitCompileLogs(anal_unit); | ||
| 924 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | 927 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { |
| 925 | kv.value.destroy(gpa); | 928 | kv.value.destroy(gpa); |
| 926 | } | 929 | } |
| ... | @@ -1293,6 +1296,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc | ... | @@ -1293,6 +1296,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc |
| 1293 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 1296 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 1294 | zcu.deleteUnitExports(anal_unit); | 1297 | zcu.deleteUnitExports(anal_unit); |
| 1295 | zcu.deleteUnitReferences(anal_unit); | 1298 | zcu.deleteUnitReferences(anal_unit); |
| 1299 | zcu.deleteUnitCompileLogs(anal_unit); | ||
| 1296 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | 1300 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { |
| 1297 | kv.value.destroy(gpa); | 1301 | kv.value.destroy(gpa); |
| 1298 | } | 1302 | } |
| ... | @@ -1527,6 +1531,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter | ... | @@ -1527,6 +1531,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: Inter |
| 1527 | _ = zcu.outdated_ready.swapRemove(anal_unit); | 1531 | _ = zcu.outdated_ready.swapRemove(anal_unit); |
| 1528 | zcu.deleteUnitExports(anal_unit); | 1532 | zcu.deleteUnitExports(anal_unit); |
| 1529 | zcu.deleteUnitReferences(anal_unit); | 1533 | zcu.deleteUnitReferences(anal_unit); |
| 1534 | zcu.deleteUnitCompileLogs(anal_unit); | ||
| 1530 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | 1535 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { |
| 1531 | kv.value.destroy(gpa); | 1536 | kv.value.destroy(gpa); |
| 1532 | } | 1537 | } |
| ... | @@ -3764,6 +3769,7 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError | ... | @@ -3764,6 +3769,7 @@ pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError |
| 3764 | // reusing the memory which is currently being used to track this state. | 3769 | // reusing the memory which is currently being used to track this state. |
| 3765 | zcu.deleteUnitExports(anal_unit); | 3770 | zcu.deleteUnitExports(anal_unit); |
| 3766 | zcu.deleteUnitReferences(anal_unit); | 3771 | zcu.deleteUnitReferences(anal_unit); |
| 3772 | zcu.deleteUnitCompileLogs(anal_unit); | ||
| 3767 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { | 3773 | if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { |
| 3768 | kv.value.destroy(gpa); | 3774 | kv.value.destroy(gpa); |
| 3769 | } | 3775 | } |
test/incremental/compile_error_then_log+2| ... | @@ -12,6 +12,7 @@ comptime { | ... | @@ -12,6 +12,7 @@ comptime { |
| 12 | @compileLog("this is a log"); | 12 | @compileLog("this is a log"); |
| 13 | } | 13 | } |
| 14 | #expect_error=main.zig:3:5: error: this is an error | 14 | #expect_error=main.zig:3:5: error: this is an error |
| 15 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") | ||
| 15 | 16 | ||
| 16 | #update=remove the compile error | 17 | #update=remove the compile error |
| 17 | #file=main.zig | 18 | #file=main.zig |
| ... | @@ -23,3 +24,4 @@ comptime { | ... | @@ -23,3 +24,4 @@ comptime { |
| 23 | @compileLog("this is a log"); | 24 | @compileLog("this is a log"); |
| 24 | } | 25 | } |
| 25 | #expect_error=main.zig:6:5: error: found compile log statement | 26 | #expect_error=main.zig:6:5: error: found compile log statement |
| 27 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") |
test/incremental/compile_log created+30| ... | @@ -0,0 +1,30 @@ | ||
| 1 | #target=x86_64-linux-selfhosted | ||
| 2 | #target=x86_64-linux-cbe | ||
| 3 | #target=x86_64-windows-cbe | ||
| 4 | #target=wasm32-wasi-selfhosted | ||
| 5 | |||
| 6 | #update=initial version with no compile log | ||
| 7 | #file=main.zig | ||
| 8 | const std = @import("std"); | ||
| 9 | pub fn main() !void { | ||
| 10 | try std.io.getStdOut().writeAll("Hello, World!\n"); | ||
| 11 | } | ||
| 12 | #expect_stdout="Hello, World!\n" | ||
| 13 | |||
| 14 | #update=add compile log | ||
| 15 | #file=main.zig | ||
| 16 | const std = @import("std"); | ||
| 17 | pub fn main() !void { | ||
| 18 | try std.io.getStdOut().writeAll("Hello, World!\n"); | ||
| 19 | @compileLog("this is a log"); | ||
| 20 | } | ||
| 21 | #expect_error=main.zig:4:5: error: found compile log statement | ||
| 22 | #expect_compile_log=@as(*const [13:0]u8, "this is a log") | ||
| 23 | |||
| 24 | #update=remove compile log | ||
| 25 | #file=main.zig | ||
| 26 | const std = @import("std"); | ||
| 27 | pub fn main() !void { | ||
| 28 | try std.io.getStdOut().writeAll("Hello, World!\n"); | ||
| 29 | } | ||
| 30 | #expect_stdout="Hello, World!\n" | ||
tools/incr-check.zig+39-8| ... | @@ -341,9 +341,9 @@ const Eval = struct { | ... | @@ -341,9 +341,9 @@ const Eval = struct { |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void { | 343 | fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void { |
| 344 | const expected_errors = switch (update.outcome) { | 344 | const expected = switch (update.outcome) { |
| 345 | .unknown => return, | 345 | .unknown => return, |
| 346 | .compile_errors => |expected_errors| expected_errors, | 346 | .compile_errors => |ce| ce, |
| 347 | .stdout, .exit_code => { | 347 | .stdout, .exit_code => { |
| 348 | const color: std.zig.Color = .auto; | 348 | const color: std.zig.Color = .auto; |
| 349 | error_bundle.renderToStdErr(color.renderOptions()); | 349 | error_bundle.renderToStdErr(color.renderOptions()); |
| ... | @@ -354,24 +354,30 @@ const Eval = struct { | ... | @@ -354,24 +354,30 @@ const Eval = struct { |
| 354 | var expected_idx: usize = 0; | 354 | var expected_idx: usize = 0; |
| 355 | 355 | ||
| 356 | for (error_bundle.getMessages()) |err_idx| { | 356 | for (error_bundle.getMessages()) |err_idx| { |
| 357 | if (expected_idx == expected_errors.len) { | 357 | if (expected_idx == expected.errors.len) { |
| 358 | const color: std.zig.Color = .auto; | 358 | const color: std.zig.Color = .auto; |
| 359 | error_bundle.renderToStdErr(color.renderOptions()); | 359 | error_bundle.renderToStdErr(color.renderOptions()); |
| 360 | eval.fatal("update '{s}': more errors than expected", .{update.name}); | 360 | eval.fatal("update '{s}': more errors than expected", .{update.name}); |
| 361 | } | 361 | } |
| 362 | eval.checkOneError(update, error_bundle, expected_errors[expected_idx], false, err_idx); | 362 | eval.checkOneError(update, error_bundle, expected.errors[expected_idx], false, err_idx); |
| 363 | expected_idx += 1; | 363 | expected_idx += 1; |
| 364 | 364 | ||
| 365 | for (error_bundle.getNotes(err_idx)) |note_idx| { | 365 | for (error_bundle.getNotes(err_idx)) |note_idx| { |
| 366 | if (expected_idx == expected_errors.len) { | 366 | if (expected_idx == expected.errors.len) { |
| 367 | const color: std.zig.Color = .auto; | 367 | const color: std.zig.Color = .auto; |
| 368 | error_bundle.renderToStdErr(color.renderOptions()); | 368 | error_bundle.renderToStdErr(color.renderOptions()); |
| 369 | eval.fatal("update '{s}': more error notes than expected", .{update.name}); | 369 | eval.fatal("update '{s}': more error notes than expected", .{update.name}); |
| 370 | } | 370 | } |
| 371 | eval.checkOneError(update, error_bundle, expected_errors[expected_idx], true, note_idx); | 371 | eval.checkOneError(update, error_bundle, expected.errors[expected_idx], true, note_idx); |
| 372 | expected_idx += 1; | 372 | expected_idx += 1; |
| 373 | } | 373 | } |
| 374 | } | 374 | } |
| 375 | |||
| 376 | if (!std.mem.eql(u8, error_bundle.getCompileLogOutput(), expected.compile_log_output)) { | ||
| 377 | const color: std.zig.Color = .auto; | ||
| 378 | error_bundle.renderToStdErr(color.renderOptions()); | ||
| 379 | eval.fatal("update '{s}': unexpected compile log output", .{update.name}); | ||
| 380 | } | ||
| 375 | } | 381 | } |
| 376 | 382 | ||
| 377 | fn checkOneError( | 383 | fn checkOneError( |
| ... | @@ -634,7 +640,10 @@ const Case = struct { | ... | @@ -634,7 +640,10 @@ const Case = struct { |
| 634 | 640 | ||
| 635 | const Outcome = union(enum) { | 641 | const Outcome = union(enum) { |
| 636 | unknown, | 642 | unknown, |
| 637 | compile_errors: []const ExpectedError, | 643 | compile_errors: struct { |
| 644 | errors: []const ExpectedError, | ||
| 645 | compile_log_output: []const u8, | ||
| 646 | }, | ||
| 638 | stdout: []const u8, | 647 | stdout: []const u8, |
| 639 | exit_code: u8, | 648 | exit_code: u8, |
| 640 | }; | 649 | }; |
| ... | @@ -759,7 +768,29 @@ const Case = struct { | ... | @@ -759,7 +768,29 @@ const Case = struct { |
| 759 | try errors.append(arena, parseExpectedError(new_val, line_n)); | 768 | try errors.append(arena, parseExpectedError(new_val, line_n)); |
| 760 | } | 769 | } |
| 761 | 770 | ||
| 762 | last_update.outcome = .{ .compile_errors = errors.items }; | 771 | var compile_log_output: std.ArrayListUnmanaged(u8) = .empty; |
| 772 | while (true) { | ||
| 773 | const next_line = it.peek() orelse break; | ||
| 774 | if (!std.mem.startsWith(u8, next_line, "#")) break; | ||
| 775 | var new_line_it = std.mem.splitScalar(u8, next_line, '='); | ||
| 776 | const new_key = new_line_it.first()[1..]; | ||
| 777 | const new_val = std.mem.trimRight(u8, new_line_it.rest(), "\r"); | ||
| 778 | if (new_val.len == 0) break; | ||
| 779 | if (!std.mem.eql(u8, new_key, "expect_compile_log")) break; | ||
| 780 | |||
| 781 | _ = it.next(); | ||
| 782 | line_n += 1; | ||
| 783 | try compile_log_output.ensureUnusedCapacity(arena, new_val.len + 1); | ||
| 784 | compile_log_output.appendSliceAssumeCapacity(new_val); | ||
| 785 | compile_log_output.appendAssumeCapacity('\n'); | ||
| 786 | } | ||
| 787 | |||
| 788 | last_update.outcome = .{ .compile_errors = .{ | ||
| 789 | .errors = errors.items, | ||
| 790 | .compile_log_output = compile_log_output.items, | ||
| 791 | } }; | ||
| 792 | } else if (std.mem.eql(u8, key, "expect_compile_log")) { | ||
| 793 | fatal("line {d}: 'expect_compile_log' must immediately follow 'expect_error'", .{line_n}); | ||
| 763 | } else { | 794 | } else { |
| 764 | fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); | 795 | fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); |
| 765 | } | 796 | } |