zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 8fe1ec0cc9cfd0f3cf85b97889b4b9735e906926 (tree)
parent 748e7c5e39fcba3ed6b2b6e4cc4c01e1d442acbe
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date:   Sun, 31 May 2026 06:13:47 +0100

compiler: refactor backend error handling

All public codegen and linker APIs now use the following error set:

    Allocator.Error || Io.Cancelable || error{AlreadyReported}

This is defined as `link.Error` and aliased as `codegen.Error`.

The compiler "backend" (including both codegen and linker) has a fairly
limited set of failure modes. Most of them are as follows:
* Bad inline assembly (codegen)
* Output file I/O error (link)
* Symbol with no definition or multiple definitions (link)
* Relocation error, e.g. overflow (link)
* Unimplemented/unsupported feature (codegen/link)
* `error.OutOfMemory` (codegen/link)
* `error.Canceled` (codegen/link)

The last two cases are special, because they are possible across most of
the compiler codebase and have fixed code paths for handling---e.g.
`error.OutOfMemory` almost always calls `Compilation.setAllocFailure`,
and `error.Canceled` should typically propagate all the way up the call
stack.

However, all of the other cases should follow the same general pattern:
the codegen/linker implementation should mark an error on `Compilation`
somehow (either through `link_diags` or `failed_codegen`), and return
`error.AlreadyReported`. This error code indicates that an operation
failed, but that the caller does not need to take action to recover,
because the failure has already been recorded in a way which will be
expressed to the user. For instance, the codegen error set used to
contain `error.Overflow`, but this case should have already been
reported to the user, because implementation-agnostic code cannot know
how best to express the failure to the user.

The `error.AlreadyReported` error code encompasses what was previously
represented by multiple errors, including `error.CodegenFail`,
`error.LinkFailure`, and `error.AnalysisFail`. It is not necessary to
differentiate these cases. (Not to be confused with the usage of
`error.AnalysisFail` in the compiler *frontend*, i.e. `Sema`, which is
unchanged by this diff.)

Because the backend error set is now very small, it is easy to
exhaustively `switch` on, and also many functions can be given concrete
error sets. There are no longer different error sets for different
operations. (As an exception, I have not tackled `link.File.open` in
this diff, which has a big inferred error set where I think most errors
are reported to the user with an `else => |e|` case.)

I have also changed how `link.MappedFile`, our abstraction for handling
the awkward "moving things around" aspect of incremental linking, does
error handling. The public API of this abstraction now uses the
following small error set:

    Allocator.Error || Io.Cancelable || error{MappedFileIo}

The first two cases are self-explanatory. Then, `error.MappedFileIo` is
a catch-all error code encompassing that an error was encountered when
accessing the underlying `Io.File`. This error may have occurred when
attempting to flush the file to disk, or to change its size, etc. In
this case, the *specific* error---which was actually returned from the
`Io.File` API---is available in the `MappedFile.io_err.?` field. Linker
implementations which use `MappedFile` (currently `Elf2` and `Coff`)
should eventually handle `error.MappedFileIo` by reporting an error in
`Compilation.link_diags`, including that specific I/O error in the error
message.

The rationale here is essentially that error codes returned from
functions exist for control flow purposes, and a linker implementation
should not care about the distinction between file I/O error codes (e.g.
`error.DiskQuota` vs `error.InputOutput`). The only reason it needs the
concrete error is to expose it to the user. Therefore, by wrapping these
failure modes under `error.MappedFileIo`, we keep the error set actually
used by the linker implementation as small as possible, which encourages
avoiding patterns like `else => |e| diags.fail(...)` (which is
potentially dangerous since it may prevent `error.OutOfMemory` or
`error.Canceled` from propagating correctly). It additionally helps
linker implementations maintain more precise error messages---for
instance, if `Elf2` encounters `error.NoSpaceLeft` writing to the output
file, the error will now read "failed to write output file: NoSpaceLeft"
instead of something more generic like "prelink failed: NoSpaceLeft".

Finally, while working on these refactors, I was able to eliminate those
pesky `src_loc: Zcu.LazySrcLoc` parameters from the codegen and link
logic. These parameters did not make sense, because at this point in the
compiler pipeline, we do not have precise source location
information---so these parameters were always passed as just the
location of the function declaration, or as some placeholder
(`.unneeded` or the top of `lib/std/std.zig`) if there wasn't an
appropriate function definition. The only logic which actually *uses*
these source locations is codegen implementations' `fail` functions. Per
my earlier list of failure modes, those functions are called in two main
cases:

* Bad inline assembly
* Unimplemented/unsupported feature

The first case should be moved to the compiler frontend (tracked by
https://github.com/ziglang/zig/issues/10761), while the second case is
essentially a compiler TODO so it is permissible for it to have
imprecise error reporting. Therefore, codegen implementations' `fail`
functions now just call `navSrcLoc` themselves when necessary, which
(once we make improvements to inline assembly) will only happen when
there is a deficiency in the codegen implementation.

I was careful to make `Elf2` and `Coff` error reporting work well in
this diff, by using no `else` case other than `else => |e| return e`
when `switch`ing on errors and by always handling `error.MappedFileIo`
by unwrapping `MappedFile.io_err.?`. I also added concrete error sets to
almost every function in `Elf2`. (That was, um, actually why I started
this diff, because it was annoying me that I wouldn't get as many
compile errors at once...)

Diffstat:
Msrc/Compilation.zig | 22+++++++++++-----------
Msrc/Zcu.zig | 25++++++++-----------------
Msrc/Zcu/PerThread.zig | 17+++++------------
Msrc/codegen.zig | 253+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/codegen/aarch64.zig | 1-
Msrc/codegen/aarch64/Mir.zig | 18++++--------------
Msrc/codegen/aarch64/Select.zig | 10+++++-----
Msrc/codegen/c.zig | 16++++------------
Msrc/codegen/llvm.zig | 6+++---
Msrc/codegen/riscv64/CodeGen.zig | 64++++++++++++++++++++++++----------------------------------------
Msrc/codegen/riscv64/Mir.zig | 5++---
Msrc/codegen/sparc64/CodeGen.zig | 39+++++++++++----------------------------
Msrc/codegen/sparc64/Mir.zig | 5++---
Msrc/codegen/spirv/CodeGen.zig | 28++++++++++++----------------
Msrc/codegen/wasm/CodeGen.zig | 13++++---------
Msrc/codegen/x86_64/CodeGen.zig | 27++++++++++-----------------
Msrc/codegen/x86_64/Emit.zig | 70+++++++++++++---------------------------------------------------------
Msrc/codegen/x86_64/Lower.zig | 4++--
Msrc/codegen/x86_64/Mir.zig | 10++++------
Msrc/link.zig | 147+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/link/C.zig | 30++++++++----------------------
Msrc/link/Coff.zig | 91+++++++++++++++++++++++++++----------------------------------------------------
Msrc/link/Dwarf.zig | 72++++++++++++++++++++++++------------------------------------------------
Msrc/link/Elf.zig | 65++++++++++++++++++++++++++++++++---------------------------------
Msrc/link/Elf/Object.zig | 12++++++------
Msrc/link/Elf/ZigObject.zig | 85++++++++++++++++++++++++++-----------------------------------------------------
Msrc/link/Elf/relocatable.zig | 8++++----
Msrc/link/Elf2.zig | 507++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/link/LdScript.zig | 2+-
Msrc/link/Lld.zig | 8++++----
Msrc/link/MachO.zig | 61++++++++++++++++++++++++++++++++-----------------------------
Msrc/link/MachO/Atom.zig | 2+-
Msrc/link/MachO/InternalObject.zig | 2+-
Msrc/link/MachO/ZigObject.zig | 72++++++++++++++++++++++++------------------------------------------------
Msrc/link/MachO/relocatable.zig | 26+++++++++++++-------------
Msrc/link/MappedFile.zig | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/link/Queue.zig | 10+++++++---
Msrc/link/SpirV.zig | 19++++---------------
Msrc/link/Wasm.zig | 38+++++++++++++++++++-------------------
Msrc/link/Wasm/Flush.zig | 6+++---
Msrc/link/Wasm/Object.zig | 2+-
Msrc/register_manager.zig | 2+-
42 files changed, 973 insertions(+), 1126 deletions(-)

diff --git a/src/Compilation.zig b/src/Compilation.zig @@ -3376,7 +3376,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel .fuzz = comp.config.any_fuzz, .lto = comp.config.lto, }) catch |err| switch (err) { - error.LinkFailure => {}, // Already reported. + error.AlreadyReported => {}, error.OutOfMemory => |e| return e, }; } @@ -3390,7 +3390,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel }; // This is needed before reading the error flags. lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) { - error.LinkFailure => {}, // Already reported. + error.AlreadyReported => {}, error.OutOfMemory, error.Canceled => |e| return e, }; } @@ -5249,7 +5249,7 @@ fn workerUpdateCObject( progress_node: std.Progress.Node, ) void { comp.updateCObject(c_object, progress_node) catch |err| switch (err) { - error.AnalysisFail => return, + error.AlreadyReported => return, else => { comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) { // Swallowing this error is OK because it's implied to be OOM when @@ -5266,7 +5266,7 @@ fn workerUpdateWin32Resource( progress_node: std.Progress.Node, ) void { comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) { - error.AnalysisFail => return, + error.AlreadyReported => return, else => { comp.reportRetryableWin32ResourceError(win32_resource, err) catch |oom| switch (oom) { // Swallowing this error is OK because it's implied to be OOM when @@ -5489,7 +5489,7 @@ fn reportRetryableCObjectError(comp: *Compilation, c_object: *CObject, err: anye c_object.status = .failure_retryable; switch (comp.failCObj(c_object, "{t}", .{err})) { - error.AnalysisFail => return, + error.AlreadyReported => return, else => |e| return e, } } @@ -6852,7 +6852,7 @@ fn failCObj( c_object: *CObject, comptime format: []const u8, args: anytype, -) error{ OutOfMemory, AnalysisFail } { +) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const diag_bundle = blk: { const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle); @@ -6876,7 +6876,7 @@ fn failCObjWithOwnedDiagBundle( comp: *Compilation, c_object: *CObject, diag_bundle: *CObject.Diag.Bundle, -) error{ OutOfMemory, AnalysisFail } { +) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); assert(diag_bundle.diags.len > 0); { @@ -6890,10 +6890,10 @@ fn failCObjWithOwnedDiagBundle( comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle); } c_object.status = .failure; - return error.AnalysisFail; + return error.AlreadyReported; } -fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AnalysisFail } { +fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); var bundle: ErrorBundle.Wip = undefined; try bundle.init(comp.gpa); @@ -6920,7 +6920,7 @@ fn failWin32ResourceWithOwnedBundle( comp: *Compilation, win32_resource: *Win32Resource, err_bundle: ErrorBundle, -) error{ OutOfMemory, AnalysisFail } { +) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); { const io = comp.io; @@ -6929,7 +6929,7 @@ fn failWin32ResourceWithOwnedBundle( try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle); } win32_resource.status = .failure; - return error.AnalysisFail; + return error.AlreadyReported; } pub const FileExt = enum { diff --git a/src/Zcu.zig b/src/Zcu.zig @@ -3912,12 +3912,12 @@ pub fn getTarget(zcu: *const Zcu) *const Target { pub fn handleUpdateExports( zcu: *Zcu, export_indices: []const Export.Index, - result: link.File.UpdateExportsError!void, -) Allocator.Error!void { + result: link.Error!void, +) (Allocator.Error || Io.Cancelable)!void { const gpa = zcu.gpa; result catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.AnalysisFail => { + else => |e| return e, + error.AlreadyReported => { const export_idx = export_indices[0]; const new_export = export_idx.ptr(zcu); new_export.status = .failed_retryable; @@ -4688,7 +4688,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) pub const CodegenFailError = error{ /// Indicates the error message has been already stored at `Zcu.failed_codegen`. - CodegenFail, + AlreadyReported, OutOfMemory, }; @@ -4713,16 +4713,7 @@ pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg errdefer msg.deinit(gpa); try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg); } - return error.CodegenFail; -} - -/// Asserts that `zcu.failed_codegen` contains the key `nav`, with the necessary lock held. -pub fn assertCodegenFailed(zcu: *Zcu, nav: InternPool.Nav.Index) void { - const comp = zcu.comp; - const io = comp.io; - comp.mutex.lockUncancelable(io); - defer comp.mutex.unlock(io); - assert(zcu.failed_codegen.contains(nav)); + return error.AlreadyReported; } pub fn codegenFailType( @@ -4735,7 +4726,7 @@ pub fn codegenFailType( try zcu.failed_types.ensureUnusedCapacity(gpa, 1); const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args); zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); - return error.CodegenFail; + return error.AlreadyReported; } pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError { @@ -4745,7 +4736,7 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) try zcu.failed_types.ensureUnusedCapacity(gpa, 1); } zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg); - return error.CodegenFail; + return error.AlreadyReported; } /// Asserts that `zcu.multi_module_err != null`. diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig @@ -3700,7 +3700,7 @@ fn processExportsInner( exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, skip_linker_work: bool, -) error{OutOfMemory}!void { +) error{ OutOfMemory, Canceled }!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -4533,7 +4533,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Ru return codegen_result catch |err| { switch (err) { error.OutOfMemory => comp.setAllocFailure(), - error.CodegenFail => zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav), + error.AlreadyReported => {}, error.NoLinkFile => assert(comp.bin_file == null), error.BackendDoesNotProduceMir => switch (target_util.zigBackend( &zcu.root_mod.resolved_target.result, @@ -4552,7 +4552,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Ru fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ OutOfMemory, Canceled, - CodegenFail, + AlreadyReported, NoLinkFile, BackendDoesNotProduceMir, }!codegen.AnyMir { @@ -4628,19 +4628,12 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e switch (err) { error.OutOfMemory => comp.link_diags.setAllocFailure(), } - return error.CodegenFail; + return error.AlreadyReported; }; return error.BackendDoesNotProduceMir; } - return codegen.generateFunction(lf, pt, zcu.navSrcLoc(nav), func_index, air, &liveness) catch |err| switch (err) { - error.OutOfMemory, - error.CodegenFail, - => |e| return e, - error.Overflow, - error.RelocationNotByteAligned, - => return zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)}), - }; + return codegen.generateFunction(lf, pt, func_index, air, &liveness); } fn printVerboseAir( diff --git a/src/codegen.zig b/src/codegen.zig @@ -24,10 +24,7 @@ const dev = @import("dev.zig"); pub const aarch64 = @import("codegen/aarch64.zig"); -pub const CodeGenError = GenerateSymbolError || error{ - /// Indicates the error is already stored in Zcu `failed_codegen`. - CodegenFail, -}; +pub const Error = link.Error; fn devFeatureForBackend(backend: std.lang.CompilerBackend) dev.Feature { return switch (backend) { @@ -141,11 +138,10 @@ pub const AnyMir = union { pub fn generateFunction( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) CodeGenError!AnyMir { +) Error!AnyMir { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; @@ -160,7 +156,7 @@ pub fn generateFunction( => |backend| { dev.check(devFeatureForBackend(backend)); const CodeGen = importBackend(backend); - const mir = try CodeGen.generate(lf, pt, src_loc, func_index, air, liveness); + const mir = try CodeGen.generate(lf, pt, func_index, air, liveness); return @unionInit(AnyMir, AnyMir.tag(backend), mir); }, } @@ -176,13 +172,12 @@ pub fn generateFunction( pub fn emitFunction( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_id: link.File.AtomId, any_mir: *const AnyMir, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (CodeGenError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; @@ -201,7 +196,7 @@ pub fn emitFunction( => |backend| { dev.check(devFeatureForBackend(backend)); const mir = &@field(any_mir, AnyMir.tag(backend)); - return mir.emit(lf, pt, src_loc, func_index, atom_id, w, debug_output); + return mir.emit(lf, pt, func_index, atom_id, w, debug_output); }, } } @@ -209,12 +204,11 @@ pub fn emitFunction( pub fn generateLazyFunction( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, lazy_sym: link.File.LazySymbol, atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (CodeGenError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index| &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result @@ -224,7 +218,7 @@ pub fn generateLazyFunction( else => unreachable, inline .stage2_riscv64, .stage2_x86_64 => |backend| { dev.check(devFeatureForBackend(backend)); - return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, atom_id, w, debug_output); + return importBackend(backend).generateLazy(lf, pt, lazy_sym, atom_id, w, debug_output); }, } } @@ -232,14 +226,13 @@ pub fn generateLazyFunction( pub fn generateLazySymbol( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, lazy_sym: link.File.LazySymbol, // TODO don't use an "out" parameter like this; put it in the result instead alignment: *Alignment, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, reloc_parent: link.File.RelocInfo.Parent, -) (CodeGenError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const tracy = trace(@src()); defer tracy.end(); tracy.addTextFmt("{t}, {f}", .{ lazy_sym.kind, Type.fromInterned(lazy_sym.ty).fmt(pt) }); @@ -257,7 +250,7 @@ pub fn generateLazySymbol( if (lazy_sym.kind == .code) { alignment.* = target_util.defaultFunctionAlignment(target); - return generateLazyFunction(bin_file, pt, src_loc, lazy_sym, reloc_parent.atom_index, w, debug_output); + return generateLazyFunction(bin_file, pt, lazy_sym, reloc_parent.atom_index, w, debug_output); } if (lazy_sym.ty == .anyerror_type) { @@ -295,22 +288,13 @@ pub fn generateLazySymbol( } } -pub const GenerateSymbolError = error{ - OutOfMemory, - /// Compiler was asked to operate on a number larger than supported. - Overflow, - /// Compiler was asked to produce a non-byte-aligned relocation. - RelocationNotByteAligned, -}; - pub fn generateSymbol( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, val: Value, w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, -) (GenerateSymbolError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const tracy = trace(@src()); defer tracy.end(); @@ -323,8 +307,11 @@ pub fn generateSymbol( log.debug("generateSymbol: val = {f}", .{val.fmtValue(pt)}); + const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse { + return zcu.comp.link_diags.fail("failed to generate symbol: type size overflow", .{}); + }; + if (val.isUndef(zcu)) { - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; try w.splatByteAll(0xaa, abi_size); return; } @@ -364,7 +351,6 @@ pub fn generateSymbol( .enum_literal, => unreachable, // non-runtime values .int => { - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; var space: Value.BigIntSpace = undefined; const int_val = val.toBigInt(&space, zcu); int_val.writeTwosComplement(try w.writableSlice(abi_size), endian); @@ -397,13 +383,13 @@ pub fn generateSymbol( // emit payload part of the error union { const begin = w.end; - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) { + try generateSymbol(bin_file, pt, Value.fromInterned(switch (error_union.val) { .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }), .payload => |payload| payload, }), w, reloc_parent); const unpadded_end = w.end - begin; const padded_end = abi_align.forward(unpadded_end); - const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; + const padding: usize = @intCast(padded_end - unpadded_end); if (padding > 0) { try w.splatByteAll(0, padding); @@ -416,7 +402,7 @@ pub fn generateSymbol( try w.writeInt(u16, err_val, endian); const unpadded_end = w.end - begin; const padded_end = abi_align.forward(unpadded_end); - const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; + const padding: usize = @intCast(padded_end - unpadded_end); if (padding > 0) { try w.splatByteAll(0, padding); @@ -425,7 +411,7 @@ pub fn generateSymbol( }, .enum_tag => |enum_tag| { const int_tag_ty = ty.intTagType(zcu); - try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent); + try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent); }, .float => |float| storage: switch (float.storage) { .f16 => |f16_val| try w.writeInt(u16, @bitCast(f16_val), endian), @@ -433,7 +419,6 @@ pub fn generateSymbol( .f64 => |f64_val| try w.writeInt(u64, @bitCast(f64_val), endian), .f80 => |f80_val| { try w.writeInt(u80, @bitCast(f80_val), endian); - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; try w.splatByteAll(0, abi_size - 10); }, .f128 => |f128_val| switch (Type.fromInterned(float.ty).floatBits(target)) { @@ -444,29 +429,28 @@ pub fn generateSymbol( 128 => try w.writeInt(u128, @bitCast(f128_val), endian), }, }, - .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), w, reloc_parent, 0), + .ptr => try lowerPtr(bin_file, pt, val.toIntern(), w, reloc_parent, 0), .slice => |slice| { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), w, reloc_parent); - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(slice.ptr), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(slice.len), w, reloc_parent); }, .opt => { const payload_type = ty.optionalChild(zcu); const payload_val = val.optionalValue(zcu); - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; if (ty.optionalReprIsPayload(zcu)) { if (payload_val) |value| { - try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent); + try generateSymbol(bin_file, pt, value, w, reloc_parent); } else { try w.splatByteAll(0, abi_size); } } else { - const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1; + const padding = abi_size - @as(usize, @intCast(payload_type.abiSize(zcu))) - 1; if (payload_type.hasRuntimeBits(zcu)) { const value = payload_val orelse Value.fromInterned(try pt.intern(.{ .undef = payload_type.toIntern(), })); - try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent); + try generateSymbol(bin_file, pt, value, w, reloc_parent); } try w.writeByte(@intFromBool(payload_val != null)); try w.splatByteAll(0, padding); @@ -478,7 +462,7 @@ pub fn generateSymbol( .elems, .repeated_elem => { var index: u64 = 0; while (index < array_type.lenIncludingSentinel()) : (index += 1) { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { + try generateSymbol(bin_file, pt, Value.fromInterned(switch (aggregate.storage) { .bytes => unreachable, .elems => |elems| elems[@intCast(index)], .repeated_elem => |elem| if (index < array_type.len) @@ -490,7 +474,6 @@ pub fn generateSymbol( }, }, .vector_type => |vector_type| { - const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow; const vector_bool_bitpacked = switch (zcu.comp.getZigBackend()) { .stage2_wasm => false, else => true, @@ -499,7 +482,9 @@ pub fn generateSymbol( const bytes = try w.writableSlice(abi_size); @memset(bytes, 0xaa); var index: usize = 0; - const len = math.cast(usize, vector_type.len) orelse return error.Overflow; + const len = math.cast(usize, vector_type.len) orelse { + return zcu.comp.link_diags.fail("failed to generate symbol: vector length overflow", .{}); + }; while (index < len) : (index += 1) { const bit_index = switch (endian) { .big => len - 1 - index, @@ -539,18 +524,16 @@ pub fn generateSymbol( .elems, .repeated_elem => { var index: u64 = 0; while (index < vector_type.len) : (index += 1) { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (aggregate.storage) { + try generateSymbol(bin_file, pt, Value.fromInterned(switch (aggregate.storage) { .bytes => unreachable, - .elems => |elems| elems[math.cast(usize, index) orelse return error.Overflow], + .elems => |elems| elems[@intCast(index)], .repeated_elem => |elem| elem, }), w, reloc_parent); } }, } - const padding = abi_size - - (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse - return error.Overflow); + const padding = abi_size - @as(usize, @intCast(Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len)); if (padding > 0) try w.splatByteAll(0, padding); } }, @@ -560,10 +543,9 @@ pub fn generateSymbol( if (field_val != .none) continue; if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; - try w.splatByteAll(0, math.cast(usize, struct_begin + - Type.fromInterned(field_ty).abiAlignment(zcu).forward(w.end - struct_begin) - w.end) orelse - return error.Overflow); - try generateSymbol(bin_file, pt, src_loc, .fromInterned(switch (aggregate.storage) { + try w.splatByteAll(0, @intCast(struct_begin + + Type.fromInterned(field_ty).abiAlignment(zcu).forward(w.end - struct_begin) - w.end)); + try generateSymbol(bin_file, pt, .fromInterned(switch (aggregate.storage) { .bytes => |bytes| try pt.intern(.{ .int = .{ .ty = field_ty, .storage = .{ .u64 = bytes.at(field_index, ip) }, @@ -572,8 +554,7 @@ pub fn generateSymbol( .repeated_elem => |elem| elem, }), w, reloc_parent); } - try w.splatByteAll(0, math.cast(usize, struct_begin + ty.abiSize(zcu) - w.end) orelse - return error.Overflow); + try w.splatByteAll(0, @intCast(struct_begin + ty.abiSize(zcu) - w.end)); }, .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); @@ -598,20 +579,15 @@ pub fn generateSymbol( .repeated_elem => |elem| elem, }; - const padding = math.cast( - usize, - offsets[field_index] - (w.end - struct_begin), - ) orelse return error.Overflow; + const padding: usize = @intCast(offsets[field_index] - (w.end - struct_begin)); if (padding > 0) try w.splatByteAll(0, padding); - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(field_val), w, reloc_parent); } assert(struct_type.alignment.check(struct_type.size)); - const padding = math.cast(usize, struct_type.size - (w.end - struct_begin)) orelse { - return error.Overflow; - }; + const padding: usize = @intCast(struct_type.size - (w.end - struct_begin)); if (padding > 0) try w.splatByteAll(0, padding); }, } @@ -622,12 +598,12 @@ pub fn generateSymbol( const layout = ty.unionGetLayout(zcu); if (layout.payload_size == 0) { - return generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent); + return generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); } // Check if we should store the tag first. if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); } const union_obj = zcu.typeToUnion(ty).?; @@ -635,28 +611,28 @@ pub fn generateSymbol( const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?; const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBits(zcu)) { - try w.splatByteAll(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); + try w.splatByteAll(0xaa, @intCast(layout.payload_size)); } else { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(un.val), w, reloc_parent); - const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow; + const padding: usize = @intCast(layout.payload_size - field_ty.abiSize(zcu)); if (padding > 0) { try w.splatByteAll(0, padding); } } } else { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(un.val), w, reloc_parent); } if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { - try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent); + try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); if (layout.padding > 0) { try w.splatByteAll(0, layout.padding); } } }, - .bitpack => |bitpack| try generateSymbol(bin_file, pt, src_loc, .fromInterned(bitpack.backing_int_val), w, reloc_parent), + .bitpack => |bitpack| try generateSymbol(bin_file, pt, .fromInterned(bitpack.backing_int_val), w, reloc_parent), .memoized_call => unreachable, } } @@ -664,23 +640,21 @@ pub fn generateSymbol( fn lowerPtr( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, ptr_val: InternPool.Index, w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, prev_offset: u64, -) (GenerateSymbolError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; const offset: u64 = prev_offset + ptr.byte_offset; return switch (ptr.base_addr) { .nav => |nav| try lowerNavRef(bin_file, pt, nav, w, reloc_parent, offset), - .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, w, reloc_parent, offset), - .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), w, reloc_parent), + .uav => |uav| try lowerUavRef(bin_file, pt, uav, w, reloc_parent, offset), + .int => try generateSymbol(bin_file, pt, try pt.intValue(Type.usize, offset), w, reloc_parent), .eu_payload => |eu_ptr| try lowerPtr( bin_file, pt, - src_loc, eu_ptr, w, reloc_parent, @@ -689,7 +663,7 @@ fn lowerPtr( zcu, ), ), - .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, src_loc, opt_ptr, w, reloc_parent, offset), + .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, opt_ptr, w, reloc_parent, offset), .field => |field| { const base_ptr = Value.fromInterned(field.base); const base_ty = base_ptr.typeOf(zcu).childType(zcu); @@ -708,13 +682,13 @@ fn lowerPtr( }, else => unreachable, }; - return lowerPtr(bin_file, pt, src_loc, field.base, w, reloc_parent, offset + field_off); + return lowerPtr(bin_file, pt, field.base, w, reloc_parent, offset + field_off); }, .arr_elem => |arr_elem| { const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); assert(base_ptr_ty.ptrSize(zcu) == .many); const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); - return lowerPtr(bin_file, pt, src_loc, arr_elem.base, w, reloc_parent, offset + elem_size * arr_elem.index); + return lowerPtr(bin_file, pt, arr_elem.base, w, reloc_parent, offset + elem_size * arr_elem.index); }, .comptime_alloc => unreachable, .comptime_field => unreachable, @@ -724,12 +698,11 @@ fn lowerPtr( fn lowerUavRef( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, uav: InternPool.Key.Ptr.BaseAddr.Uav, w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, offset: u64, -) (GenerateSymbolError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const comp = lf.comp; @@ -761,10 +734,7 @@ fn lowerUavRef( } const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu); - switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) { - .sym_index => {}, - .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}), - } + _ = try lf.lowerUav(pt, uav_val, uav_align); const vaddr = lf.getUavVAddr(uav_val, .{ .parent = reloc_parent, @@ -790,7 +760,7 @@ fn lowerNavRef( w: *std.Io.Writer, reloc_parent: link.File.RelocInfo.Parent, offset: u64, -) (GenerateSymbolError || std.Io.Writer.Error)!void { +) (Error || std.Io.Writer.Error)!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -859,15 +829,11 @@ fn lowerNavRef( } } -pub const SymbolResult = union(enum) { sym_index: link.File.SymbolId, fail: *ErrorMsg }; - pub fn genNavRef( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, nav_index: InternPool.Nav.Index, - target: *const std.Target, -) CodeGenError!SymbolResult { +) Error!link.File.SymbolId { const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); @@ -884,7 +850,7 @@ pub fn genNavRef( .internal => { const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); }, .strong, .weak => { const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); @@ -895,27 +861,19 @@ pub fn genNavRef( .link_once => unreachable, } if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); }, .link_once => unreachable, } } else if (lf.cast(.elf2)) |elf| { - return .{ .sym_index = elf.navSymbol(nav_index) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return .{ .fail = try ErrorMsg.create( - zcu.gpa, - src_loc, - "linker failed to create a nav: {t}", - .{e}, - ) }, - } }; + return elf.navSymbol(nav_index); } else if (lf.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; switch (linkage) { .internal => { const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); }, .strong, .weak => { const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); @@ -926,80 +884,67 @@ pub fn genNavRef( .link_once => unreachable, } if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); }, .link_once => unreachable, } } else if (lf.cast(.coff2)) |coff| { - return .{ .sym_index = @enumFromInt(@intFromEnum(try coff.navSymbol(zcu, nav_index))) }; + return @enumFromInt(@intFromEnum(try coff.navSymbol(zcu, nav_index))); } else { - const msg = try ErrorMsg.create(zcu.gpa, src_loc, "TODO genNavRef for target {}", .{target}); - return .{ .fail = msg }; + std.debug.panic("TODO genNavRef for '{t}'", .{lf.tag}); } } /// deprecated legacy type -pub const GenResult = union(enum) { - mcv: MCValue, - fail: *ErrorMsg, - - const MCValue = union(enum) { - none, - undef, - /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets - /// such as ARM, the immediate will never exceed 32-bits. - immediate: u64, - /// Decl with address deferred until the linker allocates everything in virtual memory. - /// Payload is a symbol index. - load_direct: link.File.SymbolId, - /// Decl with address deferred until the linker allocates everything in virtual memory. - /// Payload is a symbol index. - lea_direct: link.File.SymbolId, - /// Decl referenced via GOT with address deferred until the linker allocates - /// everything in virtual memory. - /// Payload is a symbol index. - load_got: link.File.SymbolId, - /// Direct by-address reference to memory location. - memory: u64, - /// Reference to memory location but deferred until linker allocated the Decl in memory. - /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. - load_symbol: link.File.SymbolId, - /// Reference to memory location but deferred until linker allocated the Decl in memory. - /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. - lea_symbol: link.File.SymbolId, - }; +pub const MCValue = union(enum) { + none, + undef, + /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets + /// such as ARM, the immediate will never exceed 32-bits. + immediate: u64, + /// Decl with address deferred until the linker allocates everything in virtual memory. + /// Payload is a symbol index. + load_direct: link.File.SymbolId, + /// Decl with address deferred until the linker allocates everything in virtual memory. + /// Payload is a symbol index. + lea_direct: link.File.SymbolId, + /// Decl referenced via GOT with address deferred until the linker allocates + /// everything in virtual memory. + /// Payload is a symbol index. + load_got: link.File.SymbolId, + /// Direct by-address reference to memory location. + memory: u64, + /// Reference to memory location but deferred until linker allocated the Decl in memory. + /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. + load_symbol: link.File.SymbolId, + /// Reference to memory location but deferred until linker allocated the Decl in memory. + /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. + lea_symbol: link.File.SymbolId, }; /// deprecated legacy code path pub fn genTypedValue( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, val: Value, target: *const std.Target, -) CodeGenError!GenResult { +) Error!MCValue { const res = try lowerValue(pt, val, target); return switch (res) { - .none => .{ .mcv = .none }, - .undef => .{ .mcv = .undef }, - .immediate => |imm| .{ .mcv = .{ .immediate = imm } }, - .lea_nav => |nav| switch (try genNavRef(lf, pt, src_loc, nav, target)) { - .sym_index => |sym_index| .{ .mcv = .{ .lea_symbol = sym_index } }, - .fail => |em| .{ .fail = em }, - }, - .load_uav, .lea_uav => |uav| switch (try lf.lowerUav( + .none => .none, + .undef => .undef, + .immediate => |imm| .{ .immediate = imm }, + .lea_nav => |nav| .{ .lea_symbol = try genNavRef(lf, pt, nav) }, + .load_uav => |uav| .{ .load_symbol = try lf.lowerUav( pt, uav.val, Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), - src_loc, - )) { - .sym_index => |sym_index| .{ .mcv = switch (res) { - else => unreachable, - .load_uav => .{ .load_symbol = sym_index }, - .lea_uav => .{ .lea_symbol = sym_index }, - } }, - .fail => |em| .{ .fail = em }, - }, + ) }, + .lea_uav => |uav| .{ .lea_symbol = try lf.lowerUav( + pt, + uav.val, + Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), + ) }, }; } diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig @@ -12,7 +12,6 @@ pub fn legalizeFeatures(_: *const std.Target) ?*Air.Legalize.Features { pub fn generate( _: *link.File, pt: Zcu.PerThread, - _: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig @@ -54,7 +54,6 @@ pub fn emit( mir: Mir, lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_index: link.File.AtomId, w: *std.Io.Writer, @@ -94,16 +93,11 @@ pub fn emit( lf, zcu, atom_index, - switch (try @import("../../codegen.zig").genNavRef( + try @import("../../codegen.zig").genNavRef( lf, pt, - src_loc, nav_reloc.nav, - &mod.resolved_target.result, - )) { - .sym_index => |sym_index| sym_index, - .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em), - }, + ), mir.body[nav_reloc.reloc.label], body_end - Instruction.size * (1 + nav_reloc.reloc.label), nav_reloc.reloc.addend, @@ -113,15 +107,11 @@ pub fn emit( lf, zcu, atom_index, - switch (try lf.lowerUav( + try lf.lowerUav( pt, uav_reloc.uav.val, ZigType.fromInterned(uav_reloc.uav.orig_ty).ptrAlignment(zcu), - src_loc, - )) { - .sym_index => |sym_index| sym_index, - .fail => |em| return zcu.codegenFailMsg(func.owner_nav, em), - }, + ), mir.body[uav_reloc.reloc.label], body_end - Instruction.size * (1 + uav_reloc.reloc.label), uav_reloc.reloc.addend, diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig @@ -883,7 +883,7 @@ pub fn finishAnalysis(isel: *Select) !void { } } -pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void { +pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory, AlreadyReported }!void { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; const gpa = zcu.gpa; @@ -8001,7 +8001,7 @@ fn emitLiteral(isel: *Select, bytes: []const u8) !void { } } -fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { +fn fail(isel: *Select, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); return isel.pt.zcu.codegenFail(isel.nav_index, format, args); } @@ -10595,7 +10595,7 @@ pub const Value = struct { vi: Value.Index, ra: Register.Alias, - fn finish(mat: Value.Materialize, isel: *Select) error{ OutOfMemory, CodegenFail }!void { + fn finish(mat: Value.Materialize, isel: *Select) error{ OutOfMemory, AlreadyReported }!void { const live_vi = isel.live_registers.getPtr(mat.ra); assert(live_vi.* == .allocating); var vi = mat.vi; @@ -11636,7 +11636,7 @@ fn use(isel: *Select, air_ref: Air.Inst.Ref) !Value.Index { return vi; } -fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool { +fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, AlreadyReported }!bool { switch (dst_ra) { else => {}, Register.Alias.fp, .zr, .sp, .pc, .fpcr, .fpsr, .ffr => return false, @@ -11669,7 +11669,7 @@ fn fill(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail } return true; } -fn fillMemory(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, CodegenFail }!bool { +fn fillMemory(isel: *Select, dst_ra: Register.Alias) error{ OutOfMemory, AlreadyReported }!bool { const dst_live_vi = isel.live_registers.getPtr(dst_ra); const dst_vi = switch (dst_live_vi.*) { _ => |dst_vi| dst_vi, diff --git a/src/codegen/c.zig b/src/codegen/c.zig @@ -80,7 +80,7 @@ pub const Mir = struct { } }; -pub const Error = Writer.Error || Allocator.Error || error{AnalysisFail}; +pub const Error = Writer.Error || Allocator.Error || error{AlreadyReported}; pub const CType = @import("c/type.zig").CType; @@ -637,7 +637,6 @@ pub const DeclGen = struct { owner_nav: InternPool.Nav.Index.Optional, is_naked_fn: bool, expected_block: ?u32, - error_msg: ?*Zcu.ErrorMsg, ctype_deps: CType.Dependencies, /// This map contains all the UAVs we saw generating this function. /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. @@ -648,10 +647,7 @@ pub const DeclGen = struct { fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) Error { @branchHint(.cold); - const zcu = dg.pt.zcu; - const src_loc = zcu.navSrcLoc(dg.owner_nav.unwrap().?); - dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args); - return error.AnalysisFail; + return dg.pt.zcu.codegenFail(dg.owner_nav.unwrap().?, format, args); } fn renderUav( @@ -2184,15 +2180,13 @@ pub fn genLazyCallModifierFn( pub fn generate( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) @import("../codegen.zig").CodeGenError!Mir { +) @import("../codegen.zig").Error!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; - _ = src_loc; assert(lf.tag == .c); const func = zcu.funcInfo(func_index); @@ -2210,7 +2204,6 @@ pub fn generate( .arena = arena.allocator(), .pt = pt, .mod = zcu.navFileScope(func.owner_nav).mod.?, - .error_msg = null, .owner_nav = func.owner_nav.toOptional(), .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, .expected_block = null, @@ -2237,9 +2230,8 @@ pub fn generate( defer code_header.deinit(); genFunc(&function, &fwd_decl.writer, &code_header.writer) catch |err| switch (err) { - error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.dg.error_msg.?), error.WriteFailed => return error.OutOfMemory, - error.OutOfMemory => |e| return e, + else => |e| return e, }; var mir: Mir = .{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -771,7 +771,7 @@ pub const Object = struct { lto: std.zig.LtoMode, }; - pub fn emit(o: *Object, pt: Zcu.PerThread, options: EmitOptions) error{ LinkFailure, OutOfMemory }!void { + pub fn emit(o: *Object, pt: Zcu.PerThread, options: EmitOptions) error{ AlreadyReported, OutOfMemory }!void { const zcu = o.zcu; const comp = zcu.comp; const io = comp.io; @@ -1705,7 +1705,7 @@ pub const Object = struct { o: *Object, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, - ) link.File.UpdateExportsError!void { + ) link.Error!void { const zcu = o.zcu; const ip = &zcu.intern_pool; const ty: Type, const llvm_ptr: Builder.Constant = switch (exported) { @@ -1735,7 +1735,7 @@ pub const Object = struct { global_index: Builder.Global.Index, ty: Type, export_indices: []const Zcu.Export.Index, - ) link.File.UpdateExportsError!void { + ) link.Error!void { const zcu = o.zcu; const comp = zcu.comp; const ip = &zcu.intern_pool; diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig @@ -30,8 +30,6 @@ const verbose_tracking_log = std.log.scoped(.verbose_tracking); const wip_mir_log = std.log.scoped(.wip_mir); const Alignment = InternPool.Alignment; -const CodeGenError = codegen.CodeGenError; - const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Lower = @import("Lower.zig"); @@ -49,7 +47,7 @@ const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Instruction = encoding.Instruction; -const InnerError = CodeGenError || error{OutOfRegisters}; +const InnerError = codegen.Error || error{OutOfRegisters}; pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { return comptime &.initMany(&.{ @@ -75,7 +73,6 @@ ret_mcv: InstTracking, func_index: InternPool.Index, fn_type: Type, arg_index: usize, -src_loc: Zcu.LazySrcLoc, mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, @@ -742,11 +739,10 @@ const CallView = enum(u1) { pub fn generate( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) CodeGenError!Mir { +) codegen.Error!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -777,7 +773,6 @@ pub fn generate( .fn_type = fn_type, .arg_index = 0, .branch_stack = &branch_stack, - .src_loc = src_loc, .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, .scope_generation = 0, @@ -811,10 +806,7 @@ pub fn generate( ); const fn_info = zcu.typeToFunc(fn_type).?; - var call_info = function.resolveCallingConventionValues(fn_info, &.{}) catch |err| switch (err) { - error.CodegenFail => |e| return e, - else => |e| return e, - }; + var call_info = try function.resolveCallingConventionValues(fn_info, &.{}); defer call_info.deinit(&function); @@ -841,7 +833,6 @@ pub fn generate( })); function.gen() catch |err| switch (err) { - error.CodegenFail => |e| return e, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; @@ -857,12 +848,11 @@ pub fn generate( pub fn generateLazy( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, lazy_sym: link.File.LazySymbol, atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (CodeGenError || std.Io.Writer.Error)!void { +) (codegen.Error || std.Io.Writer.Error)!void { _ = atom_index; const comp = bin_file.comp; const gpa = comp.gpa; @@ -883,7 +873,6 @@ pub fn generateLazy( .fn_type = undefined, .arg_index = 0, .branch_stack = undefined, - .src_loc = src_loc, .end_di_line = undefined, .end_di_column = undefined, .scope_generation = 0, @@ -893,7 +882,6 @@ pub fn generateLazy( defer function.mir_instructions.deinit(gpa); function.genLazy(lazy_sym) catch |err| switch (err) { - error.CodegenFail => |e| return e, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; @@ -910,7 +898,7 @@ pub fn generateLazy( .allocator = gpa, .mir = mir, .cc = .auto, - .src_loc = src_loc, + .src_loc = Type.fromInterned(lazy_sym.ty).srcLocOrNull(pt.zcu) orelse .unneeded, .output_mode = comp.config.output_mode, .link_mode = comp.config.link_mode, .pic = mod.pic, @@ -946,7 +934,10 @@ fn formatWipMir(data: FormatWipMirData, writer: *std.Io.Writer) std.Io.Writer.Er .frame_locs = data.func.frame_locs.slice(), }, .cc = .auto, - .src_loc = data.func.src_loc, + .src_loc = switch (data.func.owner) { + .nav_index => |nav| pt.zcu.navSrcLoc(nav), + .lazy_sym => |lazy_sym| Type.fromInterned(lazy_sym.ty).srcLocOrNull(pt.zcu) orelse .unneeded, + }, .output_mode = comp.config.output_mode, .link_mode = comp.config.link_mode, .pic = comp.root_mod.pic, @@ -8144,28 +8135,21 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue { const pt = func.pt; const lf = func.bin_file; - const src_loc = func.src_loc; - const result: codegen.GenResult = if (val.isUndef(pt.zcu)) - switch (try lf.lowerUav(pt, val.toIntern(), .none, src_loc)) { - .sym_index => |sym_index| .{ .mcv = .{ .load_symbol = sym_index } }, - .fail => |em| .{ .fail = em }, - } + const result: codegen.MCValue = if (val.isUndef(pt.zcu)) + .{ .load_symbol = try lf.lowerUav(pt, val.toIntern(), .none) } else - try codegen.genTypedValue(lf, pt, src_loc, val, func.target); + try codegen.genTypedValue(lf, pt, val, func.target); const mcv: MCValue = switch (result) { - .mcv => |mcv| switch (mcv) { - .none => .none, - .undef => unreachable, - .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, - .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, - .immediate => |imm| .{ .immediate = imm }, - .memory => |addr| .{ .memory = addr }, - .load_got, .load_direct, .lea_direct => { - return func.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); - }, + .none => .none, + .undef => unreachable, + .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, + .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, + .load_got, .load_direct, .lea_direct => { + return func.fail("TODO: genTypedValue {s}", .{@tagName(result)}); }, - .fail => |msg| return func.failMsg(msg), }; return mcv; } @@ -8353,24 +8337,24 @@ fn wantSafety(func: *Func) bool { }; } -fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { +fn fail(func: *const Func, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const zcu = func.pt.zcu; switch (func.owner) { .nav_index => |i| return zcu.codegenFail(i, format, args), .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args), } - return error.CodegenFail; + return error.AlreadyReported; } -fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { +fn failMsg(func: *const Func, msg: *ErrorMsg) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const zcu = func.pt.zcu; switch (func.owner) { .nav_index => |i| return zcu.codegenFailMsg(i, msg), .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg), } - return error.CodegenFail; + return error.AlreadyReported; } fn parseRegName(name: []const u8) ?Register { diff --git a/src/codegen/riscv64/Mir.zig b/src/codegen/riscv64/Mir.zig @@ -107,12 +107,11 @@ pub fn emit( mir: Mir, lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.CodeGenError || std.Io.Writer.Error)!void { +) (codegen.Error || std.Io.Writer.Error)!void { _ = atom_index; const zcu = pt.zcu; const comp = zcu.comp; @@ -127,7 +126,7 @@ pub fn emit( .allocator = gpa, .mir = mir, .cc = fn_info.cc, - .src_loc = src_loc, + .src_loc = zcu.navSrcLoc(nav), .output_mode = comp.config.output_mode, .link_mode = comp.config.link_mode, .pic = mod.pic, diff --git a/src/codegen/sparc64/CodeGen.zig b/src/codegen/sparc64/CodeGen.zig @@ -19,7 +19,6 @@ const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Type = @import("../../Type.zig"); -const CodeGenError = codegen.CodeGenError; const Endian = std.lang.Endian; const Alignment = InternPool.Alignment; @@ -39,7 +38,7 @@ const gp = abi.RegisterClass.gp; const Self = @This(); -const InnerError = CodeGenError || error{OutOfRegisters}; +const InnerError = codegen.Error || error{OutOfRegisters}; pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { return null; @@ -57,12 +56,10 @@ liveness: Air.Liveness, bin_file: *link.File, target: *const std.Target, func_index: InternPool.Index, -err_msg: ?*ErrorMsg, args: []MCValue, ret_mcv: MCValue, fn_type: Type, arg_index: usize, -src_loc: Zcu.LazySrcLoc, stack_align: Alignment, /// MIR Instructions @@ -264,11 +261,10 @@ const BigTomb = struct { pub fn generate( lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) CodeGenError!Mir { +) codegen.Error!Mir { const zcu = pt.zcu; const gpa = zcu.gpa; const func = zcu.funcInfo(func_index); @@ -292,13 +288,11 @@ pub fn generate( .target = target, .bin_file = lf, .func_index = func_index, - .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` .fn_type = func_ty, .arg_index = 0, .branch_stack = &branch_stack, - .src_loc = src_loc, .stack_align = undefined, .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, @@ -307,10 +301,7 @@ pub fn generate( defer function.blocks.deinit(gpa); defer function.exitlude_jump_relocs.deinit(gpa); - var call_info = function.resolveCallingConventionValues(func_ty, .callee) catch |err| switch (err) { - error.CodegenFail => |e| return e, - else => |e| return e, - }; + var call_info = try function.resolveCallingConventionValues(func_ty, .callee); defer call_info.deinit(&function); function.args = call_info.args; @@ -319,7 +310,6 @@ pub fn generate( function.max_end_stack = call_info.stack_byte_count; function.gen() catch |err| switch (err) { - error.CodegenFail => |e| return e, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; @@ -3446,15 +3436,15 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) } } -fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { +fn fail(self: *Self, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const zcu = self.pt.zcu; const func = zcu.funcInfo(self.func_index); - const msg = try ErrorMsg.create(zcu.gpa, self.src_loc, format, args); + const msg = try ErrorMsg.create(zcu.gpa, zcu.navSrcLoc(func.owner_nav), format, args); return zcu.codegenFailMsg(func.owner_nav, msg); } -fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, CodegenFail } { +fn failMsg(self: *Self, msg: *ErrorMsg) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const zcu = self.pt.zcu; const func = zcu.funcInfo(self.func_index); @@ -4036,21 +4026,14 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { const mcv: MCValue = switch (try codegen.genTypedValue( self.bin_file, pt, - self.src_loc, val, self.target, )) { - .mcv => |mcv| switch (mcv) { - .none => .none, - .undef => .undef, - .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO - .immediate => |imm| .{ .immediate = imm }, - .memory => |addr| .{ .memory = addr }, - }, - .fail => |msg| { - self.err_msg = msg; - return error.CodegenFail; - }, + .none => .none, + .undef => .undef, + .load_got, .load_symbol, .load_direct, .lea_symbol, .lea_direct => unreachable, // TODO + .immediate => |imm| .{ .immediate = imm }, + .memory => |addr| .{ .memory = addr }, }; return mcv; } diff --git a/src/codegen/sparc64/Mir.zig b/src/codegen/sparc64/Mir.zig @@ -378,12 +378,11 @@ pub fn emit( mir: Mir, lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_index: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) (codegen.CodeGenError || std.Io.Writer.Error)!void { +) (codegen.Error || std.Io.Writer.Error)!void { _ = atom_index; const zcu = pt.zcu; const func = zcu.funcInfo(func_index); @@ -394,7 +393,7 @@ pub fn emit( .bin_file = lf, .debug_output = debug_output, .target = &mod.resolved_target.result, - .src_loc = src_loc, + .src_loc = zcu.navSrcLoc(nav), .w = w, .prev_di_pc = 0, .prev_di_line = func.lbrace_line, diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig @@ -156,7 +156,6 @@ inst_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty, id_scratch: std.ArrayList(Id) = .empty, prologue: Section = .{}, body: Section = .{}, -error_msg: ?*Zcu.ErrorMsg = null, pub fn deinit(cg: *CodeGen) void { const gpa = cg.module.gpa; @@ -168,7 +167,7 @@ pub fn deinit(cg: *CodeGen) void { cg.body.deinit(gpa); } -const Error = error{ CodegenFail, OutOfMemory }; +const Error = error{ AlreadyReported, OutOfMemory }; pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { const gpa = cg.module.gpa; @@ -363,11 +362,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error { @branchHint(.cold); - const zcu = cg.module.zcu; - const src_loc = zcu.navSrcLoc(cg.owner_nav); - assert(cg.error_msg == null); - cg.error_msg = try Zcu.ErrorMsg.create(zcu.gpa, src_loc, format, args); - return error.CodegenFail; + return cg.module.zcu.codegenFail(cg.owner_nav, format, args); } pub fn todo(cg: *CodeGen, comptime format: []const u8, args: anytype) Error { @@ -5934,14 +5929,14 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { // them as notes here. // TODO: Translate proper error locations. assert(ass.errors.items.len != 0); - assert(cg.error_msg == null); - const src_loc = zcu.navSrcLoc(cg.owner_nav); - cg.error_msg = try Zcu.ErrorMsg.create(zcu.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); - const notes = try zcu.gpa.alloc(Zcu.ErrorMsg, ass.errors.items.len); + const msg: *Zcu.ErrorMsg = msg: { + const src_loc = zcu.navSrcLoc(cg.owner_nav); + var msg: *Zcu.ErrorMsg = try .create(zcu.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); + errdefer msg.destroy(zcu.gpa); - // Sub-scope to prevent `return error.CodegenFail` from running the errdefers. - { + const notes = try zcu.gpa.alloc(Zcu.ErrorMsg, ass.errors.items.len); errdefer zcu.gpa.free(notes); + var i: usize = 0; errdefer for (notes[0..i]) |*note| { note.deinit(zcu.gpa); @@ -5950,9 +5945,10 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { while (i < ass.errors.items.len) : (i += 1) { notes[i] = try Zcu.ErrorMsg.init(zcu.gpa, src_loc, "{s}", .{ass.errors.items[i].msg}); } - } - cg.error_msg.?.notes = notes; - return error.CodegenFail; + + break :msg msg; + }; + return zcu.codegenFailMsg(cg.owner_nav, msg); }, else => |others| return others, }; diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig @@ -329,7 +329,7 @@ const bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; const InnerError = error{ OutOfMemory, /// An error occurred when trying to lower AIR to MIR. - CodegenFail, + AlreadyReported, /// Compiler implementation could not handle a large integer. Overflow, } || link.File.UpdateDebugInfoError; @@ -355,7 +355,7 @@ pub fn deinit(cg: *CodeGen) void { cg.* = undefined; } -pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { +pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { const zcu = cg.pt.zcu; const func = zcu.funcInfo(cg.func_index); return zcu.codegenFail(func.owner_nav, fmt, args); @@ -756,21 +756,17 @@ fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { pub const Error = error{ OutOfMemory, - /// Compiler was asked to operate on a number larger than supported. - Overflow, /// Indicates the error is already stored in Zcu `failed_codegen`. - CodegenFail, + AlreadyReported, }; pub fn generate( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, ) Error!Mir { - _ = src_loc; _ = bin_file; const zcu = pt.zcu; const gpa = zcu.gpa; @@ -814,9 +810,8 @@ pub fn generate( try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {}); return generateInner(&code_gen, any_returns) catch |err| switch (err) { - error.CodegenFail, + error.AlreadyReported, error.OutOfMemory, - error.Overflow, => |e| return e, else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), }; diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig @@ -31,7 +31,7 @@ const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const FrameIndex = bits.FrameIndex; -const InnerError = codegen.CodeGenError || error{OutOfRegisters}; +const InnerError = codegen.Error || error{OutOfRegisters}; pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { return comptime &.initMany(&.{ @@ -106,7 +106,6 @@ va_info: union { ret_mcv: InstTracking, err_ret_trace_reg: Register, fn_type: Type, -src_loc: Zcu.LazySrcLoc, eflags_inst: ?Air.Inst.Index = null, @@ -869,11 +868,10 @@ const CodeGen = @This(); pub fn generate( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, air: *const Air, liveness: *const ?Air.Liveness, -) codegen.CodeGenError!Mir { +) codegen.Error!Mir { _ = bin_file; const zcu = pt.zcu; const gpa = zcu.gpa; @@ -898,7 +896,6 @@ pub fn generate( .ret_mcv = undefined, // populated after `resolveCallingConventionValues` .err_ret_trace_reg = undefined, // populated after `resolveCallingConventionValues` .fn_type = fn_type, - .src_loc = src_loc, }; defer { function.frame_allocs.deinit(gpa); @@ -937,10 +934,7 @@ pub fn generate( ); const fn_info = zcu.typeToFunc(fn_type).?; - var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { - error.CodegenFail => |e| return e, - else => |e| return e, - }; + var call_info = try function.resolveCallingConventionValues(fn_info, &.{}, .args_frame); defer call_info.deinit(&function); function.args = call_info.args; @@ -983,7 +977,6 @@ pub fn generate( } function.gen(&file.zir.?, func_zir.inst, func.comptime_args, call_info.air_arg_count) catch |err| switch (err) { - error.CodegenFail => |e| return e, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; @@ -1027,12 +1020,11 @@ pub fn getTmpMir(cg: *CodeGen) Mir { pub fn generateLazy( bin_file: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, lazy_sym: link.File.LazySymbol, atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { +) codegen.Error!void { const gpa = pt.zcu.gpa; // This function is for generating global code, so we use the root module. const mod = pt.zcu.comp.root_mod; @@ -1050,7 +1042,6 @@ pub fn generateLazy( .ret_mcv = undefined, .err_ret_trace_reg = undefined, .fn_type = undefined, - .src_loc = src_loc, }; defer { function.inst_tracking.deinit(gpa); @@ -1068,12 +1059,11 @@ pub fn generateLazy( } function.genLazy(lazy_sym) catch |err| switch (err) { - error.CodegenFail => |e| return e, error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), else => |e| return e, }; - try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, atom_id, w, debug_output); + try function.getTmpMir().emitLazy(bin_file, pt, lazy_sym, atom_id, w, debug_output); } const FormatNavData = struct { @@ -1111,7 +1101,10 @@ fn formatWipMir(data: FormatWipMirData, w: *Writer) Writer.Error!void { .allocator = data.self.gpa, .mir = data.self.getTmpMir(), .cc = .auto, - .src_loc = data.self.src_loc, + .src_loc = switch (data.self.owner) { + .nav_index => |nav| data.self.pt.zcu.navSrcLoc(nav), + .lazy_sym => |lazy_sym| Type.fromInterned(lazy_sym.ty).srcLocOrNull(data.self.pt.zcu) orelse .unneeded, + }, }; var first = true; for ((lower.lowerMir(data.inst) catch |err| switch (err) { @@ -181508,7 +181501,7 @@ fn resolveCallingConventionValues( return result; } -fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { +fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } { @branchHint(.cold); const zcu = cg.pt.zcu; return switch (cg.owner) { diff --git a/src/codegen/x86_64/Emit.zig b/src/codegen/x86_64/Emit.zig @@ -17,6 +17,7 @@ relocs: std.ArrayList(Reloc), table_relocs: std.ArrayList(TableReloc), pub const Error = Lower.Error || error{ + AlreadyReported, EmitFail, NotFile, } || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError; @@ -101,20 +102,11 @@ pub fn emitMir(emit: *Emit) Error!void { .inst => |inst| .{ .inst = inst }, .table => .table, .nav => |nav| { - const symbol_id = switch (try codegen.genNavRef( + const symbol_id = try codegen.genNavRef( emit.bin_file, emit.pt, - emit.lower.src_loc, nav, - emit.lower.target, - )) { - .sym_index => |symbol_id| symbol_id, - .fail => |em| { - assert(emit.lower.err_msg == null); - emit.lower.err_msg = em; - return error.EmitFail; - }, - }; + ); const target_symbol: RelocInfo.Target.Symbol = if (ip.getNav(nav).getExtern(ip)) |@"extern"| .{ .symbol = symbol_id, .is_extern = switch (@"extern".visibility) { @@ -133,19 +125,11 @@ pub fn emitMir(emit: *Emit) Error!void { } }, .uav => |uav| .{ .symbol = .{ - .symbol = switch (try emit.bin_file.lowerUav( + .symbol = try emit.bin_file.lowerUav( emit.pt, uav.val, Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), - emit.lower.src_loc, - )) { - .sym_index => |symbol_id| symbol_id, - .fail => |em| { - assert(emit.lower.err_msg == null); - emit.lower.err_msg = em; - return error.EmitFail; - }, - }, + ), .is_extern = false, } }, .lazy_sym => |lazy_sym| .{ .symbol = .{ @@ -168,17 +152,14 @@ pub fn emitMir(emit: *Emit) Error!void { .extern_func => |extern_func| .{ .symbol = .{ .symbol = if (emit.bin_file.cast(.elf)) |elf_file| @enumFromInt(try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)) - else if (emit.bin_file.cast(.elf2)) |elf| elf.externSymbol(.{ + else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{ .name = extern_func.toSlice(&emit.lower.mir).?, .lib_name = switch (comp.compiler_rt_strat) { .none, .lib, .obj, .zcu => null, .dyn_lib => "compiler_rt", }, .type = .FUNC, - }) catch |err| switch (err) { - error.LinkOnceUnsupported => unreachable, - else => |e| return e, - } else if (emit.bin_file.cast(.macho)) |macho_file| + }) else if (emit.bin_file.cast(.macho)) |macho_file| @enumFromInt(try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)) else if (emit.bin_file.cast(.coff2)) |coff| @enumFromInt(@intFromEnum(try coff.globalSymbol( extern_func.toSlice(&emit.lower.mir).?, @@ -313,14 +294,11 @@ pub fn emitMir(emit: *Emit) Error!void { .symbol = if (emit.bin_file.cast(.elf)) |elf_file| @enumFromInt(try elf_file.getGlobalSymbol( "__tls_get_addr", if (comp.config.link_libc) "c" else null, - )) else if (emit.bin_file.cast(.elf2)) |elf| elf.externSymbol(.{ + )) else if (emit.bin_file.cast(.elf2)) |elf| try elf.externSymbol(.{ .name = "__tls_get_addr", .lib_name = if (comp.config.link_libc) "c" else null, .type = .FUNC, - }) catch |err| switch (err) { - error.LinkOnceUnsupported => unreachable, - else => |e| return e, - } else unreachable, + }) else unreachable, .is_extern = true, } }, }}); @@ -584,37 +562,16 @@ pub fn emitMir(emit: *Emit) Error!void { .none => .{ .constu = 0 }, .reg => |reg| .{ .breg = reg.dwarfNum() }, .frame, .table, .rip_inst => unreachable, - .nav => |nav| .{ .addr_reloc = switch (codegen.genNavRef( + .nav => |nav| .{ .addr_reloc = try codegen.genNavRef( emit.bin_file, emit.pt, - emit.lower.src_loc, nav, - emit.lower.target, - ) catch |err| switch (err) { - error.CodegenFail, - => return emit.fail("unable to codegen: {s}", .{@errorName(err)}), - else => |e| return e, - }) { - .sym_index => |sym_index| sym_index, - .fail => |em| { - assert(emit.lower.err_msg == null); - emit.lower.err_msg = em; - return error.EmitFail; - }, - } }, - .uav => |uav| .{ .addr_reloc = switch (try emit.bin_file.lowerUav( + ) }, + .uav => |uav| .{ .addr_reloc = try emit.bin_file.lowerUav( emit.pt, uav.val, Type.fromInterned(uav.orig_ty).ptrAlignment(emit.pt.zcu), - emit.lower.src_loc, - )) { - .sym_index => |sym_index| sym_index, - .fail => |em| { - assert(emit.lower.err_msg == null); - emit.lower.err_msg = em; - return error.EmitFail; - }, - } }, + ) }, .lazy_sym, .extern_func => unreachable, }; break :base &loc_buf[0]; @@ -666,7 +623,6 @@ pub fn emitMir(emit: *Emit) Error!void { const local = &emit.lower.mir.locals[local_index]; local_index += 1; try dwarf.genLocalConstDebugInfo( - emit.lower.src_loc, switch (mir_inst.ops) { else => unreachable, .pseudo_dbg_arg_val => .comptime_arg, diff --git a/src/codegen/x86_64/Lower.zig b/src/codegen/x86_64/Lower.zig @@ -49,8 +49,8 @@ pub const Error = error{ LowerFail, InvalidInstruction, CannotEncode, - CodegenFail, -} || codegen.GenerateSymbolError; + AlreadyReported, +} || link.Error; pub const Reloc = struct { lowered_inst_index: ResultInstIndex, diff --git a/src/codegen/x86_64/Mir.zig b/src/codegen/x86_64/Mir.zig @@ -1974,12 +1974,11 @@ pub fn emit( mir: Mir, lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { +) codegen.Error!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -1993,7 +1992,7 @@ pub fn emit( .allocator = gpa, .mir = mir, .cc = fn_info.cc, - .src_loc = src_loc, + .src_loc = zcu.navSrcLoc(nav), }, .bin_file = lf, .pt = pt, @@ -2028,12 +2027,11 @@ pub fn emitLazy( mir: Mir, lf: *link.File, pt: Zcu.PerThread, - src_loc: Zcu.LazySrcLoc, lazy_sym: link.File.LazySymbol, atom_id: link.File.AtomId, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, -) codegen.CodeGenError!void { +) codegen.Error!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -2044,7 +2042,7 @@ pub fn emitLazy( .allocator = gpa, .mir = mir, .cc = .auto, - .src_loc = src_loc, + .src_loc = Zcu.Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse .unneeded, }, .bin_file = lf, .pt = pt, diff --git a/src/link.zig b/src/link.zig @@ -31,6 +31,12 @@ pub const LdScript = @import("link/LdScript.zig"); pub const Queue = @import("link/Queue.zig"); pub const ConstPool = @import("link/ConstPool.zig"); +pub const Error = Allocator.Error || Io.Cancelable || error{ + /// An error message has already been stored in persistent state on `Compilation` or `Zcu`, for + /// instance in `Compilation.link_diags`. + AlreadyReported, +}; + pub const Diags = struct { /// Stored here so that function definitions can distinguish between /// needing an allocator for things besides error reporting. @@ -112,7 +118,7 @@ pub const Diags = struct { err: ErrorWithNotes, comptime format: []const u8, args: anytype, - ) error{OutOfMemory}!void { + ) Allocator.Error!void { const gpa = err.diags.gpa; const err_msg = &err.diags.msgs.items[err.index]; err_msg.msg = try std.fmt.allocPrint(gpa, format, args); @@ -212,16 +218,16 @@ pub const Diags = struct { } } - pub fn fail(diags: *Diags, comptime format: []const u8, args: anytype) error{LinkFailure} { + pub fn fail(diags: *Diags, comptime format: []const u8, args: anytype) error{AlreadyReported} { @branchHint(.cold); addError(diags, format, args); - return error.LinkFailure; + return error.AlreadyReported; } - pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{LinkFailure} { + pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{AlreadyReported} { @branchHint(.cold); addErrorSourceLocation(diags, sl, format, args); - return error.LinkFailure; + return error.AlreadyReported; } pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void { @@ -251,7 +257,7 @@ pub const Diags = struct { }); } - pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { + pub fn addErrorWithNotes(diags: *Diags, note_count: usize) Allocator.Error!ErrorWithNotes { @branchHint(.cold); const gpa = diags.gpa; const io = diags.io; @@ -261,7 +267,7 @@ pub const Diags = struct { return addErrorWithNotesAssumeCapacity(diags, note_count); } - pub fn addErrorWithNotesAssumeCapacity(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { + pub fn addErrorWithNotesAssumeCapacity(diags: *Diags, note_count: usize) Allocator.Error!ErrorWithNotes { @branchHint(.cold); const gpa = diags.gpa; const index = diags.msgs.items.len; @@ -351,10 +357,10 @@ pub const Diags = struct { path: Path, comptime format: []const u8, args: anytype, - ) error{LinkFailure} { + ) error{AlreadyReported} { @branchHint(.cold); addParseError(diags, path, format, args); - return error.LinkFailure; + return error.AlreadyReported; } pub fn setAllocFailure(diags: *Diags) void { @@ -752,11 +758,6 @@ pub const File = struct { none, }; pub const UpdateDebugInfoError = Dwarf.UpdateError; - pub const FlushDebugInfoError = Dwarf.FlushError; - - /// Note that `LinkFailure` is not a member of this error set because the error message - /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`. - pub const UpdateNavError = codegen.CodeGenError; /// Opaque identifier for a function currently being emitted. /// @@ -775,7 +776,7 @@ pub const File = struct { /// be created. This symbol may get resolved once all relocatables are (re-)linked. /// Optionally, it is possible to specify where to expect the symbol defined if it /// is an import. - pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!SymbolId { + pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) Error!SymbolId { log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); switch (base.tag) { .lld => unreachable, @@ -790,7 +791,7 @@ pub const File = struct { /// May be called before or after updateExports for any given Nav. /// Asserts that the ZCU is not using the LLVM backend. - fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { + fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { assert(base.comp.zcu.?.llvm_object == null); const nav = pt.zcu.intern_pool.getNav(nav_index); assert(nav.resolved.?.value != .none); @@ -804,14 +805,8 @@ pub const File = struct { } } - pub const UpdateContainerTypeError = error{ - OutOfMemory, - /// `Zcu.failed_types` is already populated with the error message. - TypeFailureReported, - }; - /// Never called when LLVM is codegenning the ZCU. - fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) UpdateContainerTypeError!void { + fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Error!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -824,7 +819,7 @@ pub const File = struct { } /// Never called when LLVM is codegenning the ZCU. - fn clearContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { + fn clearContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) Error!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -847,7 +842,7 @@ pub const File = struct { /// that `mir.deinit` remains legal for the caller. For instance, the callee can /// take ownership of an embedded slice and replace it with `&.{}` in `mir`. mir: *codegen.AnyMir, - ) UpdateNavError!void { + ) Error!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -860,16 +855,10 @@ pub const File = struct { } } - pub const UpdateLineNumberError = error{ - OutOfMemory, - Overflow, - LinkFailure, - }; - /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. /// Never called when LLVM is codegenning the ZCU. - fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void { + fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) Error!void { assert(base.comp.zcu.?.llvm_object == null); { const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; @@ -918,7 +907,7 @@ pub const File = struct { } } - pub fn idle(base: *File, tid: Zcu.PerThread.Id) !bool { + pub fn idle(base: *File, tid: Zcu.PerThread.Id) Error!bool { switch (base.tag) { else => return false, inline .elf2, .coff2 => |tag| { @@ -928,7 +917,7 @@ pub const File = struct { } } - pub fn updateErrorData(base: *File, pt: Zcu.PerThread) !void { + pub fn updateErrorData(base: *File, pt: Zcu.PerThread) Error!void { switch (base.tag) { else => {}, inline .elf2, .coff2 => |tag| { @@ -938,14 +927,9 @@ pub const File = struct { } } - pub const FlushError = Io.Cancelable || Allocator.Error || error{ - /// Indicates an error will be present in `Compilation.link_diags`. - LinkFailure, - }; - /// Commit pending changes and write headers. Takes into account final output mode. /// `arena` has the lifetime of the call to `Compilation.update`. - pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { + pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) Error!void { const comp = base.comp; const io = comp.io; if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { @@ -985,11 +969,6 @@ pub const File = struct { } } - pub const UpdateExportsError = error{ - OutOfMemory, - AnalysisFail, - }; - /// This is called for every exported thing. `exports` is almost always /// a list of size 1, meaning that `exported` is exported once. However, it is possible /// to export the same thing with multiple different symbol names (aliases). @@ -1000,7 +979,7 @@ pub const File = struct { pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, - ) UpdateExportsError!void { + ) Error!void { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1031,7 +1010,7 @@ pub const File = struct { /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate /// the block/atom. /// Never called when LLVM is codegenning the ZCU. - pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) !u64 { + pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) Error!u64 { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1052,8 +1031,7 @@ pub const File = struct { pt: Zcu.PerThread, decl_val: InternPool.Index, decl_align: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, - ) !codegen.SymbolResult { + ) Error!SymbolId { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1063,13 +1041,13 @@ pub const File = struct { .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); - return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc); + return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align); }, } } /// Never called when LLVM is codegenning the ZCU. - pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { + pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) Error!u64 { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, @@ -1217,7 +1195,7 @@ pub const File = struct { /// Called when all linker inputs have been sent via `loadInput`. After /// this, `loadInput` will not be called anymore. - pub fn prelink(base: *File) FlushError!void { + pub fn prelink(base: *File) Error!void { assert(!base.post_prelink); // In this case, an object file is created by the LLVM backend, so @@ -1251,7 +1229,11 @@ pub const File = struct { file_writer.pos = new_offset; const size_u = std.math.cast(usize, size) orelse return error.Overflow; const n = file_writer.interface.sendFileAll(&file_reader, .limited(size_u)) catch |err| switch (err) { - error.ReadFailed => return file_reader.err.?, + error.ReadFailed => switch (file_reader.err.?) { + error.ConnectionResetByPeer => return error.Unexpected, // not a socket + error.SocketUnconnected => return error.Unexpected, // not a socket + else => |e| return e, + }, error.WriteFailed => return file_writer.err.?, }; assert(n == size_u); @@ -1367,7 +1349,7 @@ pub const File = struct { nav_index: InternPool.Nav.Index, comptime format: []const u8, args: anytype, - ) error{ CodegenFail, OutOfMemory } { + ) Zcu.CodegenFailError { @branchHint(.cold); return base.comp.zcu.?.codegenFail(nav_index, format, args); } @@ -1440,7 +1422,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { defer prog_node.end(); for (comp.link_inputs) |input| { base.loadInput(input) catch |err| switch (err) { - error.LinkFailure => return, // error reported via diags + error.AlreadyReported => return, // error reported via diags else => |e| switch (input) { .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}), .object => |obj| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}), @@ -1485,11 +1467,11 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { .preferred_mode = .dynamic, .search_strategy = .paths_first, }) catch |archive_err| switch (archive_err) { - error.LinkFailure => return, // error reported via diags + error.AlreadyReported => return, // error reported via diags else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }), }; }, - error.LinkFailure => return, // error reported via diags + error.AlreadyReported => return, // error reported via diags else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), }; }, @@ -1504,7 +1486,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { .preferred_mode = .static, .search_strategy = .no_fallback, }) catch |err| switch (err) { - error.LinkFailure => return, // error reported via diags + error.AlreadyReported => return, // error reported via diags else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), }; }, @@ -1515,7 +1497,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { const prog_node = comp.link_prog_node.start("Parse Object", 0); defer prog_node.end(); base.openLoadObject(path) catch |err| switch (err) { - error.LinkFailure => return, // error reported via diags + error.AlreadyReported => return, // error reported via diags else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}), }; }, @@ -1523,7 +1505,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { const prog_node = comp.link_prog_node.start("Parse Archive", 0); defer prog_node.end(); base.openLoadArchive(load_archive.path, load_archive.must_link) catch |err| switch (err) { - error.LinkFailure => return, // error reported via link_diags + error.AlreadyReported => return, // error reported via link_diags else => |e| diags.addParseError(load_archive.path, "failed to parse archive: {s}", .{@errorName(e)}), }; }, @@ -1534,7 +1516,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { .preferred_mode = .dynamic, .search_strategy = .paths_first, }) catch |err| switch (err) { - error.LinkFailure => return, // error reported via link_diags + error.AlreadyReported => return, // error reported via link_diags else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), }; }, @@ -1561,15 +1543,9 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void }; } else if (comp.bin_file) |lf| { lf.updateNav(pt, nav_index) catch |err| switch (err) { + error.Canceled => io.recancel(), + error.AlreadyReported => return, error.OutOfMemory => diags.setAllocFailure(), - error.CodegenFail => zcu.assertCodegenFailed(nav_index), - error.Overflow, error.RelocationNotByteAligned => { - switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) { - error.CodegenFail => return, - error.OutOfMemory => return diags.setAllocFailure(), - } - // Not a retryable failure. - }, }; } break :nav nav_index; @@ -1594,14 +1570,9 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR if (comp.bin_file) |lf| { lf.updateFunc(pt, func, &mir) catch |err| switch (err) { + error.Canceled => io.recancel(), + error.AlreadyReported => return, error.OutOfMemory => return diags.setAllocFailure(), - error.CodegenFail => return zcu.assertCodegenFailed(nav), - error.Overflow, error.RelocationNotByteAligned => { - switch (zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)})) { - error.OutOfMemory => return diags.setAllocFailure(), - error.CodegenFail => return, - } - }, }; } break :nav ip.indexToKey(func).func.owner_nav; @@ -1618,7 +1589,8 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void if (comp.bin_file) |lf| { lf.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { error.OutOfMemory => diags.setAllocFailure(), - error.TypeFailureReported => assert(zcu.failed_types.contains(container_update.ty)), + error.Canceled => io.recancel(), + error.AlreadyReported => {}, }; } } @@ -1657,7 +1629,7 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void } } } -pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) error{ OutOfMemory, LinkFailure }!bool { +pub fn doIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) Error!bool { return if (comp.bin_file) |lf| lf.idle(tid) else false; } /// After the main pipeline is done, but before flush, the compilation may need to link one final @@ -1673,15 +1645,9 @@ pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) }; } else if (comp.bin_file) |lf| { lf.updateNav(pt, nav_index) catch |err| switch (err) { + error.Canceled => comp.io.recancel(), + error.AlreadyReported => return, error.OutOfMemory => diags.setAllocFailure(), - error.CodegenFail => zcu.assertCodegenFailed(nav_index), - error.Overflow, error.RelocationNotByteAligned => { - switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) { - error.CodegenFail => return, - error.OutOfMemory => return diags.setAllocFailure(), - } - // Not a retryable failure. - }, }; } } @@ -1689,7 +1655,8 @@ pub fn updateErrorData(pt: Zcu.PerThread) void { const comp = pt.zcu.comp; if (comp.bin_file) |lf| lf.updateErrorData(pt) catch |err| switch (err) { error.OutOfMemory => comp.link_diags.setAllocFailure(), - error.LinkFailure => {}, + error.Canceled => comp.io.recancel(), + error.AlreadyReported => {}, }; } @@ -2428,19 +2395,19 @@ pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !In }; } -pub fn openObjectInput(io: Io, diags: *Diags, path: Path) error{LinkFailure}!Input { +pub fn openObjectInput(io: Io, diags: *Diags, path: Path) error{AlreadyReported}!Input { return .{ .object = openObject(io, path, false, false) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; } -pub fn openArchiveInput(io: Io, diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input { +pub fn openArchiveInput(io: Io, diags: *Diags, path: Path, must_link: bool, hidden: bool) error{AlreadyReported}!Input { return .{ .archive = openObject(io, path, must_link, hidden) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; } -pub fn openDsoInput(io: Io, diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input { +pub fn openDsoInput(io: Io, diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{AlreadyReported}!Input { return .{ .dso = openDso(io, path, needed, weak, reexport) catch |err| { return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); } }; diff --git a/src/link/C.zig b/src/link/C.zig @@ -474,7 +474,7 @@ pub fn updateContainerType( pt: Zcu.PerThread, ty: InternPool.Index, success: bool, -) link.File.UpdateContainerTypeError!void { +) link.Error!void { try c.type_pool.updateContainerType(pt, .{ .c = c }, ty, success); } @@ -570,7 +570,6 @@ pub fn updateNav( .arena = arena.allocator(), .pt = pt, .mod = zcu.navFileScope(nav_index).mod.?, - .error_msg = null, .owner_nav = nav_index.toOptional(), .is_naked_fn = false, .expected_block = null, @@ -588,10 +587,7 @@ pub fn updateNav( defer c.string_bytes = aw.toArrayList(); const start = aw.written().len; codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) { - error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) { - error.CodegenFail => return, - error.OutOfMemory => |e| return e, - }, + error.AlreadyReported => return, error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; break :fwd_decl .{ @@ -605,10 +601,7 @@ pub fn updateNav( defer c.string_bytes = aw.toArrayList(); const start = aw.written().len; codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) { - error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) { - error.CodegenFail => return, - error.OutOfMemory => |e| return e, - }, + error.AlreadyReported => return, error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; break :code .{ @@ -661,7 +654,6 @@ fn updateUav( .arena = arena.allocator(), .pt = pt, .mod = pt.zcu.root_mod, - .error_msg = null, .owner_nav = .none, .is_naked_fn = false, .expected_block = null, @@ -683,9 +675,7 @@ fn updateUav( .@"threadlocal" = false, .init_val = val, }) catch |err| switch (err) { - error.AnalysisFail => { - @panic("TODO: CBE error.AnalysisFail on uav"); - }, + error.AlreadyReported => return, error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; break :fwd_decl .{ @@ -704,9 +694,7 @@ fn updateUav( .@"threadlocal" = false, .init_val = val, }) catch |err| switch (err) { - error.AnalysisFail => { - @panic("TODO: CBE error.AnalysisFail on uav"); - }, + error.AlreadyReported => return, error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, }; break :code .{ @@ -726,7 +714,7 @@ pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst. _ = ti_id; } -pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -1112,7 +1100,6 @@ pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Prog .owner_nav = .none, .is_naked_fn = false, .expected_block = null, - .error_msg = null, .ctype_deps = .empty, .uavs = .empty, }; @@ -1156,14 +1143,14 @@ pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Prog codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, error.OutOfMemory => |e| return e, - error.AnalysisFail => unreachable, + error.AlreadyReported => unreachable, }; } for (need_never_inline_funcs.keys()) |fn_nav| { codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, error.OutOfMemory => |e| return e, - error.AnalysisFail => unreachable, + error.AlreadyReported => unreachable, }; } } @@ -1256,7 +1243,6 @@ pub fn updateExports( .owner_nav = .none, .is_naked_fn = false, .expected_block = null, - .error_msg = null, .ctype_deps = .empty, .uavs = .empty, }; diff --git a/src/link/Coff.zig b/src/link/Coff.zig @@ -43,7 +43,6 @@ lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { }), pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct { alignment: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, }), relocs: std.ArrayList(Reloc), const_prog_node: std.Progress.Node, @@ -1532,11 +1531,8 @@ pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void { pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { coff.updateNavInner(pt, nav_index) catch |err| switch (err) { - error.OutOfMemory, - error.Overflow, - error.RelocationNotByteAligned, - => |e| return e, - else => |e| return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}), + else => |e| return e, + error.MappedFileIo => return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{coff.mf.io_err.?}), }; } fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { @@ -1579,12 +1575,11 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde codegen.generateSymbol( &coff.base, pt, - zcu.navSrcLoc(nav_index), .fromInterned(nav.resolved.?.value), &nw.interface, .{ .atom_index = @enumFromInt(@intFromEnum(si)) }, ) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, + error.WriteFailed => return nw.err.?, else => |e| return e, }; si.get(coff).size = @intCast(nw.interface.end); @@ -1615,8 +1610,7 @@ pub fn lowerUav( pt: Zcu.PerThread, uav_val: InternPool.Index, uav_align: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) !link.File.SymbolId { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -1633,12 +1627,11 @@ pub fn lowerUav( } else { gop.value_ptr.* = .{ .alignment = uav_align, - .src_loc = src_loc, }; coff.const_prog_node.increaseEstimatedTotalItems(1); } } - return .{ .sym_index = @enumFromInt(@intFromEnum(si)) }; + return @enumFromInt(@intFromEnum(si)); } pub fn updateFunc( @@ -1648,15 +1641,11 @@ pub fn updateFunc( mir: *const codegen.AnyMir, ) !void { coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) { - error.OutOfMemory, - error.Overflow, - error.RelocationNotByteAligned, - error.CodegenFail, - => |e| return e, - else => |e| return coff.base.cgFail( + else => |e| return e, + error.MappedFileIo => return coff.base.cgFail( pt.zcu.funcInfo(func_index).owner_nav, - "linker failed to update function: {s}", - .{@errorName(e)}, + "linker failed to update function: {t}", + .{coff.mf.io_err.?}, ), }; } @@ -1714,7 +1703,6 @@ fn updateFuncInner( codegen.emitFunction( &coff.base, pt, - zcu.navSrcLoc(func.owner_nav), func_index, @enumFromInt(@intFromEnum(si)), mir, @@ -1733,9 +1721,11 @@ pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void { .kind = .const_data, .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return), }) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.CodegenFail => return error.LinkFailure, - else => |e| return coff.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}), + else => |e| return e, + error.MappedFileIo => return coff.base.comp.link_diags.fail( + "updateErrorData failed: {t}", + .{coff.mf.io_err.?}, + ), }; } @@ -1780,12 +1770,11 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { .{ .zcu = comp.zcu.?, .tid = tid }, pending_uav.key, pending_uav.value.alignment, - pending_uav.value.src_loc, ) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return comp.link_diags.fail( + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail( "linker failed to lower constant: {t}", - .{e}, + .{coff.mf.io_err.?}, ), }; break :task; @@ -1800,10 +1789,10 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { ); defer sub_prog_node.end(); coff.flushGlobal(pt, gmi) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return comp.link_diags.fail( + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail( "linker failed to lower constant: {t}", - .{e}, + .{coff.mf.io_err.?}, ), }; break :task; @@ -1827,10 +1816,10 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool { ); defer sub_prog_node.end(); coff.flushLazy(pt, lmr) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return comp.link_diags.fail( + else => |e| return e, + error.MappedFileIo => return comp.link_diags.fail( "linker failed to lower lazy {s}: {t}", - .{ kind, e }, + .{ kind, coff.mf.io_err.? }, ), }; break :task; @@ -1885,7 +1874,6 @@ fn flushUav( pt: Zcu.PerThread, umi: Node.UavMapIndex, uav_align: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, ) !void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -1928,12 +1916,11 @@ fn flushUav( codegen.generateSymbol( &coff.base, pt, - src_loc, .fromInterned(uav_val), &nw.interface, .{ .atom_index = @enumFromInt(@intFromEnum(si)) }, ) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, + error.WriteFailed => return nw.err.?, else => |e| return e, }; si.get(coff).size = @intCast(nw.interface.end); @@ -2139,16 +2126,18 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { var nw: MappedFile.Node.Writer = undefined; ni.writer(&coff.mf, gpa, &nw); defer nw.deinit(); - try codegen.generateLazySymbol( + codegen.generateLazySymbol( &coff.base, pt, - Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded, lazy, &required_alignment, &nw.interface, .none, .{ .atom_index = @enumFromInt(@intFromEnum(si)) }, - ); + ) catch |err| switch (err) { + error.WriteFailed => return nw.err.?, + else => |e| return e, + }; si.get(coff).size = @intCast(nw.interface.end); si.applyLocationRelocs(coff); } @@ -2315,17 +2304,6 @@ pub fn updateExports( exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, ) !void { - return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) { - error.OutOfMemory => error.OutOfMemory, - error.LinkFailure => error.AnalysisFail, - }; -} -fn updateExportsInner( - coff: *Coff, - pt: Zcu.PerThread, - exported: Zcu.Exported, - export_indices: []const Zcu.Export.Index, -) !void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -2340,18 +2318,11 @@ fn updateExportsInner( try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len); const exported_si: Symbol.Index = switch (exported) { .nav => |nav| try coff.navSymbol(zcu, nav), - .uav => |uav| @enumFromInt(@intFromEnum(switch (try coff.lowerUav( + .uav => |uav| @enumFromInt(@intFromEnum(try coff.lowerUav( pt, uav, Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu), - export_indices[0].ptr(zcu).src, - )) { - .sym_index => |si| si, - .fail => |em| { - defer em.destroy(gpa); - return coff.base.comp.link_diags.fail("{s}", .{em.msg}); - }, - })), + ))), }; while (try coff.idle(pt.tid)) {} const exported_ni = exported_si.node(coff); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig @@ -51,21 +51,15 @@ pub const UpdateError = error{ Underflow, UnexpectedEndOfFile, NonResizable, - /// TODO why is this in the error set? - ConnectionResetByPeer, - /// TODO why is this in the error set? - SocketUnconnected, + Overflow, } || - codegen.GenerateSymbolError || + link.Error || Io.File.OpenError || Io.File.LengthError || Io.File.ReadPositionalError || Io.File.WritePositionalError; -pub const FlushError = UpdateError; - -pub const RelocError = - Io.File.PWriteError; +pub const RelocError = Io.File.PWriteError; pub const AddressSize = enum(u8) { @"32" = 4, @@ -1579,19 +1573,17 @@ pub const WipNav = struct { pub const LocalConstTag = enum { comptime_arg, local_const }; pub fn genLocalConstDebugInfo( wip_nav: *WipNav, - src_loc: Zcu.LazySrcLoc, tag: LocalConstTag, opt_name: ?[]const u8, val: Value, ) UpdateError!void { - return wip_nav.genLocalConstDebugInfoWriterError(src_loc, tag, opt_name, val) catch |err| switch (err) { + return wip_nav.genLocalConstDebugInfoWriterError(tag, opt_name, val) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => |e| e, }; } fn genLocalConstDebugInfoWriterError( wip_nav: *WipNav, - src_loc: Zcu.LazySrcLoc, tag: LocalConstTag, opt_name: ?[]const u8, val: Value, @@ -1617,7 +1609,7 @@ pub const WipNav = struct { }); if (opt_name) |name| try wip_nav.strp(name); try wip_nav.refType(ty); - if (has_runtime_bits) try wip_nav.blockValue(src_loc, val); + if (has_runtime_bits) try wip_nav.blockValue(val); if (has_comptime_state) try wip_nav.refValue(val); wip_nav.any_children = true; } @@ -2106,7 +2098,6 @@ pub const WipNav = struct { fn blockValue( wip_nav: *WipNav, - src_loc: Zcu.LazySrcLoc, val: Value, ) (UpdateError || Writer.Error)!void { const ty = val.typeOf(wip_nav.pt.zcu); @@ -2118,7 +2109,6 @@ pub const WipNav = struct { try codegen.generateSymbol( wip_nav.dwarf.bin_file, wip_nav.pt, - src_loc, val, &wip_nav.debug_info.writer, .{ .debug_output = .{ .dwarf = wip_nav } }, @@ -2592,7 +2582,7 @@ pub fn initWipNav( pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, sym_index: link.File.SymbolId, -) error{ OutOfMemory, CodegenFail }!WipNav { +) error{ OutOfMemory, AlreadyReported }!WipNav { return initWipNavInner(dwarf, pt, nav_index, sym_index) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, else => |e| pt.zcu.codegenFail(nav_index, "failed to init dwarf: {s}", .{@errorName(e)}), @@ -3017,7 +3007,7 @@ fn finishWipNavWriterError( try dwarf.const_pool.flushPending(pt, .{ .dwarf = dwarf }); } -pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, CodegenFail }!void { +pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error{ OutOfMemory, AlreadyReported }!void { return updateComptimeNavInner(dwarf, pt, nav_index) catch |err| switch (err) { error.OutOfMemory => error.OutOfMemory, else => |e| pt.zcu.codegenFail(nav_index, "failed to update dwarf: {s}", .{@errorName(e)}), @@ -3027,7 +3017,6 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { const zcu = pt.zcu; const ip = &zcu.intern_pool; - const nav_src_loc = zcu.navSrcLoc(nav_index); const nav = ip.getNav(nav_index); const inst_info = nav.srcInst(ip).resolveFull(ip).?; @@ -3207,7 +3196,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo }.toSlice(ip)); const nav_ty = nav_val.typeOf(zcu); try wip_nav.refType(nav_ty); - try wip_nav.blockValue(nav_src_loc, nav_val); + try wip_nav.blockValue(nav_val); try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse nav_ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); @@ -3241,7 +3230,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse nav_ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); - if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val); + if (has_runtime_bits) try wip_nav.blockValue(nav_val); if (has_comptime_state) try wip_nav.refValue(nav_val); wip_nav.finishForward(nav_ty_reloc_index); try wip_nav.abbrevCode(.is_const); @@ -3551,19 +3540,6 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co }; defer wip_nav.deinit(); - // TODO: we really shouldn't need source locations at this point in the pipeline: we've lost - // that information by now. If the linker fundamentally cannot lower certain values, that needs - // to be caught in the frontend; if it can only hit transient failures, they should be reported - // without trying to tie them to a bogus source location. - const src_loc: Zcu.LazySrcLoc = .{ - .base_node_inst = inst: { - const mod_root_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; - const mod_root_type_index = zcu.fileRootType(mod_root_file_index); - break :inst ip.loadStructType(mod_root_type_index).zir_index; - }, - .offset = .{ .byte_abs = 0 }, - }; - const diw = &wip_nav.debug_info.writer; var big_int_space: Value.BigIntSpace = undefined; switch (value_ip_key) { @@ -3588,7 +3564,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co else => if (ptr_type.sentinel == .none) .ptr_aligned_type else .ptr_aligned_sentinel_type, }); try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); - if (ptr_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(ptr_type.sentinel)); + if (ptr_type.sentinel != .none) try wip_nav.blockValue(.fromInterned(ptr_type.sentinel)); if (ptr_type.flags.alignment.toByteUnits()) |a| try diw.writeUleb128(a); try diw.writeByte(@intFromEnum(ptr_type.flags.address_space)); if (ptr_type.flags.is_const or ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset( @@ -3633,7 +3609,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co const array_child_type: Type = .fromInterned(array_type.child); try wip_nav.abbrevCode(if (array_type.sentinel == .none) .array_type else .array_sentinel_type); try wip_nav.strpFmt("{f}", .{val.toType().fmt(pt)}); - if (array_type.sentinel != .none) try wip_nav.blockValue(src_loc, .fromInterned(array_type.sentinel)); + if (array_type.sentinel != .none) try wip_nav.blockValue(.fromInterned(array_type.sentinel)); try wip_nav.refType(array_child_type); try wip_nav.abbrevCode(.array_len); try wip_nav.refType(.usize); @@ -3880,7 +3856,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(comptime_value)) else if (has_runtime_bits) - try wip_nav.blockValue(src_loc, .fromInterned(comptime_value)); + try wip_nav.blockValue(.fromInterned(comptime_value)); } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, @@ -3967,7 +3943,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(field_init)) else if (has_runtime_bits) - try wip_nav.blockValue(ty.srcLoc(zcu), .fromInterned(field_init)); + try wip_nav.blockValue(.fromInterned(field_init)); } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); } @@ -4332,7 +4308,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(payload_val)) else - try wip_nav.blockValue(src_loc, .fromInterned(payload_val)); + try wip_nav.blockValue(.fromInterned(payload_val)); }, } { @@ -4500,7 +4476,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co { try wip_nav.abbrevCode(.comptime_value_field_runtime_bits); try wip_nav.strp("len"); - try wip_nav.blockValue(src_loc, .fromInterned(slice.len)); + try wip_nav.blockValue(.fromInterned(slice.len)); } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, @@ -4513,8 +4489,8 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co try wip_nav.strp("has_value"); switch (optRepr(opt_child_type, zcu)) { .opv_null => try diw.writeUleb128(0), - .unpacked => try wip_nav.blockValue(src_loc, .makeBool(opt.val != .none)), - .error_set, .pointer => try wip_nav.blockValue(src_loc, .fromInterned(value_index)), + .unpacked => try wip_nav.blockValue(.makeBool(opt.val != .none)), + .error_set, .pointer => try wip_nav.blockValue(.fromInterned(value_index)), } } if (opt.val != .none) child_field: { @@ -4530,7 +4506,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(opt.val)) else - try wip_nav.blockValue(src_loc, .fromInterned(opt.val)); + try wip_nav.blockValue(.fromInterned(opt.val)); } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, @@ -4561,7 +4537,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(field_value) else - try wip_nav.blockValue(src_loc, field_value); + try wip_nav.blockValue(field_value); } }, .tuple_type => |tuple_type| for (0..tuple_type.types.len) |field_index| { @@ -4588,7 +4564,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(field_value) else - try wip_nav.blockValue(src_loc, field_value); + try wip_nav.blockValue(field_value); }, inline .array_type, .vector_type => |sequence_type| { const child_type: Type = .fromInterned(sequence_type.child); @@ -4608,7 +4584,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(elem)) else - try wip_nav.blockValue(src_loc, .fromInterned(elem)); + try wip_nav.blockValue(.fromInterned(elem)); } }, else => unreachable, @@ -4636,7 +4612,7 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co if (has_comptime_state) try wip_nav.refValue(.fromInterned(un.val)) else - try wip_nav.blockValue(src_loc, .fromInterned(un.val)); + try wip_nav.blockValue(.fromInterned(un.val)); } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, @@ -4708,13 +4684,13 @@ fn refAbbrevCode( return @intFromEnum(abbrev_code); } -pub fn flush(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { +pub fn flush(dwarf: *Dwarf, pt: Zcu.PerThread) UpdateError!void { return dwarf.flushWriterError(pt) catch |err| switch (err) { error.WriteFailed => error.OutOfMemory, else => |e| e, }; } -fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (FlushError || Writer.Error)!void { +fn flushWriterError(dwarf: *Dwarf, pt: Zcu.PerThread) (UpdateError || Writer.Error)!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const comp = dwarf.bin_file.comp; diff --git a/src/link/Elf.zig b/src/link/Elf.zig @@ -476,9 +476,8 @@ pub fn lowerUav( pt: Zcu.PerThread, uav: InternPool.Index, explicit_alignment: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { - return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment, src_loc); +) !link.File.SymbolId { + return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment); } pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -743,7 +742,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { } } -pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -757,7 +756,7 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std defer sub_prog_node.end(); return flushInner(self, arena, tid) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("ELF flush failed: {t}", .{e}), }; } @@ -784,7 +783,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { .Exe => {}, } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; // If we haven't already, create a linker-generated input file comprising of // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. @@ -816,7 +815,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { } self.checkDuplicates() catch |err| switch (err) { - error.HasDuplicates => return error.LinkFailure, + error.HasDuplicates => return error.AlreadyReported, else => |e| return e, }; @@ -903,7 +902,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); - return error.LinkFailure; + return error.AlreadyReported; }, else => |e| return e, }; @@ -912,7 +911,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { try self.reportUndefinedSymbols(&undefs); - if (has_reloc_errors) return error.LinkFailure; + if (has_reloc_errors) return error.AlreadyReported; } try self.writePhdrTable(); @@ -921,10 +920,10 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { try self.writeMergeSections(); self.writeSyntheticSections() catch |err| switch (err) { - error.RelocFailure => return error.LinkFailure, + error.RelocFailure => return error.AlreadyReported, error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); - return error.LinkFailure; + return error.AlreadyReported; }, else => |e| return e, }; @@ -938,7 +937,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { try self.writeElfHeader(); } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn dumpArgvInit(self: *Elf, arena: Allocator) !void { @@ -1053,7 +1052,7 @@ pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void { const diags = &comp.link_diags; const obj = link.openObject(io, path, false, false) catch |err| { switch (diags.failParse(path, "failed to open object: {t}", .{err})) { - error.LinkFailure => return, + error.AlreadyReported => return, } }; self.parseObjectReportingFailure(obj); @@ -1063,7 +1062,7 @@ fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void { const comp = self.base.comp; const diags = &comp.link_diags; self.parseObject(obj) catch |err| switch (err) { - error.LinkFailure => return, // already reported + error.AlreadyReported => return, // already reported else => |e| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}), }; } @@ -1343,7 +1342,7 @@ fn scanRelocs(self: *Elf) !void { error.RelaxFailure => unreachable, error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); - return error.LinkFailure; + return error.AlreadyReported; }, error.RelocFailure => has_reloc_errors = true, else => |e| return e, @@ -1354,7 +1353,7 @@ fn scanRelocs(self: *Elf) !void { error.RelaxFailure => unreachable, error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); - return error.LinkFailure; + return error.AlreadyReported; }, error.RelocFailure => has_reloc_errors = true, else => |e| return e, @@ -1363,7 +1362,7 @@ fn scanRelocs(self: *Elf) !void { try self.reportUndefinedSymbols(&undefs); - if (has_reloc_errors) return error.LinkFailure; + if (has_reloc_errors) return error.AlreadyReported; if (self.zigObjectPtr()) |zo| { try zo.asFile().createSymbolIndirection(self); @@ -1690,7 +1689,7 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) link.File.UpdateNavError!void { +) link.Error!void { return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir); } @@ -1698,7 +1697,7 @@ pub fn updateNav( self: *Elf, pt: Zcu.PerThread, nav: InternPool.Nav.Index, -) link.File.UpdateNavError!void { +) link.Error!void { return self.zigObjectPtr().?.updateNav(self, pt, nav); } @@ -1707,7 +1706,7 @@ pub fn updateContainerType( pt: Zcu.PerThread, ty: InternPool.Index, success: bool, -) link.File.UpdateContainerTypeError!void { +) link.Error!void { return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) { error.OutOfMemory => |e| return e, }; @@ -1718,11 +1717,11 @@ pub fn updateExports( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) link.File.UpdateExportsError!void { +) link.Error!void { return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices); } -pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { return self.zigObjectPtr().?.updateLineNumber(pt, ti_id); } @@ -1784,12 +1783,12 @@ pub fn resolveMergeSections(self: *Elf) !void { if (!object.alive) continue; if (!object.dirty) continue; object.initInputMergeSections(self) catch |err| switch (err) { - error.LinkFailure => has_errors = true, + error.AlreadyReported => has_errors = true, else => |e| return e, }; } - if (has_errors) return error.LinkFailure; + if (has_errors) return error.AlreadyReported; for (self.objects.items) |index| { const object = self.file(index).?.object; @@ -1803,12 +1802,12 @@ pub fn resolveMergeSections(self: *Elf) !void { if (!object.alive) continue; if (!object.dirty) continue; object.resolveMergeSubsections(self) catch |err| switch (err) { - error.LinkFailure => has_errors = true, + error.AlreadyReported => has_errors = true, else => |e| return e, }; } - if (has_errors) return error.LinkFailure; + if (has_errors) return error.AlreadyReported; } pub fn finalizeMergeSections(self: *Elf) !void { @@ -2998,7 +2997,7 @@ fn writeAtoms(self: *Elf) !void { atom_list.write(&buffer, &undefs, self) catch |err| switch (err) { error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); - return error.LinkFailure; + return error.AlreadyReported; }, error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, else => |e| return e, @@ -3006,7 +3005,7 @@ fn writeAtoms(self: *Elf) !void { } try self.reportUndefinedSymbols(&undefs); - if (has_reloc_errors) return error.LinkFailure; + if (has_reloc_errors) return error.AlreadyReported; if (self.requiresThunks()) { for (self.thunks.items) |th| { @@ -3838,9 +3837,9 @@ pub fn failFile( file_index: File.Index, comptime format: []const u8, args: anytype, -) error{ OutOfMemory, LinkFailure } { +) error{ OutOfMemory, AlreadyReported } { try addFileError(self, file_index, format, args); - return error.LinkFailure; + return error.AlreadyReported; } const FormatShdr = struct { @@ -4409,7 +4408,7 @@ pub fn stringTableLookup(strtab: []const u8, off: u32) [:0]const u8 { return slice[0..mem.indexOfScalar(u8, slice, 0).? :0]; } -pub fn pwriteAll(elf_file: *Elf, bytes: []const u8, offset: u64) error{LinkFailure}!void { +pub fn pwriteAll(elf_file: *Elf, bytes: []const u8, offset: u64) error{AlreadyReported}!void { const comp = elf_file.base.comp; const io = comp.io; const diags = &comp.link_diags; @@ -4417,7 +4416,7 @@ pub fn pwriteAll(elf_file: *Elf, bytes: []const u8, offset: u64) error{LinkFailu return diags.fail("failed to write: {t}", .{err}); } -pub fn setLength(elf_file: *Elf, length: u64) error{LinkFailure}!void { +pub fn setLength(elf_file: *Elf, length: u64) error{AlreadyReported}!void { const comp = elf_file.base.comp; const io = comp.i; const diags = &comp.link_diags; @@ -4426,7 +4425,7 @@ pub fn setLength(elf_file: *Elf, length: u64) error{LinkFailure}!void { }; } -pub fn cast(elf_file: *Elf, comptime T: type, x: anytype) error{LinkFailure}!T { +pub fn cast(elf_file: *Elf, comptime T: type, x: anytype) error{AlreadyReported}!T { return std.math.cast(T, x) orelse { const comp = elf_file.base.comp; const diags = &comp.link_diags; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig @@ -282,7 +282,7 @@ pub fn validateEFlags( ); } - if (any_errors) return error.LinkFailure; + if (any_errors) return error.AlreadyReported; }, else => {}, } @@ -829,7 +829,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { var err = try diags.addErrorWithNotes(1); try err.addMsg("string not null terminated", .{}); err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); - return error.LinkFailure; + return error.AlreadyReported; } end += sh_entsize; const string = data[start..end]; @@ -844,7 +844,7 @@ pub fn initInputMergeSections(self: *Object, elf_file: *Elf) !void { var err = try diags.addErrorWithNotes(1); try err.addMsg("size not a multiple of sh_entsize", .{}); err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); - return error.LinkFailure; + return error.AlreadyReported; } var pos: u32 = 0; @@ -873,7 +873,7 @@ pub fn initOutputMergeSections(self: *Object, elf_file: *Elf) !void { } pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ - LinkFailure, + AlreadyReported, OutOfMemory, /// TODO report the error and remove this Overflow, @@ -925,7 +925,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ try err.addMsg("invalid symbol value: {x}", .{esym.st_value}); err.addNote("for symbol {s}", .{sym.name(elf_file)}); err.addNote("in {f}", .{self.fmtPath()}); - return error.LinkFailure; + return error.AlreadyReported; }; sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index }; @@ -950,7 +950,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) error{ var err = try diags.addErrorWithNotes(1); try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset}); err.addNote("in {f}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) }); - return error.LinkFailure; + return error.AlreadyReported; }; const sym_index = try self.addSymbol(gpa); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig @@ -272,24 +272,18 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { // Most lazy symbols can be updated on first use, but // anyerror needs to wait for everything to be flushed. - if (metadata.text_state != .unused) self.updateLazySymbol( + if (metadata.text_state != .unused) try self.updateLazySymbol( elf_file, pt, .{ .kind = .code, .ty = .anyerror_type }, metadata.text_symbol_index, - ) catch |err| switch (err) { - error.CodegenFail => return error.LinkFailure, - else => |e| return e, - }; - if (metadata.rodata_state != .unused) self.updateLazySymbol( + ); + if (metadata.rodata_state != .unused) try self.updateLazySymbol( elf_file, pt, .{ .kind = .const_data, .ty = .anyerror_type }, metadata.rodata_symbol_index, - ) catch |err| switch (err) { - error.CodegenFail => return error.LinkFailure, - else => |e| return e, - }; + ); } for (self.lazy_syms.values()) |*metadata| { if (metadata.text_state != .unused) metadata.text_state = .flushed; @@ -999,8 +993,7 @@ pub fn lowerUav( pt: Zcu.PerThread, uav: InternPool.Index, explicit_alignment: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) !link.File.SymbolId { const zcu = pt.zcu; const gpa = zcu.gpa; const val = Value.fromInterned(uav); @@ -1013,7 +1006,7 @@ pub fn lowerUav( const sym = self.symbol(metadata.symbol_index); const existing_alignment = sym.atom(elf_file).?.alignment; if (uav_alignment.order(existing_alignment).compare(.lte)) - return .{ .sym_index = @enumFromInt(metadata.symbol_index) }; + return @enumFromInt(metadata.symbol_index); } const osec = if (self.data_relro_index) |sym_index| @@ -1033,31 +1026,25 @@ pub fn lowerUav( const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{ @intFromEnum(uav), }) catch unreachable; - const res = self.lowerConst( + const sym_index = self.lowerConst( elf_file, pt, name, val, uav_alignment, osec, - src_loc, ) catch |err| switch (err) { error.OutOfMemory => |e| return e, - else => |e| return .{ .fail = try Zcu.ErrorMsg.create( - gpa, - src_loc, - "unable to lower constant value: {s}", - .{@errorName(e)}, - ) }, + else => |e| return elf_file.base.comp.link_diags.fail( + "failed to lower constant value: {t}", + .{e}, + ), }; - switch (res) { - .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ - .symbol_index = @intFromEnum(sym_index), - .allocated = true, - }), - .fail => {}, - } - return res; + try self.uavs.put(gpa, uav, .{ + .symbol_index = @intFromEnum(sym_index), + .allocated = true, + }); + return sym_index; } pub fn getOrCreateMetadataForLazySymbol( @@ -1370,7 +1357,7 @@ fn updateNavCode( shdr_index: u32, code: []const u8, stt_bits: u8, -) link.File.UpdateNavError!void { +) link.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const comp = elf_file.base.comp; @@ -1473,7 +1460,7 @@ fn updateTlv( sym_index: Symbol.Index, shndx: u32, code: []const u8, -) link.File.UpdateNavError!void { +) link.Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; const gpa = zcu.gpa; @@ -1531,7 +1518,7 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) link.File.UpdateNavError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -1558,7 +1545,6 @@ pub fn updateFunc( codegen.emitFunction( &elf_file.base, pt, - zcu.navSrcLoc(func.owner_nav), func_index, @enumFromInt(sym_index), mir, @@ -1645,7 +1631,7 @@ pub fn updateNav( elf_file: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, -) link.File.UpdateNavError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -1670,7 +1656,7 @@ pub fn updateNav( var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index)); defer debug_wip_nav.deinit(); dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { - error.OutOfMemory, error.Overflow => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), }; } @@ -1691,7 +1677,6 @@ pub fn updateNav( codegen.generateSymbol( &elf_file.base, pt, - zcu.navSrcLoc(nav_index), .fromInterned(nav.resolved.?.value), &aw.writer, .{ .atom_index = @enumFromInt(sym_index) }, @@ -1713,7 +1698,7 @@ pub fn updateNav( try self.updateNavCode(elf_file, pt, nav_index, sym_index, shndx, code, elf.STT_OBJECT); if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) { - error.OutOfMemory, error.Overflow => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return elf_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), }; } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); @@ -1759,7 +1744,6 @@ fn updateLazySymbol( codegen.generateLazySymbol( &elf_file.base, pt, - Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse .unneeded, sym, &required_alignment, &aw.writer, @@ -1827,8 +1811,7 @@ fn lowerConst( val: Value, required_alignment: InternPool.Alignment, output_section_index: u32, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) !link.File.SymbolId { const gpa = pt.zcu.gpa; var aw: std.Io.Writer.Allocating = .init(gpa); @@ -1840,7 +1823,6 @@ fn lowerConst( codegen.generateSymbol( &elf_file.base, pt, - src_loc, val, &aw.writer, .{ .atom_index = @enumFromInt(sym_index) }, @@ -1865,7 +1847,7 @@ fn lowerConst( try elf_file.pwriteAll(code, atom_ptr.offset(elf_file)); - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); } pub fn updateExports( @@ -1874,7 +1856,7 @@ pub fn updateExports( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) link.File.UpdateExportsError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -1886,18 +1868,7 @@ pub fn updateExports( break :blk self.navs.getPtr(nav).?; }, .uav => |uav| self.uavs.getPtr(uav) orelse blk: { - const first_exp = export_indices[0].ptr(zcu); - const res = try self.lowerUav(elf_file, pt, uav, .none, first_exp.src); - switch (res) { - .sym_index => {}, - .fail => |em| { - // TODO maybe it's enough to return an error here and let Zcu.processExportsInner - // handle the error? - try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); - zcu.failed_exports.putAssumeCapacityNoClobber(export_indices[0], em); - return; - }, - } + _ = try self.lowerUav(elf_file, pt, uav, .none); break :blk self.uavs.getPtr(uav).?; }, }; @@ -1962,12 +1933,12 @@ pub fn updateExports( } } -pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { if (self.dwarf) |*dwarf| { const comp = dwarf.bin_file.comp; const diags = &comp.link_diags; dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { - error.Overflow, error.OutOfMemory => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; } diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig @@ -23,7 +23,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void { const io = comp.io; const diags = &comp.link_diags; - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; // First, we flush relocatable object file generated with our backends. if (elf_file.zigObjectPtr()) |zig_object| { @@ -151,13 +151,13 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void { try elf_file.base.file.?.setLength(io, total_size); try elf_file.base.file.?.writePositionalAll(io, writer.buffered(), 0); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void { const diags = &comp.link_diags; - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; // Now, we are ready to resolve the symbols across all input files. // We will first resolve the files in the ZigObject, next in the parsed @@ -203,7 +203,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void { try elf_file.writeShdrTable(); try elf_file.writeElfHeader(); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn claimUnresolved(elf_file: *Elf) void { diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig @@ -162,6 +162,8 @@ const_prog_node: std.Progress.Node, synth_prog_node: std.Progress.Node, input_prog_node: std.Progress.Node, +const Error = link.Error || error{MappedFileIo}; + const Node = union(enum) { file, ehdr, @@ -478,7 +480,7 @@ const Section = struct { }; } - fn rename(shndx: Index, elf: *Elf, new_name: []const u8) !void { + fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void { const shstrtab_entry = try elf.string(.shstrtab, new_name); switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)), @@ -487,7 +489,7 @@ const Section = struct { /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused /// space to hold `n` additional `ElfN.Rela` entries. - fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) !void { + fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { const node = rela_shndx.get(elf).ni; const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) { inline else => |shdr, class| need_size: { @@ -509,11 +511,7 @@ const Section = struct { break :need_size cur_size + need_additional * ent_size; }, }; - _, const cur_node_size = node.location(&elf.mf).resolve(&elf.mf); - if (need_size > cur_node_size) { - const gpa = elf.base.comp.gpa; - try node.resize(&elf.mf, gpa, need_size +| need_size / MappedFile.growth_factor); - } + try elf.ensureNodeSize(node, need_size); } /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the @@ -1192,7 +1190,7 @@ const SymbolReloc = struct { } }; -fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void { +fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { const gpa = elf.base.comp.gpa; try elf.symtab.ensureUnusedCapacity(gpa, len); @@ -1210,11 +1208,7 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) { inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), }; - _, const cur_node_size = Section.Index.symtab.get(elf).ni.location(&elf.mf).resolve(&elf.mf); - if (cur_node_size < need_node_size) { - const new_node_size = need_node_size +| need_node_size / MappedFile.growth_factor; - try Section.Index.symtab.get(elf).ni.resize(&elf.mf, gpa, new_node_size); - } + try elf.ensureNodeSize(Section.Index.symtab.get(elf).ni, need_node_size); } switch (kind) { @@ -1232,18 +1226,14 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe const dynsym_need_size: u64 = switch (elf.shdrPtr(elf.shndx.dynsym)) { inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym), }; - _, const dynsym_cur_size = elf.shndx.dynsym.get(elf).ni.location(&elf.mf).resolve(&elf.mf); - if (dynsym_cur_size < dynsym_need_size) { - const new_size = dynsym_need_size +| dynsym_need_size / MappedFile.growth_factor; - try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size); - } + try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size); try elf.ensureUnusedPltCapacity(len); } }, } } -fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { +fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void { const gpa = elf.base.comp.gpa; try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len); @@ -1256,30 +1246,18 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void { .X86_64 => { // Ensure the `.plt` section's node is big enough const plt_need_size: usize = 16 * (1 + need_plt_capacity); - _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); - if (plt_cur_size < plt_need_size) { - const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor; - try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size); - } + try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, plt_need_size); // Ensure the `.got.plt` section's node is big enough const got_plt_need_size: usize = switch (elf.identClass()) { .NONE, _ => unreachable, inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity), }; - _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf); - if (got_plt_cur_size < got_plt_need_size) { - const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor; - try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size); - } + try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, got_plt_need_size); // Ensure the `.plt.sec` section's node is big enough const plt_sec_need_size: usize = 16 * need_plt_capacity; - _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf); - if (plt_sec_cur_size < plt_sec_need_size) { - const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor; - try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size); - } + try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, plt_sec_need_size); }, } } @@ -1375,7 +1353,7 @@ const AddGlobalSymbolOptions = struct { const Name = struct { strtab: String(.strtab), dynstr: String(.dynstr), - fn string(elf: *Elf, slice: []const u8) !Name { + fn string(elf: *Elf, slice: []const u8) Error!Name { return .{ .strtab = try elf.string(.strtab, slice), .dynstr = switch (elf.shndx.dynsym) { @@ -2205,7 +2183,14 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId { const s: Symbol.Id = .local(lsi); return s.toTypeErased(); } -pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { +pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.SymbolId { + const diags = &elf.base.comp.link_diags; + return elf.lazySymbolInner(lazy) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; +} +fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId { const gpa = elf.base.comp.gpa; try elf.ensureUnusedSymbolCapacity(1, .all_local); @@ -2246,13 +2231,21 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId { const s: Symbol.Id = .local(gop.value_ptr.lsi); return s.toTypeErased(); } -pub fn externSymbol(elf: *Elf, opts: struct { +pub const ExternSymbolOpts = struct { name: []const u8, lib_name: ?[]const u8, type: std.elf.STT, linkage: std.lang.GlobalLinkage = .strong, visibility: std.lang.SymbolVisibility = .default, -}) !link.File.SymbolId { +}; +pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.SymbolId { + const diags = &elf.base.comp.link_diags; + return elf.externSymbolInner(opts) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; +} +fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!link.File.SymbolId { try elf.ensureUnusedSymbolCapacity(1, .maybe_global); const symbol = elf.addGlobalSymbolAssumeCapacity(.{ .node = .none, @@ -2265,7 +2258,7 @@ pub fn externSymbol(elf: *Elf, opts: struct { .internal => @panic("TODO internal extern symbol"), .strong => .strong, .weak => .weak, - .link_once => return error.LinkOnceUnsupported, + .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}), }, .visibility = switch (opts.visibility) { .default => .DEFAULT, @@ -2285,12 +2278,20 @@ pub fn addReloc( target: link.File.SymbolId, addend: i64, @"type": MachineRelocType, -) !void { +) link.Error!void { const node: MappedFile.Node.Index = Node.fromAtom(atom); - try elf.ensureUnusedRelocCapacity(node, 1); - try elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type"); + const diags = &elf.base.comp.link_diags; + elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; + elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; } -pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId { +pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId { + const diags = &elf.base.comp.link_diags; const zcu = elf.base.comp.zcu.?; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); @@ -2303,7 +2304,10 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId .visibility = @"extern".visibility, }); } - const nmi = try elf.navMapIndex(zcu, nav_index); + const nmi = elf.navMapIndex(zcu, nav_index) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; const s: Symbol.Id = .local(nmi.symbol(elf)); return s.toTypeErased(); } @@ -2311,8 +2315,12 @@ pub fn uavSymbol( elf: *Elf, uav_val: InternPool.Index, uav_align: InternPool.Alignment, -) !link.File.SymbolId { - const umi = try elf.uavMapIndex(uav_val, uav_align); +) link.Error!link.File.SymbolId { + const diags = &elf.base.comp.link_diags; + const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) { + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, + }; const s: Symbol.Id = .local(umi.symbol(elf)); return s.toTypeErased(); } @@ -2321,7 +2329,7 @@ pub fn getNavVAddr( pt: Zcu.PerThread, nav: InternPool.Nav.Index, reloc_info: link.File.RelocInfo, -) !u64 { +) link.Error!u64 { _ = pt; return elf.getVAddr(reloc_info, try elf.navSymbol(nav)); } @@ -2329,41 +2337,33 @@ pub fn getUavVAddr( elf: *Elf, uav_val: InternPool.Index, reloc_info: link.File.RelocInfo, -) !u64 { +) link.Error!u64 { return elf.getVAddr(reloc_info, try elf.uavSymbol(uav_val, .none)); } -pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) !u64 { - const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index); - const target_sym: Symbol.Id = .fromTypeErased(target); - try elf.ensureUnusedRelocCapacity(node, 1); - try elf.addRelocAssumeCapacity( - node, +pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 { + try elf.addReloc( + reloc_info.parent.atom_index, reloc_info.offset, - target_sym, + target, reloc_info.addend, .absAddr(elf), ); - return target_sym.value(elf); + return Symbol.Id.fromTypeErased(target).value(elf); } pub fn lowerUav( elf: *Elf, pt: Zcu.PerThread, uav_val: InternPool.Index, uav_align: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) link.Error!link.File.SymbolId { _ = pt; + const diags = &elf.base.comp.link_diags; const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return .{ .fail = try Zcu.ErrorMsg.create( - elf.base.comp.gpa, - src_loc, - "linker failed to update constant: {s}", - .{@errorName(e)}, - ) }, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; const s: Symbol.Id = .local(umi.symbol(elf)); - return .{ .sym_index = s.toTypeErased() }; + return s.toTypeErased(); } const StringSection = enum { @@ -2390,7 +2390,7 @@ fn String(section: StringSection) type { } }; } -fn string(elf: *Elf, comptime section: StringSection, key: []const u8) !String(section) { +fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) { const st: *StringTable = &@field(elf, @tagName(section)); return @enumFromInt(try st.get(elf, section.shndx(elf), key)); } @@ -2424,7 +2424,7 @@ const StringTable = struct { } }; - pub fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) !u32 { + pub fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 { // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special // case the empty string. if (key.len == 0) return 0; @@ -2450,9 +2450,7 @@ const StringTable = struct { if (shndx == elf.shndx.dynstr) { elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size); } - _, const node_size = ni.location(&elf.mf).resolve(&elf.mf); - if (new_size > node_size) - try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor); + try elf.ensureNodeSize(ni, new_size); const slice = ni.slice(&elf.mf)[old_size..]; @memcpy(slice[0..key.len], key); slice[key.len] = 0; @@ -3615,7 +3613,13 @@ fn mapInputSection(elf: *Elf, opts: struct { flags: std.elf.SHF, addralign: std.elf.Xword, entsize: std.elf.Xword, -}) !Section.Index { +}) (Error || error{ + UnsupportedSectionFlags, + TlsSectionUnavailable, + StripSection, + SectionFlagsConflict, + SectionTypeConflict, +})!Section.Index { const gpa = elf.base.comp.gpa; if (opts.flags.INFO_LINK or opts.flags.LINK_ORDER or @@ -3733,7 +3737,7 @@ fn mapInputSection(elf: *Elf, opts: struct { } return existing_shndx; } -fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex { +fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node.NavMapIndex { const gpa = zcu.gpa; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); @@ -3826,7 +3830,7 @@ fn uavMapIndex( elf: *Elf, uav_val: InternPool.Index, uav_align: InternPool.Alignment, -) !Node.UavMapIndex { +) Error!Node.UavMapIndex { const gpa = elf.base.comp.gpa; const zcu = elf.base.comp.zcu.?; @@ -3878,26 +3882,78 @@ fn uavMapIndex( return umi; } -pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || - Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, BadMagic, LinkFailure })!void { - const io = elf.base.comp.io; +/// Internal error set used by input parsing functions `loadObject`, `loadArchive`, `loadDso`. +const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error; + +/// Returns `error.BadMagic` if a DSO or static archive has an incorrect magic number, which +/// indicates to the frontend that the input could be a GNU ld script instead. +pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void { + const diags = &elf.base.comp.link_diags; + return elf.loadInputInner(input) catch |err| switch (err) { + else => |e| return e, + error.MappedFileIo => return diags.fail( + "failed to write output file: {t}", + .{elf.mf.io_err.?}, + ), + }; +} +fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void { + const comp = elf.base.comp; + const diags = &comp.link_diags; + const io = comp.io; var buf: [4096]u8 = undefined; switch (input) { .object => |object| { var fr = object.file.reader(io, &buf); elf.loadObject(object.path, null, &fr, .{ .offset = fr.logicalPos(), - .size = try fr.getSize(), + .size = fr.getSize() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return diags.fail( + "failed to stat \"{f}\": {t}", + .{ object.path.fmtEscapeString(), e }, + ), + }, }) catch |err| switch (err) { - error.ReadFailed => return fr.err.?, else => |e| return e, + error.EndOfStream => return diags.failParse( + object.path, + "unexpected eof", + .{}, + ), + error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ object.path.fmtEscapeString(), e }, + ), + error.ReadFailed => switch (fr.err.?) { + error.Canceled => |e| return e, + else => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ object.path.fmtEscapeString(), e }, + ), + }, }; }, .archive => |archive| { var fr = archive.file.reader(io, &buf); elf.loadArchive(archive.path, &fr) catch |err| switch (err) { - error.ReadFailed => return fr.err.?, else => |e| return e, + error.EndOfStream => return diags.failParse( + archive.path, + "unexpected eof", + .{}, + ), + error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ archive.path.fmtEscapeString(), e }, + ), + error.ReadFailed => switch (fr.err.?) { + error.Canceled => |e| return e, + else => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ archive.path.fmtEscapeString(), e }, + ), + }, }; }, .res => unreachable, @@ -3905,8 +3961,23 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1); var fr = dso.file.reader(io, &buf); elf.loadDso(dso.path, &fr) catch |err| switch (err) { - error.ReadFailed => return fr.err.?, else => |e| return e, + error.EndOfStream => return diags.failParse( + dso.path, + "unexpected eof", + .{}, + ), + error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ dso.path.fmtEscapeString(), e }, + ), + error.ReadFailed => switch (fr.err.?) { + error.Canceled => |e| return e, + else => |e| return diags.fail( + "failed to read \"{f}\": {t}", + .{ dso.path.fmtEscapeString(), e }, + ), + }, }; }, .dso_exact => |dso_exact| { @@ -3919,14 +3990,22 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError || }, } } -fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { +fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { const comp = elf.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; const r = &fr.interface; log.debug("loadArchive({f})", .{path.fmtEscapeString()}); - if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG)) return error.BadMagic; + { + const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) { + error.ReadFailed => |e| return e, + error.EndOfStream => return error.BadMagic, + }; + if (!std.mem.eql(u8, magic, std.elf.ARMAG)) { + return error.BadMagic; + } + } var strtab: std.Io.Writer.Allocating = .init(gpa); defer strtab.deinit(); while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| { @@ -3987,7 +4066,7 @@ fn loadObject( member: ?[]const u8, fr: *Io.File.Reader, fl: MappedFile.Node.FileLocation, -) !void { +) LoadParseInputError!void { const comp = elf.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; @@ -3995,7 +4074,14 @@ fn loadObject( const input_index: Node.InputIndex = @enumFromInt(elf.inputs.items.len); log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) }); - try elf.checkInputIdent(path, r); + elf.checkInputIdent(path, r) catch |err| switch (err) { + else => |e| return e, + error.BadMagic => return diags.failParse( + path, + "bad ELF magic", + .{}, + ), + }; try elf.ensureUnusedSymbolCapacity(1, .all_local); try elf.inputs.ensureUnusedCapacity(gpa, 1); const file_symbol = elf.addLocalSymbolAssumeCapacity(.{ @@ -4376,7 +4462,7 @@ fn loadObject( }, } } -fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { +fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { const comp = elf.base.comp; const gpa = comp.gpa; const diags = &comp.link_diags; @@ -4593,23 +4679,29 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { /// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input. /// -/// Returns an error if it is incompatible, or if the ident is broken or missing. +/// Returns an error if it is incompatible, or if the ident is broken or missing---usually +/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns +/// `error.BadMagic` instead. /// /// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer. fn checkInputIdent( elf: *const Elf, path: std.Build.Cache.Path, r: *Io.Reader, -) !void { +) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void { const diags = &elf.base.comp.link_diags; - const ident = try r.peekStructPointer(std.elf.Ident); - const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]); - - if (!std.mem.eql(u8, &ident.magic, std.elf.MAGIC)) { + const magic = r.peek(std.elf.MAGIC.len) catch |err| switch (err) { + error.ReadFailed => |e| return e, + error.EndOfStream => return error.BadMagic, + }; + if (!std.mem.eql(u8, magic, std.elf.MAGIC)) { return error.BadMagic; } + const ident = try r.peekStructPointer(std.elf.Ident); + const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]); + if (ident.class != target.class) return diags.failParse( path, "bad ELF class ({?s})", @@ -4649,7 +4741,7 @@ fn createInitFiniArraySection( shndx: *Section.Index, comptime name: []const u8, @"type": std.elf.SHT, -) !void { +) Error!void { assert(shndx.* == .UNDEF); const gpa = elf.base.comp.gpa; const addr_align: std.mem.Alignment = switch (elf.identClass()) { @@ -4722,14 +4814,15 @@ fn updateInitFiniArraySectionSize( Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr); } -pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { +pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void { _ = prog_node; + const diags = &elf.base.comp.link_diags; elf.prelinkInner() catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return elf.base.comp.link_diags.fail("prelink failed: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; } -fn prelinkInner(elf: *Elf) !void { +fn prelinkInner(elf: *Elf) Error!void { const comp = elf.base.comp; const gpa = comp.gpa; try elf.ensureUnusedSymbolCapacity(1, .all_local); @@ -4954,7 +5047,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { entsize: std.elf.Word = 0, node_align: std.mem.Alignment = .@"1", fixed: bool = false, -}) !Section.Index { +}) Error!Section.Index { switch (opts.type) { .NULL => assert(opts.size == 0), .PROGBITS => assert(opts.size > 0), @@ -4996,9 +5089,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { break :shndx .{ @enumFromInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) }; }, }; - _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf); - if (new_shdr_size > shdr_node_size) - try elf.ni.shdr.resize(&elf.mf, gpa, new_shdr_size +| new_shdr_size / MappedFile.growth_factor); + try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size); const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) { .NONE, .CORE, _ => unreachable, .REL => elf.ni.file, @@ -5045,7 +5136,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { return shndx; } -fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void { +fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) Error!void { if (len == 0) return; const gpa = elf.base.comp.gpa; try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); @@ -5090,14 +5181,11 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD try elf.got.ensureUnusedCapacity(gpa, new_got_entries); - const got_ni = elf.shndx.got.get(elf).ni; - _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf); const need_got_size = switch (class) { .NONE, _ => unreachable, inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr), }; - if (need_got_size > got_node_size) - try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor); + try elf.ensureNodeSize(elf.shndx.got.get(elf).ni, need_got_size); if (elf.shndx.dynamic != .UNDEF) { try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries); @@ -5114,7 +5202,7 @@ fn addRelocAssumeCapacity( target: Symbol.Id, addend: i64, @"type": MachineRelocType, -) !void { +) Error!void { assert(node != .none); switch (elf.ehdrField(.type)) { .NONE, .CORE, _ => unreachable, @@ -5233,7 +5321,7 @@ fn addSymbolRelocAssumeCapacity( target: Symbol.Id, addend: i64, @"type": SymbolReloc.Type, -) !void { +) Error!void { assert(elf.ehdrField(.type) != .REL); const rela_index: Section.RelaIndex.Optional = r: { @@ -5594,7 +5682,7 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye /// global where needed---the caller does not need to do this. /// /// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global. -fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) !bool { +fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool { assert(elf.shndx.dynamic != .UNDEF); const gpa = elf.base.comp.gpa; @@ -5657,16 +5745,14 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) !bool { return true; } -pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { +pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void { + const diags = &elf.base.comp.link_diags; elf.updateNavInner(pt, nav_index) catch |err| switch (err) { - error.OutOfMemory, - error.Overflow, - error.RelocationNotByteAligned, - => |e| return e, - else => |e| return elf.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; } -fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { +fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -5689,12 +5775,11 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) codegen.generateSymbol( &elf.base, pt, - zcu.navSrcLoc(nav_index), .fromInterned(nav.resolved.?.value), &nw.interface, .{ .atom_index = Node.toAtom(ni) }, ) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, + error.WriteFailed => return nw.err.?, else => |e| return e, }; switch (elf.symPtr(nmi.symbol(elf).index())) { @@ -5707,18 +5792,11 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) !void { +) link.Error!void { + const diags = &elf.base.comp.link_diags; elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) { - error.OutOfMemory, - error.Overflow, - error.RelocationNotByteAligned, - error.CodegenFail, - => |e| return e, - else => |e| return elf.base.cgFail( - pt.zcu.funcInfo(func_index).owner_nav, - "linker failed to update function: {s}", - .{@errorName(e)}, - ), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; } fn updateFuncInner( @@ -5726,7 +5804,7 @@ fn updateFuncInner( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) !void { +) Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; @@ -5748,7 +5826,6 @@ fn updateFuncInner( codegen.emitFunction( &elf.base, pt, - zcu.navSrcLoc(func.owner_nav), func_index, Node.toAtom(ni), mir, @@ -5763,14 +5840,14 @@ fn updateFuncInner( } } -pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) !void { +pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void { + const diags = &elf.base.comp.link_diags; elf.flushLazy(pt, .{ .kind = .const_data, .index = @intCast(elf.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return), }) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.CodegenFail => return error.LinkFailure, - else => |e| return elf.base.comp.link_diags.fail("updateErrorData failed: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; } @@ -5779,8 +5856,9 @@ pub fn flush( arena: std.mem.Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) !void { +) link.Error!void { const comp = elf.base.comp; + const diags = &comp.link_diags; _ = arena; _ = prog_node; @@ -5791,12 +5869,12 @@ pub fn flush( any_undef = true; comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)}); } - if (any_undef) return error.LinkFailure; + if (any_undef) return error.AlreadyReported; } elf.updateDynamicTextrel() catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return elf.base.comp.link_diags.fail("updateDynamicTextrel failed: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; while (try elf.idle(tid)) {} @@ -5812,8 +5890,8 @@ pub fn flush( .named => |named| named, }; const sym_name_strtab = elf.string(.strtab, sym_name_slice) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; if (elf.globalByName(sym_name_strtab) == null) break :entry 0; break :entry Symbol.Id.global(sym_name_strtab).value(elf); @@ -5823,11 +5901,11 @@ pub fn flush( } elf.mf.flush() catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; } -fn updateDynamicTextrel(elf: *Elf) !void { +fn updateDynamicTextrel(elf: *Elf) Error!void { if (elf.shndx.dynamic == .UNDEF) return; const dynamic_ni = elf.shndx.dynamic.get(elf).ni; switch (elf.shdrPtr(elf.shndx.dynamic)) { @@ -5844,10 +5922,7 @@ fn updateDynamicTextrel(elf: *Elf) !void { if (!has_textrel) { // Add a DT_TEXTREL entry before the final DT_NULL entry. const new_size = cur_size + @sizeOf([2]class.ElfN().Addr); - _, const node_size = dynamic_ni.location(&elf.mf).resolve(&elf.mf); - if (node_size < new_size) { - try dynamic_ni.resize(&elf.mf, elf.base.comp.gpa, new_size); - } + try elf.ensureNodeSize(dynamic_ni, new_size); elf.targetStore(&shdr.size, new_size); const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast( dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)], @@ -5869,18 +5944,16 @@ fn updateDynamicTextrel(elf: *Elf) !void { } } -pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { +pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { const comp = elf.base.comp; + const diags = &comp.link_diags; task: { while (elf.pending_uavs.pop()) |umi| { const sub_prog_node = elf.idleProgNode(tid, elf.const_prog_node, .{ .uav = umi }); defer sub_prog_node.end(); elf.flushUav(.{ .zcu = comp.zcu.?, .tid = tid }, umi) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return comp.link_diags.fail( - "linker failed to lower constant: {t}", - .{e}, - ), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; break :task; } @@ -5903,11 +5976,8 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { ); defer sub_prog_node.end(); elf.flushLazy(pt, lmr) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| return comp.link_diags.fail( - "linker failed to lower lazy {s}: {t}", - .{ kind, e }, - ), + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; break :task; }; @@ -5917,18 +5987,8 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf))); defer sub_prog_node.end(); elf.flushInputSection(isi) catch |err| switch (err) { - else => |e| { - const ii = isi.input(elf); - return comp.link_diags.fail( - "linker failed to read input section '{s}' from \"{f}{f}\": {t}", - .{ - elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), - ii.path(elf).fmtEscapeString(), - fmtMemberString(ii.member(elf)), - e, - }, - ); - }, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), + else => |e| return e, }; break :task; } @@ -6005,10 +6065,9 @@ fn flushUav( elf: *Elf, pt: Zcu.PerThread, umi: Node.UavMapIndex, -) !void { +) Error!void { const comp = elf.base.comp; const gpa = comp.gpa; - const zcu = pt.zcu; const uav_val = umi.uavValue(elf); const ni = umi.symbol(elf).index().ptr(elf).node; @@ -6017,23 +6076,14 @@ fn flushUav( var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); - // TODO: UAV lowering should never require source locations. - const dummy_src_loc: Zcu.LazySrcLoc = .{ - .base_node_inst = try zcu.intern_pool.trackZir(gpa, comp.io, pt.tid, .{ - .file = zcu.module_roots.get(zcu.std_mod).?.unwrap().?, - .inst = .main_struct_inst, - }), - .offset = .{ .byte_abs = 0 }, - }; codegen.generateSymbol( &elf.base, pt, - dummy_src_loc, .fromInterned(uav_val), &nw.interface, .{ .atom_index = Node.toAtom(ni) }, ) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, + error.WriteFailed => return nw.err.?, else => |e| return e, }; switch (elf.symPtr(umi.symbol(elf).index())) { @@ -6044,7 +6094,7 @@ fn flushUav( assert(ni.hasMoved(&elf.mf)); } -fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { +fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; @@ -6060,44 +6110,70 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void { var nw: MappedFile.Node.Writer = undefined; ni.writer(&elf.mf, gpa, &nw); defer nw.deinit(); - try codegen.generateLazySymbol( + codegen.generateLazySymbol( &elf.base, pt, - Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded, lazy, &required_alignment, &nw.interface, .none, .{ .atom_index = Node.toAtom(ni) }, - ); + ) catch |err| switch (err) { + error.WriteFailed => return nw.err.?, + else => |e| return e, + }; switch (elf.symPtr(lmr.symbol(elf).index())) { inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)), } } -fn flushInputSection(elf: *Elf, isi: InputSection.Index) !void { +fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void { const file_loc = isi.fileLocation(elf); if (file_loc.size == 0) return; const comp = elf.base.comp; const io = comp.io; const gpa = comp.gpa; + const diags = &comp.link_diags; const ii = isi.input(elf); const path = ii.path(elf); - const file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); + const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }), + }; defer file.close(io); var fr = file.reader(io, &.{}); - try fr.seekTo(file_loc.offset); + fr.seekTo(file_loc.offset) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{ + elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), + path.fmtEscapeString(), + fmtMemberString(ii.member(elf)), + e, + }), + }; var nw: MappedFile.Node.Writer = undefined; isi.node(elf).writer(&elf.mf, gpa, &nw); defer nw.deinit(); - if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) - return error.EndOfStream; + const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) { + error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{ + elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), + path.fmtEscapeString(), + fmtMemberString(ii.member(elf)), + fr.err orelse (fr.seek_err orelse fr.size_err.?), + }), + error.WriteFailed => return nw.err.?, + }; + if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{ + elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf), + path.fmtEscapeString(), + fmtMemberString(ii.member(elf)), + }); // The input section should already be considered to have moved, because it is created as moved // and pending calls to `flushInputSection` always happen before pending calls to `flushMoved`. assert(isi.node(elf).hasMoved(&elf.mf)); } -fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void { +fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) void { switch (elf.getNode(ni)) { else => unreachable, .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0), @@ -6118,7 +6194,7 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void { }, } var child_it = ni.children(&elf.mf); - while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni); + while (child_it.next()) |child_ni| elf.flushFileOffset(child_ni); }, .section => |shndx| switch (elf.shdrPtr(shndx)) { inline else => |shdr| elf.targetStore(&shdr.offset, @intCast( @@ -6128,14 +6204,14 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void { } } -fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { +fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { const trace = tracy.trace(@src()); defer trace.end(); switch (elf.getNode(ni)) { .file => unreachable, - .ehdr, .shdr => try elf.flushFileOffset(ni), + .ehdr, .shdr => elf.flushFileOffset(ni), .segment => |phndx| { - try elf.flushFileOffset(ni); + elf.flushFileOffset(ni); switch (elf.phdrSlice()) { inline else => |phdr| { const ph = &phdr[phndx]; @@ -6156,7 +6232,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { } }, .section => |shndx| { - try elf.flushFileOffset(ni); + elf.flushFileOffset(ni); const addr = elf.computeNodeVAddr(ni); const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) { inline else => |shdr| .{ @@ -6293,7 +6369,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); } -fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void { +fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { const trace = tracy.trace(@src()); defer trace.end(); _, const size = ni.location(&elf.mf).resolve(&elf.mf); @@ -6486,16 +6562,11 @@ pub fn updateExports( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) !void { +) link.Error!void { + const diags = &elf.base.comp.link_diags; return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) { - error.OutOfMemory => error.OutOfMemory, - error.LinkFailure => error.AnalysisFail, - else => |e| switch (elf.base.comp.link_diags.fail( - "linker failed to update exports: {t}", - .{e}, - )) { - error.LinkFailure => return error.AnalysisFail, - }, + else => |e| return e, + error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}), }; } fn updateExportsInner( @@ -6503,7 +6574,7 @@ fn updateExportsInner( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) !void { +) Error!void { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -6543,7 +6614,7 @@ fn updateExportsInner( .internal => @panic("TODO internal linkage"), .strong => .strong, .weak => .weak, - .link_once => return error.LinkOnceUnsupported, + .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): link_once is not supported", .{}), }, .visibility = switch (@"export".opts.visibility) { .default => .DEFAULT, @@ -6591,10 +6662,10 @@ pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) Io.Cancelable!void { pub fn printNode( elf: *Elf, tid: Zcu.PerThread.Id, - w: *std.Io.Writer, + w: *Io.Writer, ni: MappedFile.Node.Index, indent: usize, -) !void { +) Io.Writer.Error!void { const node = elf.getNode(ni); try w.splatByteAll(' ', indent); try w.writeAll(@tagName(node)); @@ -6698,3 +6769,15 @@ pub fn printNode( try w.writeByte('\n'); } } + +fn ensureNodeSize( + elf: *Elf, + node: MappedFile.Node.Index, + need_size: u64, +) Error!void { + _, const node_size = node.location(&elf.mf).resolve(&elf.mf); + if (need_size <= node_size) return; + const gpa = elf.base.comp.gpa; + const new_size = need_size + need_size / MappedFile.growth_factor; + try node.resize(&elf.mf, gpa, new_size); +} diff --git a/src/link/LdScript.zig b/src/link/LdScript.zig @@ -13,7 +13,7 @@ pub fn deinit(ls: *LdScript, gpa: Allocator) void { } pub const Error = error{ - LinkFailure, + AlreadyReported, UnknownCpuArch, OutOfMemory, }; diff --git a/src/link/Lld.zig b/src/link/Lld.zig @@ -255,7 +255,7 @@ pub fn flush( arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) link.File.FlushError!void { +) link.Error!void { dev.check(.lld_linker); _ = tid; @@ -277,7 +277,7 @@ pub fn flush( .wasm => wasmLink(lld, arena), }; result catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {t}", .{e}), }; } @@ -1620,7 +1620,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi const exit_code = try lldMain(arena, argv, false); if (exit_code == 0) return; if (comp.clang_passthrough_mode) std.process.exit(exit_code); - return error.LinkFailure; + return error.AlreadyReported; } var stderr: []u8 = &.{}; @@ -1720,7 +1720,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi .exited => |code| if (code != 0) { if (comp.clang_passthrough_mode) std.process.exit(code); diags.lockAndParseLldStderr(argv[1], stderr); - return error.LinkFailure; + return error.AlreadyReported; }, .signal => |sig| { if (comp.clang_passthrough_mode) std.process.abort(); diff --git a/src/link/MachO.zig b/src/link/MachO.zig @@ -341,7 +341,7 @@ pub fn flush( arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) link.File.FlushError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -490,7 +490,7 @@ pub fn flush( } }; - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); @@ -504,7 +504,7 @@ pub fn flush( try self.resolveSymbols(); try self.convertTentativeDefsAndResolveSpecialSymbols(); self.dedupLiterals() catch |err| switch (err) { - error.LinkFailure => |e| return e, + error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to deduplicate literals: {s}", .{@errorName(e)}), }; @@ -513,7 +513,7 @@ pub fn flush( } self.checkDuplicates() catch |err| switch (err) { - error.HasDuplicates => return error.LinkFailure, + error.HasDuplicates => return error.AlreadyReported, else => |e| return diags.fail("failed to check for duplicate symbol definitions: {s}", .{@errorName(e)}), }; @@ -528,7 +528,7 @@ pub fn flush( self.claimUnresolved(); self.scanRelocs() catch |err| switch (err) { - error.HasUndefinedSymbols => return error.LinkFailure, + error.HasUndefinedSymbols => return error.AlreadyReported, else => |e| return diags.fail("failed to scan relocations: {s}", .{@errorName(e)}), }; @@ -542,7 +542,7 @@ pub fn flush( try self.initSegments(); self.allocateSections() catch |err| switch (err) { - error.LinkFailure => |e| return e, + error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}), }; self.allocateSegments(); @@ -558,7 +558,7 @@ pub fn flush( if (self.getZigObject()) |zo| { zo.resolveRelocs(self) catch |err| switch (err) { - error.ResolveFailed => return error.LinkFailure, + error.ResolveFailed => return error.AlreadyReported, else => |e| return e, }; } @@ -567,7 +567,7 @@ pub fn flush( try self.writeSectionsToFile(); try self.allocateLinkeditSegment(); self.writeLinkeditSectionsToFile() catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to write linkedit sections to file: {t}", .{e}), }; @@ -594,11 +594,11 @@ pub fn flush( const ncmds, const sizeofcmds, const uuid_cmd_offset = self.writeLoadCommands() catch |err| switch (err) { error.WriteFailed => unreachable, - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, }; try self.writeHeader(ncmds, sizeofcmds); self.writeUuid(uuid_cmd_offset, self.requiresCodeSig()) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to calculate and write uuid: {s}", .{@errorName(e)}), }; if (self.getDebugSymbols()) |dsym| dsym.flush(self) catch |err| switch (err) { @@ -609,7 +609,7 @@ pub fn flush( // Code signing always comes last. if (codesig) |*csig| { self.writeCodeSignature(csig) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to write code signature: {s}", .{@errorName(e)}), }; const emit = self.base.emit; @@ -968,7 +968,7 @@ pub fn parseInputFiles(self: *MachO) !void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn parseInputFileWorker(self: *MachO, file: File) void { @@ -1365,7 +1365,7 @@ fn convertTentativeDefsAndResolveSpecialSymbols(self: *MachO) !void { resolveSpecialSymbolsWorker(self, obj); } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn convertTentativeDefinitionsWorker(self: *MachO, object: *Object) void { @@ -1450,7 +1450,7 @@ fn checkDuplicates(self: *MachO) !void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; try self.reportDuplicates(); } @@ -1517,7 +1517,7 @@ fn scanRelocs(self: *MachO) !void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; if (self.getInternalObject()) |obj| { try obj.checkUndefs(self); @@ -1990,7 +1990,7 @@ fn calcSectionSizes(self: *MachO) !void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; try self.calcSymtabSize(); @@ -2527,7 +2527,7 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { }; } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn writeAtomsWorker(self: *MachO, file: File) void { @@ -3074,15 +3074,15 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) link.File.UpdateNavError!void { +) link.Error!void { return self.getZigObject().?.updateFunc(self, pt, func_index, mir); } -pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { +pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.Error!void { return self.getZigObject().?.updateNav(self, pt, nav); } -pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { return self.getZigObject().?.updateLineNumber(pt, ti_id); } @@ -3091,7 +3091,7 @@ pub fn updateExports( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) link.File.UpdateExportsError!void { +) link.Error!void { return self.getZigObject().?.updateExports(self, pt, exported, export_indices); } @@ -3116,9 +3116,8 @@ pub fn lowerUav( pt: Zcu.PerThread, uav: InternPool.Index, explicit_alignment: InternPool.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { - return self.getZigObject().?.lowerUav(self, pt, uav, explicit_alignment, src_loc); +) !link.File.SymbolId { + return self.getZigObject().?.lowerUav(self, pt, uav, explicit_alignment); } pub fn getUavVAddr(self: *MachO, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -3265,7 +3264,11 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 file_writer.pos = new_offset; const size_u = math.cast(usize, size) orelse return error.Overflow; const n = file_writer.interface.sendFileAll(&file_reader, .limited(size_u)) catch |err| switch (err) { - error.ReadFailed => return file_reader.err.?, + error.ReadFailed => switch (file_reader.err.?) { + error.ConnectionResetByPeer => return error.Unexpected, // not a socket + error.SocketUnconnected => return error.Unexpected, // not a socket + else => |e| return e, + }, error.WriteFailed => return file_writer.err.?, }; assert(n == size_u); @@ -5373,7 +5376,7 @@ fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool { return true; } -pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkFailure}!void { +pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{AlreadyReported}!void { const comp = macho_file.base.comp; const io = comp.io; const diags = &comp.link_diags; @@ -5381,7 +5384,7 @@ pub fn pwriteAll(macho_file: *MachO, bytes: []const u8, offset: u64) error{LinkF return diags.fail("failed to write: {t}", .{err}); } -pub fn setLength(macho_file: *MachO, length: u64) error{LinkFailure}!void { +pub fn setLength(macho_file: *MachO, length: u64) error{AlreadyReported}!void { const comp = macho_file.base.comp; const io = comp.io; const diags = &comp.link_diags; @@ -5389,7 +5392,7 @@ pub fn setLength(macho_file: *MachO, length: u64) error{LinkFailure}!void { return diags.fail("failed to set file end pos: {t}", .{err}); } -pub fn cast(macho_file: *MachO, comptime T: type, x: anytype) error{LinkFailure}!T { +pub fn cast(macho_file: *MachO, comptime T: type, x: anytype) error{AlreadyReported}!T { return std.math.cast(T, x) orelse { const comp = macho_file.base.comp; const diags = &comp.link_diags; @@ -5397,7 +5400,7 @@ pub fn cast(macho_file: *MachO, comptime T: type, x: anytype) error{LinkFailure} }; } -pub fn alignPow(macho_file: *MachO, x: u32) error{LinkFailure}!u32 { +pub fn alignPow(macho_file: *MachO, x: u32) error{AlreadyReported}!u32 { const result, const ov = @shlWithOverflow(@as(u32, 1), try cast(macho_file, u5, x)); if (ov != 0) { const comp = macho_file.base.comp; diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig @@ -930,7 +930,7 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 { } } -pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) error{ LinkFailure, OutOfMemory }!void { +pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) error{ AlreadyReported, OutOfMemory }!void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig @@ -648,7 +648,7 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8, return n_sect; } -fn getSectionData(self: *const InternalObject, index: u32, macho_file: *MachO) error{LinkFailure}![]const u8 { +fn getSectionData(self: *const InternalObject, index: u32, macho_file: *MachO) error{AlreadyReported}![]const u8 { const slice = self.sections.slice(); assert(index < slice.items(.header).len); const sect = slice.items(.header)[index]; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig @@ -427,7 +427,7 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void { } } -pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ LinkFailure, OutOfMemory }!void { +pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) error{ AlreadyReported, OutOfMemory }!void { const gpa = macho_file.base.comp.gpa; const diags = &macho_file.base.comp.link_diags; @@ -555,7 +555,7 @@ pub fn getInputSection(self: ZigObject, atom: Atom, macho_file: *MachO) macho.se return sect; } -pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.File.FlushError!void { +pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.Error!void { const diags = &macho_file.base.comp.link_diags; // Handle any lazy symbols that were emitted by incremental compilation. @@ -571,7 +571,7 @@ pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.F .{ .kind = .code, .ty = .anyerror_type }, metadata.text_symbol_index, ) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}), }; if (metadata.const_state != .unused) self.updateLazySymbol( @@ -580,7 +580,7 @@ pub fn flush(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id) link.F .{ .kind = .const_data, .ty = .anyerror_type }, metadata.const_symbol_index, ) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}), }; } @@ -704,8 +704,7 @@ pub fn lowerUav( pt: Zcu.PerThread, uav: InternPool.Index, explicit_alignment: Atom.Alignment, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) !link.File.SymbolId { const zcu = pt.zcu; const gpa = zcu.gpa; const val = Value.fromInterned(uav); @@ -717,35 +716,29 @@ pub fn lowerUav( const sym = self.symbols.items[metadata.symbol_index]; const existing_alignment = sym.getAtom(macho_file).?.alignment; if (uav_alignment.order(existing_alignment).compare(.lte)) - return .{ .sym_index = @enumFromInt(metadata.symbol_index) }; + return @enumFromInt(metadata.symbol_index); } var name_buf: [32]u8 = undefined; const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{ @intFromEnum(uav), }) catch unreachable; - const res = self.lowerConst( + const sym_index = self.lowerConst( macho_file, pt, name, val, uav_alignment, macho_file.zig_const_sect_index.?, - src_loc, ) catch |err| switch (err) { error.OutOfMemory => |e| return e, - else => |e| return .{ .fail = try Zcu.ErrorMsg.create( - gpa, - src_loc, - "unable to lower constant value: {s}", - .{@errorName(e)}, - ) }, + else => |e| return macho_file.base.comp.link_diags.fail( + "failed to lower constant value: {t}", + .{e}, + ), }; - switch (res) { - .sym_index => |sym_index| try self.uavs.put(gpa, uav, .{ .symbol_index = @intFromEnum(sym_index) }), - .fail => {}, - } - return res; + try self.uavs.put(gpa, uav, .{ .symbol_index = @intFromEnum(sym_index) }); + return sym_index; } fn freeNavMetadata(self: *ZigObject, macho_file: *MachO, sym_index: Symbol.Index) void { @@ -776,7 +769,7 @@ pub fn updateFunc( pt: Zcu.PerThread, func_index: InternPool.Index, mir: *const codegen.AnyMir, -) link.File.UpdateNavError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -796,7 +789,6 @@ pub fn updateFunc( codegen.emitFunction( &macho_file.base, pt, - zcu.navSrcLoc(func.owner_nav), func_index, @enumFromInt(sym_index), mir, @@ -867,7 +859,7 @@ pub fn updateNav( macho_file: *MachO, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, -) link.File.UpdateNavError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -887,7 +879,7 @@ pub fn updateNav( var debug_wip_nav = try dwarf.initWipNav(pt, nav_index, @enumFromInt(sym_index)); defer debug_wip_nav.deinit(); dwarf.finishWipNav(pt, nav_index, &debug_wip_nav) catch |err| switch (err) { - error.OutOfMemory, error.Overflow => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), }; } @@ -908,7 +900,6 @@ pub fn updateNav( codegen.generateSymbol( &macho_file.base, pt, - zcu.navSrcLoc(nav_index), .fromInterned(nav.resolved.?.value), &aw.writer, .{ .atom_index = @enumFromInt(sym_index) }, @@ -925,7 +916,7 @@ pub fn updateNav( try self.updateNavCode(macho_file, pt, nav_index, sym_index, sect_index, code); if (debug_wip_nav) |*wip_nav| self.dwarf.?.finishWipNav(pt, nav_index, wip_nav) catch |err| switch (err) { - error.OutOfMemory, error.Overflow => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return macho_file.base.cgFail(nav_index, "failed to finish dwarf nav: {s}", .{@errorName(e)}), }; } else if (self.dwarf) |*dwarf| try dwarf.updateComptimeNav(pt, nav_index); @@ -941,7 +932,7 @@ fn updateNavCode( sym_index: Symbol.Index, sect_index: u8, code: []const u8, -) link.File.UpdateNavError!void { +) link.Error!void { const zcu = pt.zcu; const gpa = zcu.gpa; const comp = zcu.comp; @@ -1198,8 +1189,7 @@ fn lowerConst( val: Value, required_alignment: Atom.Alignment, output_section_index: u8, - src_loc: Zcu.LazySrcLoc, -) !codegen.SymbolResult { +) !link.File.SymbolId { const gpa = macho_file.base.comp.gpa; var aw: std.Io.Writer.Allocating = .init(gpa); @@ -1211,7 +1201,6 @@ fn lowerConst( codegen.generateSymbol( &macho_file.base, pt, - src_loc, val, &aw.writer, .{ .atom_index = @enumFromInt(sym_index) }, @@ -1242,7 +1231,7 @@ fn lowerConst( const file_offset = sect.offset + atom.value; try macho_file.pwriteAll(code, file_offset); - return .{ .sym_index = @enumFromInt(sym_index) }; + return @enumFromInt(sym_index); } pub fn updateExports( @@ -1251,7 +1240,7 @@ pub fn updateExports( pt: Zcu.PerThread, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index, -) link.File.UpdateExportsError!void { +) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -1263,18 +1252,7 @@ pub fn updateExports( break :blk self.navs.getPtr(nav).?; }, .uav => |uav| self.uavs.getPtr(uav) orelse blk: { - const first_exp = export_indices[0].ptr(zcu); - const res = try self.lowerUav(macho_file, pt, uav, .none, first_exp.src); - switch (res) { - .sym_index => {}, - .fail => |em| { - // TODO maybe it's enough to return an error here and let Zcu.processExportsInner - // handle the error? - try zcu.failed_exports.ensureUnusedCapacity(zcu.gpa, 1); - zcu.failed_exports.putAssumeCapacityNoClobber(export_indices[0], em); - return; - }, - } + _ = try self.lowerUav(macho_file, pt, uav, .none); break :blk self.uavs.getPtr(uav).?; }, }; @@ -1368,11 +1346,9 @@ fn updateLazySymbol( break :blk try self.addString(gpa, name); }; - const src = Type.fromInterned(lazy_sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded; try codegen.generateLazySymbol( &macho_file.base, pt, - src, lazy_sym, &required_alignment, &aw.writer, @@ -1413,12 +1389,12 @@ fn updateLazySymbol( try macho_file.pwriteAll(code, file_offset); } -pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { if (self.dwarf) |*dwarf| { const comp = dwarf.bin_file.comp; const diags = &comp.link_diags; dwarf.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { - error.Overflow, error.OutOfMemory => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; } diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig @@ -1,4 +1,4 @@ -pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { +pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { const gpa = comp.gpa; const io = comp.io; const diags = &comp.link_diags; @@ -34,15 +34,15 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; try macho_file.parseInputFiles(); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; try macho_file.resolveSymbols(); macho_file.dedupLiterals() catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update ar size: {s}", .{@errorName(e)}), }; markExports(macho_file); @@ -54,7 +54,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat try createSegment(macho_file); allocateSections(macho_file) catch |err| switch (err) { - error.LinkFailure => |e| return e, + error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to allocate sections: {s}", .{@errorName(e)}), }; allocateSegment(macho_file); @@ -75,7 +75,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat try writeHeader(macho_file, ncmds, sizeofcmds); } -pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { +pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { const gpa = comp.gpa; const io = comp.io; const diags = &macho_file.base.comp.link_diags; @@ -105,11 +105,11 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? diags.addParseError(link_input.path().?, "failed to read input file: {s}", .{@errorName(err)}); } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; try parseInputFilesAr(macho_file); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; // First, we flush relocatable object file generated with our backends. if (macho_file.getZigObject()) |zo| { @@ -231,7 +231,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? try macho_file.setLength(total_size); try macho_file.pwriteAll(writer.buffered(), 0); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn parseInputFilesAr(macho_file: *MachO) !void { @@ -339,7 +339,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { } try calcSymtabSize(macho_file); - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; } fn calcSectionSizeWorker(macho_file: *MachO, sect_id: u8) void { @@ -586,7 +586,7 @@ fn sortRelocs(macho_file: *MachO) void { } } -fn writeSections(macho_file: *MachO) link.File.FlushError!void { +fn writeSections(macho_file: *MachO) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -632,7 +632,7 @@ fn writeSections(macho_file: *MachO) link.File.FlushError!void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; if (macho_file.getZigObject()) |zo| { try zo.writeRelocs(macho_file); @@ -685,7 +685,7 @@ fn writeSectionsToFile(macho_file: *MachO) !void { try macho_file.pwriteAll(macho_file.strtab.items, macho_file.symtab_cmd.stroff); } -fn writeLoadCommands(macho_file: *MachO) error{ LinkFailure, OutOfMemory }!struct { usize, usize } { +fn writeLoadCommands(macho_file: *MachO) error{ AlreadyReported, OutOfMemory }!struct { usize, usize } { const gpa = macho_file.base.comp.gpa; const needed_size = load_commands.calcLoadCommandsSizeObject(macho_file); const buffer = try gpa.alloc(u8, needed_size); diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig @@ -5,6 +5,7 @@ const is_linux = builtin.os.tag == .linux; const is_windows = builtin.os.tag == .windows; const std = @import("std"); +const Allocator = std.mem.Allocator; const Io = std.Io; const assert = std.debug.assert; const linux = std.os.linux; @@ -24,14 +25,40 @@ large: std.ArrayList(u64), updates: std.ArrayList(Node.Index), update_prog_node: std.Progress.Node, writers: std.SinglyLinkedList, +io_err: ?IoError, pub const growth_factor = 4; -pub const Error = error{ +pub const IoError = Io.UnexpectedError || error{ + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + PermissionDenied, + SystemResources, + LockViolation, + LockedMemoryLimitExceeded, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + FileBusy, + DeviceBusy, + NoDevice, + PathAlreadyExists, + IsDir, NotFile, -} || Io.File.MemoryMap.CreateError || Io.File.MemoryMap.SetLengthError || Io.File.WritePositionalError; + BrokenPipe, + NonResizable, + Unseekable, +}; + +pub const Error = Allocator.Error || Io.Cancelable || error{ + /// Some I/O operation on the memory-mapped file failed. The underlying error is available in + /// the `MappedFile.io_err` field. + MappedFileIo, +}; -pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile { +pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) (Allocator.Error || Io.Cancelable || IoError)!MappedFile { var mf: MappedFile = .{ .io = io, .flags = undefined, @@ -47,10 +74,14 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile { .updates = .empty, .update_prog_node = .none, .writers = .{}, + .io_err = null, }; errdefer mf.deinit(gpa); const size: u64, const block_size = stat: { - const stat = try file.stat(io); + const stat = file.stat(io) catch |err| switch (err) { + error.Streaming => return error.PathAlreadyExists, + else => |e| return e, + }; if (stat.kind != .file) return error.PathAlreadyExists; break :stat .{ stat.size, @max(std.heap.pageSize(), stat.block_size) }; }; @@ -61,14 +92,16 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) !MappedFile { .fallocate_punch_hole_unsupported = false, }; try mf.nodes.ensureUnusedCapacity(gpa, 1); - assert(try mf.addNode(gpa, .{ - .add_node = .{ - .size = size, - .alignment = mf.flags.block_size, - .fixed = true, - }, - }) == Node.Index.root); - try mf.ensureTotalCapacity(@intCast(size)); + const root_ni = mf.addNode(gpa, .{ .add_node = .{ + .size = size, + .alignment = mf.flags.block_size, + .fixed = true, + } }) catch |err| switch (err) { + error.MappedFileIo => return mf.io_err.?, + else => |e| return e, + }; + assert(root_ni == Node.Index.root); + try mf.ensureTotalCapacityInner(@intCast(size)); return mf; } @@ -174,7 +207,7 @@ pub const Node = extern struct { return .{ .mf = mf, .ni = ni.get(mf).last }; } - pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void { + pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void { var child_ni = ni.get(mf).last; while (child_ni != .none) { try child_ni.moved(gpa, mf); @@ -192,7 +225,7 @@ pub const Node = extern struct { } return false; } - pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void { + pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void { try mf.updates.ensureUnusedCapacity(gpa, 1); ni.movedAssumeCapacity(mf); } @@ -213,7 +246,7 @@ pub const Node = extern struct { pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool { return ni.get(mf).flags.resized; } - pub fn resized(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void { + pub fn resized(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void { try mf.updates.ensureUnusedCapacity(gpa, 1); ni.resizedAssumeCapacity(mf); } @@ -296,8 +329,16 @@ pub const Node = extern struct { return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)]; } - pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) !void { - try mf.resizeNode(gpa, ni, size); + pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) Error!void { + mf.resizeNode(gpa, ni, size) catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + else => |e| { + mf.io_err = e; + return error.MappedFileIo; + }, + }; var writers_it = mf.writers.first; while (writers_it) |writer_node| : (writers_it = writer_node.next) { const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node); @@ -313,8 +354,16 @@ pub const Node = extern struct { mf: *MappedFile, gpa: std.mem.Allocator, new_alignment: std.mem.Alignment, - ) !void { - try mf.realignNode(gpa, ni, new_alignment); + ) Error!void { + mf.realignNode(gpa, ni, new_alignment) catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + else => |e| { + mf.io_err = e; + return error.MappedFileIo; + }, + }; var writers_it = mf.writers.first; while (writers_it) |writer_node| : (writers_it = writer_node.next) { const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node); @@ -422,9 +471,16 @@ pub const Node = extern struct { file_reader.pos, w.ni.fileLocation(w.mf, true).offset + interface.end, limit.minInt(interface.unusedCapacityLen()), - ) catch |err| { - w.err = err; - return error.WriteFailed; + ) catch |err| switch (err) { + error.Canceled => |e| { + w.err = e; + return error.WriteFailed; + }, + else => |e| { + w.mf.io_err = e; + w.err = error.MappedFileIo; + return error.WriteFailed; + }, }); if (n == 0) return error.Unimplemented; file_reader.pos += n; @@ -472,7 +528,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct { next: Node.Index = .none, offset: u64 = 0, add_node: AddNodeOptions, -}) !Node.Index { +}) Error!Node.Index { if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1); const offset = opts.add_node.alignment.forward(@intCast(opts.offset)); const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: { @@ -544,7 +600,7 @@ pub fn addOnlyChildNode( gpa: std.mem.Allocator, parent_ni: Node.Index, opts: AddNodeOptions, -) !Node.Index { +) Error!Node.Index { try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); assert(parent.first == .none and parent.last == .none); @@ -559,7 +615,7 @@ pub fn addFirstChildNode( gpa: std.mem.Allocator, parent_ni: Node.Index, opts: AddNodeOptions, -) !Node.Index { +) Error!Node.Index { try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); return mf.addNode(gpa, .{ @@ -574,7 +630,7 @@ pub fn addLastChildNode( gpa: std.mem.Allocator, parent_ni: Node.Index, opts: AddNodeOptions, -) !Node.Index { +) Error!Node.Index { try mf.nodes.ensureUnusedCapacity(gpa, 1); const parent = parent_ni.get(mf); return mf.addNode(gpa, .{ @@ -596,7 +652,7 @@ pub fn addNodeAfter( gpa: std.mem.Allocator, prev_ni: Node.Index, opts: AddNodeOptions, -) !Node.Index { +) Error!Node.Index { assert(prev_ni != .none); try mf.nodes.ensureUnusedCapacity(gpa, 1); const prev = prev_ni.get(mf); @@ -610,7 +666,7 @@ pub fn addNodeAfter( }); } -fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void { +fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void { const io = mf.io; const node = ni.get(mf); const old_offset, const old_size = node.location().resolve(mf); @@ -618,9 +674,13 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested // Resize the entire file if (ni == Node.Index.root) { try mf.ensureCapacityForSetLocation(gpa); - try mf.memory_map.write(io); + mf.memory_map.write(io) catch |err| switch (err) { + error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking + error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing + else => |e| return e, + }; try mf.memory_map.file.setLength(io, new_size); - try mf.ensureTotalCapacity(@intCast(new_size)); + try mf.ensureTotalCapacityInner(@intCast(new_size)); ni.setLocationAssumeCapacity(mf, old_offset, new_size); return; } @@ -643,7 +703,11 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested if (is_linux and !mf.flags.fallocate_insert_range_unsupported and node.flags.alignment.order(mf.flags.block_size).compare(.gte)) insert_range: { - try mf.memory_map.write(io); + mf.memory_map.write(io) catch |err| switch (err) { + error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking + error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing + else => |e| return e, + }; // Ask the filesystem driver to insert extents into the file without copying any data const last_offset, const last_size = parent.last.location(mf).resolve(mf); const last_end = last_offset + last_size; @@ -674,7 +738,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested enclosing_ni.setLocationAssumeCapacity(mf, enclosing_offset, new_enclosing_size); if (enclosing_ni == Node.Index.root) { assert(enclosing_offset == 0); - try mf.ensureTotalCapacity(@intCast(new_enclosing_size)); + try mf.ensureTotalCapacityInner(@intCast(new_enclosing_size)); break; } var after_ni = enclosing.next; @@ -865,7 +929,7 @@ fn realignNode( gpa: std.mem.Allocator, ni: Node.Index, new_alignment: std.mem.Alignment, -) !void { +) (Allocator.Error || Io.Cancelable || IoError)!void { assert(ni != Node.Index.root); // currently unsupported const node = ni.get(mf); @@ -936,7 +1000,7 @@ fn realignNode( } } -fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void { +fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) (Io.Cancelable || IoError)!void { // make a copy of this node at the new location try mf.copyRange(old_file_offset, new_file_offset, size); // delete the copy of this node at the old location @@ -966,7 +1030,7 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0); } -fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void { +fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) (Io.Cancelable || IoError)!void { const copy_size = try mf.copyFileRange(mf.memory_map.file, old_file_offset, new_file_offset, size); if (copy_size < size) @memcpy( mf.memory_map.memory[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)], @@ -980,9 +1044,13 @@ fn copyFileRange( old_file_offset: u64, new_file_offset: u64, size: u64, -) !u64 { +) (Io.Cancelable || IoError)!u64 { const io = mf.io; - try mf.memory_map.write(io); + mf.memory_map.write(io) catch |err| switch (err) { + error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking + error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing + else => |e| return e, + }; var remaining_size = size; if (is_linux and !mf.flags.copy_file_range_unsupported) { var old_file_offset_mut: i64 = @intCast(old_file_offset); @@ -1021,17 +1089,41 @@ fn copyFileRange( return size - remaining_size; } -fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void { +fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) Allocator.Error!void { try mf.large.ensureUnusedCapacity(gpa, 2); try mf.updates.ensureUnusedCapacity(gpa, 1); } -pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void { +pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void { + mf.ensureTotalCapacityInner(new_capacity) catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + + else => |e| { + mf.io_err = e; + return error.MappedFileIo; + }, + }; +} +fn ensureTotalCapacityInner(mf: *MappedFile, new_capacity: usize) (Allocator.Error || Io.Cancelable || IoError)!void { if (mf.memory_map.memory.len >= new_capacity) return; - try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / growth_factor); + try mf.ensureTotalCapacityPreciseInner(new_capacity +| new_capacity / growth_factor); } -pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void { +pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) Error!void { + mf.ensureTotalCapacityPreciseInner(new_capacity) catch |err| switch (err) { + error.OutOfMemory, + error.Canceled, + => |e| return e, + + else => |e| { + mf.io_err = e; + return error.MappedFileIo; + }, + }; +} +fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Allocator.Error || Io.Cancelable || IoError)!void { if (mf.memory_map.memory.len >= new_capacity) return; const io = mf.io; const aligned_capacity = mf.flags.block_size.forward(new_capacity); @@ -1047,7 +1139,11 @@ pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void { } const file = mf.memory_map.file; - mf.memory_map = try .create(io, file, .{ .len = aligned_capacity }); + mf.memory_map = Io.File.MemoryMap.create(io, file, .{ .len = aligned_capacity }) catch |err| switch (err) { + error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking + error.NotOpenForReading => return error.Unexpected, // we definitely opened the file for writing + else => |e| return e, + }; } pub fn unmap(mf: *MappedFile) void { @@ -1059,9 +1155,22 @@ pub fn unmap(mf: *MappedFile) void { mf.memory_map.file = file; } -pub fn flush(mf: *MappedFile) Io.File.WritePositionalError!void { - const io = mf.io; - try mf.memory_map.write(io); +pub fn flush(mf: *MappedFile) (Io.Cancelable || error{MappedFileIo})!void { + mf.memory_map.write(mf.io) catch |err| switch (err) { + error.Canceled => |e| return e, + + error.WouldBlock, // file was not opened as non-blocking + error.NotOpenForWriting, // we definitely opened the file for writing + => { + mf.io_err = error.Unexpected; + return error.MappedFileIo; + }, + + else => |e| { + mf.io_err = e; + return error.MappedFileIo; + }, + }; } fn verify(mf: *MappedFile) void { diff --git a/src/link/Queue.zig b/src/link/Queue.zig @@ -135,7 +135,7 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void { lf.post_prelink = true; } else |err| switch (err) { error.OutOfMemory => comp.link_diags.setAllocFailure(), - error.LinkFailure => {}, + error.AlreadyReported => {}, error.Canceled => |e| return e, } } @@ -178,7 +178,7 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void { } else |err| switch (err) { error.OutOfMemory => comp.link_diags.setAllocFailure(), error.Canceled => @panic("TODO"), - error.LinkFailure => {}, + error.AlreadyReported => {}, } } } @@ -205,7 +205,11 @@ fn runIdleTask(comp: *Compilation, tid: Zcu.PerThread.Id) bool { comp.link_diags.setAllocFailure(); break :have_more false; }, - error.LinkFailure => false, + error.AlreadyReported => false, + error.Canceled => { + comp.io.recancel(); + return false; + }, }; } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig @@ -140,19 +140,8 @@ fn generate( }; linker.cg.genNav(do_codegen) catch |err| switch (err) { - error.CodegenFail => switch (zcu.codegenFailMsg(nav_index, linker.cg.error_msg.?)) { - error.CodegenFail => {}, - error.OutOfMemory => |e| return e, - }, - else => |other| { - // There might be an error that happened *after* linker.error_msg - // was already allocated, so be sure to free it. - if (linker.cg.error_msg) |error_msg| { - error_msg.deinit(gpa); - } - - return other; - }, + error.AlreadyReported => return, + else => |e| return e, }; } @@ -168,7 +157,7 @@ pub fn updateFunc( try linker.generate(pt, nav, air.*, liveness.*.?, true); } -pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void { +pub fn updateNav(linker: *Linker, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.Error!void { const ip = &pt.zcu.intern_pool; log.debug("lowering nav {f}({d})", .{ ip.getNav(nav).fqn.fmt(ip), nav }); try linker.generate(pt, nav, undefined, undefined, false); @@ -231,7 +220,7 @@ pub fn flush( arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) link.File.FlushError!void { +) link.Error!void { // The goal is to never use this because it's only needed if we need to // write to InternPool, but flush is too late to be writing to the // InternPool. diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig @@ -568,7 +568,7 @@ pub const SourceLocation = enum(u32) { err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl }; } - pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} { + pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{AlreadyReported} { return diags.failSourceLocation(.{ .wasm = sl }, format, args); } @@ -3027,12 +3027,12 @@ fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void { const diags = &comp.link_diags; const obj = link.openObject(io, path, false, false) catch |err| { switch (diags.failParse(path, "failed to open object: {t}", .{err})) { - error.LinkFailure => return, + error.AlreadyReported => return, } }; wasm.parseObject(obj) catch |err| { switch (diags.failParse(path, "failed to parse object: {t}", .{err})) { - error.LinkFailure => return, + error.AlreadyReported => return, } }; } @@ -3336,12 +3336,12 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index } } -pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void { +pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) link.Error!void { const comp = wasm.base.comp; const diags = &comp.link_diags; if (wasm.dwarf) |*dw| { dw.updateLineNumber(pt.zcu, ti_id) catch |err| switch (err) { - error.Overflow, error.OutOfMemory => |e| return e, + error.OutOfMemory, error.Canceled, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to update dwarf line numbers: {s}", .{@errorName(e)}), }; } @@ -3417,7 +3417,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { } } -pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.File.FlushError!void { +pub fn prelink(wasm: *Wasm, prog_node: std.Progress.Node) link.Error!void { const tracy = trace(@src()); defer tracy.end(); @@ -3526,7 +3526,7 @@ pub fn markFunctionImport( name: String, import: *FunctionImport, func_index: FunctionImport.Index, -) link.File.FlushError!void { +) link.Error!void { // import.flags.alive might be already true from a previous update. In such // case, we must still run the logic in this function, in case the item // being marked was reverted by the `flush` logic that resets the hash @@ -3557,7 +3557,7 @@ pub fn markFunctionImport( } /// Recursively mark alive everything referenced by the function. -fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.File.FlushError!void { +fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex, override_export: bool) link.Error!void { const comp = wasm.base.comp; const gpa = comp.gpa; const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i)); @@ -3590,7 +3590,7 @@ fn markGlobalImport( name: String, import: *GlobalImport, global_index: GlobalImport.Index, -) link.File.FlushError!void { +) link.Error!void { // import.flags.alive might be already true from a previous update. In such // case, we must still run the logic in this function, in case the item // being marked was reverted by the `flush` logic that resets the hash @@ -3630,7 +3630,7 @@ fn markGlobalImport( } } -fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.File.FlushError!void { +fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex, override_export: bool) link.Error!void { const comp = wasm.base.comp; const gpa = comp.gpa; const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i)); @@ -3653,7 +3653,7 @@ fn markTableImport( name: String, import: *TableImport, table_index: TableImport.Index, -) link.File.FlushError!void { +) link.Error!void { if (import.flags.alive) return; import.flags.alive = true; @@ -3675,7 +3675,7 @@ fn markTableImport( } } -fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void { +fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.Error!void { const comp = wasm.base.comp; const segment = segment_index.ptr(wasm); if (segment.flags.alive) return; @@ -3693,7 +3693,7 @@ pub fn markDataImport( name: String, import: *ObjectDataImport, data_index: ObjectDataImport.Index, -) link.File.FlushError!void { +) link.Error!void { if (import.flags.alive) return; import.flags.alive = true; @@ -3715,7 +3715,7 @@ pub fn markDataImport( } } -fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void { +fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Error!void { const gpa = wasm.base.comp.gpa; for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| { if (offset >= relocs.end) break; @@ -3812,7 +3812,7 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil } } -fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.File.FlushError!void { +fn markTable(wasm: *Wasm, i: ObjectTableIndex) link.Error!void { try wasm.tables.put(wasm.base.comp.gpa, .fromObjectTable(i), {}); } @@ -3821,7 +3821,7 @@ pub fn flush( arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node, -) link.File.FlushError!void { +) link.Error!void { // The goal is to never use this because it's only needed if we need to // write to InternPool, but flush is too late to be writing to the // InternPool. @@ -3864,7 +3864,7 @@ pub fn flush( try wasm.flush_buffer.data_imports.reinit(gpa, wasm.data_imports.keys(), wasm.data_imports.values()); return wasm.flush_buffer.finish(wasm) catch |err| switch (err) { - error.OutOfMemory, error.LinkFailure => |e| return e, + error.OutOfMemory, error.AlreadyReported => |e| return e, else => |e| return diags.fail("failed to flush wasm: {s}", .{@errorName(e)}), }; } @@ -4275,7 +4275,7 @@ fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Zcu { var aw: std.Io.Writer.Allocating = .fromArrayList(wasm.base.comp.gpa, &wasm.string_bytes); defer wasm.string_bytes = aw.toArrayList(); - codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &aw.writer, .none) catch |err| switch (err) { + codegen.generateSymbol(&wasm.base, pt, .fromInterned(ip_index), &aw.writer, .none) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, else => |e| return e, }; @@ -4349,7 +4349,7 @@ fn resolveFunctionSynthetic( res: FunctionImport.Resolution, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype, -) link.File.FlushError!void { +) link.Error!void { import.resolution = res; wasm.functions.putAssumeCapacity(res, {}); // This is not only used for type-checking but also ensures the function diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig @@ -274,7 +274,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { } } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; // Merge indirect function tables. try f.indirect_function_table.ensureUnusedCapacity(gpa, wasm.zcu_indirect_function_set.entries.len + @@ -513,7 +513,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (initial_memory > std.math.maxInt(u32)) { diags.addError("initial memory value {d} exceeds 32-bit address space", .{initial_memory}); } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; memory_ptr = initial_memory; } else { memory_ptr = mem.alignForward(u64, memory_ptr, std.wasm.page_size); @@ -535,7 +535,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { if (max_memory > std.math.maxInt(u32)) { diags.addError("maximum memory value {d} exceeds 32-bit address space", .{max_memory}); } - if (diags.hasErrors()) return error.LinkFailure; + if (diags.hasErrors()) return error.AlreadyReported; wasm.memories.limits.max = @intCast(max_memory / page_size); wasm.memories.limits.flags.has_max = true; if (shared_memory) wasm.memories.limits.flags.is_shared = true; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig @@ -1431,7 +1431,7 @@ fn parseFeatures( bytes: []const u8, start_pos: usize, path: Path, -) error{ OutOfMemory, LinkFailure }!struct { Wasm.Feature.Set, usize } { +) error{ OutOfMemory, AlreadyReported }!struct { Wasm.Feature.Set, usize } { const gpa = wasm.base.comp.gpa; const diags = &wasm.base.comp.link_diags; const features_len, var pos = readLeb(u32, bytes, start_pos); diff --git a/src/register_manager.zig b/src/register_manager.zig @@ -14,7 +14,7 @@ const link = @import("link.zig"); const log = std.log.scoped(.register_manager); -pub const AllocationError = @import("codegen.zig").CodeGenError || error{OutOfRegisters}; +pub const AllocationError = @import("codegen.zig").Error || error{OutOfRegisters}; pub fn RegisterManager( comptime Function: type,