mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 14:55:25 +00:00
wasm linker: improve error messages by making source locations more lazy
This commit is contained in:
parent
7d224516c4
commit
4fccb5ae7a
@ -11,6 +11,11 @@ string_bytes: []const u8,
|
||||
/// The first thing in this array is an `ErrorMessageList`.
|
||||
extra: []const u32,
|
||||
|
||||
/// Index into `string_bytes`.
|
||||
pub const String = u32;
|
||||
/// Index into `string_bytes`, or null.
|
||||
pub const OptionalString = u32;
|
||||
|
||||
/// Special encoding when there are no errors.
|
||||
pub const empty: ErrorBundle = .{
|
||||
.string_bytes = &.{},
|
||||
@ -33,14 +38,13 @@ pub const ErrorMessageList = struct {
|
||||
len: u32,
|
||||
start: u32,
|
||||
/// null-terminated string index. 0 means no compile log text.
|
||||
compile_log_text: u32,
|
||||
compile_log_text: OptionalString,
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// * ReferenceTrace for each reference_trace_len
|
||||
pub const SourceLocation = struct {
|
||||
/// null terminated string index
|
||||
src_path: u32,
|
||||
src_path: String,
|
||||
line: u32,
|
||||
column: u32,
|
||||
/// byte offset of starting token
|
||||
@ -49,17 +53,15 @@ pub const SourceLocation = struct {
|
||||
span_main: u32,
|
||||
/// byte offset of end of last token
|
||||
span_end: u32,
|
||||
/// null terminated string index, possibly null.
|
||||
/// Does not include the trailing newline.
|
||||
source_line: u32 = 0,
|
||||
source_line: OptionalString = 0,
|
||||
reference_trace_len: u32 = 0,
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// * MessageIndex for each notes_len.
|
||||
pub const ErrorMessage = struct {
|
||||
/// null terminated string index
|
||||
msg: u32,
|
||||
msg: String,
|
||||
/// Usually one, but incremented for redundant messages.
|
||||
count: u32 = 1,
|
||||
src_loc: SourceLocationIndex = .none,
|
||||
@ -71,7 +73,7 @@ pub const ReferenceTrace = struct {
|
||||
/// Except for the sentinel ReferenceTrace element, in which case:
|
||||
/// * 0 means remaining references hidden
|
||||
/// * >0 means N references hidden
|
||||
decl_name: u32,
|
||||
decl_name: String,
|
||||
/// Index into extra of a SourceLocation
|
||||
/// If this is 0, this is the sentinel ReferenceTrace element.
|
||||
src_loc: SourceLocationIndex,
|
||||
@ -138,7 +140,7 @@ fn extraData(eb: ErrorBundle, comptime T: type, index: usize) struct { data: T,
|
||||
}
|
||||
|
||||
/// Given an index into `string_bytes` returns the null-terminated string found there.
|
||||
pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 {
|
||||
pub fn nullTerminatedString(eb: ErrorBundle, index: String) [:0]const u8 {
|
||||
const string_bytes = eb.string_bytes;
|
||||
var end: usize = index;
|
||||
while (string_bytes[end] != 0) {
|
||||
@ -384,18 +386,18 @@ pub const Wip = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!u32 {
|
||||
pub fn addString(wip: *Wip, s: []const u8) Allocator.Error!String {
|
||||
const gpa = wip.gpa;
|
||||
const index: u32 = @intCast(wip.string_bytes.items.len);
|
||||
const index: String = @intCast(wip.string_bytes.items.len);
|
||||
try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
|
||||
wip.string_bytes.appendSliceAssumeCapacity(s);
|
||||
wip.string_bytes.appendAssumeCapacity(0);
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!u32 {
|
||||
pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) Allocator.Error!String {
|
||||
const gpa = wip.gpa;
|
||||
const index: u32 = @intCast(wip.string_bytes.items.len);
|
||||
const index: String = @intCast(wip.string_bytes.items.len);
|
||||
try wip.string_bytes.writer(gpa).print(fmt, args);
|
||||
try wip.string_bytes.append(gpa, 0);
|
||||
return index;
|
||||
|
||||
@ -3291,7 +3291,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
||||
}));
|
||||
}
|
||||
|
||||
try comp.link_diags.addMessagesToBundle(&bundle);
|
||||
try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
|
||||
|
||||
if (comp.zcu) |zcu| {
|
||||
if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
|
||||
|
||||
29
src/link.zig
29
src/link.zig
@ -38,6 +38,11 @@ pub const Diags = struct {
|
||||
flags: Flags,
|
||||
lld: std.ArrayListUnmanaged(Lld),
|
||||
|
||||
pub const SourceLocation = union(enum) {
|
||||
none,
|
||||
wasm: File.Wasm.SourceLocation,
|
||||
};
|
||||
|
||||
pub const Flags = packed struct {
|
||||
no_entry_point_found: bool = false,
|
||||
missing_libc: bool = false,
|
||||
@ -70,9 +75,25 @@ pub const Diags = struct {
|
||||
};
|
||||
|
||||
pub const Msg = struct {
|
||||
source_location: SourceLocation = .none,
|
||||
msg: []const u8,
|
||||
notes: []Msg = &.{},
|
||||
|
||||
fn string(
|
||||
msg: *const Msg,
|
||||
bundle: *std.zig.ErrorBundle.Wip,
|
||||
base: ?*File,
|
||||
) Allocator.Error!std.zig.ErrorBundle.String {
|
||||
return switch (msg.source_location) {
|
||||
.none => try bundle.addString(msg.msg),
|
||||
.wasm => |sl| {
|
||||
dev.check(.wasm_linker);
|
||||
const wasm = base.?.cast(.wasm).?;
|
||||
return sl.string(msg.msg, bundle, wasm);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Msg, gpa: Allocator) void {
|
||||
for (self.notes) |*note| note.deinit(gpa);
|
||||
gpa.free(self.notes);
|
||||
@ -326,16 +347,16 @@ pub const Diags = struct {
|
||||
diags.flags.alloc_failure_occurred = true;
|
||||
}
|
||||
|
||||
pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
|
||||
pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip, base: ?*File) Allocator.Error!void {
|
||||
for (diags.msgs.items) |link_err| {
|
||||
try bundle.addRootErrorMessage(.{
|
||||
.msg = try bundle.addString(link_err.msg),
|
||||
.msg = try link_err.string(bundle, base),
|
||||
.notes_len = @intCast(link_err.notes.len),
|
||||
});
|
||||
const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
|
||||
for (link_err.notes, 0..) |note, i| {
|
||||
bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
|
||||
.msg = try bundle.addString(note.msg),
|
||||
.msg = try note.string(bundle, base),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -2224,7 +2245,7 @@ fn resolvePathInputLib(
|
||||
try wip_errors.init(gpa);
|
||||
defer wip_errors.deinit();
|
||||
|
||||
try diags.addMessagesToBundle(&wip_errors);
|
||||
try diags.addMessagesToBundle(&wip_errors, null);
|
||||
|
||||
var error_bundle = try wip_errors.toOwnedBundle("");
|
||||
defer error_bundle.deinit(gpa);
|
||||
|
||||
@ -465,17 +465,33 @@ pub const SourceLocation = enum(u32) {
|
||||
|
||||
pub fn addNote(
|
||||
sl: SourceLocation,
|
||||
wasm: *Wasm,
|
||||
err: *link.Diags.ErrorWithNotes,
|
||||
comptime f: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
switch (sl.unpack(wasm)) {
|
||||
.none => err.addNote(f, args),
|
||||
.zig_object_nofile => err.addNote("zig compilation unit: " ++ f, args),
|
||||
.object_index => |i| err.addNote("{}: " ++ f, .{i.ptr(wasm).path} ++ args),
|
||||
err.addNote(f, args);
|
||||
const err_msg = &err.diags.msgs.items[err.index];
|
||||
err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl };
|
||||
}
|
||||
|
||||
pub fn string(
|
||||
sl: SourceLocation,
|
||||
msg: []const u8,
|
||||
bundle: *std.zig.ErrorBundle.Wip,
|
||||
wasm: *const Wasm,
|
||||
) Allocator.Error!std.zig.ErrorBundle.String {
|
||||
return switch (sl.unpack(wasm)) {
|
||||
.none => try bundle.addString(msg),
|
||||
.zig_object_nofile => try bundle.printString("zig compilation unit: {s}", .{msg}),
|
||||
.object_index => |i| {
|
||||
const obj = i.ptr(wasm);
|
||||
return if (obj.archive_member_name.slice(wasm)) |obj_name|
|
||||
try bundle.printString("{} ({s}): {s}", .{ obj.path, std.fs.path.basename(obj_name), msg })
|
||||
else
|
||||
try bundle.printString("{}: {s}", .{ obj.path, msg });
|
||||
},
|
||||
.source_location_index => @panic("TODO"),
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -3679,6 +3695,12 @@ fn defaultEntrySymbolName(
|
||||
};
|
||||
}
|
||||
|
||||
pub fn internOptionalString(wasm: *Wasm, optional_bytes: ?[]const u8) Allocator.Error!OptionalString {
|
||||
const bytes = optional_bytes orelse return .none;
|
||||
const string = try internString(wasm, bytes);
|
||||
return string.toOptional();
|
||||
}
|
||||
|
||||
pub fn internString(wasm: *Wasm, bytes: []const u8) Allocator.Error!String {
|
||||
assert(mem.indexOfScalar(u8, bytes, 0) == null);
|
||||
wasm.string_bytes_lock.lock();
|
||||
|
||||
@ -17,7 +17,7 @@ path: Path,
|
||||
/// For error reporting purposes only.
|
||||
/// If this represents an object in an archive, it's the basename of the
|
||||
/// object, and path refers to the archive.
|
||||
archive_member_name: ?[]const u8,
|
||||
archive_member_name: Wasm.OptionalString,
|
||||
/// Represents the function ID that must be called on startup.
|
||||
/// This is `null` by default as runtimes may determine the startup
|
||||
/// function themselves. This is essentially legacy.
|
||||
@ -965,21 +965,21 @@ pub fn parse(
|
||||
if (gop.value_ptr.type != fn_ty_index) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching function signatures", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "imported as {} here", .{
|
||||
gop.value_ptr.source_location.addNote(&err, "imported as {} here", .{
|
||||
gop.value_ptr.type.fmt(wasm),
|
||||
});
|
||||
err.addNote("{}: imported as {} here", .{ path, fn_ty_index.fmt(wasm) });
|
||||
source_location.addNote(&err, "imported as {} here", .{fn_ty_index.fmt(wasm)});
|
||||
continue;
|
||||
}
|
||||
if (gop.value_ptr.module_name != ptr.module_name.toOptional()) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
|
||||
if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});
|
||||
gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name});
|
||||
} else {
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "no module here", .{});
|
||||
gop.value_ptr.source_location.addNote(&err, "no module here", .{});
|
||||
}
|
||||
err.addNote("{}: module '{s}' here", .{ path, ptr.module_name.slice(wasm) });
|
||||
source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
|
||||
continue;
|
||||
}
|
||||
if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
|
||||
@ -1008,18 +1008,18 @@ pub fn parse(
|
||||
if (ptr.valtype != existing_ty.valtype) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching global types", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "type {s} here", .{@tagName(existing_ty.valtype)});
|
||||
err.addNote("{}: type {s} here", .{ path, @tagName(ptr.valtype) });
|
||||
gop.value_ptr.source_location.addNote(&err, "type {s} here", .{@tagName(existing_ty.valtype)});
|
||||
source_location.addNote(&err, "type {s} here", .{@tagName(ptr.valtype)});
|
||||
continue;
|
||||
}
|
||||
if (ptr.mutable != existing_ty.mutable) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching global mutability", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{
|
||||
gop.value_ptr.source_location.addNote(&err, "{s} here", .{
|
||||
if (existing_ty.mutable) "mutable" else "not mutable",
|
||||
});
|
||||
err.addNote("{}: {s} here", .{
|
||||
path, if (ptr.mutable) "mutable" else "not mutable",
|
||||
source_location.addNote(&err, "{s} here", .{
|
||||
if (ptr.mutable) "mutable" else "not mutable",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@ -1027,11 +1027,11 @@ pub fn parse(
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
|
||||
if (gop.value_ptr.module_name.slice(wasm)) |module_name| {
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{module_name});
|
||||
gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{module_name});
|
||||
} else {
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "no module here", .{});
|
||||
gop.value_ptr.source_location.addNote(&err, "no module here", .{});
|
||||
}
|
||||
err.addNote("{}: module '{s}' here", .{ path, ptr.module_name.slice(wasm) });
|
||||
source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
|
||||
continue;
|
||||
}
|
||||
if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
|
||||
@ -1063,17 +1063,17 @@ pub fn parse(
|
||||
if (ptr.ref_type != existing_reftype) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching table reftypes", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "{s} here", .{@tagName(existing_reftype)});
|
||||
err.addNote("{}: {s} here", .{ path, @tagName(ptr.ref_type) });
|
||||
gop.value_ptr.source_location.addNote(&err, "{s} here", .{@tagName(existing_reftype)});
|
||||
source_location.addNote(&err, "{s} here", .{@tagName(ptr.ref_type)});
|
||||
continue;
|
||||
}
|
||||
if (gop.value_ptr.module_name != ptr.module_name) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol '{s}' mismatching module names", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "module '{s}' here", .{
|
||||
gop.value_ptr.source_location.addNote(&err, "module '{s}' here", .{
|
||||
gop.value_ptr.module_name.slice(wasm),
|
||||
});
|
||||
err.addNote("{}: module '{s}' here", .{ path, ptr.module_name.slice(wasm) });
|
||||
source_location.addNote(&err, "module '{s}' here", .{ptr.module_name.slice(wasm)});
|
||||
continue;
|
||||
}
|
||||
if (symbol.flags.binding == .strong) gop.value_ptr.flags.binding = .strong;
|
||||
@ -1105,11 +1105,11 @@ pub fn parse(
|
||||
if (gop.value_ptr.type != ptr.type_index) {
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("function signature mismatch: {s}", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "exported as {} here", .{
|
||||
gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{
|
||||
ptr.type_index.fmt(wasm),
|
||||
});
|
||||
const word = if (gop.value_ptr.resolution == .unresolved) "imported" else "exported";
|
||||
err.addNote("{}: {s} as {} here", .{ path, word, gop.value_ptr.type.fmt(wasm) });
|
||||
source_location.addNote(&err, "{s} as {} here", .{ word, gop.value_ptr.type.fmt(wasm) });
|
||||
continue;
|
||||
}
|
||||
if (gop.value_ptr.resolution == .unresolved or gop.value_ptr.flags.binding == .weak) {
|
||||
@ -1121,8 +1121,8 @@ pub fn parse(
|
||||
}
|
||||
var err = try diags.addErrorWithNotes(2);
|
||||
try err.addMsg("symbol collision: {s}", .{name.slice(wasm)});
|
||||
gop.value_ptr.source_location.addNote(wasm, &err, "exported as {} here", .{ptr.type_index.fmt(wasm)});
|
||||
err.addNote("{}: exported as {} here", .{ path, gop.value_ptr.type.fmt(wasm) });
|
||||
gop.value_ptr.source_location.addNote(&err, "exported as {} here", .{ptr.type_index.fmt(wasm)});
|
||||
source_location.addNote(&err, "exported as {} here", .{gop.value_ptr.type.fmt(wasm)});
|
||||
continue;
|
||||
} else {
|
||||
gop.value_ptr.* = .{
|
||||
@ -1242,7 +1242,7 @@ pub fn parse(
|
||||
return .{
|
||||
.version = version,
|
||||
.path = path,
|
||||
.archive_member_name = archive_member_name,
|
||||
.archive_member_name = try wasm.internOptionalString(archive_member_name),
|
||||
.start_function = start_function,
|
||||
.features = features,
|
||||
.functions = .{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user