stage2: implement referenced by trace for error messages

Closes #7668
Closes #12141
This commit is contained in:
Veikka Tuominen 2022-09-09 18:55:58 +03:00
parent 5e4483fff8
commit 31daea74d2
8 changed files with 197 additions and 40 deletions

View File

@ -185,6 +185,16 @@ pub fn main() !void {
builder.use_stage1 = true; builder.use_stage1 = true;
} else if (mem.eql(u8, arg, "-fno-stage1")) { } else if (mem.eql(u8, arg, "-fno-stage1")) {
builder.use_stage1 = false; builder.use_stage1 = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
builder.reference_trace = 256;
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
const num = arg["-freference-trace=".len..];
builder.reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
process.exit(1);
};
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
builder.reference_trace = null;
} else if (mem.eql(u8, arg, "--")) { } else if (mem.eql(u8, arg, "--")) {
builder.args = argsRest(args, arg_idx); builder.args = argsRest(args, arg_idx);
break; break;
@ -308,6 +318,8 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
\\Advanced Options: \\Advanced Options:
\\ -fstage1 Force using bootstrap compiler as the codegen backend \\ -fstage1 Force using bootstrap compiler as the codegen backend
\\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
\\ --build-file [file] Override path to build.zig \\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to local Zig cache directory \\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory

View File

@ -45,6 +45,7 @@ pub const Builder = struct {
/// The purpose of executing the command is for a human to read compile errors from the terminal /// The purpose of executing the command is for a human to read compile errors from the terminal
prominent_compile_errors: bool, prominent_compile_errors: bool,
color: enum { auto, on, off } = .auto, color: enum { auto, on, off } = .auto,
reference_trace: ?u32 = null,
use_stage1: ?bool = null, use_stage1: ?bool = null,
invalid_user_input: bool, invalid_user_input: bool,
zig_exe: []const u8, zig_exe: []const u8,
@ -2475,6 +2476,10 @@ pub const LibExeObjStep = struct {
try zig_args.append(@tagName(builder.color)); try zig_args.append(@tagName(builder.color));
} }
if (builder.reference_trace) |some| {
try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
}
if (self.use_stage1) |stage1| { if (self.use_stage1) |stage1| {
if (stage1) { if (stage1) {
try zig_args.append("-fstage1"); try zig_args.append("-fstage1");

View File

@ -154,6 +154,10 @@ owned_link_dir: ?std.fs.Dir,
/// Don't use this for anything other than stage1 compatibility. /// Don't use this for anything other than stage1 compatibility.
color: Color = .auto, color: Color = .auto,
/// How many lines of reference trace should be included per compile error.
/// Null means only show snippet on first error.
reference_trace: ?u32 = null,
libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default,
/// This mutex guards all `Compilation` mutable state. /// This mutex guards all `Compilation` mutable state.
@ -348,6 +352,7 @@ pub const AllErrors = struct {
/// Does not include the trailing newline. /// Does not include the trailing newline.
source_line: ?[]const u8, source_line: ?[]const u8,
notes: []Message = &.{}, notes: []Message = &.{},
reference_trace: []Message = &.{},
/// Splits the error message up into lines to properly indent them /// Splits the error message up into lines to properly indent them
/// to allow for long, good-looking error messages. /// to allow for long, good-looking error messages.
@ -447,6 +452,34 @@ pub const AllErrors = struct {
for (src.notes) |note| { for (src.notes) |note| {
try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent); try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent);
} }
if (src.reference_trace.len != 0) {
ttyconf.setColor(stderr, .Reset);
ttyconf.setColor(stderr, .Dim);
try stderr.print("referenced by:\n", .{});
for (src.reference_trace) |reference| {
switch (reference) {
.src => |ref_src| try stderr.print(" {s}: {s}:{d}:{d}\n", .{
ref_src.msg,
ref_src.src_path,
ref_src.line + 1,
ref_src.column + 1,
}),
.plain => |plain| if (plain.count != 0) {
try stderr.print(
" {d} reference(s) hidden; use '-freference-trace={d}' to see all references\n",
.{ plain.count, plain.count + src.reference_trace.len - 1 },
);
} else {
try stderr.print(
" remaining reference traces hidden; use '-freference-trace' to see all reference traces\n",
.{},
);
},
}
}
try stderr.writeByte('\n');
ttyconf.setColor(stderr, .Reset);
}
}, },
.plain => |plain| { .plain => |plain| {
ttyconf.setColor(stderr, color); ttyconf.setColor(stderr, color);
@ -572,6 +605,32 @@ pub const AllErrors = struct {
}); });
return; return;
} }
const reference_trace = try allocator.alloc(Message, module_err_msg.reference_trace.len);
for (reference_trace) |*reference, i| {
const module_reference = module_err_msg.reference_trace[i];
if (module_reference.hidden != 0) {
reference.* = .{ .plain = .{ .msg = undefined, .count = module_reference.hidden } };
break;
} else if (module_reference.decl == null) {
reference.* = .{ .plain = .{ .msg = undefined, .count = 0 } };
break;
}
const source = try module_reference.src_loc.file_scope.getSource(module.gpa);
const span = try module_reference.src_loc.span(module.gpa);
const loc = std.zig.findLineColumn(source.bytes, span.main);
const file_path = try module_reference.src_loc.file_scope.fullPath(allocator);
reference.* = .{
.src = .{
.src_path = file_path,
.msg = try allocator.dupe(u8, std.mem.sliceTo(module_reference.decl.?, 0)),
.span = span,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.source_line = null,
},
};
}
const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator); const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);
try errors.append(.{ try errors.append(.{
.src = .{ .src = .{
@ -581,6 +640,7 @@ pub const AllErrors = struct {
.line = @intCast(u32, err_loc.line), .line = @intCast(u32, err_loc.line),
.column = @intCast(u32, err_loc.column), .column = @intCast(u32, err_loc.column),
.notes = notes_buf[0..note_i], .notes = notes_buf[0..note_i],
.reference_trace = reference_trace,
.source_line = try allocator.dupe(u8, err_loc.source_line), .source_line = try allocator.dupe(u8, err_loc.source_line),
}, },
}); });
@ -929,6 +989,7 @@ pub const InitOptions = struct {
clang_preprocessor_mode: ClangPreprocessorMode = .no, clang_preprocessor_mode: ClangPreprocessorMode = .no,
/// This is for stage1 and should be deleted upon completion of self-hosting. /// This is for stage1 and should be deleted upon completion of self-hosting.
color: Color = .auto, color: Color = .auto,
reference_trace: ?u32 = null,
test_filter: ?[]const u8 = null, test_filter: ?[]const u8 = null,
test_name_prefix: ?[]const u8 = null, test_name_prefix: ?[]const u8 = null,
subsystem: ?std.Target.SubSystem = null, subsystem: ?std.Target.SubSystem = null,
@ -1838,6 +1899,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.disable_c_depfile = options.disable_c_depfile, .disable_c_depfile = options.disable_c_depfile,
.owned_link_dir = owned_link_dir, .owned_link_dir = owned_link_dir,
.color = options.color, .color = options.color,
.reference_trace = options.reference_trace,
.time_report = options.time_report, .time_report = options.time_report,
.stack_report = options.stack_report, .stack_report = options.stack_report,
.unwind_tables = unwind_tables, .unwind_tables = unwind_tables,

View File

@ -166,6 +166,11 @@ decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
referencer: Decl.Index,
src: LazySrcLoc,
}) = .{},
pub const StringLiteralContext = struct { pub const StringLiteralContext = struct {
bytes: *std.ArrayListUnmanaged(u8), bytes: *std.ArrayListUnmanaged(u8),
@ -2084,6 +2089,13 @@ pub const ErrorMsg = struct {
src_loc: SrcLoc, src_loc: SrcLoc,
msg: []const u8, msg: []const u8,
notes: []ErrorMsg = &.{}, notes: []ErrorMsg = &.{},
reference_trace: []Trace = &.{},
pub const Trace = struct {
decl: ?[*:0]const u8,
src_loc: SrcLoc,
hidden: u32 = 0,
};
pub fn create( pub fn create(
gpa: Allocator, gpa: Allocator,
@ -2122,8 +2134,15 @@ pub const ErrorMsg = struct {
} }
gpa.free(err_msg.notes); gpa.free(err_msg.notes);
gpa.free(err_msg.msg); gpa.free(err_msg.msg);
gpa.free(err_msg.reference_trace);
err_msg.* = undefined; err_msg.* = undefined;
} }
pub fn clearTrace(err_msg: *ErrorMsg, gpa: Allocator) void {
if (err_msg.reference_trace.len == 0) return;
gpa.free(err_msg.reference_trace);
err_msg.reference_trace = &.{};
}
}; };
/// Canonical reference to a position within a source file. /// Canonical reference to a position within a source file.
@ -3411,6 +3430,7 @@ pub fn deinit(mod: *Module) void {
mod.decls_free_list.deinit(gpa); mod.decls_free_list.deinit(gpa);
mod.allocated_decls.deinit(gpa); mod.allocated_decls.deinit(gpa);
mod.global_assembly.deinit(gpa); mod.global_assembly.deinit(gpa);
mod.reference_table.deinit(gpa);
mod.string_literal_table.deinit(gpa); mod.string_literal_table.deinit(gpa);
mod.string_literal_bytes.deinit(gpa); mod.string_literal_bytes.deinit(gpa);

View File

@ -111,6 +111,7 @@ const crash_report = @import("crash_report.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
pub const default_branch_quota = 1000; pub const default_branch_quota = 1000;
pub const default_reference_trace_len = 2;
pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);
@ -1958,13 +1959,53 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
} }
const mod = sema.mod; const mod = sema.mod;
{ ref: {
errdefer err_msg.destroy(mod.gpa); errdefer err_msg.destroy(mod.gpa);
if (err_msg.src_loc.lazy == .unneeded) { if (err_msg.src_loc.lazy == .unneeded) {
return error.NeededSourceLocation; return error.NeededSourceLocation;
} }
try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1); try mod.failed_files.ensureUnusedCapacity(mod.gpa, 1);
const max_references = blk: {
if (sema.mod.comp.reference_trace) |num| break :blk num;
// Do not add multiple traces without explicit request.
if (sema.mod.failed_decls.count() != 0) break :ref;
break :blk default_reference_trace_len;
};
var referenced_by = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;
var reference_stack = std.ArrayList(Module.ErrorMsg.Trace).init(sema.gpa);
defer reference_stack.deinit();
// Avoid infinite loops.
var seen = std.AutoHashMap(Module.Decl.Index, void).init(sema.gpa);
defer seen.deinit();
var cur_reference_trace: u32 = 0;
while (sema.mod.reference_table.get(referenced_by)) |ref| : (cur_reference_trace += 1) {
const gop = try seen.getOrPut(ref.referencer);
if (gop.found_existing) break;
if (cur_reference_trace < max_references) {
const decl = sema.mod.declPtr(ref.referencer);
try reference_stack.append(.{ .decl = decl.name, .src_loc = ref.src.toSrcLoc(decl) });
}
referenced_by = ref.referencer;
}
if (sema.mod.comp.reference_trace == null and cur_reference_trace > 0) {
try reference_stack.append(.{
.decl = null,
.src_loc = undefined,
.hidden = 0,
});
} else if (cur_reference_trace > max_references) {
try reference_stack.append(.{
.decl = undefined,
.src_loc = undefined,
.hidden = cur_reference_trace - max_references,
});
}
err_msg.reference_trace = reference_stack.toOwnedSlice();
} }
if (sema.owner_func) |func| { if (sema.owner_func) |func| {
func.state = .sema_failure; func.state = .sema_failure;
@ -5366,14 +5407,8 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const src = inst_data.src(); const src = inst_data.src();
const decl_name = inst_data.get(sema.code); const decl_name = inst_data.get(sema.code);
const decl_index = try sema.lookupIdentifier(block, src, decl_name); const decl_index = try sema.lookupIdentifier(block, src, decl_name);
return sema.analyzeDeclRef(decl_index) catch |err| switch (err) { try sema.addReferencedBy(block, src, decl_index);
error.AnalysisFail => { return sema.analyzeDeclRef(decl_index);
const msg = sema.err orelse return err;
try sema.errNote(block, src, msg, "referenced here", .{});
return err;
},
else => return err,
};
} }
fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -6107,6 +6142,7 @@ fn analyzeCall(
error.AnalysisFail => { error.AnalysisFail => {
const err_msg = sema.err orelse return err; const err_msg = sema.err orelse return err;
try sema.errNote(block, call_src, err_msg, "called from here", .{}); try sema.errNote(block, call_src, err_msg, "called from here", .{});
err_msg.clearTrace(sema.gpa);
return err; return err;
}, },
else => |e| return e, else => |e| return e,
@ -21741,14 +21777,8 @@ fn namespaceLookupRef(
decl_name: []const u8, decl_name: []const u8,
) CompileError!?Air.Inst.Ref { ) CompileError!?Air.Inst.Ref {
const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null; const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
return sema.analyzeDeclRef(decl) catch |err| switch (err) { try sema.addReferencedBy(block, src, decl);
error.AnalysisFail => { return try sema.analyzeDeclRef(decl);
const msg = sema.err orelse return err;
try sema.errNote(block, src, msg, "referenced here", .{});
return err;
},
else => return err,
};
} }
fn namespaceLookupVal( fn namespaceLookupVal(
@ -26001,14 +26031,8 @@ fn analyzeDeclVal(
if (sema.decl_val_table.get(decl_index)) |result| { if (sema.decl_val_table.get(decl_index)) |result| {
return result; return result;
} }
const decl_ref = sema.analyzeDeclRef(decl_index) catch |err| switch (err) { try sema.addReferencedBy(block, src, decl_index);
error.AnalysisFail => { const decl_ref = try sema.analyzeDeclRef(decl_index);
const msg = sema.err orelse return err;
try sema.errNote(block, src, msg, "referenced here", .{});
return err;
},
else => return err,
};
const result = try sema.analyzeLoad(block, src, decl_ref, src); const result = try sema.analyzeLoad(block, src, decl_ref, src);
if (Air.refToIndex(result)) |index| { if (Air.refToIndex(result)) |index| {
if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) { if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {
@ -26018,6 +26042,19 @@ fn analyzeDeclVal(
return result; return result;
} }
fn addReferencedBy(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
decl_index: Decl.Index,
) !void {
if (sema.mod.comp.reference_trace == @as(u32, 0)) return;
try sema.mod.reference_table.put(sema.gpa, decl_index, .{
.referencer = block.src_decl,
.src = src,
});
}
fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
const decl = sema.mod.declPtr(decl_index); const decl = sema.mod.declPtr(decl_index);
if (decl.analysis == .in_progress) { if (decl.analysis == .in_progress) {

View File

@ -396,6 +396,8 @@ const usage_build_generic =
\\ -fno-Clang Prevent using Clang as the C/C++ compilation backend \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend
\\ -fstage1 Force using bootstrap compiler as the codegen backend \\ -fstage1 Force using bootstrap compiler as the codegen backend
\\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
\\ -fsingle-threaded Code assumes there is only one thread \\ -fsingle-threaded Code assumes there is only one thread
\\ -fno-single-threaded Code may not assume there is only one thread \\ -fno-single-threaded Code may not assume there is only one thread
\\ -fbuiltin Enable implicit builtin knowledge of functions \\ -fbuiltin Enable implicit builtin knowledge of functions
@ -742,6 +744,7 @@ fn buildOutputType(
var headerpad_size: ?u32 = null; var headerpad_size: ?u32 = null;
var headerpad_max_install_names: bool = false; var headerpad_max_install_names: bool = false;
var dead_strip_dylibs: bool = false; var dead_strip_dylibs: bool = false;
var reference_trace: ?u32 = null;
// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
// This array is populated by zig cc frontend and then has to be converted to zig-style // This array is populated by zig cc frontend and then has to be converted to zig-style
@ -928,14 +931,14 @@ fn buildOutputType(
fatal("expected parameter after {s}", .{arg}); fatal("expected parameter after {s}", .{arg});
}; };
stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse stack size '{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "--image-base")) { } else if (mem.eql(u8, arg, "--image-base")) {
const next_arg = args_iter.next() orelse { const next_arg = args_iter.next() orelse {
fatal("expected parameter after {s}", .{arg}); fatal("expected parameter after {s}", .{arg});
}; };
image_base_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { image_base_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse image base override '{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "--name")) { } else if (mem.eql(u8, arg, "--name")) {
provided_name = args_iter.next() orelse { provided_name = args_iter.next() orelse {
@ -984,7 +987,7 @@ fn buildOutputType(
fatal("expected parameter after {s}", .{arg}); fatal("expected parameter after {s}", .{arg});
}; };
pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-search_paths_first")) { } else if (mem.eql(u8, arg, "-search_paths_first")) {
search_strategy = .paths_first; search_strategy = .paths_first;
@ -995,7 +998,7 @@ fn buildOutputType(
fatal("expected parameter after {s}", .{arg}); fatal("expected parameter after {s}", .{arg});
}; };
headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse headerpat size '{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
headerpad_max_install_names = true; headerpad_max_install_names = true;
@ -1214,6 +1217,15 @@ fn buildOutputType(
use_stage1 = true; use_stage1 = true;
} else if (mem.eql(u8, arg, "-fno-stage1")) { } else if (mem.eql(u8, arg, "-fno-stage1")) {
use_stage1 = false; use_stage1 = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
reference_trace = 256;
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
const num = arg["-freference-trace=".len..];
reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
};
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
reference_trace = null;
} else if (mem.eql(u8, arg, "-rdynamic")) { } else if (mem.eql(u8, arg, "-rdynamic")) {
rdynamic = true; rdynamic = true;
} else if (mem.eql(u8, arg, "-fsoname")) { } else if (mem.eql(u8, arg, "-fsoname")) {
@ -1785,11 +1797,11 @@ fn buildOutputType(
fatal("expected linker arg after '{s}'", .{arg}); fatal("expected linker arg after '{s}'", .{arg});
} }
linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| { linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse optimization level '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
} else if (mem.startsWith(u8, arg, "-O")) { } else if (mem.startsWith(u8, arg, "-O")) {
linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| { linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-pagezero_size")) { } else if (mem.eql(u8, arg, "-pagezero_size")) {
i += 1; i += 1;
@ -1798,7 +1810,7 @@ fn buildOutputType(
} }
const next_arg = linker_args.items[i]; const next_arg = linker_args.items[i];
pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-headerpad")) { } else if (mem.eql(u8, arg, "-headerpad")) {
i += 1; i += 1;
@ -1807,7 +1819,7 @@ fn buildOutputType(
} }
const next_arg = linker_args.items[i]; const next_arg = linker_args.items[i];
headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
headerpad_max_install_names = true; headerpad_max_install_names = true;
@ -1899,7 +1911,7 @@ fn buildOutputType(
fatal("expected linker arg after '{s}'", .{arg}); fatal("expected linker arg after '{s}'", .{arg});
} }
version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse major image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
have_version = true; have_version = true;
} else if (mem.eql(u8, arg, "--minor-image-version")) { } else if (mem.eql(u8, arg, "--minor-image-version")) {
@ -1908,7 +1920,7 @@ fn buildOutputType(
fatal("expected linker arg after '{s}'", .{arg}); fatal("expected linker arg after '{s}'", .{arg});
} }
version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse minor image version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
have_version = true; have_version = true;
} else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) { } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
@ -1923,7 +1935,7 @@ fn buildOutputType(
fatal("expected linker arg after '{s}'", .{arg}); fatal("expected linker arg after '{s}'", .{arg});
} }
stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse stack size override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "--image-base")) { } else if (mem.eql(u8, arg, "--image-base")) {
i += 1; i += 1;
@ -1931,7 +1943,7 @@ fn buildOutputType(
fatal("expected linker arg after '{s}'", .{arg}); fatal("expected linker arg after '{s}'", .{arg});
} }
image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse image base override '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
i += 1; i += 1;
@ -1984,7 +1996,7 @@ fn buildOutputType(
linker_args.items[i], linker_args.items[i],
10, 10,
) catch |err| { ) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse major subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "--minor-subsystem-version")) { } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
i += 1; i += 1;
@ -1997,7 +2009,7 @@ fn buildOutputType(
linker_args.items[i], linker_args.items[i],
10, 10,
) catch |err| { ) catch |err| {
fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); fatal("unable to parse minor subsystem version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
}; };
} else if (mem.eql(u8, arg, "-framework")) { } else if (mem.eql(u8, arg, "-framework")) {
i += 1; i += 1;
@ -2981,6 +2993,7 @@ fn buildOutputType(
.headerpad_size = headerpad_size, .headerpad_size = headerpad_size,
.headerpad_max_install_names = headerpad_max_install_names, .headerpad_max_install_names = headerpad_max_install_names,
.dead_strip_dylibs = dead_strip_dylibs, .dead_strip_dylibs = dead_strip_dylibs,
.reference_trace = reference_trace,
}) catch |err| switch (err) { }) catch |err| switch (err) {
error.LibCUnavailable => { error.LibCUnavailable => {
const target = target_info.target; const target = target_info.target;
@ -3740,6 +3753,8 @@ pub const usage_build =
\\Options: \\Options:
\\ -fstage1 Force using bootstrap compiler as the codegen backend \\ -fstage1 Force using bootstrap compiler as the codegen backend
\\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
\\ --build-file [file] Override path to build.zig \\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to local Zig cache directory \\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory \\ --global-cache-dir [path] Override path to global Zig cache directory
@ -3812,6 +3827,12 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
} else if (mem.eql(u8, arg, "-fno-stage1")) { } else if (mem.eql(u8, arg, "-fno-stage1")) {
use_stage1 = false; use_stage1 = false;
try child_argv.append(arg); try child_argv.append(arg);
} else if (mem.eql(u8, arg, "-freference-trace")) {
try child_argv.append(arg);
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
try child_argv.append(arg);
} else if (mem.eql(u8, arg, "-fno-reference-trace")) {
try child_argv.append(arg);
} }
} }
try child_argv.append(arg); try child_argv.append(arg);

View File

@ -1548,6 +1548,7 @@ pub const TestContext = struct {
.self_exe_path = std.testing.zig_exe_path, .self_exe_path = std.testing.zig_exe_path,
// TODO instead of turning off color, pass in a std.Progress.Node // TODO instead of turning off color, pass in a std.Progress.Node
.color = .off, .color = .off,
.reference_trace = 0,
// TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in // TODO: force self-hosted linkers with stage2 backend to avoid LLD creeping in
// until the auto-select mechanism deems them worthy // until the auto-select mechanism deems them worthy
.use_lld = switch (case.backend) { .use_lld = switch (case.backend) {

View File

@ -10,4 +10,3 @@ export fn entry() void {
// target=native // target=native
// //
// :1:1: error: dependency loop detected // :1:1: error: dependency loop detected
// :2:19: note: referenced here