From 70040778fbde5d7fcbbfc26dbabc700024c538d5 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 30 Mar 2025 22:59:25 +0100 Subject: [PATCH 1/5] Compilation: fix reference trace behavior without `-freference-trace` When `-freference-trace` is not passed, we want to show exactly one reference trace. Previously, we set the reference trace root in `Sema` iff there were no other failed analyses. However, this results in an arbitrary error being the one with the reference trace after error sorting. It is also incompatible with incremental compilation, where some errors might be unreferenced. Instead, set the field on all analysis errors, and decide in `Compilation.getAllErrorsAlloc` which reference trace[s] to actually show. --- src/Compilation.zig | 29 ++++++++++++++++++++--------- src/Sema.zig | 8 ++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 351b81c122..57e36bfbf7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3328,7 +3328,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { if (comp.zcu) |zcu| zcu_errors: { for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| { if (error_msg) |msg| { - try addModuleErrorMsg(zcu, &bundle, msg.*); + try addModuleErrorMsg(zcu, &bundle, msg.*, false); } else { // Must be ZIR or Zoir errors. Note that this may include AST errors. _ = try file.getTree(gpa); // Tree must be loaded. @@ -3378,6 +3378,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { break :s entries.slice(); }; defer sorted_failed_analysis.deinit(gpa); + var added_any_analysis_error = false; for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| { if (comp.incremental) { const refs = try zcu.resolveReferences(); @@ -3389,7 +3390,9 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { zcu.fmtAnalUnit(anal_unit), }); - try addModuleErrorMsg(zcu, &bundle, error_msg.*); + try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error); + added_any_analysis_error = true; + if (zcu.cimport_errors.get(anal_unit)) |errors| { for (errors.getMessages()) |err_msg_index| { const err_msg = errors.getErrorMessage(err_msg_index); @@ -3412,13 +3415,13 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } } for (zcu.failed_codegen.values()) |error_msg| { - try addModuleErrorMsg(zcu, &bundle, error_msg.*); + try addModuleErrorMsg(zcu, &bundle, error_msg.*, false); } for (zcu.failed_types.values()) |error_msg| { - try addModuleErrorMsg(zcu, &bundle, error_msg.*); + try addModuleErrorMsg(zcu, &bundle, error_msg.*, false); } for (zcu.failed_exports.values()) |value| { - try addModuleErrorMsg(zcu, &bundle, value.*); + try addModuleErrorMsg(zcu, &bundle, value.*, false); } const actual_error_count = zcu.intern_pool.global_error_set.getNamesFromMainThread().len; @@ -3527,7 +3530,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { // We don't actually include the error here if `!include_compile_log_sources`. // The sorting above was still necessary, though, to get `log_text` in the right order. if (include_compile_log_sources) { - try addModuleErrorMsg(zcu, &bundle, messages.items[0]); + try addModuleErrorMsg(zcu, &bundle, messages.items[0], false); } break :compile_log_text try log_text.toOwnedSlice(gpa); @@ -3631,10 +3634,14 @@ pub const ErrorNoteHashContext = struct { } }; +const default_reference_trace_len = 2; pub fn addModuleErrorMsg( zcu: *Zcu, eb: *ErrorBundle.Wip, module_err_msg: Zcu.ErrorMsg, + /// If `-freference-trace` is not specified, we only want to show the one reference trace. + /// So, this is whether we have already emitted an error with a reference trace. + already_added_error: bool, ) !void { const gpa = eb.gpa; const ip = &zcu.intern_pool; @@ -3657,14 +3664,18 @@ pub fn addModuleErrorMsg( var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .empty; defer ref_traces.deinit(gpa); - if (module_err_msg.reference_trace_root.unwrap()) |rt_root| { + rt: { + const rt_root = module_err_msg.reference_trace_root.unwrap() orelse break :rt; + const max_references = zcu.comp.reference_trace orelse refs: { + if (already_added_error) break :rt; + break :refs default_reference_trace_len; + }; + const all_references = try zcu.resolveReferences(); var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty; defer seen.deinit(gpa); - const max_references = zcu.comp.reference_trace orelse Sema.default_reference_trace_len; - var referenced_by = rt_root; while (all_references.get(referenced_by)) |maybe_ref| { const ref = maybe_ref orelse break; diff --git a/src/Sema.zig b/src/Sema.zig index 8889b475fd..e13aba3349 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -191,7 +191,6 @@ const LowerZon = @import("Sema/LowerZon.zig"); const arith = @import("Sema/arith.zig"); pub const default_branch_quota = 1000; -pub const default_reference_trace_len = 2; pub const InferredErrorSet = struct { /// The function body from which this error set originates. @@ -2580,7 +2579,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) { var wip_errors: std.zig.ErrorBundle.Wip = undefined; wip_errors.init(gpa) catch @panic("out of memory"); - Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*) catch @panic("out of memory"); + Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); std.debug.print("compile error during Sema:\n", .{}); var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); error_bundle.renderToStdErr(.{ .ttyconf = .no_color }); @@ -2600,10 +2599,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg } } - const use_ref_trace = if (zcu.comp.reference_trace) |n| n > 0 else zcu.failed_analysis.count() == 0; - if (use_ref_trace) { - err_msg.reference_trace_root = sema.owner.toOptional(); - } + err_msg.reference_trace_root = sema.owner.toOptional(); const gop = try zcu.failed_analysis.getOrPut(gpa, sema.owner); if (gop.found_existing) { From d717c96877355090ea3a8e3662a2584eea02445c Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 15 May 2025 13:18:08 +0100 Subject: [PATCH 2/5] compiler: include inline calls in the reference trace Inline calls which happened in the erroring `AnalUnit` still show as error notes, because they tend to make very important context (e.g. to see how comptime values propagate through them). However, "earlier" inline calls are still useful to see to understand how something is being referenced, so we should include them in the reference trace. --- src/Compilation.zig | 66 +++++++++++++++++-------- src/Sema.zig | 84 +++++++++++++++++++------------- src/Sema/comptime_ptr_access.zig | 2 +- src/Zcu.zig | 80 +++++++++++++++++++++++++++++- 4 files changed, 175 insertions(+), 57 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 57e36bfbf7..d3cf9e8f38 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3681,32 +3681,27 @@ pub fn addModuleErrorMsg( const ref = maybe_ref orelse break; const gop = try seen.getOrPut(gpa, ref.referencer); if (gop.found_existing) break; - if (ref_traces.items.len < max_references) skip: { - const src = ref.src.upgrade(zcu); - const source = try src.file_scope.getSource(gpa); - const span = try src.span(gpa); - const loc = std.zig.findLineColumn(source.bytes, span.main); - const rt_file_path = try src.file_scope.fullPath(gpa); - defer gpa.free(rt_file_path); - const name = switch (ref.referencer.unwrap()) { + if (ref_traces.items.len < max_references) { + var last_call_src = ref.src; + var opt_inline_frame = ref.inline_frame; + while (opt_inline_frame.unwrap()) |inline_frame| { + const f = inline_frame.ptr(zcu).*; + const func_nav = ip.indexToKey(f.callee).func.owner_nav; + const func_name = ip.getNav(func_nav).name.toSlice(ip); + try addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true); + last_call_src = f.call_src; + opt_inline_frame = f.parent; + } + const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) { .@"comptime" => "comptime", .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip), .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip), .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip), - .memoized_state => break :skip, + .memoized_state => null, }; - try ref_traces.append(gpa, .{ - .decl_name = try eb.addString(name), - .src_loc = try eb.addSourceLocation(.{ - .src_path = try eb.addString(rt_file_path), - .span_start = span.start, - .span_main = span.main, - .span_end = span.end, - .line = @intCast(loc.line), - .column = @intCast(loc.column), - .source_line = 0, - }), - }); + if (root_name) |n| { + try addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false); + } } referenced_by = ref.referencer; } @@ -3786,6 +3781,35 @@ pub fn addModuleErrorMsg( } } +fn addReferenceTraceFrame( + zcu: *Zcu, + eb: *ErrorBundle.Wip, + ref_traces: *std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace), + name: []const u8, + lazy_src: Zcu.LazySrcLoc, + inlined: bool, +) !void { + const gpa = zcu.gpa; + const src = lazy_src.upgrade(zcu); + const source = try src.file_scope.getSource(gpa); + const span = try src.span(gpa); + const loc = std.zig.findLineColumn(source.bytes, span.main); + const rt_file_path = try src.file_scope.fullPath(gpa); + defer gpa.free(rt_file_path); + try ref_traces.append(gpa, .{ + .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), + .src_loc = try eb.addSourceLocation(.{ + .src_path = try eb.addString(rt_file_path), + .span_start = span.start, + .span_main = span.main, + .span_end = span.end, + .line = @intCast(loc.line), + .column = @intCast(loc.column), + .source_line = 0, + }), + }); +} + pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { const gpa = eb.gpa; const src_path = try file.fullPath(gpa); diff --git a/src/Sema.zig b/src/Sema.zig index e13aba3349..d267dcdb47 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -448,6 +448,21 @@ pub const Block = struct { func: InternPool.Index, comptime_result: Air.Inst.Ref, merges: Merges, + /// Populated lazily by `refFrame`. + ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none, + + fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index { + if (inlining.ref_frame == .none) { + inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{ + .callee = inlining.func, + .call_src = inlining.call_src, + .parent = if (inlining.call_block.inlining) |parent_inlining| p: { + break :p (try parent_inlining.refFrame(zcu)).toOptional(); + } else .none, + })).toOptional(); + } + return inlining.ref_frame.unwrap().?; + } }; pub const Merges = struct { @@ -4287,7 +4302,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (zcu.intern_pool.isFuncBody(val)) { const ty = Type.fromInterned(zcu.intern_pool.typeOf(val)); if (try ty.fnHasRuntimeBitsSema(pt)) { - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = val })); + try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = val })); try zcu.ensureFuncBodyAnalysisQueued(val); } } @@ -6615,7 +6630,7 @@ pub fn analyzeExport( if (options.linkage == .internal) return; - try sema.ensureNavResolved(src, orig_nav_index, .fully); + try sema.ensureNavResolved(block, src, orig_nav_index, .fully); const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { .variable => |v| v.owner_nav, @@ -6644,7 +6659,7 @@ pub fn analyzeExport( return sema.fail(block, src, "export target cannot be extern", .{}); } - try sema.maybeQueueFuncBodyAnalysis(src, exported_nav_index); + try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index); try sema.exports.append(gpa, .{ .opts = options, @@ -6892,7 +6907,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .no_embedded_nulls, ); const nav_index = try sema.lookupIdentifier(block, src, decl_name); - return sema.analyzeNavRef(src, nav_index); + return sema.analyzeNavRef(block, src, nav_index); } fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -6988,7 +7003,7 @@ fn lookupInNamespace( } for (usingnamespaces.items) |sub_ns_nav| { - try sema.ensureNavResolved(src, sub_ns_nav, .fully); + try sema.ensureNavResolved(block, src, sub_ns_nav, .fully); const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val); const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu)); try checked_namespaces.put(gpa, sub_ns, {}); @@ -7720,8 +7735,8 @@ fn analyzeCall( var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{ .call_block = block, .call_src = call_src, + .func = func_val.?.toIntern(), .has_comptime_args = false, // unused by error reporting - .func = .none, // unused by error reporting .comptime_result = .none, // unused by error reporting .merges = undefined, // unused because we'll never `return` } else undefined; @@ -7999,7 +8014,7 @@ fn analyzeCall( ref_func: { const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; - try sema.addReferenceEntry(call_src, .wrap(.{ .func = runtime_func_val.toIntern() })); + try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = runtime_func_val.toIntern() })); try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern()); } @@ -17254,7 +17269,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat .@"comptime" => |index| return Air.internedToRef(index), .runtime => |index| index, .nav_val => |nav| return sema.analyzeNavVal(block, src, nav), - .nav_ref => |nav| return sema.analyzeNavRef(src, nav), + .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav), }; // The comptime case is handled already above. Runtime case below. @@ -18407,7 +18422,7 @@ fn typeInfoNamespaceDecls( if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) { continue; } - try sema.ensureNavResolved(src, nav, .fully); + try sema.ensureNavResolved(block, src, nav, .fully); const namespace_ty = Type.fromInterned(ip.getNav(nav).status.fully_resolved.val); try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces); } @@ -27932,7 +27947,7 @@ fn namespaceLookupRef( decl_name: InternPool.NullTerminatedString, ) CompileError!?Air.Inst.Ref { const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; - return try sema.analyzeNavRef(src, nav); + return try sema.analyzeNavRef(block, src, nav); } fn namespaceLookupVal( @@ -29095,7 +29110,7 @@ fn coerceExtra( .@"extern" => |e| e.owner_nav, else => unreachable, }; - const inst_as_ptr = try sema.analyzeNavRef(inst_src, fn_nav); + const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); } @@ -30748,7 +30763,7 @@ fn coerceVarArgParam( .@"fn" => fn_ptr: { const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined); const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav; - break :fn_ptr try sema.analyzeNavRef(inst_src, fn_nav); + break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav); }, .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), .float => float: { @@ -31758,12 +31773,13 @@ fn analyzeNavVal( src: LazySrcLoc, nav_index: InternPool.Nav.Index, ) CompileError!Air.Inst.Ref { - const ref = try sema.analyzeNavRefInner(src, nav_index, false); + const ref = try sema.analyzeNavRefInner(block, src, nav_index, false); return sema.analyzeLoad(block, src, ref, src); } fn addReferenceEntry( sema: *Sema, + opt_block: ?*Block, src: LazySrcLoc, referenced_unit: AnalUnit, ) !void { @@ -31771,10 +31787,12 @@ fn addReferenceEntry( if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return; const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); if (gop.found_existing) return; - // TODO: we need to figure out how to model inline calls here. - // They aren't references in the analysis sense, but ought to show up in the reference trace! - // Would representing inline calls in the reference table cause excessive memory usage? - try zcu.addUnitReference(sema.owner, referenced_unit, src); + try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: { + const block = opt_block orelse break :inline_frame .none; + const inlining = block.inlining orelse break :inline_frame .none; + const frame = try inlining.refFrame(zcu); + break :inline_frame frame.toOptional(); + }); } pub fn addTypeReferenceEntry( @@ -31793,7 +31811,7 @@ fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.M const pt = sema.pt; const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); - try sema.addReferenceEntry(src, unit); + try sema.addReferenceEntry(null, src, unit); try sema.declareDependency(.{ .memoized_state = stage }); if (pt.zcu.analysis_in_progress.contains(unit)) { @@ -31802,7 +31820,7 @@ fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.M try pt.ensureMemoizedStateUpToDate(stage); } -pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { +pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -31825,7 +31843,7 @@ pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav .type => .{ .nav_ty = nav_index }, .fully => .{ .nav_val = nav_index }, }); - try sema.addReferenceEntry(src, anal_unit); + try sema.addReferenceEntry(block, src, anal_unit); if (zcu.analysis_in_progress.contains(anal_unit)) { return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{ @@ -31855,25 +31873,25 @@ fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { } })); } -fn analyzeNavRef(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref { - return sema.analyzeNavRefInner(src, nav_index, true); +fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref { + return sema.analyzeNavRefInner(block, src, nav_index, true); } /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed. /// If this pointer will be used directly, `is_ref` must be `true`. /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`. -fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { +fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - try sema.ensureNavResolved(src, orig_nav_index, if (is_ref) .type else .fully); + try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully); const nav_index = nav: { if (ip.getNav(orig_nav_index).isExternOrFn(ip)) { // Getting a pointer to this `Nav` might mean we actually get a pointer to something else! // We need to resolve the value to know for sure. - if (is_ref) try sema.ensureNavResolved(src, orig_nav_index, .fully); + if (is_ref) try sema.ensureNavResolved(block, src, orig_nav_index, .fully); switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) { .func => |f| break :nav f.owner_nav, .@"extern" => |e| break :nav e.owner_nav, @@ -31897,7 +31915,7 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N }, }); if (is_ref) { - try sema.maybeQueueFuncBodyAnalysis(src, nav_index); + try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index); } return Air.internedToRef((try pt.intern(.{ .ptr = .{ .ty = ptr_ty.toIntern(), @@ -31906,7 +31924,7 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N } }))); } -fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void { +fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -31914,16 +31932,16 @@ fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, nav_index: InternPoo // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function. // If it is, we can resolve the *value*, and queue analysis as needed. - try sema.ensureNavResolved(src, nav_index, .type); + try sema.ensureNavResolved(block, src, nav_index, .type); const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip)); if (nav_ty.zigTypeTag(zcu) != .@"fn") return; if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return; - try sema.ensureNavResolved(src, nav_index, .fully); + try sema.ensureNavResolved(block, src, nav_index, .fully); const nav_val = zcu.navValue(nav_index); if (!ip.isFuncBody(nav_val.toIntern())) return; - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = nav_val.toIntern() })); + try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = nav_val.toIntern() })); try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern()); } @@ -31939,8 +31957,8 @@ fn analyzeRef( if (try sema.resolveValue(operand)) |val| { switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .@"extern" => |e| return sema.analyzeNavRef(src, e.owner_nav), - .func => |f| return sema.analyzeNavRef(src, f.owner_nav), + .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav), + .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav), else => return uavRef(sema, val.toIntern()), } } @@ -35504,7 +35522,7 @@ fn resolveInferredErrorSet( } // In this case we are dealing with the actual InferredErrorSet object that // corresponds to the function, not one created to track an inline/comptime call. - try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = func_index })); + try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = func_index })); try pt.ensureFuncBodyUpToDate(func_index); } diff --git a/src/Sema/comptime_ptr_access.zig b/src/Sema/comptime_ptr_access.zig index 2e21c31f2b..f4ac897154 100644 --- a/src/Sema/comptime_ptr_access.zig +++ b/src/Sema/comptime_ptr_access.zig @@ -228,7 +228,7 @@ fn loadComptimePtrInner( const base_val: MutableValue = switch (ptr.base_addr) { .nav => |nav| val: { - try sema.ensureNavResolved(src, nav, .fully); + try sema.ensureNavResolved(block, src, nav, .fully); const val = ip.getNav(nav).status.fully_resolved.val; switch (ip.indexToKey(val)) { .variable => return .runtime_load, diff --git a/src/Zcu.zig b/src/Zcu.zig index fab40763e8..584eb8ca3d 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -215,6 +215,9 @@ all_references: std.ArrayListUnmanaged(Reference) = .empty, /// Freelist of indices in `all_references`. free_references: std.ArrayListUnmanaged(u32) = .empty, +inline_reference_frames: std.ArrayListUnmanaged(InlineReferenceFrame) = .empty, +free_inline_reference_frames: std.ArrayListUnmanaged(InlineReferenceFrame.Index) = .empty, + /// Key is the `AnalUnit` *performing* the reference. This representation allows /// incremental updates to quickly delete references caused by a specific `AnalUnit`. /// Value is index into `all_type_reference` of the first reference triggered by the unit. @@ -583,6 +586,42 @@ pub const Reference = struct { next: u32, /// The source location of the reference. src: LazySrcLoc, + /// If not `.none`, this is the index of the `InlineReferenceFrame` which should appear + /// between the referencer and `referenced` in the reference trace. These frames represent + /// inline calls, which do not create actual references (since they happen in the caller's + /// `AnalUnit`), but do show in the reference trace. + inline_frame: InlineReferenceFrame.Index.Optional, +}; + +pub const InlineReferenceFrame = struct { + /// The inline *callee*; that is, the function which was called inline. + /// The *caller* is either `parent`, or else the unit causing the original `Reference`. + callee: InternPool.Index, + /// The source location of the inline call, in the *caller*. + call_src: LazySrcLoc, + /// If not `.none`, a frame which should appear directly below this one. + /// This will be the "parent" inline call; this frame's `callee` is our caller. + parent: InlineReferenceFrame.Index.Optional, + + pub const Index = enum(u32) { + _, + pub fn ptr(idx: Index, zcu: *Zcu) *InlineReferenceFrame { + return &zcu.inline_reference_frames.items[@intFromEnum(idx)]; + } + pub fn toOptional(idx: Index) Optional { + return @enumFromInt(@intFromEnum(idx)); + } + pub const Optional = enum(u32) { + none = std.math.maxInt(u32), + _, + pub fn unwrap(opt: Optional) ?Index { + return switch (opt) { + .none => null, + _ => @enumFromInt(@intFromEnum(opt)), + }; + } + }; + }; }; pub const TypeReference = struct { @@ -3440,12 +3479,28 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { var idx = kv.value; while (idx != std.math.maxInt(u32)) { + const ref = zcu.all_references.items[idx]; zcu.free_references.append(gpa, idx) catch { // This space will be reused eventually, so we need not propagate this error. // Just leak it for now, and let GC reclaim it later on. break :unit_refs; }; - idx = zcu.all_references.items[idx].next; + idx = ref.next; + + var opt_inline_frame = ref.inline_frame; + while (opt_inline_frame.unwrap()) |inline_frame| { + // The same inline frame could be used multiple times by one unit. We need to + // detect this case to avoid adding it to `free_inline_reference_frames` more + // than once. We do that by setting `parent` to itself as a marker. + if (inline_frame.ptr(zcu).parent == inline_frame.toOptional()) break; + zcu.free_inline_reference_frames.append(gpa, inline_frame) catch { + // This space will be reused eventually, so we need not propagate this error. + // Just leak it for now, and let GC reclaim it later on. + break :unit_refs; + }; + opt_inline_frame = inline_frame.ptr(zcu).parent; + inline_frame.ptr(zcu).parent = inline_frame.toOptional(); // signal to code above + } } } @@ -3480,7 +3535,22 @@ pub fn deleteUnitCompileLogs(zcu: *Zcu, anal_unit: AnalUnit) void { } } -pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void { +pub fn addInlineReferenceFrame(zcu: *Zcu, frame: InlineReferenceFrame) Allocator.Error!Zcu.InlineReferenceFrame.Index { + const frame_idx: InlineReferenceFrame.Index = zcu.free_inline_reference_frames.pop() orelse idx: { + _ = try zcu.inline_reference_frames.addOne(zcu.gpa); + break :idx @enumFromInt(zcu.inline_reference_frames.items.len - 1); + }; + frame_idx.ptr(zcu).* = frame; + return frame_idx; +} + +pub fn addUnitReference( + zcu: *Zcu, + src_unit: AnalUnit, + referenced_unit: AnalUnit, + ref_src: LazySrcLoc, + inline_frame: InlineReferenceFrame.Index.Optional, +) Allocator.Error!void { const gpa = zcu.gpa; zcu.clearCachedResolvedReferences(); @@ -3500,6 +3570,7 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit .referenced = referenced_unit, .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32), .src = ref_src, + .inline_frame = inline_frame, }; gop.value_ptr.* = @intCast(ref_idx); @@ -3828,7 +3899,10 @@ pub fn unionTagFieldIndex(zcu: *const Zcu, loaded_union: InternPool.LoadedUnionT pub const ResolvedReference = struct { referencer: AnalUnit, + /// If `inline_frame` is not `.none`, this is the *deepest* source location in the chain of + /// inline calls. For source locations further up the inline call stack, consult `inline_frame`. src: LazySrcLoc, + inline_frame: InlineReferenceFrame.Index.Optional, }; /// Returns a mapping from an `AnalUnit` to where it is referenced. @@ -4037,6 +4111,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv try unit_queue.put(gpa, ref.referenced, .{ .referencer = unit, .src = ref.src, + .inline_frame = ref.inline_frame, }); } ref_idx = ref.next; @@ -4055,6 +4130,7 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv try type_queue.put(gpa, ref.referenced, .{ .referencer = unit, .src = ref.src, + .inline_frame = .none, }); } ref_idx = ref.next; From 429672705090f073adedb885057a4603eaf66c95 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 10 May 2025 18:22:43 +0100 Subject: [PATCH 3/5] Sema: improve "called from here" notes To an average user, it may be unclear why these notes are not just in the reference trace; that's because they are more important, because they are inline calls through which comptime values may propagate. There are now 3 possible wordings for this note: * "called at comptime here" * "called inline here" * "generic function instantiated here" An alternative could be these wordings: * "while analyzing comptime call here" * "while analyzing inline call here" * "while analyzing generic instantiation here" I'm not sure which is better -- but this commit is certainly better than status quo. --- src/Sema.zig | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d267dcdb47..41453d2bba 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -444,13 +444,19 @@ pub const Block = struct { pub const Inlining = struct { call_block: *Block, call_src: LazySrcLoc, - has_comptime_args: bool, func: InternPool.Index, - comptime_result: Air.Inst.Ref, - merges: Merges, + /// Populated lazily by `refFrame`. ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none, + /// If `true`, the following fields are `undefined`. This doesn't represent a true inline + /// call, but rather a generic call analyzing the instantiation's generic type bodies. + is_generic_instantiation: bool, + + has_comptime_args: bool, + comptime_result: Air.Inst.Ref, + merges: Merges, + fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index { if (inlining.ref_frame == .none) { inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{ @@ -2604,12 +2610,12 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg if (block) |start_block| { var block_it = start_block; while (block_it.inlining) |inlining| { - try sema.errNote( - inlining.call_src, - err_msg, - "called from here", - .{}, - ); + const note_str = note: { + if (inlining.is_generic_instantiation) break :note "generic function instantiated here"; + if (inlining.call_block.isComptime()) break :note "called at comptime here"; + break :note "called inline here"; + }; + try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str}); block_it = inlining.call_block; } } @@ -7736,9 +7742,10 @@ fn analyzeCall( .call_block = block, .call_src = call_src, .func = func_val.?.toIntern(), - .has_comptime_args = false, // unused by error reporting - .comptime_result = .none, // unused by error reporting - .merges = undefined, // unused because we'll never `return` + .is_generic_instantiation = true, // this allows the following fields to be `undefined` + .has_comptime_args = undefined, + .comptime_result = undefined, + .merges = undefined, } else undefined; // This is the block in which we evaluate generic function components: that is, generic parameter @@ -8216,10 +8223,11 @@ fn analyzeCall( var inlining: Block.Inlining = .{ .call_block = block, .call_src = call_src, + .func = func_val.?.toIntern(), + .is_generic_instantiation = false, .has_comptime_args = for (args) |a| { if (try sema.isComptimeKnown(a)) break true; } else false, - .func = func_val.?.toIntern(), .comptime_result = undefined, .merges = .{ .block_inst = block_inst, @@ -8250,7 +8258,10 @@ fn analyzeCall( if (!inlining.has_comptime_args) { var block_it = block; while (block_it.inlining) |parent_inlining| { - if (!parent_inlining.has_comptime_args and parent_inlining.func == func_val.?.toIntern()) { + if (!parent_inlining.is_generic_instantiation and + !parent_inlining.has_comptime_args and + parent_inlining.func == func_val.?.toIntern()) + { return sema.fail(block, call_src, "inline call is recursive", .{}); } block_it = parent_inlining.call_block; @@ -19454,6 +19465,7 @@ fn analyzeRet( }; if (block.inlining) |inlining| { + assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr if (block.isComptime()) { const ret_val = try sema.resolveConstValue(block, operand_src, operand, null); inlining.comptime_result = operand; From 16481c8ef347f7fc5ffdc4636cf904b335a6dc09 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 16 May 2025 13:27:52 +0100 Subject: [PATCH 4/5] cases: update to new "called from here" notes --- .../add_overflow_in_function_evaluation.zig | 4 +--- .../compile_errors/closure_get_depends_on_failed_decl.zig | 4 +--- .../compile_errors/compile_time_division_by_zero.zig | 2 +- test/cases/compile_errors/comptime_try_non_error.zig | 4 +--- .../compile_errors/comptime_var_referenced_by_type.zig | 2 +- ...onstant_inside_comptime_function_has_compile_error.zig | 3 +-- ...or_in_comptime_call_in_container_level_initializer.zig | 4 +--- ...unction_instantiation_inherits_parent_branch_quota.zig | 4 ++-- ...tantiation_failure_in_generic_function_return_type.zig | 4 +--- .../compile_errors/missing_main_fn_in_executable.zig | 5 ++--- .../missing_struct_field_in_fn_called_at_comptime.zig | 4 +--- .../mul_overflow_in_function_evaluation.zig | 4 +--- .../negation_overflow_in_function_evaluation.zig | 4 +--- .../non-comptime-parameter-used-as-array-size.zig | 2 +- test/cases/compile_errors/private_main_fn.zig | 5 ++--- test/cases/compile_errors/recursive_inline_fn.zig | 8 ++++---- .../referring_to_a_struct_that_is_invalid.zig | 4 +--- ...rcion_error_in_generic_fn_called_from_non_fn_scope.zig | 4 +--- .../runtime_operation_in_comptime_scope.zig | 2 +- .../compile_errors/sema_src_used_after_inline_call.zig | 4 +--- .../compile_errors/stack_usage_in_naked_function.zig | 3 +-- .../compile_errors/store_to_comptime_var_through_call.zig | 2 +- .../sub_overflow_in_function_evaluation.zig | 4 +--- .../compile_errors/unreachable_executed_at_comptime.zig | 4 +--- 24 files changed, 30 insertions(+), 60 deletions(-) diff --git a/test/cases/compile_errors/add_overflow_in_function_evaluation.zig b/test/cases/compile_errors/add_overflow_in_function_evaluation.zig index ef1a016711..ccb9f531c9 100644 --- a/test/cases/compile_errors/add_overflow_in_function_evaluation.zig +++ b/test/cases/compile_errors/add_overflow_in_function_evaluation.zig @@ -8,8 +8,6 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // // :3:14: error: overflow of integer type 'u16' with value '65540' -// :1:14: note: called from here +// :1:14: note: called at comptime here diff --git a/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig b/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig index 43993eca3c..82abf03968 100644 --- a/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig +++ b/test/cases/compile_errors/closure_get_depends_on_failed_decl.zig @@ -18,9 +18,7 @@ pub export fn entry() void { } // error -// backend=stage2 -// target=native // // :11:5: error: expected 0 argument(s), found 1 // :1:12: note: function declared here -// :17:19: note: called from here +// :17:19: note: called inline here diff --git a/test/cases/compile_errors/compile_time_division_by_zero.zig b/test/cases/compile_errors/compile_time_division_by_zero.zig index 6fabf55b33..e1f05e26b2 100644 --- a/test/cases/compile_errors/compile_time_division_by_zero.zig +++ b/test/cases/compile_errors/compile_time_division_by_zero.zig @@ -10,4 +10,4 @@ export fn entry() usize { // error // // :3:16: error: division by zero here causes illegal behavior -// :1:14: note: called from here +// :1:14: note: called at comptime here diff --git a/test/cases/compile_errors/comptime_try_non_error.zig b/test/cases/compile_errors/comptime_try_non_error.zig index 935148414c..8d61df6e9a 100644 --- a/test/cases/compile_errors/comptime_try_non_error.zig +++ b/test/cases/compile_errors/comptime_try_non_error.zig @@ -11,8 +11,6 @@ pub fn bar() u8 { } // error -// backend=stage2 -// target=native // // :6:12: error: expected error union type, found 'u8' -// :2:8: note: called from here +// :2:8: note: called at comptime here diff --git a/test/cases/compile_errors/comptime_var_referenced_by_type.zig b/test/cases/compile_errors/comptime_var_referenced_by_type.zig index b9b0c756a9..1f1ba8318c 100644 --- a/test/cases/compile_errors/comptime_var_referenced_by_type.zig +++ b/test/cases/compile_errors/comptime_var_referenced_by_type.zig @@ -22,4 +22,4 @@ comptime { // // :7:16: error: captured value contains reference to comptime var // :16:30: note: 'wrapper.ptr' points to comptime var declared here -// :17:29: note: called from here +// :17:29: note: called at comptime here diff --git a/test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig b/test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig index 2afe5c4630..ffc4486bc2 100644 --- a/test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig +++ b/test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig @@ -15,9 +15,8 @@ export fn entry() void { } // error -// target=native // // :4:5: error: unreachable code // :4:25: note: control flow is diverted here // :4:25: error: aoeu -// :1:36: note: called from here +// :1:36: note: called at comptime here diff --git a/test/cases/compile_errors/error_in_comptime_call_in_container_level_initializer.zig b/test/cases/compile_errors/error_in_comptime_call_in_container_level_initializer.zig index 9b8b35ad60..597dc5e66f 100644 --- a/test/cases/compile_errors/error_in_comptime_call_in_container_level_initializer.zig +++ b/test/cases/compile_errors/error_in_comptime_call_in_container_level_initializer.zig @@ -15,8 +15,6 @@ pub export fn entry() void { } // error -// backend=stage2 -// target=native // // :9:48: error: caught unexpected error 'InvalidVersion' // :?:?: note: error returned here @@ -24,4 +22,4 @@ pub export fn entry() void { // :?:?: note: error returned here // :?:?: note: error returned here // :?:?: note: error returned here -// :12:37: note: called from here +// :12:37: note: called at comptime here diff --git a/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig b/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig index 4db27f0620..89202fea0f 100644 --- a/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig +++ b/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig @@ -25,5 +25,5 @@ fn Type(comptime n: usize) type { // // :21:16: error: evaluation exceeded 1001 backwards branches // :21:16: note: use @setEvalBranchQuota() to raise the branch limit from 1001 -// :16:34: note: called from here -// :8:15: note: called from here +// :16:34: note: called at comptime here +// :8:15: note: generic function instantiated here diff --git a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig index 4316b6fc79..96d8077085 100644 --- a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig +++ b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig @@ -36,8 +36,6 @@ pub fn is(comptime id: std.builtin.TypeId) TraitFn { } // error -// backend=stage2 -// target=native // // :8:48: error: expected type 'type', found 'bool' -// :5:21: note: called from here +// :5:21: note: generic function instantiated here diff --git a/test/cases/compile_errors/missing_main_fn_in_executable.zig b/test/cases/compile_errors/missing_main_fn_in_executable.zig index 75d5ef352f..56e87f7283 100644 --- a/test/cases/compile_errors/missing_main_fn_in_executable.zig +++ b/test/cases/compile_errors/missing_main_fn_in_executable.zig @@ -1,9 +1,8 @@ // error -// backend=stage2 // target=x86_64-linux // output_mode=Exe // // : error: root source file struct 'tmp' has no member named 'main' // : note: struct declared here -// : note: called from here -// : note: called from here +// : note: called inline here +// : note: called inline here diff --git a/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig b/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig index aaded91209..539c08afbe 100644 --- a/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig +++ b/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig @@ -10,9 +10,7 @@ comptime { } // error -// backend=stage2 -// target=native // // :5:17: error: missing struct field: b // :1:11: note: struct declared here -// :9:15: note: called from here +// :9:15: note: called at comptime here diff --git a/test/cases/compile_errors/mul_overflow_in_function_evaluation.zig b/test/cases/compile_errors/mul_overflow_in_function_evaluation.zig index c484df7540..350f2e7870 100644 --- a/test/cases/compile_errors/mul_overflow_in_function_evaluation.zig +++ b/test/cases/compile_errors/mul_overflow_in_function_evaluation.zig @@ -8,8 +8,6 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // // :3:14: error: overflow of integer type 'u16' with value '1800000' -// :1:14: note: called from here +// :1:14: note: called at comptime here diff --git a/test/cases/compile_errors/negation_overflow_in_function_evaluation.zig b/test/cases/compile_errors/negation_overflow_in_function_evaluation.zig index 208f761005..3329dcd9db 100644 --- a/test/cases/compile_errors/negation_overflow_in_function_evaluation.zig +++ b/test/cases/compile_errors/negation_overflow_in_function_evaluation.zig @@ -8,8 +8,6 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // // :3:12: error: overflow of integer type 'i8' with value '128' -// :1:14: note: called from here +// :1:14: note: called at comptime here diff --git a/test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig b/test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig index 65b2886d07..ba9806deed 100644 --- a/test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig +++ b/test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig @@ -11,4 +11,4 @@ fn makeLlamas(count: usize) [count]u8 {} // // :8:30: error: unable to resolve comptime value // :8:30: note: array length must be comptime-known -// :2:31: note: called from here +// :2:31: note: generic function instantiated here diff --git a/test/cases/compile_errors/private_main_fn.zig b/test/cases/compile_errors/private_main_fn.zig index d02f41ae9e..0e85de9f0f 100644 --- a/test/cases/compile_errors/private_main_fn.zig +++ b/test/cases/compile_errors/private_main_fn.zig @@ -1,11 +1,10 @@ fn main() void {} // error -// backend=stage2 // target=x86_64-linux // output_mode=Exe // // : error: 'main' is not marked 'pub' // :1:1: note: declared here -// : note: called from here -// : note: called from here +// : note: called inline here +// : note: called inline here diff --git a/test/cases/compile_errors/recursive_inline_fn.zig b/test/cases/compile_errors/recursive_inline_fn.zig index 7f96ad6709..d7b2fb3ded 100644 --- a/test/cases/compile_errors/recursive_inline_fn.zig +++ b/test/cases/compile_errors/recursive_inline_fn.zig @@ -31,8 +31,8 @@ pub export fn entry2() void { // error // // :5:27: error: inline call is recursive -// :12:12: note: called from here +// :12:12: note: called inline here // :24:10: error: inline call is recursive -// :20:10: note: called from here -// :16:11: note: called from here -// :28:10: note: called from here +// :20:10: note: called inline here +// :16:11: note: called inline here +// :28:10: note: called inline here diff --git a/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig b/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig index 946ead9954..4fd76792c4 100644 --- a/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig +++ b/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig @@ -11,8 +11,6 @@ fn assert(ok: bool) void { } // error -// backend=stage2 -// target=native // // :10:14: error: reached unreachable code -// :6:20: note: called from here +// :6:20: note: called at comptime here diff --git a/test/cases/compile_errors/ret_coercion_error_in_generic_fn_called_from_non_fn_scope.zig b/test/cases/compile_errors/ret_coercion_error_in_generic_fn_called_from_non_fn_scope.zig index ba17172b42..afb1e52310 100644 --- a/test/cases/compile_errors/ret_coercion_error_in_generic_fn_called_from_non_fn_scope.zig +++ b/test/cases/compile_errors/ret_coercion_error_in_generic_fn_called_from_non_fn_scope.zig @@ -6,9 +6,7 @@ comptime { } // error -// backend=stage2 -// target=native // // :2:12: error: expected type 'fn () void', found 'type' // :1:10: note: function return type declared here -// :5:12: note: called from here +// :5:12: note: called at comptime here diff --git a/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig b/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig index e3576c179b..73220b0ba4 100644 --- a/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig +++ b/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig @@ -30,7 +30,7 @@ var rt: u32 = undefined; // :10:12: note: call to function with comptime-only return type 'type' is evaluated at comptime // :13:10: note: return type declared here // :10:12: note: types are not available at runtime -// :2:8: note: called from here +// :2:8: note: called inline here // :19:8: error: unable to evaluate comptime expression // :19:5: note: operation is runtime due to this operand // :6:8: note: called at comptime from here diff --git a/test/cases/compile_errors/sema_src_used_after_inline_call.zig b/test/cases/compile_errors/sema_src_used_after_inline_call.zig index 4e3f31e410..0678113322 100644 --- a/test/cases/compile_errors/sema_src_used_after_inline_call.zig +++ b/test/cases/compile_errors/sema_src_used_after_inline_call.zig @@ -18,9 +18,7 @@ export fn entry() void { } // error -// backend=stage2 -// target=native // // :13:30: error: expected type 'u32', found 'i32' // :13:30: note: unsigned 32-bit int cannot represent all possible signed 32-bit values -// :17:33: note: called from here +// :17:33: note: called inline here diff --git a/test/cases/compile_errors/stack_usage_in_naked_function.zig b/test/cases/compile_errors/stack_usage_in_naked_function.zig index 2b6f7df9a1..aee0ea5952 100644 --- a/test/cases/compile_errors/stack_usage_in_naked_function.zig +++ b/test/cases/compile_errors/stack_usage_in_naked_function.zig @@ -36,10 +36,9 @@ export fn d() callconv(.naked) noreturn { } // error -// backend=stage2 // // :2:5: error: local variable in naked function // :10:5: error: local variable in naked function // :23:5: error: local variable in naked function // :30:13: error: local variable in naked function -// :35:12: note: called from here +// :35:12: note: called inline here diff --git a/test/cases/compile_errors/store_to_comptime_var_through_call.zig b/test/cases/compile_errors/store_to_comptime_var_through_call.zig index 0fb1246e31..5c812d5eac 100644 --- a/test/cases/compile_errors/store_to_comptime_var_through_call.zig +++ b/test/cases/compile_errors/store_to_comptime_var_through_call.zig @@ -12,4 +12,4 @@ fn incr(x: *comptime_int) void { // // :8:9: error: store to comptime variable depends on runtime condition // :3:9: note: runtime condition here -// :4:22: note: called from here +// :4:22: note: called at comptime here diff --git a/test/cases/compile_errors/sub_overflow_in_function_evaluation.zig b/test/cases/compile_errors/sub_overflow_in_function_evaluation.zig index 651ef34bdc..6949e87427 100644 --- a/test/cases/compile_errors/sub_overflow_in_function_evaluation.zig +++ b/test/cases/compile_errors/sub_overflow_in_function_evaluation.zig @@ -8,8 +8,6 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // // :3:14: error: overflow of integer type 'u16' with value '-10' -// :1:14: note: called from here +// :1:14: note: called at comptime here diff --git a/test/cases/compile_errors/unreachable_executed_at_comptime.zig b/test/cases/compile_errors/unreachable_executed_at_comptime.zig index 5131a3362d..6d2e534136 100644 --- a/test/cases/compile_errors/unreachable_executed_at_comptime.zig +++ b/test/cases/compile_errors/unreachable_executed_at_comptime.zig @@ -9,8 +9,6 @@ export fn entry() void { } // error -// backend=stage2 -// target=native // // :4:9: error: reached unreachable code -// :8:21: note: called from here +// :8:21: note: called at comptime here From 46d7e808dcef3c9f9200d6cc1ed4e3a787ba054d Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 16 May 2025 13:37:30 +0100 Subject: [PATCH 5/5] build runner: don't incorrectly omit reference traces It's incorrect to ever set `include_reference_trace` here, because the compiler has already given or not given reference traces depending on the `-freference-trace` option propagated to the compiler process by `std.Build.Step.Compile`. Perhaps in future we could make the compiler always return the reference trace when communicating over the compiler protocol; that'd be more versatile than the current behavior, because the build runner could, for instance, show a reference trace on-demand without having to even invoke the compiler. That seems really useful, since the reference trace is *often* unnecessary noise, but *sometimes* essential. However, we don't live in that world right now, so passing the option here doesn't make sense. Resolves: #23415 --- lib/compiler/build_runner.zig | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 668c60361f..e2b8683609 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -750,7 +750,7 @@ fn runStepNames( if (run.prominent_compile_errors and total_compile_errors > 0) { for (step_stack.keys()) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { - s.result_error_bundle.renderToStdErr(.{ .ttyconf = ttyconf, .include_reference_trace = (b.reference_trace orelse 0) > 0 }); + s.result_error_bundle.renderToStdErr(.{ .ttyconf = ttyconf }); } } @@ -1129,11 +1129,7 @@ fn workerMakeOneStep( defer std.debug.unlockStdErr(); const gpa = b.allocator; - const options: std.zig.ErrorBundle.RenderOptions = .{ - .ttyconf = run.ttyconf, - .include_reference_trace = (b.reference_trace orelse 0) > 0, - }; - printErrorMessages(gpa, s, options, run.stderr, run.prominent_compile_errors) catch {}; + printErrorMessages(gpa, s, .{ .ttyconf = run.ttyconf }, run.stderr, run.prominent_compile_errors) catch {}; } handle_result: {