authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-11-12 19:33:50+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-05 19:58:38+00:00
log4d7818a76ad951f0a16c3831b31841430b1368f7
tree697fb00dcd9127bd00653e81f7db026f3f625775
parentcbc05e0b1d86d57b533abeedc97e6fb6a648c64b
signaturelock-open Commit is signed but in an unrecognized format.

compiler: allow files with AstGen errors to undergo semantic analysis

This commit enhances AstGen to introduce a form of error resilience which allows valid ZIR to be emitted even when AstGen errors occur. When a non-fatal AstGen error (e.g. `appendErrorNode`) occurs, ZIR generation is not affected; the error is added to `astgen.errors` and ultimately to the errors stored in `extra`, but that doesn't stop us getting valid ZIR. Fatal AstGen errors (e.g. `failNode`) are a bit trickier. These errors return `error.AnalysisFail`, which is propagated up the stack. In theory, any parent expression can catch this error and handle it, continuing ZIR generation whilst throwing away whatever was lost. For now, we only do this in one place: when creating declarations. If a call to `fnDecl`, `comptimeDecl`, `globalVarDecl`, etc, returns `error.AnalysisFail`, the `declaration` instruction is still created, but its body simply contains the new `extended(astgen_error())` instruction, which instructs Sema to terminate semantic analysis with a transitive error. This means that a fatal AstGen error causes the innermost declaration containing the error to fail, but the rest of the file remains intact. If a source file contains parse errors, or an `error.AnalysisFail` happens when lowering the top-level struct (e.g. there is an error in one of its fields, or a name has multiple declarations), then lowering for the entire file fails. Alongside the existing `Zir.hasCompileErrors` query, this commit introduces `Zir.loweringFailed`, which returns `true` only in this case. The end result here is that files with AstGen failures will almost always still emit valid ZIR, and hence can undergo semantic analysis on the parts of the file which are (from AstGen's perspective) valid. This is a noteworthy improvement to UX, but the main motivation here is actually incremental compilation. Previously, AstGen failures caused lots of semantic analysis work to be thrown out, because all `AnalUnit`s in the file required re-analysis so as to trigger necessary transitive failures and remove stored compile errors which would no longer make sense (because a fresh compilation of this code would not emit those errors, as the units those errors applied to would fail sooner due to referencing a failed file). Now, this case only applies when a file has severe top-level errors, which is far less common than something like having an unused variable. Lastly, this commit changes a few errors in `AstGen` to become fatal when they were previously non-fatal and vice versa. If there is still a reasonable way to continue AstGen and lower to ZIR after an error, it is non-fatal; otherwise, it is fatal. For instance, `comptime const`, while redundant syntax, has a clear meaning we can lower; on the other hand, using an undeclared identifer has no sane lowering, so must trigger a fatal error.

17 files changed, 238 insertions(+), 93 deletions(-)

lib/std/multi_array_list.zig+6
......@@ -74,6 +74,12 @@ pub fn MultiArrayList(comptime T: type) type {
7474 len: usize,
7575 capacity: usize,
7676
77 pub const empty: Slice = .{
78 .ptrs = undefined,
79 .len = 0,
80 .capacity = 0,
81 };
82
7783 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
7884 const F = FieldType(field);
7985 if (self.capacity == 0) {
lib/std/zig/AstGen.zig+143-35
......@@ -172,9 +172,9 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
172172 };
173173 defer gz_instructions.deinit(gpa);
174174
175 // The AST -> ZIR lowering process assumes an AST that does not have any
176 // parse errors.
177 if (tree.errors.len == 0) {
175 // The AST -> ZIR lowering process assumes an AST that does not have any parse errors.
176 // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR.
177 const fatal = if (tree.errors.len == 0) fatal: {
178178 if (AstGen.structDeclInner(
179179 &gen_scope,
180180 &gen_scope.base,
......@@ -184,13 +184,15 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
184184 0,
185185 )) |struct_decl_ref| {
186186 assert(struct_decl_ref.toIndex().? == .main_struct_inst);
187 break :fatal false;
187188 } else |err| switch (err) {
188189 error.OutOfMemory => return error.OutOfMemory,
189 error.AnalysisFail => {}, // Handled via compile_errors below.
190 error.AnalysisFail => break :fatal true, // Handled via compile_errors below.
190191 }
191 } else {
192 } else fatal: {
192193 try lowerAstErrors(&astgen);
193 }
194 break :fatal true;
195 };
194196
195197 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
196198 if (astgen.compile_errors.items.len == 0) {
......@@ -228,8 +230,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
228230 }
229231 }
230232
231 return Zir{
232 .instructions = astgen.instructions.toOwnedSlice(),
233 return .{
234 .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(),
233235 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
234236 .extra = try astgen.extra.toOwnedSlice(gpa),
235237 };
......@@ -2101,7 +2103,7 @@ fn comptimeExprAst(
21012103) InnerError!Zir.Inst.Ref {
21022104 const astgen = gz.astgen;
21032105 if (gz.is_comptime) {
2104 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
2106 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
21052107 }
21062108 const tree = astgen.tree;
21072109 const node_datas = tree.nodes.items(.data);
......@@ -3275,6 +3277,9 @@ fn varDecl(
32753277 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
32763278 }
32773279
3280 // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`.
3281 const force_comptime = var_decl.comptime_token != null;
3282
32783283 // Depending on the type of AST the initialization expression is, we may need an lvalue
32793284 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
32803285 // the variable, no memory location needed.
......@@ -3288,7 +3293,7 @@ fn varDecl(
32883293 } else .{ .rl = .none, .ctx = .const_init };
32893294 const prev_anon_name_strategy = gz.anon_name_strategy;
32903295 gz.anon_name_strategy = .dbg_var;
3291 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
3296 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, force_comptime);
32923297 gz.anon_name_strategy = prev_anon_name_strategy;
32933298
32943299 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
......@@ -3358,7 +3363,7 @@ fn varDecl(
33583363 const prev_anon_name_strategy = gz.anon_name_strategy;
33593364 gz.anon_name_strategy = .dbg_var;
33603365 defer gz.anon_name_strategy = prev_anon_name_strategy;
3361 const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node);
3366 const init_inst = try reachableExprComptime(gz, scope, init_result_info, var_decl.ast.init_node, node, force_comptime);
33623367
33633368 // The const init expression may have modified the error return trace, so signal
33643369 // to Sema that it should save the new index for restoring later.
......@@ -3503,7 +3508,7 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
35033508
35043509 const full = tree.assignDestructure(node);
35053510 if (full.comptime_token != null and gz.is_comptime) {
3506 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3511 return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
35073512 }
35083513
35093514 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
......@@ -3562,7 +3567,7 @@ fn assignDestructureMaybeDecls(
35623567
35633568 const full = tree.assignDestructure(node);
35643569 if (full.comptime_token != null and gz.is_comptime) {
3565 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3570 try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
35663571 }
35673572
35683573 const is_comptime = full.comptime_token != null or gz.is_comptime;
......@@ -3676,6 +3681,7 @@ fn assignDestructureMaybeDecls(
36763681
36773682 if (full.comptime_token != null and !any_non_const_variables) {
36783683 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3684 // Note that this is non-fatal; we will still evaluate at comptime.
36793685 }
36803686
36813687 // If this expression is marked comptime, we must wrap it in a comptime block.
......@@ -4125,8 +4131,8 @@ fn fnDecl(
41254131 // The source slice is added towards the *end* of this function.
41264132 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
41274133
4128 // missing function name already happened in scanContainer()
4129 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
4134 // missing function name already checked in scanContainer()
4135 const fn_name_token = fn_proto.name_token.?;
41304136
41314137 // We insert this at the beginning so that its instruction index marks the
41324138 // start of the top level declaration.
......@@ -5167,8 +5173,7 @@ fn structDeclInner(
51675173
51685174 if (is_comptime) {
51695175 switch (layout) {
5170 .@"packed" => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
5171 .@"extern" => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
5176 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
51725177 .auto => any_comptime_fields = true,
51735178 }
51745179 } else {
......@@ -5195,7 +5200,7 @@ fn structDeclInner(
51955200
51965201 if (have_align) {
51975202 if (layout == .@"packed") {
5198 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
5203 return astgen.failNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
51995204 }
52005205 any_aligned_fields = true;
52015206 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
......@@ -5289,8 +5294,7 @@ fn tupleDecl(
52895294
52905295 switch (layout) {
52915296 .auto => {},
5292 .@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),
5293 .@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
5297 .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}),
52945298 }
52955299
52965300 if (backing_int_node != 0) {
......@@ -5673,7 +5677,7 @@ fn containerDecl(
56735677 };
56745678 };
56755679 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
5676 try astgen.appendErrorNodeNotes(
5680 return astgen.failNodeNotes(
56775681 node,
56785682 "non-exhaustive enum missing integer tag type",
56795683 .{},
......@@ -5896,9 +5900,19 @@ fn containerMember(
58965900 const full = tree.fullFnProto(&buf, member_node).?;
58975901 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
58985902
5903 const prev_decl_index = wip_members.decl_index;
58995904 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
59005905 error.OutOfMemory => return error.OutOfMemory,
5901 error.AnalysisFail => {},
5906 error.AnalysisFail => {
5907 wip_members.decl_index = prev_decl_index;
5908 try addFailedDeclaration(
5909 wip_members,
5910 gz,
5911 .{ .named = full.name_token.? },
5912 full.ast.proto_node,
5913 full.visib_token != null,
5914 );
5915 },
59025916 };
59035917 },
59045918
......@@ -5907,28 +5921,77 @@ fn containerMember(
59075921 .simple_var_decl,
59085922 .aligned_var_decl,
59095923 => {
5910 astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
5924 const full = tree.fullVarDecl(member_node).?;
5925 const prev_decl_index = wip_members.decl_index;
5926 astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) {
59115927 error.OutOfMemory => return error.OutOfMemory,
5912 error.AnalysisFail => {},
5928 error.AnalysisFail => {
5929 wip_members.decl_index = prev_decl_index;
5930 try addFailedDeclaration(
5931 wip_members,
5932 gz,
5933 .{ .named = full.ast.mut_token + 1 },
5934 member_node,
5935 full.visib_token != null,
5936 );
5937 },
59135938 };
59145939 },
59155940
59165941 .@"comptime" => {
5942 const prev_decl_index = wip_members.decl_index;
59175943 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
59185944 error.OutOfMemory => return error.OutOfMemory,
5919 error.AnalysisFail => {},
5945 error.AnalysisFail => {
5946 wip_members.decl_index = prev_decl_index;
5947 try addFailedDeclaration(
5948 wip_members,
5949 gz,
5950 .@"comptime",
5951 member_node,
5952 false,
5953 );
5954 },
59205955 };
59215956 },
59225957 .@"usingnamespace" => {
5958 const prev_decl_index = wip_members.decl_index;
59235959 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
59245960 error.OutOfMemory => return error.OutOfMemory,
5925 error.AnalysisFail => {},
5961 error.AnalysisFail => {
5962 wip_members.decl_index = prev_decl_index;
5963 try addFailedDeclaration(
5964 wip_members,
5965 gz,
5966 .@"usingnamespace",
5967 member_node,
5968 is_pub: {
5969 const main_tokens = tree.nodes.items(.main_token);
5970 const token_tags = tree.tokens.items(.tag);
5971 const main_token = main_tokens[member_node];
5972 break :is_pub main_token > 0 and token_tags[main_token - 1] == .keyword_pub;
5973 },
5974 );
5975 },
59265976 };
59275977 },
59285978 .test_decl => {
5979 const prev_decl_index = wip_members.decl_index;
5980 // We need to have *some* decl here so that the decl count matches what's expected.
5981 // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble
5982 // of duplicating the test name logic, and just assume this is an unnamed test.
59295983 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
59305984 error.OutOfMemory => return error.OutOfMemory,
5931 error.AnalysisFail => {},
5985 error.AnalysisFail => {
5986 wip_members.decl_index = prev_decl_index;
5987 try addFailedDeclaration(
5988 wip_members,
5989 gz,
5990 .unnamed_test,
5991 member_node,
5992 false,
5993 );
5994 },
59325995 };
59335996 },
59345997 else => unreachable,
......@@ -6140,7 +6203,7 @@ fn orelseCatchExpr(
61406203 const payload = payload_token orelse break :blk &else_scope.base;
61416204 const err_str = tree.tokenSlice(payload);
61426205 if (mem.eql(u8, err_str, "_")) {
6143 return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
6206 try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{});
61446207 }
61456208 const err_name = try astgen.identAsString(payload);
61466209
......@@ -6599,7 +6662,7 @@ fn whileExpr(
65996662
66006663 const is_inline = while_full.inline_token != null;
66016664 if (parent_gz.is_comptime and is_inline) {
6602 return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6665 try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
66036666 }
66046667 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
66056668 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
......@@ -6889,7 +6952,7 @@ fn forExpr(
68896952
68906953 const is_inline = for_full.inline_token != null;
68916954 if (parent_gz.is_comptime and is_inline) {
6892 return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6955 try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
68936956 }
68946957 const tree = astgen.tree;
68956958 const token_tags = tree.tokens.items(.tag);
......@@ -6950,7 +7013,7 @@ fn forExpr(
69507013 .none;
69517014
69527015 if (end_val == .none and is_discard) {
6953 return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
7016 try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{});
69547017 }
69557018
69567019 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
......@@ -7467,6 +7530,7 @@ fn switchExprErrUnion(
74677530 const err_name = blk: {
74687531 const err_str = tree.tokenSlice(error_payload);
74697532 if (mem.eql(u8, err_str, "_")) {
7533 // This is fatal because we already know we're switching on the captured error.
74707534 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
74717535 }
74727536 const err_name = try astgen.identAsString(error_payload);
......@@ -7521,7 +7585,7 @@ fn switchExprErrUnion(
75217585
75227586 const capture_slice = tree.tokenSlice(capture_token);
75237587 if (mem.eql(u8, capture_slice, "_")) {
7524 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
7588 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{});
75257589 }
75267590 const tag_name = try astgen.identAsString(capture_token);
75277591 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
......@@ -8018,7 +8082,7 @@ fn switchExpr(
80188082 break :blk payload_sub_scope;
80198083 const tag_slice = tree.tokenSlice(tag_token);
80208084 if (mem.eql(u8, tag_slice, "_")) {
8021 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
8085 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
80228086 } else if (case.inline_token == null) {
80238087 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
80248088 }
......@@ -13699,6 +13763,8 @@ fn scanContainer(
1369913763 const main_tokens = tree.nodes.items(.main_token);
1370013764 const token_tags = tree.tokens.items(.tag);
1370113765
13766 var any_invalid_declarations = false;
13767
1370213768 // This type forms a linked list of source tokens declaring the same name.
1370313769 const NameEntry = struct {
1370413770 tok: Ast.TokenIndex,
......@@ -13758,6 +13824,7 @@ fn scanContainer(
1375813824 const ident = main_tokens[member_node] + 1;
1375913825 if (token_tags[ident] != .identifier) {
1376013826 try astgen.appendErrorNode(member_node, "missing function name", .{});
13827 any_invalid_declarations = true;
1376113828 continue;
1376213829 }
1376313830 break :blk .{ .decl, ident };
......@@ -13853,6 +13920,7 @@ fn scanContainer(
1385313920 token_bytes,
1385413921 }),
1385513922 });
13923 any_invalid_declarations = true;
1385613924 continue;
1385713925 }
1385813926
......@@ -13870,6 +13938,7 @@ fn scanContainer(
1387013938 .{},
1387113939 ),
1387213940 });
13941 any_invalid_declarations = true;
1387313942 break;
1387413943 }
1387513944 s = local_val.parent;
......@@ -13886,6 +13955,7 @@ fn scanContainer(
1388613955 .{},
1388713956 ),
1388813957 });
13958 any_invalid_declarations = true;
1388913959 break;
1389013960 }
1389113961 s = local_ptr.parent;
......@@ -13897,7 +13967,10 @@ fn scanContainer(
1389713967 };
1389813968 }
1389913969
13900 if (!any_duplicates) return decl_count;
13970 if (!any_duplicates) {
13971 if (any_invalid_declarations) return error.AnalysisFail;
13972 return decl_count;
13973 }
1390113974
1390213975 for (names.keys(), names.values()) |name, first| {
1390313976 if (first.next == null) continue;
......@@ -13909,6 +13982,7 @@ fn scanContainer(
1390913982 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
1391013983 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
1391113984 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
13985 any_invalid_declarations = true;
1391213986 }
1391313987
1391413988 for (test_names.keys(), test_names.values()) |name, first| {
......@@ -13921,6 +13995,7 @@ fn scanContainer(
1392113995 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
1392213996 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
1392313997 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
13998 any_invalid_declarations = true;
1392413999 }
1392514000
1392614001 for (decltest_names.keys(), decltest_names.values()) |name, first| {
......@@ -13933,9 +14008,11 @@ fn scanContainer(
1393314008 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
1393414009 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
1393514010 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
14011 any_invalid_declarations = true;
1393614012 }
1393714013
13938 return decl_count;
14014 assert(any_invalid_declarations);
14015 return error.AnalysisFail;
1393914016}
1394014017
1394114018fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
......@@ -14083,6 +14160,37 @@ const DeclarationName = union(enum) {
1408314160 @"usingnamespace",
1408414161};
1408514162
14163fn addFailedDeclaration(
14164 wip_members: *WipMembers,
14165 gz: *GenZir,
14166 name: DeclarationName,
14167 src_node: Ast.Node.Index,
14168 is_pub: bool,
14169) !void {
14170 const decl_inst = try gz.makeDeclaration(src_node);
14171 wip_members.nextDecl(decl_inst);
14172 var decl_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
14173 _ = try decl_gz.add(.{
14174 .tag = .extended,
14175 .data = .{ .extended = .{
14176 .opcode = .astgen_error,
14177 .small = undefined,
14178 .operand = undefined,
14179 } },
14180 });
14181 try setDeclaration(
14182 decl_inst,
14183 @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
14184 name,
14185 gz.astgen.source_line,
14186 is_pub,
14187 false, // we don't care about exports since semantic analysis will fail
14188 .empty,
14189 &decl_gz,
14190 null,
14191 );
14192}
14193
1408614194/// Sets all extra data for a `declaration` instruction.
1408714195/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
1408814196fn setDeclaration(
lib/std/zig/Zir.zig+23-1
......@@ -120,7 +120,21 @@ pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
120120}
121121
122122pub fn hasCompileErrors(code: Zir) bool {
123 return code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0;
123 if (code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0) {
124 return true;
125 } else {
126 assert(code.instructions.len != 0); // i.e. lowering did not fail
127 return false;
128 }
129}
130
131pub fn loweringFailed(code: Zir) bool {
132 if (code.instructions.len == 0) {
133 assert(code.hasCompileErrors());
134 return true;
135 } else {
136 return false;
137 }
124138}
125139
126140pub fn deinit(code: *Zir, gpa: Allocator) void {
......@@ -2089,7 +2103,14 @@ pub const Inst = struct {
20892103 /// `small` is an `Inst.InplaceOp`.
20902104 inplace_arith_result_ty,
20912105 /// Marks a statement that can be stepped to but produces no code.
2106 /// `operand` and `small` are ignored.
20922107 dbg_empty_stmt,
2108 /// At this point, AstGen encountered a fatal error which terminated ZIR lowering for this body.
2109 /// A file-level error has been reported. Sema should terminate semantic analysis.
2110 /// `operand` and `small` are ignored.
2111 /// This instruction is always `noreturn`, however, it is not considered as such by ZIR-level queries. This allows AstGen to assume that
2112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.
2113 astgen_error,
20932114
20942115 pub const InstData = struct {
20952116 opcode: Extended,
......@@ -4065,6 +4086,7 @@ fn findDeclsInner(
40654086 .inplace_arith_result_ty,
40664087 .tuple_decl,
40674088 .dbg_empty_stmt,
4089 .astgen_error,
40684090 => return,
40694091
40704092 // `@TypeOf` has a body.
src/Sema.zig+1
......@@ -1360,6 +1360,7 @@ fn analyzeBodyInner(
13601360 i += 1;
13611361 continue;
13621362 },
1363 .astgen_error => return error.AnalysisFail,
13631364 };
13641365 },
13651366
src/Zcu/PerThread.zig+21-22
......@@ -185,11 +185,11 @@ pub fn astGenFile(
185185 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
186186
187187 if (file.zir.hasCompileErrors()) {
188 {
189 comp.mutex.lock();
190 defer comp.mutex.unlock();
191 try zcu.failed_files.putNoClobber(gpa, file, null);
192 }
188 comp.mutex.lock();
189 defer comp.mutex.unlock();
190 try zcu.failed_files.putNoClobber(gpa, file, null);
191 }
192 if (file.zir.loweringFailed()) {
193193 file.status = .astgen_failure;
194194 return error.AnalysisFail;
195195 }
......@@ -226,7 +226,7 @@ pub fn astGenFile(
226226 // single-threaded context, so we need to keep both versions around
227227 // until that point in the pipeline. Previous ZIR data is freed after
228228 // that.
229 if (file.zir_loaded and !file.zir.hasCompileErrors()) {
229 if (file.zir_loaded and !file.zir.loweringFailed()) {
230230 assert(file.prev_zir == null);
231231 const prev_zir_ptr = try gpa.create(Zir);
232232 file.prev_zir = prev_zir_ptr;
......@@ -321,11 +321,11 @@ pub fn astGenFile(
321321 };
322322
323323 if (file.zir.hasCompileErrors()) {
324 {
325 comp.mutex.lock();
326 defer comp.mutex.unlock();
327 try zcu.failed_files.putNoClobber(gpa, file, null);
328 }
324 comp.mutex.lock();
325 defer comp.mutex.unlock();
326 try zcu.failed_files.putNoClobber(gpa, file, null);
327 }
328 if (file.zir.loweringFailed()) {
329329 file.status = .astgen_failure;
330330 return error.AnalysisFail;
331331 }
......@@ -363,7 +363,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
363363 .file = file,
364364 .inst_map = .{},
365365 };
366 if (!new_zir.hasCompileErrors()) {
366 if (!new_zir.loweringFailed()) {
367367 try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
368368 }
369369 }
......@@ -379,20 +379,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
379379
380380 const file = updated_file.file;
381381
382 if (file.zir.hasCompileErrors()) {
383 // If we mark this as outdated now, users of this inst will just get a transitive analysis failure.
384 // Ultimately, they would end up throwing out potentially useful analysis results.
385 // So, do nothing. We already have the file failure -- that's sufficient for now!
386 continue;
387 }
388382 const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts
389383 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
390384 .tid = @enumFromInt(tid),
391385 .index = @intCast(tracked_inst_unwrapped_index),
392386 }).wrap(ip);
393387 const new_inst = updated_file.inst_map.get(old_inst) orelse {
394 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
395 log.debug("tracking failed for %{d}", .{old_inst});
388 // Tracking failed for this instruction.
389 // This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file.
390 // Either way, invalidate associated `src_hash` deps.
391 log.debug("tracking failed for %{d}{s}", .{
392 old_inst,
393 if (file.zir.loweringFailed()) " due to AstGen failure" else "",
394 });
396395 tracked_inst.inst = .lost;
397396 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
398397 continue;
......@@ -494,8 +493,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
494493
495494 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
496495 const file = updated_file.file;
497 if (file.zir.hasCompileErrors()) {
498 // Keep `prev_zir` around: it's the last non-error ZIR.
496 if (file.zir.loweringFailed()) {
497 // Keep `prev_zir` around: it's the last usable ZIR.
499498 // Don't update the namespace, as we have no new data to update *to*.
500499 } else {
501500 const prev_zir = file.prev_zir.?;
src/main.zig+2-2
......@@ -6457,7 +6457,7 @@ fn cmdChangelist(
64576457 file.zir_loaded = true;
64586458 defer file.zir.deinit(gpa);
64596459
6460 if (file.zir.hasCompileErrors()) {
6460 if (file.zir.loweringFailed()) {
64616461 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
64626462 try wip_errors.init(gpa);
64636463 defer wip_errors.deinit();
......@@ -6492,7 +6492,7 @@ fn cmdChangelist(
64926492 file.zir = try AstGen.generate(gpa, new_tree);
64936493 file.zir_loaded = true;
64946494
6495 if (file.zir.hasCompileErrors()) {
6495 if (file.zir.loweringFailed()) {
64966496 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
64976497 try wip_errors.init(gpa);
64986498 defer wip_errors.deinit();
src/print_zir.zig+1
......@@ -623,6 +623,7 @@ const Writer = struct {
623623 .inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),
624624
625625 .dbg_empty_stmt => try stream.writeAll("))"),
626 .astgen_error => try stream.writeAll("))"),
626627 }
627628 }
628629
test/cases/compile_errors/access_invalid_typeInfo_decl.zig+3-6
......@@ -1,11 +1,8 @@
1const A = B;
2test "Crash" {
1pub const A = B;
2export fn foo() void {
33 _ = @typeInfo(@This()).@"struct".decls[0];
44}
55
66// error
7// backend=stage2
8// target=native
9// is_test=true
107//
11// :1:11: error: use of undeclared identifier 'B'
8// :1:15: error: use of undeclared identifier 'B'
test/cases/compile_errors/astgen_sema_errors_combined.zig created+17
......@@ -0,0 +1,17 @@
1const a = bogus; // astgen error (undeclared identifier)
2const b: u32 = "hi"; // sema error (type mismatch)
3
4comptime {
5 _ = b;
6 @compileError("not hit because 'b' failed");
7}
8
9comptime {
10 @compileError("this should be hit");
11}
12
13// error
14//
15// :1:11: error: use of undeclared identifier 'bogus'
16// :2:16: error: expected type 'u32', found '*const [2:0]u8'
17// :10:5: error: this should be hit
test/cases/compile_errors/colliding_invalid_top_level_functions.zig+2-7
......@@ -1,13 +1,8 @@
1fn func() bogus {}
2fn func() bogus {}
3export fn entry() usize {
4 return @sizeOf(@TypeOf(func));
5}
1fn func() void {}
2fn func() void {}
63
74// error
85//
96// :1:4: error: duplicate struct member name 'func'
107// :2:4: note: duplicate name here
118// :1:1: note: struct declared here
12// :1:11: error: use of undeclared identifier 'bogus'
13// :2:11: error: use of undeclared identifier 'bogus'
test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig+2
......@@ -19,3 +19,5 @@ export fn entry() void {
1919//
2020// :4:5: error: unreachable code
2121// :4:25: note: control flow is diverted here
22// :4:25: error: aoeu
23// :1:36: note: called from here
test/cases/compile_errors/invalid_compare_string.zig+13-7
......@@ -1,22 +1,27 @@
11comptime {
22 const a = "foo";
3 if (a == "foo") unreachable;
3 if (a != "foo") unreachable;
44}
55comptime {
66 const a = "foo";
7 if (a == ("foo")) unreachable; // intentionally allow
7 if (a == "foo") {} else unreachable;
8}
9comptime {
10 const a = "foo";
11 if (a != ("foo")) {} // intentionally allow
12 if (a == ("foo")) {} // intentionally allow
813}
914comptime {
1015 const a = "foo";
1116 switch (a) {
12 "foo" => unreachable,
13 else => {},
17 "foo" => {},
18 else => unreachable,
1419 }
1520}
1621comptime {
1722 const a = "foo";
1823 switch (a) {
19 ("foo") => unreachable, // intentionally allow
24 ("foo") => {}, // intentionally allow
2025 else => {},
2126 }
2227}
......@@ -25,5 +30,6 @@ comptime {
2530// backend=stage2
2631// target=native
2732//
28// :3:11: error: cannot compare strings with ==
29// :12:9: error: cannot switch on strings
33// :3:11: error: cannot compare strings with !=
34// :7:11: error: cannot compare strings with ==
35// :17:9: error: cannot switch on strings
test/cases/compile_errors/invalid_decltest.zig+1-1
......@@ -1,6 +1,6 @@
11export fn foo() void {
22 const a = 1;
3 struct {
3 _ = struct {
44 test a {}
55 };
66}
test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig-4
......@@ -28,10 +28,6 @@ fn foo() void {
2828 _ = jd;
2929}
3030
31export fn entry() usize {
32 return @sizeOf(@TypeOf(foo));
33}
34
3531// error
3632// backend=stage2
3733// target=native
test/cases/compile_errors/noreturn_builtins_divert_control_flow.zig+2-1
......@@ -7,7 +7,7 @@ export fn entry2() void {
77 @panic("");
88}
99export fn entry3() void {
10 @compileError("");
10 @compileError("expect to hit this");
1111 @compileError("");
1212}
1313
......@@ -21,3 +21,4 @@ export fn entry3() void {
2121// :6:5: note: control flow is diverted here
2222// :11:5: error: unreachable code
2323// :10:5: note: control flow is diverted here
24// :10:5: error: expect to hit this
test/cases/function_redeclaration.zig-6
......@@ -2,14 +2,8 @@
22fn entry() void {}
33fn entry() void {}
44
5fn foo() void {
6 var foo = 1234;
7}
8
95// error
106//
117// :2:4: error: duplicate struct member name 'entry'
128// :3:4: note: duplicate name here
139// :2:1: note: struct declared here
14// :6:9: error: local variable shadows declaration of 'foo'
15// :5:1: note: declared here
test/cases/unused_vars.zig+1-1
......@@ -1,6 +1,6 @@
11pub fn main() void {
22 const x = 1;
3 const y, var z = .{ 2, 3 };
3 const y, var z: u32 = .{ 2, 3 };
44}
55
66// error