authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-02 15:27:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-02 15:27:00-07:00
logd979dd9b58f6d5462b009002e0a0eb79f94fb9a7
tree6cb9504f15c34fd32f732b930b8d770346576617
parent5103053977573131d8040a53d9ab3f2afd1b01b0

stage2: improve AstGen FileNotFound error message

Partially addresses #9203. It fixes the first case, but not the second one mentioned in the issue.

7 files changed, 111 insertions(+), 34 deletions(-)

lib/std/mem.zig+5-5
...@@ -2297,14 +2297,14 @@ pub fn replaceOwned(comptime T: type, allocator: *Allocator, input: []const T, n...@@ -2297,14 +2297,14 @@ pub fn replaceOwned(comptime T: type, allocator: *Allocator, input: []const T, n
2297}2297}
22982298
2299test "replaceOwned" {2299test "replaceOwned" {
2300 const allocator = std.heap.page_allocator;2300 const gpa = std.testing.allocator;
23012301
2302 const base_replace = replaceOwned(u8, allocator, "All your base are belong to us", "base", "Zig") catch unreachable;2302 const base_replace = replaceOwned(u8, gpa, "All your base are belong to us", "base", "Zig") catch @panic("out of memory");
2303 defer allocator.free(base_replace);2303 defer gpa.free(base_replace);
2304 try testing.expect(eql(u8, base_replace, "All your Zig are belong to us"));2304 try testing.expect(eql(u8, base_replace, "All your Zig are belong to us"));
23052305
2306 const zen_replace = replaceOwned(u8, allocator, "Favor reading code over writing code.", " code", "") catch unreachable;2306 const zen_replace = replaceOwned(u8, gpa, "Favor reading code over writing code.", " code", "") catch @panic("out of memory");
2307 defer allocator.free(zen_replace);2307 defer gpa.free(zen_replace);
2308 try testing.expect(eql(u8, zen_replace, "Favor reading over writing."));2308 try testing.expect(eql(u8, zen_replace, "Favor reading over writing."));
2309}2309}
23102310
src/AstGen.zig+10-4
...@@ -34,8 +34,9 @@ string_table: std.StringHashMapUnmanaged(u32) = .{},...@@ -34,8 +34,9 @@ string_table: std.StringHashMapUnmanaged(u32) = .{},
34compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},34compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
35/// The topmost block of the current function.35/// The topmost block of the current function.
36fn_block: ?*GenZir = null,36fn_block: ?*GenZir = null,
37/// String table indexes, keeps track of all `@import` operands.37/// Maps string table indexes to the first `@import` ZIR instruction
38imports: std.AutoArrayHashMapUnmanaged(u32, void) = .{},38/// that uses this string as the operand.
39imports: std.AutoArrayHashMapUnmanaged(u32, Zir.Inst.Index) = .{},
3940
40const InnerError = error{ OutOfMemory, AnalysisFail };41const InnerError = error{ OutOfMemory, AnalysisFail };
4142
...@@ -154,7 +155,7 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) Allocator.Error!Zir {...@@ -154,7 +155,7 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) Allocator.Error!Zir {
154 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{155 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
155 .imports_len = @intCast(u32, astgen.imports.count()),156 .imports_len = @intCast(u32, astgen.imports.count()),
156 });157 });
157 astgen.extra.appendSliceAssumeCapacity(astgen.imports.keys());158 astgen.extra.appendSliceAssumeCapacity(astgen.imports.values());
158 }159 }
159160
160 return Zir{161 return Zir{
...@@ -6863,8 +6864,13 @@ fn builtinCall(...@@ -6863,8 +6864,13 @@ fn builtinCall(
6863 }6864 }
6864 const str_lit_token = main_tokens[operand_node];6865 const str_lit_token = main_tokens[operand_node];
6865 const str = try astgen.strLitAsString(str_lit_token);6866 const str = try astgen.strLitAsString(str_lit_token);
6866 try astgen.imports.put(astgen.gpa, str.index, {});
6867 const result = try gz.addStrTok(.import, str.index, str_lit_token);6867 const result = try gz.addStrTok(.import, str.index, str_lit_token);
6868 if (gz.refToIndex(result)) |import_inst_index| {
6869 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
6870 if (!gop.found_existing) {
6871 gop.value_ptr.* = import_inst_index;
6872 }
6873 }
6868 return rvalue(gz, rl, result, node);6874 return rvalue(gz, rl, result, node);
6869 },6875 },
6870 .compile_log => {6876 .compile_log => {
src/Compilation.zig+44-13
...@@ -1958,7 +1958,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1958,7 +1958,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1958 while (self.astgen_work_queue.readItem()) |file| {1958 while (self.astgen_work_queue.readItem()) |file| {
1959 self.astgen_wait_group.start();1959 self.astgen_wait_group.start();
1960 try self.thread_pool.spawn(workerAstGenFile, .{1960 try self.thread_pool.spawn(workerAstGenFile, .{
1961 self, file, &zir_prog_node, &self.astgen_wait_group,1961 self, file, &zir_prog_node, &self.astgen_wait_group, .root,
1962 });1962 });
1963 }1963 }
19641964
...@@ -2310,11 +2310,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2310,11 +2310,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2310 };2310 };
2311}2311}
23122312
2313const AstGenSrc = union(enum) {
2314 root,
2315 import: struct {
2316 importing_file: *Module.Scope.File,
2317 import_inst: Zir.Inst.Index,
2318 },
2319};
2320
2313fn workerAstGenFile(2321fn workerAstGenFile(
2314 comp: *Compilation,2322 comp: *Compilation,
2315 file: *Module.Scope.File,2323 file: *Module.Scope.File,
2316 prog_node: *std.Progress.Node,2324 prog_node: *std.Progress.Node,
2317 wg: *WaitGroup,2325 wg: *WaitGroup,
2326 src: AstGenSrc,
2318) void {2327) void {
2319 defer wg.finish();2328 defer wg.finish();
23202329
...@@ -2327,7 +2336,7 @@ fn workerAstGenFile(...@@ -2327,7 +2336,7 @@ fn workerAstGenFile(
2327 error.AnalysisFail => return,2336 error.AnalysisFail => return,
2328 else => {2337 else => {
2329 file.status = .retryable_failure;2338 file.status = .retryable_failure;
2330 comp.reportRetryableAstGenError(file, err) catch |oom| switch (oom) {2339 comp.reportRetryableAstGenError(src, file, err) catch |oom| switch (oom) {
2331 // Swallowing this error is OK because it's implied to be OOM when2340 // Swallowing this error is OK because it's implied to be OOM when
2332 // there is a missing `failed_files` error message.2341 // there is a missing `failed_files` error message.
2333 error.OutOfMemory => {},2342 error.OutOfMemory => {},
...@@ -2344,8 +2353,9 @@ fn workerAstGenFile(...@@ -2344,8 +2353,9 @@ fn workerAstGenFile(
2344 if (imports_index != 0) {2353 if (imports_index != 0) {
2345 const imports_len = file.zir.extra[imports_index];2354 const imports_len = file.zir.extra[imports_index];
23462355
2347 for (file.zir.extra[imports_index + 1 ..][0..imports_len]) |str_index| {2356 for (file.zir.extra[imports_index + 1 ..][0..imports_len]) |import_inst| {
2348 const import_path = file.zir.nullTerminatedString(str_index);2357 const inst_data = file.zir.instructions.items(.data)[import_inst].str_tok;
2358 const import_path = inst_data.get(file.zir);
23492359
2350 const import_result = blk: {2360 const import_result = blk: {
2351 const lock = comp.mutex.acquire();2361 const lock = comp.mutex.acquire();
...@@ -2357,9 +2367,13 @@ fn workerAstGenFile(...@@ -2357,9 +2367,13 @@ fn workerAstGenFile(
2357 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{2367 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{
2358 file.sub_file_path, import_path, import_result.file.sub_file_path,2368 file.sub_file_path, import_path, import_result.file.sub_file_path,
2359 });2369 });
2370 const sub_src: AstGenSrc = .{ .import = .{
2371 .importing_file = file,
2372 .import_inst = import_inst,
2373 } };
2360 wg.start();2374 wg.start();
2361 comp.thread_pool.spawn(workerAstGenFile, .{2375 comp.thread_pool.spawn(workerAstGenFile, .{
2362 comp, import_result.file, prog_node, wg,2376 comp, import_result.file, prog_node, wg, sub_src,
2363 }) catch {2377 }) catch {
2364 wg.finish();2378 wg.finish();
2365 continue;2379 continue;
...@@ -2570,6 +2584,7 @@ fn reportRetryableCObjectError(...@@ -2570,6 +2584,7 @@ fn reportRetryableCObjectError(
25702584
2571fn reportRetryableAstGenError(2585fn reportRetryableAstGenError(
2572 comp: *Compilation,2586 comp: *Compilation,
2587 src: AstGenSrc,
2573 file: *Module.Scope.File,2588 file: *Module.Scope.File,
2574 err: anyerror,2589 err: anyerror,
2575) error{OutOfMemory}!void {2590) error{OutOfMemory}!void {
...@@ -2578,22 +2593,38 @@ fn reportRetryableAstGenError(...@@ -2578,22 +2593,38 @@ fn reportRetryableAstGenError(
25782593
2579 file.status = .retryable_failure;2594 file.status = .retryable_failure;
25802595
2581 const src_loc: Module.SrcLoc = .{2596 const src_loc: Module.SrcLoc = switch (src) {
2582 .file_scope = file,2597 .root => .{
2583 .parent_decl_node = 0,2598 .file_scope = file,
2584 .lazy = .entire_file,2599 .parent_decl_node = 0,
2600 .lazy = .entire_file,
2601 },
2602 .import => |info| blk: {
2603 const importing_file = info.importing_file;
2604 const import_inst = info.import_inst;
2605 const inst_data = importing_file.zir.instructions.items(.data)[import_inst].str_tok;
2606 break :blk .{
2607 .file_scope = importing_file,
2608 .parent_decl_node = 0,
2609 .lazy = .{ .token_offset = inst_data.src_tok },
2610 };
2611 },
2585 };2612 };
25862613
2587 const err_msg = if (file.pkg.root_src_directory.path) |dir_path|2614 const err_msg = if (file.pkg.root_src_directory.path) |dir_path|
2588 try Module.ErrorMsg.create(2615 try Module.ErrorMsg.create(
2589 gpa,2616 gpa,
2590 src_loc,2617 src_loc,
2591 "unable to load {s}" ++ std.fs.path.sep_str ++ "{s}: {s}",2618 "unable to load '{'}" ++ std.fs.path.sep_str ++ "{'}': {s}",
2592 .{ dir_path, file.sub_file_path, @errorName(err) },2619 .{
2620 std.zig.fmtEscapes(dir_path),
2621 std.zig.fmtEscapes(file.sub_file_path),
2622 @errorName(err),
2623 },
2593 )2624 )
2594 else2625 else
2595 try Module.ErrorMsg.create(gpa, src_loc, "unable to load {s}: {s}", .{2626 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{'}': {s}", .{
2596 file.sub_file_path, @errorName(err),2627 std.zig.fmtEscapes(file.sub_file_path), @errorName(err),
2597 });2628 });
2598 errdefer err_msg.destroy(gpa);2629 errdefer err_msg.destroy(gpa);
25992630
src/Module.zig+1-1
...@@ -2485,7 +2485,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {...@@ -2485,7 +2485,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File) !void {
2485 .file_scope = file,2485 .file_scope = file,
2486 .parent_decl_node = 0,2486 .parent_decl_node = 0,
2487 .lazy = .{ .byte_abs = byte_abs },2487 .lazy = .{ .byte_abs = byte_abs },
2488 }, err_msg, "invalid byte: '{'}'", .{ std.zig.fmtEscapes(source[byte_abs..][0..1]) });2488 }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])});
2489 }2489 }
24902490
2491 {2491 {
src/Zir.zig+12-5
...@@ -139,9 +139,15 @@ pub fn renderAsTextToFile(...@@ -139,9 +139,15 @@ pub fn renderAsTextToFile(
139 if (imports_index != 0) {139 if (imports_index != 0) {
140 try fs_file.writeAll("Imports:\n");140 try fs_file.writeAll("Imports:\n");
141 const imports_len = scope_file.zir.extra[imports_index];141 const imports_len = scope_file.zir.extra[imports_index];
142 for (scope_file.zir.extra[imports_index + 1 ..][0..imports_len]) |str_index| {142 for (scope_file.zir.extra[imports_index + 1 ..][0..imports_len]) |import_inst| {
143 const import_path = scope_file.zir.nullTerminatedString(str_index);143 const inst_data = writer.code.instructions.items(.data)[import_inst].str_tok;
144 try fs_file.writer().print(" {s}\n", .{import_path});144 const src = inst_data.src();
145 const import_path = inst_data.get(writer.code);
146 try fs_file.writer().print(" @import(\"{}\") ", .{
147 std.zig.fmtEscapes(import_path),
148 });
149 try writer.writeSrc(fs_file.writer(), src);
150 try fs_file.writer().writeAll("\n");
145 }151 }
146 }152 }
147}153}
...@@ -2767,9 +2773,10 @@ pub const Inst = struct {...@@ -2767,9 +2773,10 @@ pub const Inst = struct {
2767 };2773 };
2768 };2774 };
27692775
2770 /// Trailing: for each `imports_len` there is a string table index.2776 /// Trailing: for each `imports_len` there is an instruction index
2777 /// to an import instruction.
2771 pub const Imports = struct {2778 pub const Imports = struct {
2772 imports_len: u32,2779 imports_len: Zir.Inst.Index,
2773 };2780 };
2774};2781};
27752782
src/test.zig+38-4
...@@ -699,6 +699,11 @@ pub const TestContext = struct {...@@ -699,6 +699,11 @@ pub const TestContext = struct {
699 arena,699 arena,
700 &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path },700 &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path },
701 );701 );
702 const tmp_dir_path_plus_slash = try std.fmt.allocPrint(
703 arena,
704 "{s}" ++ std.fs.path.sep_str,
705 .{tmp_dir_path},
706 );
702 const local_cache_path = try std.fs.path.join(707 const local_cache_path = try std.fs.path.join(
703 arena,708 arena,
704 &[_][]const u8{ tmp_dir_path, "zig-cache" },709 &[_][]const u8{ tmp_dir_path, "zig-cache" },
...@@ -773,7 +778,14 @@ pub const TestContext = struct {...@@ -773,7 +778,14 @@ pub const TestContext = struct {
773 var i: usize = 0;778 var i: usize = 0;
774 ok = while (err_iter.next()) |line| : (i += 1) {779 ok = while (err_iter.next()) |line| : (i += 1) {
775 if (i >= case_error_list.len) break false;780 if (i >= case_error_list.len) break false;
776 const expected = try std.fmt.allocPrint(arena, "{s}", .{case_error_list[i]});781 const expected = try std.mem.replaceOwned(
782 u8,
783 arena,
784 try std.fmt.allocPrint(arena, "{s}", .{case_error_list[i]}),
785 "${DIR}",
786 tmp_dir_path_plus_slash,
787 );
788
777 if (std.mem.indexOf(u8, line, expected) == null) break false;789 if (std.mem.indexOf(u8, line, expected) == null) break false;
778 continue;790 continue;
779 } else true;791 } else true;
...@@ -789,7 +801,13 @@ pub const TestContext = struct {...@@ -789,7 +801,13 @@ pub const TestContext = struct {
789 }801 }
790 } else {802 } else {
791 for (case_error_list) |msg| {803 for (case_error_list) |msg| {
792 const expected = try std.fmt.allocPrint(arena, "{s}", .{msg});804 const expected = try std.mem.replaceOwned(
805 u8,
806 arena,
807 try std.fmt.allocPrint(arena, "{s}", .{msg}),
808 "${DIR}",
809 tmp_dir_path_plus_slash,
810 );
793 if (std.mem.indexOf(u8, result.stderr, expected) == null) {811 if (std.mem.indexOf(u8, result.stderr, expected) == null) {
794 print(812 print(
795 \\813 \\
...@@ -971,12 +989,20 @@ pub const TestContext = struct {...@@ -971,12 +989,20 @@ pub const TestContext = struct {
971 const src_path_ok = case_msg.src.src_path.len == 0 or989 const src_path_ok = case_msg.src.src_path.len == 0 or
972 std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path);990 std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path);
973991
992 const expected_msg = try std.mem.replaceOwned(
993 u8,
994 arena,
995 case_msg.src.msg,
996 "${DIR}",
997 tmp_dir_path_plus_slash,
998 );
999
974 if (src_path_ok and1000 if (src_path_ok and
975 (case_msg.src.line == std.math.maxInt(u32) or1001 (case_msg.src.line == std.math.maxInt(u32) or
976 actual_msg.line == case_msg.src.line) and1002 actual_msg.line == case_msg.src.line) and
977 (case_msg.src.column == std.math.maxInt(u32) or1003 (case_msg.src.column == std.math.maxInt(u32) or
978 actual_msg.column == case_msg.src.column) and1004 actual_msg.column == case_msg.src.column) and
979 std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and1005 std.mem.eql(u8, expected_msg, actual_msg.msg) and
980 case_msg.src.kind == .@"error")1006 case_msg.src.kind == .@"error")
981 {1007 {
982 handled_errors[i] = true;1008 handled_errors[i] = true;
...@@ -1012,11 +1038,19 @@ pub const TestContext = struct {...@@ -1012,11 +1038,19 @@ pub const TestContext = struct {
1012 }1038 }
1013 if (ex_tag != .src) continue;1039 if (ex_tag != .src) continue;
10141040
1041 const expected_msg = try std.mem.replaceOwned(
1042 u8,
1043 arena,
1044 case_msg.src.msg,
1045 "${DIR}",
1046 tmp_dir_path_plus_slash,
1047 );
1048
1015 if ((case_msg.src.line == std.math.maxInt(u32) or1049 if ((case_msg.src.line == std.math.maxInt(u32) or
1016 actual_msg.line == case_msg.src.line) and1050 actual_msg.line == case_msg.src.line) and
1017 (case_msg.src.column == std.math.maxInt(u32) or1051 (case_msg.src.column == std.math.maxInt(u32) or
1018 actual_msg.column == case_msg.src.column) and1052 actual_msg.column == case_msg.src.column) and
1019 std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and1053 std.mem.eql(u8, expected_msg, actual_msg.msg) and
1020 case_msg.src.kind == .note)1054 case_msg.src.kind == .note)
1021 {1055 {
1022 handled_errors[i] = true;1056 handled_errors[i] = true;
test/compile_errors.zig+1-2
...@@ -4976,9 +4976,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -4976,9 +4976,8 @@ pub fn addCases(ctx: *TestContext) !void {
49764976
4977 ctx.objErrStage1("bad import",4977 ctx.objErrStage1("bad import",
4978 \\const bogus = @import("bogus-does-not-exist.zig",);4978 \\const bogus = @import("bogus-does-not-exist.zig",);
4979 \\export fn entry() void { bogus.bogo(); }
4980 , &[_][]const u8{4979 , &[_][]const u8{
4981 "tmp.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'",4980 "tmp.zig:1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound",
4982 });4981 });
49834982
4984 ctx.objErrStage1("undeclared identifier",4983 ctx.objErrStage1("undeclared identifier",