Skip to content

Commit 1440519

Browse files
committed
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.
1 parent 3d25a9c commit 1440519

File tree

16 files changed

+647
-345
lines changed

16 files changed

+647
-345
lines changed

lib/std/zig/LibCDirs.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn detect(
1919
is_native_abi: bool,
2020
link_libc: bool,
2121
libc_installation: ?*const LibCInstallation,
22-
) !LibCDirs {
22+
) LibCInstallation.FindError!LibCDirs {
2323
if (!link_libc) {
2424
return .{
2525
.libc_include_dir_list = &[0][]u8{},
@@ -114,7 +114,7 @@ fn detectFromInstallation(arena: Allocator, target: *const std.Target, lci: *con
114114
}
115115
}
116116
if (target.os.tag == .haiku) {
117-
const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
117+
const include_dir_path = lci.include_dir.?;
118118
const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
119119
list.appendAssumeCapacity(os_dir);
120120
// Errors.h

src/Compilation.zig

Lines changed: 410 additions & 180 deletions
Large diffs are not rendered by default.

src/Package/Module.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ pub const ResolvedTarget = struct {
9292
llvm_cpu_features: ?[*:0]const u8 = null,
9393
};
9494

95+
pub const CreateError = error{
96+
OutOfMemory,
97+
ValgrindUnsupportedOnTarget,
98+
TargetRequiresSingleThreaded,
99+
BackendRequiresSingleThreaded,
100+
TargetRequiresPic,
101+
PieRequiresPic,
102+
DynamicLinkingRequiresPic,
103+
TargetHasNoRedZone,
104+
StackCheckUnsupportedByTarget,
105+
StackProtectorUnsupportedByTarget,
106+
StackProtectorUnavailableWithoutLibC,
107+
};
108+
95109
/// At least one of `parent` and `resolved_target` must be non-null.
96110
pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
97111
if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread);

src/Sema.zig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5726,6 +5726,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
57265726
.global = comp.config,
57275727
.parent = parent_mod,
57285728
}) catch |err| switch (err) {
5729+
error.OutOfMemory => |e| return e,
57295730
// None of these are possible because we are creating a package with
57305731
// the exact same configuration as the parent package, which already
57315732
// passed these checks.
@@ -5739,8 +5740,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
57395740
error.StackCheckUnsupportedByTarget => unreachable,
57405741
error.StackProtectorUnsupportedByTarget => unreachable,
57415742
error.StackProtectorUnavailableWithoutLibC => unreachable,
5742-
5743-
else => |e| return e,
57445743
};
57455744
const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig");
57465745
errdefer c_import_file_path.deinit(gpa);

src/Zcu.zig

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,9 @@ pub const File = struct {
10381038
stat: Cache.File.Stat,
10391039
};
10401040

1041-
pub fn getSource(file: *File, zcu: *const Zcu) !Source {
1041+
pub const GetSourceError = error{ OutOfMemory, FileTooBig } || std.fs.File.OpenError || std.fs.File.ReadError;
1042+
1043+
pub fn getSource(file: *File, zcu: *const Zcu) GetSourceError!Source {
10421044
const gpa = zcu.gpa;
10431045

10441046
if (file.source) |source| return .{
@@ -1062,7 +1064,7 @@ pub const File = struct {
10621064

10631065
var file_reader = f.reader(&.{});
10641066
file_reader.size = stat.size;
1065-
try file_reader.interface.readSliceAll(source);
1067+
file_reader.interface.readSliceAll(source) catch return file_reader.err.?;
10661068

10671069
// Here we do not modify stat fields because this function is the one
10681070
// used for error reporting. We need to keep the stat fields stale so that
@@ -1081,7 +1083,7 @@ pub const File = struct {
10811083
};
10821084
}
10831085

1084-
pub fn getTree(file: *File, zcu: *const Zcu) !*const Ast {
1086+
pub fn getTree(file: *File, zcu: *const Zcu) GetSourceError!*const Ast {
10851087
if (file.tree) |*tree| return tree;
10861088

10871089
const source = try file.getSource(zcu);
@@ -1127,7 +1129,7 @@ pub const File = struct {
11271129
file: *File,
11281130
zcu: *const Zcu,
11291131
eb: *std.zig.ErrorBundle.Wip,
1130-
) !std.zig.ErrorBundle.SourceLocationIndex {
1132+
) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
11311133
return eb.addSourceLocation(.{
11321134
.src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
11331135
.span_start = 0,
@@ -1138,17 +1140,17 @@ pub const File = struct {
11381140
.source_line = 0,
11391141
});
11401142
}
1143+
/// Asserts that the tree has already been loaded with `getTree`.
11411144
pub fn errorBundleTokenSrc(
11421145
file: *File,
11431146
tok: Ast.TokenIndex,
11441147
zcu: *const Zcu,
11451148
eb: *std.zig.ErrorBundle.Wip,
1146-
) !std.zig.ErrorBundle.SourceLocationIndex {
1147-
const source = try file.getSource(zcu);
1148-
const tree = try file.getTree(zcu);
1149+
) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
1150+
const tree = &file.tree.?;
11491151
const start = tree.tokenStart(tok);
11501152
const end = start + tree.tokenSlice(tok).len;
1151-
const loc = std.zig.findLineColumn(source.bytes, start);
1153+
const loc = std.zig.findLineColumn(file.source.?, start);
11521154
return eb.addSourceLocation(.{
11531155
.src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
11541156
.span_start = start,
@@ -2665,8 +2667,9 @@ pub const LazySrcLoc = struct {
26652667
}
26662668

26672669
/// Used to sort error messages, so that they're printed in a consistent order.
2668-
/// If an error is returned, that error makes sorting impossible.
2669-
pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu) !bool {
2670+
/// If an error is returned, a file could not be read in order to resolve a source location.
2671+
/// In that case, `bad_file_out` is populated, and sorting is impossible.
2672+
pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu, bad_file_out: **Zcu.File) File.GetSourceError!bool {
26702673
const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse {
26712674
// LHS source location lost, so should never be referenced. Just sort it to the end.
26722675
return false;
@@ -2684,8 +2687,14 @@ pub const LazySrcLoc = struct {
26842687
return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
26852688
}
26862689

2687-
const lhs_span = try lhs_src.span(zcu);
2688-
const rhs_span = try rhs_src.span(zcu);
2690+
const lhs_span = lhs_src.span(zcu) catch |err| {
2691+
bad_file_out.* = lhs_src.file_scope;
2692+
return err;
2693+
};
2694+
const rhs_span = rhs_src.span(zcu) catch |err| {
2695+
bad_file_out.* = rhs_src.file_scope;
2696+
return err;
2697+
};
26892698
return lhs_span.main < rhs_span.main;
26902699
}
26912700
};
@@ -4584,7 +4593,7 @@ pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg)
45844593
pub fn addFileInMultipleModulesError(
45854594
zcu: *Zcu,
45864595
eb: *std.zig.ErrorBundle.Wip,
4587-
) !void {
4596+
) Allocator.Error!void {
45884597
const gpa = zcu.gpa;
45894598

45904599
const info = zcu.multi_module_err.?;
@@ -4631,7 +4640,7 @@ fn explainWhyFileIsInModule(
46314640
file: File.Index,
46324641
in_module: *Package.Module,
46334642
ref: File.Reference,
4634-
) !void {
4643+
) Allocator.Error!void {
46354644
const gpa = zcu.gpa;
46364645

46374646
// error: file is the root of module 'foo'
@@ -4666,7 +4675,13 @@ fn explainWhyFileIsInModule(
46664675
const thing: []const u8 = if (is_first) "file" else "which";
46674676
is_first = false;
46684677

4669-
const import_src = try zcu.fileByIndex(import.importer).errorBundleTokenSrc(import.tok, zcu, eb);
4678+
const importer_file = zcu.fileByIndex(import.importer);
4679+
// `errorBundleTokenSrc` expects the tree to be loaded
4680+
_ = importer_file.getTree(zcu) catch |err| {
4681+
try Compilation.unableToLoadZcuFile(zcu, eb, importer_file, err);
4682+
return; // stop the explanation early
4683+
};
4684+
const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb);
46704685

46714686
const importer_ref = zcu.alive_files.get(import.importer).?;
46724687
const importer_root: ?*Package.Module = switch (importer_ref) {

src/libs/freebsd.zig

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fn libcPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const
5757
}
5858

5959
/// TODO replace anyerror with explicit error set, recording user-friendly errors with
60-
/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
60+
/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
6161
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
6262
if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
6363

@@ -414,7 +414,7 @@ fn wordDirective(target: *const std.Target) []const u8 {
414414
}
415415

416416
/// TODO replace anyerror with explicit error set, recording user-friendly errors with
417-
/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
417+
/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
418418
pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
419419
// See also glibc.zig which this code is based on.
420420

@@ -1065,7 +1065,10 @@ fn buildSharedLib(
10651065
},
10661066
};
10671067

1068-
const sub_compilation = try Compilation.create(comp.gpa, arena, .{
1068+
const misc_task: Compilation.MiscTask = .@"freebsd libc shared object";
1069+
1070+
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
1071+
const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{
10691072
.dirs = comp.dirs.withoutLocalCache(),
10701073
.thread_pool = comp.thread_pool,
10711074
.self_exe_path = comp.self_exe_path,
@@ -1090,8 +1093,14 @@ fn buildSharedLib(
10901093
.soname = soname,
10911094
.c_source_files = &c_source_files,
10921095
.skip_linker_dependencies = true,
1093-
});
1096+
}) catch |err| switch (err) {
1097+
error.CreateFail => {
1098+
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
1099+
return error.AlreadyReported;
1100+
},
1101+
else => |e| return e,
1102+
};
10941103
defer sub_compilation.destroy();
10951104

1096-
try comp.updateSubCompilation(sub_compilation, .@"freebsd libc shared object", prog_node);
1105+
try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
10971106
}

src/libs/glibc.zig

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub const CrtFile = enum {
162162
};
163163

164164
/// TODO replace anyerror with explicit error set, recording user-friendly errors with
165-
/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
165+
/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
166166
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
167167
if (!build_options.have_llvm) {
168168
return error.ZigCompilerNotBuiltWithLLVMExtensions;
@@ -656,7 +656,7 @@ fn wordDirective(target: *const std.Target) []const u8 {
656656
}
657657

658658
/// TODO replace anyerror with explicit error set, recording user-friendly errors with
659-
/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
659+
/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
660660
pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
661661
const tracy = trace(@src());
662662
defer tracy.end();
@@ -1223,7 +1223,10 @@ fn buildSharedLib(
12231223
},
12241224
};
12251225

1226-
const sub_compilation = try Compilation.create(comp.gpa, arena, .{
1226+
const misc_task: Compilation.MiscTask = .@"glibc shared object";
1227+
1228+
var sub_create_diag: Compilation.CreateDiagnostic = undefined;
1229+
const sub_compilation = Compilation.create(comp.gpa, arena, &sub_create_diag, .{
12271230
.dirs = comp.dirs.withoutLocalCache(),
12281231
.thread_pool = comp.thread_pool,
12291232
.self_exe_path = comp.self_exe_path,
@@ -1248,10 +1251,16 @@ fn buildSharedLib(
12481251
.soname = soname,
12491252
.c_source_files = &c_source_files,
12501253
.skip_linker_dependencies = true,
1251-
});
1254+
}) catch |err| switch (err) {
1255+
error.CreateFail => {
1256+
comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
1257+
return error.AlreadyReported;
1258+
},
1259+
else => |e| return e,
1260+
};
12521261
defer sub_compilation.destroy();
12531262

1254-
try comp.updateSubCompilation(sub_compilation, .@"glibc shared object", prog_node);
1263+
try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
12551264
}
12561265

12571266
pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile {

0 commit comments

Comments
 (0)