diff --git a/lib/std/zig/LibCDirs.zig b/lib/std/zig/LibCDirs.zig index f742b24c4a..8f19aa3458 100644 --- a/lib/std/zig/LibCDirs.zig +++ b/lib/std/zig/LibCDirs.zig @@ -19,7 +19,7 @@ pub fn detect( is_native_abi: bool, link_libc: bool, libc_installation: ?*const LibCInstallation, -) !LibCDirs { +) LibCInstallation.FindError!LibCDirs { if (!link_libc) { return .{ .libc_include_dir_list = &[0][]u8{}, @@ -114,7 +114,7 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con } } if (target.os.tag == .haiku) { - const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable; + const include_dir_path = lci.include_dir.?; const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" }); list.appendAssumeCapacity(os_dir); // Errors.h diff --git a/src/Compilation.zig b/src/Compilation.zig index 8382da2804..9e7e2dd89c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1391,6 +1391,7 @@ pub const Win32Resource = struct { }; pub const MiscTask = enum { + open_output, write_builtin_zig, rename_results, check_whole_cache, @@ -1874,7 +1875,49 @@ fn addModuleTableToCacheHash( const RtStrat = enum { none, lib, obj, zcu, dyn_lib }; -pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compilation { +pub const CreateDiagnostic = union(enum) { + export_table_import_table_conflict, + emit_h_without_zcu, + illegal_zig_import, + cross_libc_unavailable, + find_native_libc: std.zig.LibCInstallation.FindError, + libc_installation_missing_crt_dir, + create_cache_path: CreateCachePath, + open_output_bin: link.File.OpenError, + pub const CreateCachePath = struct { + which: enum { local, global }, + sub: []const u8, + err: (fs.Dir.MakeError || fs.Dir.OpenError || fs.Dir.StatFileError), + }; + pub fn format(diag: CreateDiagnostic, w: *std.Io.Writer) std.Io.Writer.Error!void { + switch (diag) { + .export_table_import_table_conflict => try w.writeAll("'--import-table' and '--export-table' cannot be used together"), + .emit_h_without_zcu => try w.writeAll("cannot emit C header with no Zig source files"), + .illegal_zig_import => try w.writeAll("this compiler implementation does not support importing the root source file of a provided module"), + .cross_libc_unavailable => try w.writeAll("unable to provide libc for this target"), + .find_native_libc => |err| try w.print("failed to find libc installation: {t}", .{err}), + .libc_installation_missing_crt_dir => try w.writeAll("libc installation is missing crt directory"), + .create_cache_path => |cache| try w.print("failed to create path '{s}' in {t} cache directory: {t}", .{ + cache.sub, + cache.which, + cache.err, + }), + .open_output_bin => |err| try w.print("failed to open output binary: {t}", .{err}), + } + } + + fn fail(out: *CreateDiagnostic, result: CreateDiagnostic) error{CreateFail} { + out.* = result; + return error.CreateFail; + } +}; +pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options: CreateOptions) error{ + OutOfMemory, + Unexpected, + CurrentWorkingDirectoryUnlinked, + /// An error has been stored to `diag`. + CreateFail, +}!*Compilation { const output_mode = options.config.output_mode; const is_dyn_lib = switch (output_mode) { .Obj, .Exe => false, @@ -1887,7 +1930,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }; if (options.linker_export_table and options.linker_import_table) { - return error.ExportTableAndImportTableConflict; + return diag.fail(.export_table_import_table_conflict); } const have_zcu = options.config.have_zcu; @@ -1920,14 +1963,18 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil const link_libc = options.config.link_libc; - const libc_dirs = try std.zig.LibCDirs.detect( + const libc_dirs = std.zig.LibCDirs.detect( arena, options.dirs.zig_lib.path.?, target, options.root_mod.resolved_target.is_native_abi, link_libc, options.libc_installation, - ); + ) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + // Every other error is specifically related to finding the native installation + else => |e| return diag.fail(.{ .find_native_libc = e }), + }; const sysroot = options.sysroot orelse libc_dirs.sysroot; @@ -1949,7 +1996,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil if (compiler_rt_strat == .zcu) { // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");` // injected into the object. - const compiler_rt_mod = try Package.Module.create(arena, .{ + const compiler_rt_mod = Package.Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "compiler_rt.zig", @@ -1963,7 +2010,22 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }, .global = options.config, .parent = options.root_mod, - }); + }) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + // None of these are possible because the configuration matches the root module + // which already passed these checks. + error.ValgrindUnsupportedOnTarget => unreachable, + error.TargetRequiresSingleThreaded => unreachable, + error.BackendRequiresSingleThreaded => unreachable, + error.TargetRequiresPic => unreachable, + error.PieRequiresPic => unreachable, + error.DynamicLinkingRequiresPic => unreachable, + error.TargetHasNoRedZone => unreachable, + // These are not possible because are explicitly *not* requesting these things. + error.StackCheckUnsupportedByTarget => unreachable, + error.StackProtectorUnsupportedByTarget => unreachable, + error.StackProtectorUnavailableWithoutLibC => unreachable, + }; try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod); } @@ -1981,7 +2043,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil }; if (ubsan_rt_strat == .zcu) { - const ubsan_rt_mod = try Package.Module.create(arena, .{ + const ubsan_rt_mod = Package.Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "ubsan_rt.zig", @@ -1991,7 +2053,21 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .inherited = .{}, .global = options.config, .parent = options.root_mod, - }); + }) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + // None of these are possible because the configuration matches the root module + // which already passed these checks. + error.ValgrindUnsupportedOnTarget => unreachable, + error.TargetRequiresSingleThreaded => unreachable, + error.BackendRequiresSingleThreaded => unreachable, + error.TargetRequiresPic => unreachable, + error.PieRequiresPic => unreachable, + error.DynamicLinkingRequiresPic => unreachable, + error.TargetHasNoRedZone => unreachable, + error.StackCheckUnsupportedByTarget => unreachable, + error.StackProtectorUnsupportedByTarget => unreachable, + error.StackProtectorUnavailableWithoutLibC => unreachable, + }; try options.root_mod.deps.putNoClobber(arena, "ubsan_rt", ubsan_rt_mod); } @@ -2019,7 +2095,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil const cache = try arena.create(Cache); cache.* = .{ .gpa = gpa, - .manifest_dir = try options.dirs.local_cache.handle.makeOpenPath("h", .{}), + .manifest_dir = options.dirs.local_cache.handle.makeOpenPath("h", .{}) catch |err| { + return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = "h", .err = err } }); + }, }; // These correspond to std.zig.Server.Message.PathPrefix. cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); @@ -2067,20 +2145,24 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // to redundantly happen for each AstGen operation. const zir_sub_dir = "z"; - var local_zir_dir = try options.dirs.local_cache.handle.makeOpenPath(zir_sub_dir, .{}); + var local_zir_dir = options.dirs.local_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| { + return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = zir_sub_dir, .err = err } }); + }; errdefer local_zir_dir.close(); const local_zir_cache: Cache.Directory = .{ .handle = local_zir_dir, .path = try options.dirs.local_cache.join(arena, &.{zir_sub_dir}), }; - var global_zir_dir = try options.dirs.global_cache.handle.makeOpenPath(zir_sub_dir, .{}); + var global_zir_dir = options.dirs.global_cache.handle.makeOpenPath(zir_sub_dir, .{}) catch |err| { + return diag.fail(.{ .create_cache_path = .{ .which = .global, .sub = zir_sub_dir, .err = err } }); + }; errdefer global_zir_dir.close(); const global_zir_cache: Cache.Directory = .{ .handle = global_zir_dir, .path = try options.dirs.global_cache.join(arena, &.{zir_sub_dir}), }; - const std_mod = options.std_mod orelse try Package.Module.create(arena, .{ + const std_mod = options.std_mod orelse Package.Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, options.dirs, .zig_lib, "std"), .root_src_path = "std.zig", @@ -2090,7 +2172,21 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .inherited = .{}, .global = options.config, .parent = options.root_mod, - }); + }) catch |err| return switch (err) { + error.OutOfMemory => |e| return e, + // None of these are possible because the configuration matches the root module + // which already passed these checks. + error.ValgrindUnsupportedOnTarget => unreachable, + error.TargetRequiresSingleThreaded => unreachable, + error.BackendRequiresSingleThreaded => unreachable, + error.TargetRequiresPic => unreachable, + error.PieRequiresPic => unreachable, + error.DynamicLinkingRequiresPic => unreachable, + error.TargetHasNoRedZone => unreachable, + error.StackCheckUnsupportedByTarget => unreachable, + error.StackProtectorUnsupportedByTarget => unreachable, + error.StackProtectorUnavailableWithoutLibC => unreachable, + }; const zcu = try arena.create(Zcu); zcu.* = .{ @@ -2109,7 +2205,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil try zcu.init(options.thread_pool.getIdCount()); break :blk zcu; } else blk: { - if (options.emit_h != .no) return error.NoZigModuleForCHeader; + if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu); break :blk null; }; errdefer if (opt_zcu) |zcu| zcu.deinit(); @@ -2204,7 +2300,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // Populate `zcu.module_roots`. const pt: Zcu.PerThread = .activate(zcu, .main); defer pt.deactivate(); - try pt.populateModuleRootTable(); + pt.populateModuleRootTable() catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.IllegalZigImport => return diag.fail(.illegal_zig_import), + }; } const lf_open_opts: link.File.OpenOptions = .{ @@ -2279,10 +2378,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil none.* = .{ .tmp_artifact_directory = null }; comp.cache_use = .{ .none = none }; if (comp.emit_bin) |path| { - comp.bin_file = try link.File.open(arena, comp, .{ + comp.bin_file = link.File.open(arena, comp, .{ .root_dir = .cwd(), .sub_path = path, - }, lf_open_opts); + }, lf_open_opts) catch |err| { + return diag.fail(.{ .open_output_bin = err }); + }; } }, .incremental => { @@ -2320,7 +2421,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil const digest = hash.final(); const artifact_sub_dir = "o" ++ fs.path.sep_str ++ digest; - var artifact_dir = try options.dirs.local_cache.handle.makeOpenPath(artifact_sub_dir, .{}); + var artifact_dir = options.dirs.local_cache.handle.makeOpenPath(artifact_sub_dir, .{}) catch |err| { + return diag.fail(.{ .create_cache_path = .{ .which = .local, .sub = artifact_sub_dir, .err = err } }); + }; errdefer artifact_dir.close(); const artifact_directory: Cache.Directory = .{ .handle = artifact_dir, @@ -2338,7 +2441,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .root_dir = artifact_directory, .sub_path = cache_rel_path, }; - comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); + comp.bin_file = link.File.open(arena, comp, emit, lf_open_opts) catch |err| { + return diag.fail(.{ .open_output_bin = err }); + }; } }, .whole => { @@ -2433,7 +2538,10 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .link_mode = comp.config.link_mode, .pie = comp.config.pie, }); - const paths = try lci.resolveCrtPaths(arena, basenames, target); + const paths = lci.resolveCrtPaths(arena, basenames, target) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.LibCInstallationMissingCrtDir => return diag.fail(.libc_installation_missing_crt_dir), + }; const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; try comp.link_task_queue.queued_prelink.ensureUnusedCapacity(gpa, fields.len + 1); @@ -2445,7 +2553,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. comp.link_task_queue.queued_prelink.appendAssumeCapacity(.load_host_libc); } else if (target.isMuslLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true; @@ -2455,7 +2563,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true, } } else if (target.isGnuLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); if (glibc.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true; @@ -2464,7 +2572,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true; } else if (target.isFreeBSDLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); if (freebsd.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true; @@ -2472,7 +2580,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.queued_jobs.freebsd_shared_objects = true; } else if (target.isNetBSDLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); if (netbsd.needsCrt0(comp.config.output_mode)) |f| { comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true; @@ -2480,12 +2588,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.queued_jobs.netbsd_shared_objects = true; } else if (target.isWasiLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true; comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true; } else if (target.isMinGW()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o; comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true; @@ -2495,7 +2603,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {}); } else { - return error.LibCUnavailable; + return diag.fail(.cross_libc_unavailable); } if ((target.isMuslLibC() and comp.config.link_mode == .static) or @@ -2737,8 +2845,14 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { } } +pub const UpdateError = error{ + OutOfMemory, + Unexpected, + CurrentWorkingDirectoryUnlinked, +}; + /// Detect changes to source files, perform semantic analysis, and update the output files. -pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { +pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateError!void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -2769,10 +2883,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { tmp_dir_rand_int = std.crypto.random.int(u64); const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); - break :d .{ - .path = path, - .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}), + const handle = comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}) catch |err| { + return comp.setMiscFailure(.open_output, "failed to create output directory '{s}': {t}", .{ path, err }); }; + break :d .{ .path = path, .handle = handle }; }; }, .incremental => {}, @@ -2849,17 +2963,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { tmp_dir_rand_int = std.crypto.random.int(u64); const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); - break :d .{ - .path = path, - .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}), + const handle = comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}) catch |err| { + return comp.setMiscFailure(.open_output, "failed to create output directory '{s}': {t}", .{ path, err }); }; + break :d .{ .path = path, .handle = handle }; }; if (comp.emit_bin) |sub_path| { const emit: Cache.Path = .{ .root_dir = whole.tmp_artifact_directory.?, .sub_path = sub_path, }; - comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); + comp.bin_file = link.File.createEmpty(arena, comp, emit, whole.lf_open_opts) catch |err| { + return comp.setMiscFailure(.open_output, "failed to open output file '{f}': {t}", .{ emit, err }); + }; } }, } @@ -3036,11 +3152,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| { return comp.setMiscFailure( .rename_results, - "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {s}", + "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}", .{ comp.dirs.local_cache, tmp_dir_sub_path, comp.dirs.local_cache, o_sub_path, - @errorName(err), + err, }, ); }; @@ -3054,15 +3170,21 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { .root_dir = comp.dirs.local_cache, .sub_path = try fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }), }; - - switch (need_writable_dance) { + const result: link.File.OpenError!void = switch (need_writable_dance) { .no => {}, - .lf_only => try lf.makeWritable(), - .lf_and_debug => { - try lf.makeWritable(); - try lf.reopenDebugInfo(); + .lf_only => lf.makeWritable(), + .lf_and_debug => res: { + lf.makeWritable() catch |err| break :res err; + lf.reopenDebugInfo() catch |err| break :res err; }, - } + }; + result catch |err| { + return comp.setMiscFailure( + .rename_results, + "failed to re-open renamed compilation results ('{f}{s}'): {t}", + .{ comp.dirs.local_cache, o_sub_path, err }, + ); + }; } try flush(comp, arena, .main); @@ -3155,7 +3277,7 @@ fn flush( comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, -) !void { +) Allocator.Error!void { if (comp.zcu) |zcu| { if (zcu.llvm_object) |llvm_object| { const pt: Zcu.PerThread = .activate(zcu, tid); @@ -3173,7 +3295,7 @@ fn flush( comp.time_report.?.stats.real_ns_llvm_emit = ns; }; - try llvm_object.emit(pt, .{ + llvm_object.emit(pt, .{ .pre_ir_path = comp.verbose_llvm_ir, .pre_bc_path = comp.verbose_llvm_bc, @@ -3204,7 +3326,10 @@ fn flush( .sanitize_thread = comp.config.any_sanitize_thread, .fuzz = comp.config.any_fuzz, .lto = comp.config.lto, - }); + }) catch |err| switch (err) { + error.LinkFailure => {}, // Already reported. + error.OutOfMemory => return error.OutOfMemory, + }; } } if (comp.bin_file) |lf| { @@ -3746,7 +3871,7 @@ fn addBuf(list: *std.ArrayList([]const u8), buf: []const u8) void { } /// This function is temporally single-threaded. -pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { +pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { const gpa = comp.gpa; var bundle: ErrorBundle.Wip = undefined; @@ -3796,8 +3921,14 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { for (zcu.failed_imports.items) |failed| { assert(zcu.alive_files.contains(failed.file_index)); // otherwise it wouldn't have been added const file = zcu.fileByIndex(failed.file_index); - const source = try file.getSource(zcu); - const tree = try file.getTree(zcu); + const source = file.getSource(zcu) catch |err| { + try unableToLoadZcuFile(zcu, &bundle, file, err); + continue; + }; + const tree = file.getTree(zcu) catch |err| { + try unableToLoadZcuFile(zcu, &bundle, file, err); + continue; + }; const start = tree.tokenStart(failed.import_token); const end = start + tree.tokenSlice(failed.import_token).len; const loc = std.zig.findLineColumn(source.bytes, start); @@ -3853,7 +3984,11 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { } else { assert(!is_retryable); // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors. - _ = try file.getTree(zcu); // Tree must be loaded. + // Tree must be loaded. + _ = file.getTree(zcu) catch |err| { + try unableToLoadZcuFile(zcu, &bundle, file, err); + continue; + }; const path = try std.fmt.allocPrint(gpa, "{f}", .{file.path.fmt(comp)}); defer gpa.free(path); if (file.zir != null) { @@ -3871,16 +4006,19 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { const SortOrder = struct { zcu: *Zcu, errors: []const *Zcu.ErrorMsg, - err: *?Error, - - const Error = @typeInfo( - @typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?, - ).error_union.error_set; - + read_err: *?ReadError, + const ReadError = struct { + file: *Zcu.File, + err: Zcu.File.GetSourceError, + }; pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { - if (ctx.err.* != null) return lhs_index < rhs_index; - return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu) catch |e| { - ctx.err.* = e; + if (ctx.read_err.* != null) return lhs_index < rhs_index; + var bad_file: *Zcu.File = undefined; + return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu, &bad_file) catch |err| { + ctx.read_err.* = .{ + .file = bad_file, + .err = err, + }; return lhs_index < rhs_index; }; } @@ -3892,13 +4030,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { var entries = try zcu.failed_analysis.entries.clone(gpa); errdefer entries.deinit(gpa); - var err: ?SortOrder.Error = null; + var read_err: ?SortOrder.ReadError = null; entries.sort(SortOrder{ .zcu = zcu, .errors = entries.items(.value), - .err = &err, + .read_err = &read_err, }); - if (err) |e| return e; + if (read_err) |e| { + try unableToLoadZcuFile(zcu, &bundle, e.file, e.err); + break :zcu_errors; + } break :s entries.slice(); }; defer sorted_failed_analysis.deinit(gpa); @@ -4018,23 +4159,33 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { // Okay, there *are* referenced compile logs. Sort them into a consistent order. - const SortContext = struct { - err: *?Error, - zcu: *Zcu, - const Error = @typeInfo( - @typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?, - ).error_union.error_set; - fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool { - if (ctx.err.* != null) return false; - return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu) catch |e| { - ctx.err.* = e; - return false; + { + const SortContext = struct { + zcu: *Zcu, + read_err: *?ReadError, + const ReadError = struct { + file: *Zcu.File, + err: Zcu.File.GetSourceError, }; + fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool { + if (ctx.read_err.* != null) return false; + var bad_file: *Zcu.File = undefined; + return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu, &bad_file) catch |err| { + ctx.read_err.* = .{ + .file = bad_file, + .err = err, + }; + return false; + }; + } + }; + var read_err: ?SortContext.ReadError = null; + std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .read_err = &read_err, .zcu = zcu }), SortContext.lessThan); + if (read_err) |e| { + try unableToLoadZcuFile(zcu, &bundle, e.file, e.err); + break :compile_log_text ""; } - }; - var sort_err: ?SortContext.Error = null; - std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .err = &sort_err, .zcu = zcu }), SortContext.lessThan); - if (sort_err) |e| return e; + } var log_text: std.ArrayListUnmanaged(u8) = .empty; defer log_text.deinit(gpa); @@ -4068,18 +4219,19 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error. // However, we haven't reported any such error. // This is a compiler bug. - var stderr_w = std.debug.lockStderrWriter(&.{}); - defer std.debug.unlockStderrWriter(); - try stderr_w.writeAll("referenced transitive analysis errors, but none actually emitted\n"); - try stderr_w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}); - while (ref) |r| { - try stderr_w.print("referenced by: {f}{s}\n", .{ - zcu.fmtAnalUnit(r.referencer), - if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "", - }); - ref = refs.get(r.referencer).?; + print_ctx: { + var stderr_w = std.debug.lockStderrWriter(&.{}); + defer std.debug.unlockStderrWriter(); + stderr_w.writeAll("referenced transitive analysis errors, but none actually emitted\n") catch break :print_ctx; + stderr_w.print("{f} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)}) catch break :print_ctx; + while (ref) |r| { + stderr_w.print("referenced by: {f}{s}\n", .{ + zcu.fmtAnalUnit(r.referencer), + if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "", + }) catch break :print_ctx; + ref = refs.get(r.referencer).?; + } } - @panic("referenced transitive analysis errors, but none actually emitted"); } }; @@ -4166,19 +4318,16 @@ pub fn addModuleErrorMsg( /// If `-freference-trace` is not specified, we only want to show the one reference trace. /// So, this is whether we have already emitted an error with a reference trace. already_added_error: bool, -) !void { +) Allocator.Error!void { const gpa = eb.gpa; const ip = &zcu.intern_pool; const err_src_loc = module_err_msg.src_loc.upgrade(zcu); const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| { - try eb.addRootErrorMessage(.{ - .msg = try eb.printString("unable to load '{f}': {s}", .{ - err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err), - }), - }); - return; + return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err); + }; + const err_span = err_src_loc.span(zcu) catch |err| { + return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err); }; - const err_span = try err_src_loc.span(zcu); const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main); var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .empty; @@ -4208,7 +4357,13 @@ pub fn addModuleErrorMsg( const f = inline_frame.ptr(zcu).*; const func_nav = ip.indexToKey(f.callee).func.owner_nav; const func_name = ip.getNav(func_nav).name.toSlice(ip); - try addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true); + addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.AlreadyReported => { + // An incomplete reference trace isn't the end of the world; just cut it off. + break :rt; + }, + }; last_call_src = f.call_src; opt_inline_frame = f.parent; } @@ -4220,7 +4375,13 @@ pub fn addModuleErrorMsg( .memoized_state => null, }; if (root_name) |n| { - try addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false); + addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.AlreadyReported => { + // An incomplete reference trace isn't the end of the world; just cut it off. + break :rt; + }, + }; } } referenced_by = ref.referencer; @@ -4257,8 +4418,12 @@ pub fn addModuleErrorMsg( var last_note_loc: ?std.zig.Loc = null; for (module_err_msg.notes) |module_note| { const note_src_loc = module_note.src_loc.upgrade(zcu); - const source = try note_src_loc.file_scope.getSource(zcu); - const span = try note_src_loc.span(zcu); + const source = note_src_loc.file_scope.getSource(zcu) catch |err| { + return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err); + }; + const span = note_src_loc.span(zcu) catch |err| { + return unableToLoadZcuFile(zcu, eb, note_src_loc.file_scope, err); + }; const loc = std.zig.findLineColumn(source.bytes, span.main); const omit_source_line = loc.eql(err_loc) or (last_note_loc != null and loc.eql(last_note_loc.?)); @@ -4303,11 +4468,17 @@ fn addReferenceTraceFrame( name: []const u8, lazy_src: Zcu.LazySrcLoc, inlined: bool, -) !void { +) error{ OutOfMemory, AlreadyReported }!void { const gpa = zcu.gpa; const src = lazy_src.upgrade(zcu); - const source = try src.file_scope.getSource(zcu); - const span = try src.span(zcu); + const source = src.file_scope.getSource(zcu) catch |err| { + try unableToLoadZcuFile(zcu, eb, src.file_scope, err); + return error.AlreadyReported; + }; + const span = src.span(zcu) catch |err| { + try unableToLoadZcuFile(zcu, eb, src.file_scope, err); + return error.AlreadyReported; + }; const loc = std.zig.findLineColumn(source.bytes, span.main); try ref_traces.append(gpa, .{ .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }), @@ -4323,19 +4494,26 @@ fn addReferenceTraceFrame( }); } -pub fn addWholeFileError( +fn addWholeFileError( zcu: *Zcu, eb: *ErrorBundle.Wip, file_index: Zcu.File.Index, msg: []const u8, -) !void { +) Allocator.Error!void { // note: "file imported here" on the import reference token const imported_note: ?ErrorBundle.MessageIndex = switch (zcu.alive_files.get(file_index).?) { .analysis_root => null, - .import => |import| try eb.addErrorMessage(.{ - .msg = try eb.addString("file imported here"), - .src_loc = try zcu.fileByIndex(import.importer).errorBundleTokenSrc(import.tok, zcu, eb), - }), + .import => |import| note: { + const file = zcu.fileByIndex(import.importer); + // `errorBundleTokenSrc` expects the tree to be loaded + _ = file.getTree(zcu) catch |err| { + return unableToLoadZcuFile(zcu, eb, file, err); + }; + break :note try eb.addErrorMessage(.{ + .msg = try eb.addString("file imported here"), + .src_loc = try file.errorBundleTokenSrc(import.tok, zcu, eb), + }); + }, }; try eb.addRootErrorMessage(.{ @@ -4349,6 +4527,20 @@ pub fn addWholeFileError( } } +/// Adds an error to `eb` that the contents of `file` could not be loaded due to `err`. This is +/// useful if `Zcu.File.getSource`/`Zcu.File.getTree` fails while lowering compile errors. +pub fn unableToLoadZcuFile( + zcu: *const Zcu, + eb: *ErrorBundle.Wip, + file: *Zcu.File, + err: Zcu.File.GetSourceError, +) Allocator.Error!void { + try eb.addRootErrorMessage(.{ + .msg = try eb.printString("unable to load: {t}", .{err}), + .src_loc = try file.errorBundleWholeFileSrc(zcu, eb), + }); +} + fn performAllTheWork( comp: *Compilation, main_progress_node: std.Progress.Node, @@ -5002,18 +5194,15 @@ pub fn separateCodegenThreadOk(comp: *const Compilation) bool { } fn workerDocsCopy(comp: *Compilation) void { - docsCopyFallible(comp) catch |err| { - return comp.lockAndSetMiscFailure( - .docs_copy, - "unable to copy autodocs artifacts: {s}", - .{@errorName(err)}, - ); - }; + docsCopyFallible(comp) catch |err| return comp.lockAndSetMiscFailure( + .docs_copy, + "unable to copy autodocs artifacts: {s}", + .{@errorName(err)}, + ); } fn docsCopyFallible(comp: *Compilation) anyerror!void { - const zcu = comp.zcu orelse - return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); + const zcu = comp.zcu orelse return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); const docs_path = comp.resolveEmitPath(comp.emit_docs.?); var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { @@ -5127,12 +5316,12 @@ fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void defer prog_node.end(); workerDocsWasmFallible(comp, prog_node) catch |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {t}", .{err}), }; } -fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void { +fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubUpdateError!void { const gpa = comp.gpa; var arena_allocator = std.heap.ArenaAllocator.init(gpa); @@ -5162,7 +5351,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .is_explicit_dynamic_linker = false, }; - const config = try Config.resolve(.{ + const config = Config.resolve(.{ .output_mode = output_mode, .resolved_target = resolved_target, .is_test = false, @@ -5171,14 +5360,17 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .root_optimize_mode = optimize_mode, .link_libc = false, .rdynamic = true, - }); + }) catch |err| { + comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to resolve compilation config: {t}", .{err}); + return error.AlreadyReported; + }; const src_basename = "main.zig"; const root_name = fs.path.stem(src_basename); const dirs = comp.dirs.withoutLocalCache(); - const root_mod = try Package.Module.create(arena, .{ + const root_mod = Package.Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"), .root_src_path = src_basename, @@ -5191,8 +5383,11 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .global = config, .cc_argv = &.{}, .parent = null, - }); - const walk_mod = try Package.Module.create(arena, .{ + }) catch |err| { + comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create root module: {t}", .{err}); + return error.AlreadyReported; + }; + const walk_mod = Package.Module.create(arena, .{ .paths = .{ .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"), .root_src_path = "Walk.zig", @@ -5205,10 +5400,14 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .global = config, .cc_argv = &.{}, .parent = root_mod, - }); + }) catch |err| { + comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: failed to create 'Walk' module: {t}", .{err}); + return error.AlreadyReported; + }; try root_mod.deps.put(arena, "Walk", walk_mod); - const sub_compilation = try Compilation.create(gpa, arena, .{ + var sub_create_diag: CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(gpa, arena, &sub_create_diag, .{ .dirs = dirs, .self_exe_path = comp.self_exe_path, .config = config, @@ -5228,7 +5427,13 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(.docs_wasm, "sub-compilation of docs_wasm failed: {f}", .{sub_create_diag}); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node); @@ -5241,11 +5446,12 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye const docs_path = comp.resolveEmitPath(comp.emit_docs.?); var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| { - return comp.lockAndSetMiscFailure( + comp.lockAndSetMiscFailure( .docs_copy, - "unable to create output directory '{f}': {s}", - .{ docs_path, @errorName(err) }, + "unable to create output directory '{f}': {t}", + .{ docs_path, err }, ); + return error.AlreadyReported; }; defer out_dir.close(); @@ -5255,9 +5461,10 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye "main.wasm", .{}, ) catch |err| { - return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {s}", .{ - crt_file.full_object_path, docs_path, @errorName(err), + comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{f}' to '{f}': {t}", .{ + crt_file.full_object_path, docs_path, err, }); + return error.AlreadyReported; }; } @@ -5324,15 +5531,11 @@ fn workerUpdateFile( } fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void { - Builtin.updateFileOnDisk(file, comp) catch |err| { - comp.mutex.lock(); - defer comp.mutex.unlock(); - comp.setMiscFailure( - .write_builtin_zig, - "unable to write '{f}': {s}", - .{ file.path.fmt(comp), @errorName(err) }, - ); - }; + Builtin.updateFileOnDisk(file, comp) catch |err| comp.lockAndSetMiscFailure( + .write_builtin_zig, + "unable to write '{f}': {s}", + .{ file.path.fmt(comp), @errorName(err) }, + ); } fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void { @@ -5632,7 +5835,7 @@ fn buildRt( options, out, ) catch |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{ @tagName(misc_task), @errorName(err), }), @@ -5644,7 +5847,7 @@ fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.P if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.musl_crt_file, "unable to build musl {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5656,7 +5859,7 @@ fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5669,7 +5872,7 @@ fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) voi // The job should no longer be queued up since it succeeded. comp.queued_jobs.glibc_shared_objects = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.glibc_shared_objects, "unable to build glibc shared objects: {s}", .{ @errorName(err), }), @@ -5681,7 +5884,7 @@ fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.freebsd_crt_file, "unable to build FreeBSD {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5694,7 +5897,7 @@ fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) v // The job should no longer be queued up since it succeeded. comp.queued_jobs.freebsd_shared_objects = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.freebsd_shared_objects, "unable to build FreeBSD libc shared objects: {s}", .{ @errorName(err), }), @@ -5706,7 +5909,7 @@ fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: s if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.netbsd_crt_file, "unable to build NetBSD {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5719,7 +5922,7 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo // The job should no longer be queued up since it succeeded. comp.queued_jobs.netbsd_shared_objects = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.netbsd_shared_objects, "unable to build NetBSD libc shared objects: {s}", .{ @errorName(err), }), @@ -5731,7 +5934,7 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.mingw_crt_file, "unable to build mingw-w64 {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5743,7 +5946,7 @@ fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_no if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| { comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.wasi_libc_crt_file, "unable to build WASI libc {s}: {s}", .{ @tagName(crt_file), @errorName(err), }), @@ -5755,7 +5958,7 @@ fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void { if (libunwind.buildStaticLib(comp, prog_node)) |_| { comp.queued_jobs.libunwind = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.libunwind, "unable to build libunwind: {s}", .{@errorName(err)}), } } @@ -5765,7 +5968,7 @@ fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void { if (libcxx.buildLibCxx(comp, prog_node)) |_| { comp.queued_jobs.libcxx = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.libcxx, "unable to build libcxx: {s}", .{@errorName(err)}), } } @@ -5775,7 +5978,7 @@ fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void { if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| { comp.queued_jobs.libcxxabi = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}), } } @@ -5785,7 +5988,7 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void { if (libtsan.buildTsan(comp, prog_node)) |_| { comp.queued_jobs.libtsan = false; } else |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}), } } @@ -5802,7 +6005,7 @@ fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void { .{}, &comp.zigc_static_lib, ) catch |err| switch (err) { - error.SubCompilationFailed => return, // error reported already + error.AlreadyReported => return, else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}), }; } @@ -7439,12 +7642,13 @@ pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { return target_util.zigBackend(target, comp.config.use_llvm); } +pub const SubUpdateError = UpdateError || error{AlreadyReported}; pub fn updateSubCompilation( parent_comp: *Compilation, sub_comp: *Compilation, misc_task: MiscTask, prog_node: std.Progress.Node, -) !void { +) SubUpdateError!void { { const sub_node = prog_node.start(@tagName(misc_task), 0); defer sub_node.end(); @@ -7454,20 +7658,20 @@ pub fn updateSubCompilation( // Look for compilation errors in this sub compilation const gpa = parent_comp.gpa; - var keep_errors = false; + var errors = try sub_comp.getAllErrorsAlloc(); - defer if (!keep_errors) errors.deinit(gpa); + defer errors.deinit(gpa); if (errors.errorMessageCount() > 0) { + parent_comp.mutex.lock(); + defer parent_comp.mutex.unlock(); try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1); parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{ - .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ - @tagName(misc_task), - }), + .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {t} failed", .{misc_task}), .children = errors, }); - keep_errors = true; - return error.SubCompilationFailed; + errors = .empty; // ownership moved to the failures map + return error.AlreadyReported; } } @@ -7481,7 +7685,7 @@ fn buildOutputFromZig( prog_node: std.Progress.Node, options: RtOptions, out: *?CrtFile, -) !void { +) SubUpdateError!void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -7495,7 +7699,7 @@ fn buildOutputFromZig( const strip = comp.compilerRtStrip(); const optimize_mode = comp.compilerRtOptMode(); - const config = try Config.resolve(.{ + const config = Config.resolve(.{ .output_mode = output_mode, .link_mode = link_mode, .resolved_target = comp.root_mod.resolved_target, @@ -7509,9 +7713,12 @@ fn buildOutputFromZig( .any_error_tracing = false, .root_error_tracing = false, .lto = if (options.allow_lto) comp.config.lto else .none, - }); + }) catch |err| { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err }); + return error.AlreadyReported; + }; - const root_mod = try Package.Module.create(arena, .{ + const root_mod = Package.Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = src_basename, @@ -7536,7 +7743,10 @@ fn buildOutputFromZig( .global = config, .cc_argv = &.{}, .parent = null, - }); + }) catch |err| { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err }); + return error.AlreadyReported; + }; const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) { .whole => |whole| .{ @@ -7552,7 +7762,8 @@ fn buildOutputFromZig( .incremental, .none => null, }; - const sub_compilation = try Compilation.create(gpa, arena, .{ + var sub_create_diag: CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .cache_mode = .whole, .parent_whole_cache = parent_whole_cache, @@ -7576,7 +7787,13 @@ fn buildOutputFromZig( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); @@ -7609,7 +7826,7 @@ pub fn build_crt_file( /// created within this function. c_source_files: []CSourceFile, options: CrtFileOptions, -) !void { +) SubUpdateError!void { const tracy_trace = trace(@src()); defer tracy_trace.end(); @@ -7624,7 +7841,7 @@ pub fn build_crt_file( .output_mode = output_mode, }); - const config = try Config.resolve(.{ + const config = Config.resolve(.{ .output_mode = output_mode, .resolved_target = comp.root_mod.resolved_target, .is_test = false, @@ -7638,8 +7855,11 @@ pub fn build_crt_file( .Lib => if (options.allow_lto) comp.config.lto else .none, .Obj, .Exe => .none, }, - }); - const root_mod = try Package.Module.create(arena, .{ + }) catch |err| { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to resolve compilation config: {t}", .{ misc_task_tag, err }); + return error.AlreadyReported; + }; + const root_mod = Package.Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "", @@ -7669,13 +7889,17 @@ pub fn build_crt_file( .global = config, .cc_argv = &.{}, .parent = null, - }); + }) catch |err| { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: failed to create module: {t}", .{ misc_task_tag, err }); + return error.AlreadyReported; + }; for (c_source_files) |*item| { item.owner = root_mod; } - const sub_compilation = try Compilation.create(gpa, arena, .{ + var sub_create_diag: CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, @@ -7699,7 +7923,13 @@ pub fn build_crt_file( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task_tag, "sub-compilation of {t} failed: {f}", .{ misc_task_tag, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 1c941f51f4..aa680fd5b4 100644 --- a/src/Package/Module.zig +++ b/src/Package/Module.zig @@ -92,6 +92,20 @@ pub const ResolvedTarget = struct { llvm_cpu_features: ?[*:0]const u8 = null, }; +pub const CreateError = error{ + OutOfMemory, + ValgrindUnsupportedOnTarget, + TargetRequiresSingleThreaded, + BackendRequiresSingleThreaded, + TargetRequiresPic, + PieRequiresPic, + DynamicLinkingRequiresPic, + TargetHasNoRedZone, + StackCheckUnsupportedByTarget, + StackProtectorUnsupportedByTarget, + StackProtectorUnavailableWithoutLibC, +}; + /// At least one of `parent` and `resolved_target` must be non-null. pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module { if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread); diff --git a/src/Sema.zig b/src/Sema.zig index 8cfcadea66..d06545e164 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5726,6 +5726,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr .global = comp.config, .parent = parent_mod, }) catch |err| switch (err) { + error.OutOfMemory => |e| return e, // None of these are possible because we are creating a package with // the exact same configuration as the parent package, which already // passed these checks. @@ -5739,8 +5740,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr error.StackCheckUnsupportedByTarget => unreachable, error.StackProtectorUnsupportedByTarget => unreachable, error.StackProtectorUnavailableWithoutLibC => unreachable, - - else => |e| return e, }; const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig"); errdefer c_import_file_path.deinit(gpa); diff --git a/src/Zcu.zig b/src/Zcu.zig index f27b369515..158badfcaa 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -1038,7 +1038,9 @@ pub const File = struct { stat: Cache.File.Stat, }; - pub fn getSource(file: *File, zcu: *const Zcu) !Source { + pub const GetSourceError = error{ OutOfMemory, FileTooBig } || std.fs.File.OpenError || std.fs.File.ReadError; + + pub fn getSource(file: *File, zcu: *const Zcu) GetSourceError!Source { const gpa = zcu.gpa; if (file.source) |source| return .{ @@ -1062,7 +1064,7 @@ pub const File = struct { var file_reader = f.reader(&.{}); file_reader.size = stat.size; - try file_reader.interface.readSliceAll(source); + file_reader.interface.readSliceAll(source) catch return file_reader.err.?; // Here we do not modify stat fields because this function is the one // used for error reporting. We need to keep the stat fields stale so that @@ -1081,7 +1083,7 @@ pub const File = struct { }; } - pub fn getTree(file: *File, zcu: *const Zcu) !*const Ast { + pub fn getTree(file: *File, zcu: *const Zcu) GetSourceError!*const Ast { if (file.tree) |*tree| return tree; const source = try file.getSource(zcu); @@ -1127,7 +1129,7 @@ pub const File = struct { file: *File, zcu: *const Zcu, eb: *std.zig.ErrorBundle.Wip, - ) !std.zig.ErrorBundle.SourceLocationIndex { + ) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex { return eb.addSourceLocation(.{ .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}), .span_start = 0, @@ -1138,17 +1140,17 @@ pub const File = struct { .source_line = 0, }); } + /// Asserts that the tree has already been loaded with `getTree`. pub fn errorBundleTokenSrc( file: *File, tok: Ast.TokenIndex, zcu: *const Zcu, eb: *std.zig.ErrorBundle.Wip, - ) !std.zig.ErrorBundle.SourceLocationIndex { - const source = try file.getSource(zcu); - const tree = try file.getTree(zcu); + ) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex { + const tree = &file.tree.?; const start = tree.tokenStart(tok); const end = start + tree.tokenSlice(tok).len; - const loc = std.zig.findLineColumn(source.bytes, start); + const loc = std.zig.findLineColumn(file.source.?, start); return eb.addSourceLocation(.{ .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}), .span_start = start, @@ -2665,8 +2667,9 @@ pub const LazySrcLoc = struct { } /// Used to sort error messages, so that they're printed in a consistent order. - /// If an error is returned, that error makes sorting impossible. - pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu) !bool { + /// If an error is returned, a file could not be read in order to resolve a source location. + /// In that case, `bad_file_out` is populated, and sorting is impossible. + pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu, bad_file_out: **Zcu.File) File.GetSourceError!bool { const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse { // LHS source location lost, so should never be referenced. Just sort it to the end. return false; @@ -2684,8 +2687,14 @@ pub const LazySrcLoc = struct { return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt); } - const lhs_span = try lhs_src.span(zcu); - const rhs_span = try rhs_src.span(zcu); + const lhs_span = lhs_src.span(zcu) catch |err| { + bad_file_out.* = lhs_src.file_scope; + return err; + }; + const rhs_span = rhs_src.span(zcu) catch |err| { + bad_file_out.* = rhs_src.file_scope; + return err; + }; return lhs_span.main < rhs_span.main; } }; @@ -4584,7 +4593,7 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) pub fn addFileInMultipleModulesError( zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip, -) !void { +) Allocator.Error!void { const gpa = zcu.gpa; const info = zcu.multi_module_err.?; @@ -4631,7 +4640,7 @@ fn explainWhyFileIsInModule( file: File.Index, in_module: *Package.Module, ref: File.Reference, -) !void { +) Allocator.Error!void { const gpa = zcu.gpa; // error: file is the root of module 'foo' @@ -4666,7 +4675,13 @@ fn explainWhyFileIsInModule( const thing: []const u8 = if (is_first) "file" else "which"; is_first = false; - const import_src = try zcu.fileByIndex(import.importer).errorBundleTokenSrc(import.tok, zcu, eb); + const importer_file = zcu.fileByIndex(import.importer); + // `errorBundleTokenSrc` expects the tree to be loaded + _ = importer_file.getTree(zcu) catch |err| { + try Compilation.unableToLoadZcuFile(zcu, eb, importer_file, err); + return; // stop the explanation early + }; + const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb); const importer_ref = zcu.alive_files.get(import.importer).?; const importer_root: ?*Package.Module = switch (importer_ref) { diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 0e8e8f850b..29a0c30978 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -57,7 +57,7 @@ fn libcPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; @@ -414,7 +414,7 @@ fn wordDirective(target: *const std.Target) []const u8 { } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void { // See also glibc.zig which this code is based on. @@ -1065,7 +1065,10 @@ fn buildSharedLib( }, }; - const sub_compilation = try Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .@"freebsd libc shared object"; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, @@ -1090,8 +1093,14 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"freebsd libc shared object", prog_node); + try comp.updateSubCompilation(sub_compilation, misc_task, prog_node); } diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index 5518dc9321..e63355af33 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -162,7 +162,7 @@ pub const CrtFile = enum { }; /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; @@ -656,7 +656,7 @@ fn wordDirective(target: *const std.Target) []const u8 { } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void { const tracy = trace(@src()); defer tracy.end(); @@ -1223,7 +1223,10 @@ fn buildSharedLib( }, }; - const sub_compilation = try Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .@"glibc shared object"; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, @@ -1248,10 +1251,16 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node); + try comp.updateSubCompilation(sub_compilation, misc_task, prog_node); } pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile { diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 12c9bdedde..be73fb7ea4 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -102,7 +102,7 @@ const libcxx_thread_files = [_][]const u8{ pub const BuildError = error{ OutOfMemory, - SubCompilationFailed, + AlreadyReported, ZigCompilerNotBuiltWithLLVMExtensions, }; @@ -144,12 +144,12 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .lto = comp.config.lto, .any_sanitize_thread = comp.config.any_sanitize_thread, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxx, "unable to build libc++: resolving configuration failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const root_mod = Module.create(arena, .{ @@ -177,12 +177,12 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .cc_argv = &.{}, .parent = null, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxx, "unable to build libc++: creating module failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const libcxx_files = if (comp.config.any_non_single_threaded) @@ -255,7 +255,10 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! }); } - const sub_compilation = Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .libcxx; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, @@ -276,24 +279,19 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, }) catch |err| { - comp.setMiscFailure( - .libcxx, - "unable to build libc++: create compilation failed: {s}", - .{@errorName(err)}, - ); - return error.SubCompilationFailed; + switch (err) { + else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}), + error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {f}", .{sub_create_diag}), + } + return error.AlreadyReported; }; defer sub_compilation.destroy(); - comp.updateSubCompilation(sub_compilation, .libcxx, prog_node) catch |err| switch (err) { - error.SubCompilationFailed => return error.SubCompilationFailed, + comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { + error.AlreadyReported => return error.AlreadyReported, else => |e| { - comp.setMiscFailure( - .libcxx, - "unable to build libc++: compilation failed: {s}", - .{@errorName(e)}, - ); - return error.SubCompilationFailed; + comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: compilation failed: {t}", .{e}); + return error.AlreadyReported; }, }; @@ -345,12 +343,12 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .lto = comp.config.lto, .any_sanitize_thread = comp.config.any_sanitize_thread, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: resolving configuration failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const root_mod = Module.create(arena, .{ @@ -379,12 +377,12 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .cc_argv = &.{}, .parent = null, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: creating module failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; var c_source_files = try std.ArrayList(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len); @@ -446,7 +444,10 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr }); } - const sub_compilation = Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .libcxxabi; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, @@ -467,24 +468,23 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, }) catch |err| { - comp.setMiscFailure( - .libcxxabi, - "unable to build libc++abi: create compilation failed: {s}", - .{@errorName(err)}, - ); - return error.SubCompilationFailed; + switch (err) { + else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}), + error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {f}", .{sub_create_diag}), + } + return error.AlreadyReported; }; defer sub_compilation.destroy(); - comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node) catch |err| switch (err) { - error.SubCompilationFailed => return error.SubCompilationFailed, + comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { + error.AlreadyReported => return error.AlreadyReported, else => |e| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: compilation failed: {s}", .{@errorName(e)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }, }; diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index d17baf8fa1..7c31c15bd3 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -8,7 +8,7 @@ const Module = @import("../Package/Module.zig"); pub const BuildError = error{ OutOfMemory, - SubCompilationFailed, + AlreadyReported, ZigCompilerNotBuiltWithLLVMExtensions, TSANUnsupportedCPUArchitecture, }; @@ -66,12 +66,12 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo // LLVM disables LTO for its libtsan. .lto = .none, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libtsan, "unable to build thread sanitizer runtime: resolving configuration failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const common_flags = [_][]const u8{ @@ -105,12 +105,12 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .cc_argv = &common_flags, .parent = null, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libtsan, "unable to build thread sanitizer runtime: creating module failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); @@ -273,7 +273,11 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo null; // Workaround for https://github.com/llvm/llvm-project/issues/97627 const headerpad_size: ?u32 = if (target.os.tag.isDarwin()) 32 else null; - const sub_compilation = Compilation.create(comp.gpa, arena, .{ + + const misc_task: Compilation.MiscTask = .libtsan; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, @@ -297,24 +301,19 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .install_name = install_name, .headerpad_size = headerpad_size, }) catch |err| { - comp.setMiscFailure( - .libtsan, - "unable to build thread sanitizer runtime: create compilation failed: {s}", - .{@errorName(err)}, - ); - return error.SubCompilationFailed; + switch (err) { + else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), + error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {f}", .{ misc_task, sub_create_diag }), + } + return error.AlreadyReported; }; defer sub_compilation.destroy(); - comp.updateSubCompilation(sub_compilation, .libtsan, prog_node) catch |err| switch (err) { - error.SubCompilationFailed => return error.SubCompilationFailed, + comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { + error.AlreadyReported => return error.AlreadyReported, else => |e| { - comp.setMiscFailure( - .libtsan, - "unable to build thread sanitizer runtime: compilation failed: {s}", - .{@errorName(e)}, - ); - return error.SubCompilationFailed; + comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: compilation failed: {s}", .{ misc_task, @errorName(e) }); + return error.AlreadyReported; }, }; diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index 71cc6ccbc0..32b2dbb947 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -10,7 +10,7 @@ const trace = @import("../tracy.zig").trace; pub const BuildError = error{ OutOfMemory, - SubCompilationFailed, + AlreadyReported, ZigCompilerNotBuiltWithLLVMExtensions, }; @@ -42,12 +42,12 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .any_unwind_tables = unwind_tables != .none, .lto = comp.config.lto, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libunwind, "unable to build libunwind: resolving configuration failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const root_mod = Module.create(arena, .{ .paths = .{ @@ -76,12 +76,12 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .cc_argv = &.{}, .parent = null, }) catch |err| { - comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libunwind, "unable to build libunwind: creating module failed: {s}", .{@errorName(err)}, ); - return error.SubCompilationFailed; + return error.AlreadyReported; }; const root_name = "unwind"; @@ -139,7 +139,11 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .owner = root_mod, }; } - const sub_compilation = Compilation.create(comp.gpa, arena, .{ + + const misc_task: Compilation.MiscTask = .libunwind; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .config = config, @@ -162,24 +166,19 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, }) catch |err| { - comp.setMiscFailure( - .libunwind, - "unable to build libunwind: create compilation failed: {s}", - .{@errorName(err)}, - ); - return error.SubCompilationFailed; + switch (err) { + else => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {t}", .{ misc_task, err }), + error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: create compilation failed: {f}", .{ misc_task, sub_create_diag }), + } + return error.AlreadyReported; }; defer sub_compilation.destroy(); - comp.updateSubCompilation(sub_compilation, .libunwind, prog_node) catch |err| switch (err) { - error.SubCompilationFailed => return error.SubCompilationFailed, + comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { + error.AlreadyReported => return error.AlreadyReported, else => |e| { - comp.setMiscFailure( - .libunwind, - "unable to build libunwind: compilation failed: {s}", - .{@errorName(e)}, - ); - return error.SubCompilationFailed; + comp.lockAndSetMiscFailure(misc_task, "unable to build {t}: compilation failed: {s}", .{ misc_task, @errorName(e) }); + return error.AlreadyReported; }, }; diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 5603a15a57..09f0f635bf 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -18,7 +18,7 @@ pub const CrtFile = enum { }; /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; diff --git a/src/libs/musl.zig b/src/libs/musl.zig index e0a6ee6b45..97204b1075 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -17,7 +17,7 @@ pub const CrtFile = enum { }; /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; @@ -243,7 +243,10 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro .parent = null, }); - const sub_compilation = try Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .@"musl libc.so"; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, @@ -268,10 +271,16 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro }, .skip_linker_dependencies = true, .soname = "libc.so", - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"musl libc.so", prog_node); + try comp.updateSubCompilation(sub_compilation, misc_task, prog_node); const basename = try comp.gpa.dupe(u8, "libc.so"); errdefer comp.gpa.free(basename); diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 945c6853a0..534145e4d7 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -49,7 +49,7 @@ fn csuPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; @@ -360,7 +360,7 @@ fn wordDirective(target: *const std.Target) []const u8 { } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void { // See also glibc.zig which this code is based on. @@ -729,7 +729,10 @@ fn buildSharedLib( }, }; - const sub_compilation = try Compilation.create(comp.gpa, arena, .{ + const misc_task: Compilation.MiscTask = .@"netbsd libc shared object"; + + var sub_create_diag: Compilation.CreateDiagnostic = undefined; + const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .thread_pool = comp.thread_pool, .self_exe_path = comp.self_exe_path, @@ -753,8 +756,14 @@ fn buildSharedLib( .soname = soname, .c_source_files = &c_source_files, .skip_linker_dependencies = true, - }); + }) catch |err| switch (err) { + error.CreateFail => { + comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag }); + return error.AlreadyReported; + }, + else => |e| return e, + }; defer sub_compilation.destroy(); - try comp.updateSubCompilation(sub_compilation, .@"netbsd libc shared object", prog_node); + try comp.updateSubCompilation(sub_compilation, misc_task, prog_node); } diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index 6dced677a7..390d08f038 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -28,7 +28,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co } /// TODO replace anyerror with explicit error set, recording user-friendly errors with -/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. +/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example. pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; diff --git a/src/link.zig b/src/link.zig index 1b63e7793b..7db8a39e2a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -509,6 +509,8 @@ pub const File = struct { }; }; + pub const OpenError = @typeInfo(@typeInfo(@TypeOf(open)).@"fn".return_type.?).error_union.error_set; + /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and /// rewriting it. A malicious file is detected as incremental link failure diff --git a/src/main.zig b/src/main.zig index 5bc6c2e923..32b6e91fde 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3395,7 +3395,8 @@ fn buildOutputType( var file_system_inputs: std.ArrayListUnmanaged(u8) = .empty; defer file_system_inputs.deinit(gpa); - const comp = Compilation.create(gpa, arena, .{ + var create_diag: Compilation.CreateDiagnostic = undefined; + const comp = Compilation.create(gpa, arena, &create_diag, .{ .dirs = dirs, .thread_pool = &thread_pool, .self_exe_path = switch (native_os) { @@ -3521,47 +3522,45 @@ fn buildOutputType( .file_system_inputs = &file_system_inputs, .debug_compiler_runtime_libs = debug_compiler_runtime_libs, }) catch |err| switch (err) { - error.LibCUnavailable => { - const triple_name = try target.zigTriple(arena); - std.log.err("unable to find or provide libc for target '{s}'", .{triple_name}); + error.CreateFail => switch (create_diag) { + .cross_libc_unavailable => { + // We can emit a more informative error for this. + const triple_name = try target.zigTriple(arena); + std.log.err("unable to provide libc for target '{s}'", .{triple_name}); - for (std.zig.target.available_libcs) |t| { - if (t.arch == target.cpu.arch and t.os == target.os.tag) { - // If there's a `glibc_min`, there's also an `os_ver`. - if (t.glibc_min) |glibc_min| { - std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{ - @tagName(t.arch), - @tagName(t.os), - t.os_ver.?, - @tagName(t.abi), - glibc_min.major, - glibc_min.minor, - }); - } else if (t.os_ver) |os_ver| { - std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{ - @tagName(t.arch), - @tagName(t.os), - os_ver, - @tagName(t.abi), - }); - } else { - std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{ - @tagName(t.arch), - @tagName(t.os), - @tagName(t.abi), - }); + for (std.zig.target.available_libcs) |t| { + if (t.arch == target.cpu.arch and t.os == target.os.tag) { + // If there's a `glibc_min`, there's also an `os_ver`. + if (t.glibc_min) |glibc_min| { + std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{ + @tagName(t.arch), + @tagName(t.os), + t.os_ver.?, + @tagName(t.abi), + glibc_min.major, + glibc_min.minor, + }); + } else if (t.os_ver) |os_ver| { + std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{ + @tagName(t.arch), + @tagName(t.os), + os_ver, + @tagName(t.abi), + }); + } else { + std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{ + @tagName(t.arch), + @tagName(t.os), + @tagName(t.abi), + }); + } } } - } - process.exit(1); + process.exit(1); + }, + else => fatal("{f}", .{create_diag}), }, - error.ExportTableAndImportTableConflict => { - fatal("--import-table and --export-table may not be used together", .{}); - }, - error.IllegalZigImport => { - fatal("this compiler implementation does not support importing the root source file of a provided module", .{}); - }, - else => fatal("unable to create compilation: {s}", .{@errorName(err)}), + else => fatal("failed to create compilation: {s}", .{@errorName(err)}), }; var comp_destroyed = false; defer if (!comp_destroyed) comp.destroy(); @@ -3627,7 +3626,7 @@ fn buildOutputType( } updateModule(comp, color, root_prog_node) catch |err| switch (err) { - error.SemanticAnalyzeFail => { + error.CompileErrorsReported => { assert(listen == .none); saveState(comp, incremental); process.exit(1); @@ -4521,7 +4520,12 @@ fn runOrTestHotSwap( } } -fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) !void { +const UpdateModuleError = Compilation.UpdateError || error{ + /// The update caused compile errors. The error bundle has already been + /// reported to the user by being rendered to stderr. + CompileErrorsReported, +}; +fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) UpdateModuleError!void { try comp.update(prog_node); var errors = try comp.getAllErrorsAlloc(); @@ -4529,7 +4533,7 @@ fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) if (errors.errorMessageCount() > 0) { errors.renderToStdErr(color.renderOptions()); - return error.SemanticAnalyzeFail; + return error.CompileErrorsReported; } } @@ -5373,7 +5377,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { try root_mod.deps.put(arena, "@build", build_mod); - const comp = Compilation.create(gpa, arena, .{ + var create_diag: Compilation.CreateDiagnostic = undefined; + const comp = Compilation.create(gpa, arena, &create_diag, .{ .libc_installation = libc_installation, .dirs = dirs, .root_name = "build", @@ -5395,13 +5400,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .cache_mode = .whole, .reference_trace = reference_trace, .debug_compile_errors = debug_compile_errors, - }) catch |err| { - fatal("unable to create compilation: {s}", .{@errorName(err)}); + }) catch |err| switch (err) { + error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), + else => fatal("failed to create compilation: {s}", .{@errorName(err)}), }; defer comp.destroy(); updateModule(comp, color, root_prog_node) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(2), + error.CompileErrorsReported => process.exit(2), else => |e| return e, }; @@ -5614,7 +5620,8 @@ fn jitCmd( try root_mod.deps.put(arena, "aro", aro_mod); } - const comp = Compilation.create(gpa, arena, .{ + var create_diag: Compilation.CreateDiagnostic = undefined; + const comp = Compilation.create(gpa, arena, &create_diag, .{ .dirs = dirs, .root_name = options.cmd_name, .config = config, @@ -5624,8 +5631,9 @@ fn jitCmd( .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .cache_mode = .whole, - }) catch |err| { - fatal("unable to create compilation: {s}", .{@errorName(err)}); + }) catch |err| switch (err) { + error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), + else => fatal("failed to create compilation: {s}", .{@errorName(err)}), }; defer comp.destroy(); @@ -5646,7 +5654,7 @@ fn jitCmd( } } else { updateModule(comp, color, root_prog_node) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(2), + error.CompileErrorsReported => process.exit(2), else => |e| return e, }; }