compiler: improve error reporting

The functions `Compilation.create` and `Compilation.update` previously
returned inferred error sets, which had built up a lot of crap over
time. This meant that certain error conditions -- particularly certain
filesystem errors -- were not being reported properly (at best the CLI
would just print the error name). This was also a problem in
sub-compilations, where at times only the error name -- which might just
be something like `LinkFailed` -- would be visible.

This commit makes the error handling here more disciplined by
introducing concrete error sets to these functions (and a few more as a
consequence). These error sets are small: errors in `update` are almost
all reported via compile errors, and errors in `create` are reported
through a new `Compilation.CreateDiagnostic` type, a tagged union of
possible error cases. This allows for better error reporting.

Sub-compilations also report errors more correctly in several cases,
leading to more informative errors in the case of compiler bugs.

Also fixes some race conditions in library building by replacing calls
to `setMiscFailure` with calls to `lockAndSetMiscFailure`. Compilation
of libraries such as libc happens on the thread pool, so the logic must
synchronize its access to shared `Compilation` state.
This commit is contained in:
mlugg 2025-08-08 15:07:03 +01:00 committed by Matthew Lugg
parent 3d25a9c1e0
commit 1440519239
16 changed files with 646 additions and 344 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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);
}

View file

@ -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 {

View file

@ -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;
},
};

View file

@ -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;
},
};

View file

@ -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;
},
};

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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,
};
}