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 {...@@ -74,6 +74,12 @@ pub fn MultiArrayList(comptime T: type) type {
74 len: usize,74 len: usize,
75 capacity: usize,75 capacity: usize,
7676
77 pub const empty: Slice = .{
78 .ptrs = undefined,
79 .len = 0,
80 .capacity = 0,
81 };
82
77 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {83 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
78 const F = FieldType(field);84 const F = FieldType(field);
79 if (self.capacity == 0) {85 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 {...@@ -172,9 +172,9 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
172 };172 };
173 defer gz_instructions.deinit(gpa);173 defer gz_instructions.deinit(gpa);
174174
175 // The AST -> ZIR lowering process assumes an AST that does not have any175 // The AST -> ZIR lowering process assumes an AST that does not have any parse errors.
176 // parse errors.176 // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR.
177 if (tree.errors.len == 0) {177 const fatal = if (tree.errors.len == 0) fatal: {
178 if (AstGen.structDeclInner(178 if (AstGen.structDeclInner(
179 &gen_scope,179 &gen_scope,
180 &gen_scope.base,180 &gen_scope.base,
...@@ -184,13 +184,15 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -184,13 +184,15 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
184 0,184 0,
185 )) |struct_decl_ref| {185 )) |struct_decl_ref| {
186 assert(struct_decl_ref.toIndex().? == .main_struct_inst);186 assert(struct_decl_ref.toIndex().? == .main_struct_inst);
187 break :fatal false;
187 } else |err| switch (err) {188 } else |err| switch (err) {
188 error.OutOfMemory => return error.OutOfMemory,189 error.OutOfMemory => return error.OutOfMemory,
189 error.AnalysisFail => {}, // Handled via compile_errors below.190 error.AnalysisFail => break :fatal true, // Handled via compile_errors below.
190 }191 }
191 } else {192 } else fatal: {
192 try lowerAstErrors(&astgen);193 try lowerAstErrors(&astgen);
193 }194 break :fatal true;
195 };
194196
195 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);197 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
196 if (astgen.compile_errors.items.len == 0) {198 if (astgen.compile_errors.items.len == 0) {
...@@ -228,8 +230,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -228,8 +230,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
228 }230 }
229 }231 }
230232
231 return Zir{233 return .{
232 .instructions = astgen.instructions.toOwnedSlice(),234 .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(),
233 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),235 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
234 .extra = try astgen.extra.toOwnedSlice(gpa),236 .extra = try astgen.extra.toOwnedSlice(gpa),
235 };237 };
...@@ -2101,7 +2103,7 @@ fn comptimeExprAst(...@@ -2101,7 +2103,7 @@ fn comptimeExprAst(
2101) InnerError!Zir.Inst.Ref {2103) InnerError!Zir.Inst.Ref {
2102 const astgen = gz.astgen;2104 const astgen = gz.astgen;
2103 if (gz.is_comptime) {2105 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", .{});
2105 }2107 }
2106 const tree = astgen.tree;2108 const tree = astgen.tree;
2107 const node_datas = tree.nodes.items(.data);2109 const node_datas = tree.nodes.items(.data);
...@@ -3275,6 +3277,9 @@ fn varDecl(...@@ -3275,6 +3277,9 @@ fn varDecl(
3275 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});3277 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3276 }3278 }
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
3278 // Depending on the type of AST the initialization expression is, we may need an lvalue3283 // Depending on the type of AST the initialization expression is, we may need an lvalue
3279 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as3284 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
3280 // the variable, no memory location needed.3285 // the variable, no memory location needed.
...@@ -3288,7 +3293,7 @@ fn varDecl(...@@ -3288,7 +3293,7 @@ fn varDecl(
3288 } else .{ .rl = .none, .ctx = .const_init };3293 } else .{ .rl = .none, .ctx = .const_init };
3289 const prev_anon_name_strategy = gz.anon_name_strategy;3294 const prev_anon_name_strategy = gz.anon_name_strategy;
3290 gz.anon_name_strategy = .dbg_var;3295 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);
3292 gz.anon_name_strategy = prev_anon_name_strategy;3297 gz.anon_name_strategy = prev_anon_name_strategy;
32933298
3294 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);3299 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
...@@ -3358,7 +3363,7 @@ fn varDecl(...@@ -3358,7 +3363,7 @@ fn varDecl(
3358 const prev_anon_name_strategy = gz.anon_name_strategy;3363 const prev_anon_name_strategy = gz.anon_name_strategy;
3359 gz.anon_name_strategy = .dbg_var;3364 gz.anon_name_strategy = .dbg_var;
3360 defer gz.anon_name_strategy = prev_anon_name_strategy;3365 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
3363 // The const init expression may have modified the error return trace, so signal3368 // The const init expression may have modified the error return trace, so signal
3364 // to Sema that it should save the new index for restoring later.3369 // 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...@@ -3503,7 +3508,7 @@ fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerErro
35033508
3504 const full = tree.assignDestructure(node);3509 const full = tree.assignDestructure(node);
3505 if (full.comptime_token != null and gz.is_comptime) {3510 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", .{});
3507 }3512 }
35083513
3509 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.3514 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
...@@ -3562,7 +3567,7 @@ fn assignDestructureMaybeDecls(...@@ -3562,7 +3567,7 @@ fn assignDestructureMaybeDecls(
35623567
3563 const full = tree.assignDestructure(node);3568 const full = tree.assignDestructure(node);
3564 if (full.comptime_token != null and gz.is_comptime) {3569 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", .{});
3566 }3571 }
35673572
3568 const is_comptime = full.comptime_token != null or gz.is_comptime;3573 const is_comptime = full.comptime_token != null or gz.is_comptime;
...@@ -3676,6 +3681,7 @@ fn assignDestructureMaybeDecls(...@@ -3676,6 +3681,7 @@ fn assignDestructureMaybeDecls(
36763681
3677 if (full.comptime_token != null and !any_non_const_variables) {3682 if (full.comptime_token != null and !any_non_const_variables) {
3678 try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});3683 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.
3679 }3685 }
36803686
3681 // If this expression is marked comptime, we must wrap it in a comptime block.3687 // If this expression is marked comptime, we must wrap it in a comptime block.
...@@ -4125,8 +4131,8 @@ fn fnDecl(...@@ -4125,8 +4131,8 @@ fn fnDecl(
4125 // The source slice is added towards the *end* of this function.4131 // The source slice is added towards the *end* of this function.
4126 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));4132 astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
41274133
4128 // missing function name already happened in scanContainer()4134 // missing function name already checked in scanContainer()
4129 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;4135 const fn_name_token = fn_proto.name_token.?;
41304136
4131 // We insert this at the beginning so that its instruction index marks the4137 // We insert this at the beginning so that its instruction index marks the
4132 // start of the top level declaration.4138 // start of the top level declaration.
...@@ -5167,8 +5173,7 @@ fn structDeclInner(...@@ -5167,8 +5173,7 @@ fn structDeclInner(
51675173
5168 if (is_comptime) {5174 if (is_comptime) {
5169 switch (layout) {5175 switch (layout) {
5170 .@"packed" => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),5176 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
5171 .@"extern" => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
5172 .auto => any_comptime_fields = true,5177 .auto => any_comptime_fields = true,
5173 }5178 }
5174 } else {5179 } else {
...@@ -5195,7 +5200,7 @@ fn structDeclInner(...@@ -5195,7 +5200,7 @@ fn structDeclInner(
51955200
5196 if (have_align) {5201 if (have_align) {
5197 if (layout == .@"packed") {5202 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", .{});
5199 }5204 }
5200 any_aligned_fields = true;5205 any_aligned_fields = true;
5201 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);5206 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
...@@ -5289,8 +5294,7 @@ fn tupleDecl(...@@ -5289,8 +5294,7 @@ fn tupleDecl(
52895294
5290 switch (layout) {5295 switch (layout) {
5291 .auto => {},5296 .auto => {},
5292 .@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),5297 .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}),
5293 .@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
5294 }5298 }
52955299
5296 if (backing_int_node != 0) {5300 if (backing_int_node != 0) {
...@@ -5673,7 +5677,7 @@ fn containerDecl(...@@ -5673,7 +5677,7 @@ fn containerDecl(
5673 };5677 };
5674 };5678 };
5675 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {5679 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
5676 try astgen.appendErrorNodeNotes(5680 return astgen.failNodeNotes(
5677 node,5681 node,
5678 "non-exhaustive enum missing integer tag type",5682 "non-exhaustive enum missing integer tag type",
5679 .{},5683 .{},
...@@ -5896,9 +5900,19 @@ fn containerMember(...@@ -5896,9 +5900,19 @@ fn containerMember(
5896 const full = tree.fullFnProto(&buf, member_node).?;5900 const full = tree.fullFnProto(&buf, member_node).?;
5897 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;5901 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;
5899 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {5904 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
5900 error.OutOfMemory => return error.OutOfMemory,5905 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 },
5902 };5916 };
5903 },5917 },
59045918
...@@ -5907,28 +5921,77 @@ fn containerMember(...@@ -5907,28 +5921,77 @@ fn containerMember(
5907 .simple_var_decl,5921 .simple_var_decl,
5908 .aligned_var_decl,5922 .aligned_var_decl,
5909 => {5923 => {
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) {
5911 error.OutOfMemory => return error.OutOfMemory,5927 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 },
5913 };5938 };
5914 },5939 },
59155940
5916 .@"comptime" => {5941 .@"comptime" => {
5942 const prev_decl_index = wip_members.decl_index;
5917 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {5943 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5918 error.OutOfMemory => return error.OutOfMemory,5944 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 },
5920 };5955 };
5921 },5956 },
5922 .@"usingnamespace" => {5957 .@"usingnamespace" => {
5958 const prev_decl_index = wip_members.decl_index;
5923 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {5959 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5924 error.OutOfMemory => return error.OutOfMemory,5960 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 },
5926 };5976 };
5927 },5977 },
5928 .test_decl => {5978 .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.
5929 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {5983 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5930 error.OutOfMemory => return error.OutOfMemory,5984 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 },
5932 };5995 };
5933 },5996 },
5934 else => unreachable,5997 else => unreachable,
...@@ -6140,7 +6203,7 @@ fn orelseCatchExpr(...@@ -6140,7 +6203,7 @@ fn orelseCatchExpr(
6140 const payload = payload_token orelse break :blk &else_scope.base;6203 const payload = payload_token orelse break :blk &else_scope.base;
6141 const err_str = tree.tokenSlice(payload);6204 const err_str = tree.tokenSlice(payload);
6142 if (mem.eql(u8, err_str, "_")) {6205 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", .{});
6144 }6207 }
6145 const err_name = try astgen.identAsString(payload);6208 const err_name = try astgen.identAsString(payload);
61466209
...@@ -6599,7 +6662,7 @@ fn whileExpr(...@@ -6599,7 +6662,7 @@ fn whileExpr(
65996662
6600 const is_inline = while_full.inline_token != null;6663 const is_inline = while_full.inline_token != null;
6601 if (parent_gz.is_comptime and is_inline) {6664 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", .{});
6603 }6666 }
6604 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;6667 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
6605 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);6668 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
...@@ -6889,7 +6952,7 @@ fn forExpr(...@@ -6889,7 +6952,7 @@ fn forExpr(
68896952
6890 const is_inline = for_full.inline_token != null;6953 const is_inline = for_full.inline_token != null;
6891 if (parent_gz.is_comptime and is_inline) {6954 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", .{});
6893 }6956 }
6894 const tree = astgen.tree;6957 const tree = astgen.tree;
6895 const token_tags = tree.tokens.items(.tag);6958 const token_tags = tree.tokens.items(.tag);
...@@ -6950,7 +7013,7 @@ fn forExpr(...@@ -6950,7 +7013,7 @@ fn forExpr(
6950 .none;7013 .none;
69517014
6952 if (end_val == .none and is_discard) {7015 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", .{});
6954 }7017 }
69557018
6956 const start_is_zero = nodeIsTriviallyZero(tree, start_node);7019 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
...@@ -7467,6 +7530,7 @@ fn switchExprErrUnion(...@@ -7467,6 +7530,7 @@ fn switchExprErrUnion(
7467 const err_name = blk: {7530 const err_name = blk: {
7468 const err_str = tree.tokenSlice(error_payload);7531 const err_str = tree.tokenSlice(error_payload);
7469 if (mem.eql(u8, err_str, "_")) {7532 if (mem.eql(u8, err_str, "_")) {
7533 // This is fatal because we already know we're switching on the captured error.
7470 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});7534 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
7471 }7535 }
7472 const err_name = try astgen.identAsString(error_payload);7536 const err_name = try astgen.identAsString(error_payload);
...@@ -7521,7 +7585,7 @@ fn switchExprErrUnion(...@@ -7521,7 +7585,7 @@ fn switchExprErrUnion(
75217585
7522 const capture_slice = tree.tokenSlice(capture_token);7586 const capture_slice = tree.tokenSlice(capture_token);
7523 if (mem.eql(u8, capture_slice, "_")) {7587 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", .{});
7525 }7589 }
7526 const tag_name = try astgen.identAsString(capture_token);7590 const tag_name = try astgen.identAsString(capture_token);
7527 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);7591 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
...@@ -8018,7 +8082,7 @@ fn switchExpr(...@@ -8018,7 +8082,7 @@ fn switchExpr(
8018 break :blk payload_sub_scope;8082 break :blk payload_sub_scope;
8019 const tag_slice = tree.tokenSlice(tag_token);8083 const tag_slice = tree.tokenSlice(tag_token);
8020 if (mem.eql(u8, tag_slice, "_")) {8084 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", .{});
8022 } else if (case.inline_token == null) {8086 } else if (case.inline_token == null) {
8023 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});8087 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
8024 }8088 }
...@@ -13699,6 +13763,8 @@ fn scanContainer(...@@ -13699,6 +13763,8 @@ fn scanContainer(
13699 const main_tokens = tree.nodes.items(.main_token);13763 const main_tokens = tree.nodes.items(.main_token);
13700 const token_tags = tree.tokens.items(.tag);13764 const token_tags = tree.tokens.items(.tag);
1370113765
13766 var any_invalid_declarations = false;
13767
13702 // This type forms a linked list of source tokens declaring the same name.13768 // This type forms a linked list of source tokens declaring the same name.
13703 const NameEntry = struct {13769 const NameEntry = struct {
13704 tok: Ast.TokenIndex,13770 tok: Ast.TokenIndex,
...@@ -13758,6 +13824,7 @@ fn scanContainer(...@@ -13758,6 +13824,7 @@ fn scanContainer(
13758 const ident = main_tokens[member_node] + 1;13824 const ident = main_tokens[member_node] + 1;
13759 if (token_tags[ident] != .identifier) {13825 if (token_tags[ident] != .identifier) {
13760 try astgen.appendErrorNode(member_node, "missing function name", .{});13826 try astgen.appendErrorNode(member_node, "missing function name", .{});
13827 any_invalid_declarations = true;
13761 continue;13828 continue;
13762 }13829 }
13763 break :blk .{ .decl, ident };13830 break :blk .{ .decl, ident };
...@@ -13853,6 +13920,7 @@ fn scanContainer(...@@ -13853,6 +13920,7 @@ fn scanContainer(
13853 token_bytes,13920 token_bytes,
13854 }),13921 }),
13855 });13922 });
13923 any_invalid_declarations = true;
13856 continue;13924 continue;
13857 }13925 }
1385813926
...@@ -13870,6 +13938,7 @@ fn scanContainer(...@@ -13870,6 +13938,7 @@ fn scanContainer(
13870 .{},13938 .{},
13871 ),13939 ),
13872 });13940 });
13941 any_invalid_declarations = true;
13873 break;13942 break;
13874 }13943 }
13875 s = local_val.parent;13944 s = local_val.parent;
...@@ -13886,6 +13955,7 @@ fn scanContainer(...@@ -13886,6 +13955,7 @@ fn scanContainer(
13886 .{},13955 .{},
13887 ),13956 ),
13888 });13957 });
13958 any_invalid_declarations = true;
13889 break;13959 break;
13890 }13960 }
13891 s = local_ptr.parent;13961 s = local_ptr.parent;
...@@ -13897,7 +13967,10 @@ fn scanContainer(...@@ -13897,7 +13967,10 @@ fn scanContainer(
13897 };13967 };
13898 }13968 }
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
13902 for (names.keys(), names.values()) |name, first| {13975 for (names.keys(), names.values()) |name, first| {
13903 if (first.next == null) continue;13976 if (first.next == null) continue;
...@@ -13909,6 +13982,7 @@ fn scanContainer(...@@ -13909,6 +13982,7 @@ fn scanContainer(
13909 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));13982 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13910 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));13983 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13911 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);13984 try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
13985 any_invalid_declarations = true;
13912 }13986 }
1391313987
13914 for (test_names.keys(), test_names.values()) |name, first| {13988 for (test_names.keys(), test_names.values()) |name, first| {
...@@ -13921,6 +13995,7 @@ fn scanContainer(...@@ -13921,6 +13995,7 @@ fn scanContainer(
13921 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));13995 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13922 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));13996 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13923 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);13997 try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
13998 any_invalid_declarations = true;
13924 }13999 }
1392514000
13926 for (decltest_names.keys(), decltest_names.values()) |name, first| {14001 for (decltest_names.keys(), decltest_names.values()) |name, first| {
...@@ -13933,9 +14008,11 @@ fn scanContainer(...@@ -13933,9 +14008,11 @@ fn scanContainer(
13933 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));14008 try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
13934 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));14009 const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
13935 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);14010 try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
14011 any_invalid_declarations = true;
13936 }14012 }
1393714013
13938 return decl_count;14014 assert(any_invalid_declarations);
14015 return error.AnalysisFail;
13939}14016}
1394014017
13941fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {14018fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
...@@ -14083,6 +14160,37 @@ const DeclarationName = union(enum) {...@@ -14083,6 +14160,37 @@ const DeclarationName = union(enum) {
14083 @"usingnamespace",14160 @"usingnamespace",
14084};14161};
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
14086/// Sets all extra data for a `declaration` instruction.14194/// Sets all extra data for a `declaration` instruction.
14087/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.14195/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
14088fn setDeclaration(14196fn setDeclaration(
lib/std/zig/Zir.zig+23-1
...@@ -120,7 +120,21 @@ pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {...@@ -120,7 +120,21 @@ pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
120}120}
121121
122pub fn hasCompileErrors(code: Zir) bool {122pub 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 }
124}138}
125139
126pub fn deinit(code: *Zir, gpa: Allocator) void {140pub fn deinit(code: *Zir, gpa: Allocator) void {
...@@ -2089,7 +2103,14 @@ pub const Inst = struct {...@@ -2089,7 +2103,14 @@ pub const Inst = struct {
2089 /// `small` is an `Inst.InplaceOp`.2103 /// `small` is an `Inst.InplaceOp`.
2090 inplace_arith_result_ty,2104 inplace_arith_result_ty,
2091 /// Marks a statement that can be stepped to but produces no code.2105 /// Marks a statement that can be stepped to but produces no code.
2106 /// `operand` and `small` are ignored.
2092 dbg_empty_stmt,2107 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
2094 pub const InstData = struct {2115 pub const InstData = struct {
2095 opcode: Extended,2116 opcode: Extended,
...@@ -4065,6 +4086,7 @@ fn findDeclsInner(...@@ -4065,6 +4086,7 @@ fn findDeclsInner(
4065 .inplace_arith_result_ty,4086 .inplace_arith_result_ty,
4066 .tuple_decl,4087 .tuple_decl,
4067 .dbg_empty_stmt,4088 .dbg_empty_stmt,
4089 .astgen_error,
4068 => return,4090 => return,
40694091
4070 // `@TypeOf` has a body.4092 // `@TypeOf` has a body.
src/Sema.zig+1
...@@ -1360,6 +1360,7 @@ fn analyzeBodyInner(...@@ -1360,6 +1360,7 @@ fn analyzeBodyInner(
1360 i += 1;1360 i += 1;
1361 continue;1361 continue;
1362 },1362 },
1363 .astgen_error => return error.AnalysisFail,
1363 };1364 };
1364 },1365 },
13651366
src/Zcu/PerThread.zig+21-22
...@@ -185,11 +185,11 @@ pub fn astGenFile(...@@ -185,11 +185,11 @@ pub fn astGenFile(
185 log.debug("AstGen cached success: {s}", .{file.sub_file_path});185 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
186186
187 if (file.zir.hasCompileErrors()) {187 if (file.zir.hasCompileErrors()) {
188 {188 comp.mutex.lock();
189 comp.mutex.lock();189 defer comp.mutex.unlock();
190 defer comp.mutex.unlock();190 try zcu.failed_files.putNoClobber(gpa, file, null);
191 try zcu.failed_files.putNoClobber(gpa, file, null);191 }
192 }192 if (file.zir.loweringFailed()) {
193 file.status = .astgen_failure;193 file.status = .astgen_failure;
194 return error.AnalysisFail;194 return error.AnalysisFail;
195 }195 }
...@@ -226,7 +226,7 @@ pub fn astGenFile(...@@ -226,7 +226,7 @@ pub fn astGenFile(
226 // single-threaded context, so we need to keep both versions around226 // single-threaded context, so we need to keep both versions around
227 // until that point in the pipeline. Previous ZIR data is freed after227 // until that point in the pipeline. Previous ZIR data is freed after
228 // that.228 // that.
229 if (file.zir_loaded and !file.zir.hasCompileErrors()) {229 if (file.zir_loaded and !file.zir.loweringFailed()) {
230 assert(file.prev_zir == null);230 assert(file.prev_zir == null);
231 const prev_zir_ptr = try gpa.create(Zir);231 const prev_zir_ptr = try gpa.create(Zir);
232 file.prev_zir = prev_zir_ptr;232 file.prev_zir = prev_zir_ptr;
...@@ -321,11 +321,11 @@ pub fn astGenFile(...@@ -321,11 +321,11 @@ pub fn astGenFile(
321 };321 };
322322
323 if (file.zir.hasCompileErrors()) {323 if (file.zir.hasCompileErrors()) {
324 {324 comp.mutex.lock();
325 comp.mutex.lock();325 defer comp.mutex.unlock();
326 defer comp.mutex.unlock();326 try zcu.failed_files.putNoClobber(gpa, file, null);
327 try zcu.failed_files.putNoClobber(gpa, file, null);327 }
328 }328 if (file.zir.loweringFailed()) {
329 file.status = .astgen_failure;329 file.status = .astgen_failure;
330 return error.AnalysisFail;330 return error.AnalysisFail;
331 }331 }
...@@ -363,7 +363,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -363,7 +363,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
363 .file = file,363 .file = file,
364 .inst_map = .{},364 .inst_map = .{},
365 };365 };
366 if (!new_zir.hasCompileErrors()) {366 if (!new_zir.loweringFailed()) {
367 try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);367 try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
368 }368 }
369 }369 }
...@@ -379,20 +379,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -379,20 +379,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
379379
380 const file = updated_file.file;380 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 }
388 const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts382 const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts
389 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{383 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
390 .tid = @enumFromInt(tid),384 .tid = @enumFromInt(tid),
391 .index = @intCast(tracked_inst_unwrapped_index),385 .index = @intCast(tracked_inst_unwrapped_index),
392 }).wrap(ip);386 }).wrap(ip);
393 const new_inst = updated_file.inst_map.get(old_inst) orelse {387 const new_inst = updated_file.inst_map.get(old_inst) orelse {
394 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.388 // Tracking failed for this instruction.
395 log.debug("tracking failed for %{d}", .{old_inst});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 });
396 tracked_inst.inst = .lost;395 tracked_inst.inst = .lost;
397 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });396 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
398 continue;397 continue;
...@@ -494,8 +493,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {...@@ -494,8 +493,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
494493
495 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {494 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
496 const file = updated_file.file;495 const file = updated_file.file;
497 if (file.zir.hasCompileErrors()) {496 if (file.zir.loweringFailed()) {
498 // Keep `prev_zir` around: it's the last non-error ZIR.497 // Keep `prev_zir` around: it's the last usable ZIR.
499 // Don't update the namespace, as we have no new data to update *to*.498 // Don't update the namespace, as we have no new data to update *to*.
500 } else {499 } else {
501 const prev_zir = file.prev_zir.?;500 const prev_zir = file.prev_zir.?;
src/main.zig+2-2
...@@ -6457,7 +6457,7 @@ fn cmdChangelist(...@@ -6457,7 +6457,7 @@ fn cmdChangelist(
6457 file.zir_loaded = true;6457 file.zir_loaded = true;
6458 defer file.zir.deinit(gpa);6458 defer file.zir.deinit(gpa);
64596459
6460 if (file.zir.hasCompileErrors()) {6460 if (file.zir.loweringFailed()) {
6461 var wip_errors: std.zig.ErrorBundle.Wip = undefined;6461 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6462 try wip_errors.init(gpa);6462 try wip_errors.init(gpa);
6463 defer wip_errors.deinit();6463 defer wip_errors.deinit();
...@@ -6492,7 +6492,7 @@ fn cmdChangelist(...@@ -6492,7 +6492,7 @@ fn cmdChangelist(
6492 file.zir = try AstGen.generate(gpa, new_tree);6492 file.zir = try AstGen.generate(gpa, new_tree);
6493 file.zir_loaded = true;6493 file.zir_loaded = true;
64946494
6495 if (file.zir.hasCompileErrors()) {6495 if (file.zir.loweringFailed()) {
6496 var wip_errors: std.zig.ErrorBundle.Wip = undefined;6496 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6497 try wip_errors.init(gpa);6497 try wip_errors.init(gpa);
6498 defer wip_errors.deinit();6498 defer wip_errors.deinit();
src/print_zir.zig+1
...@@ -623,6 +623,7 @@ const Writer = struct {...@@ -623,6 +623,7 @@ const Writer = struct {
623 .inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),623 .inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),
624624
625 .dbg_empty_stmt => try stream.writeAll("))"),625 .dbg_empty_stmt => try stream.writeAll("))"),
626 .astgen_error => try stream.writeAll("))"),
626 }627 }
627 }628 }
628629
test/cases/compile_errors/access_invalid_typeInfo_decl.zig+3-6
...@@ -1,11 +1,8 @@...@@ -1,11 +1,8 @@
1const A = B;1pub const A = B;
2test "Crash" {2export fn foo() void {
3 _ = @typeInfo(@This()).@"struct".decls[0];3 _ = @typeInfo(@This()).@"struct".decls[0];
4}4}
55
6// error6// error
7// backend=stage2
8// target=native
9// is_test=true
10//7//
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 @@...@@ -1,13 +1,8 @@
1fn func() bogus {}1fn func() void {}
2fn func() bogus {}2fn func() void {}
3export fn entry() usize {
4 return @sizeOf(@TypeOf(func));
5}
63
7// error4// error
8//5//
9// :1:4: error: duplicate struct member name 'func'6// :1:4: error: duplicate struct member name 'func'
10// :2:4: note: duplicate name here7// :2:4: note: duplicate name here
11// :1:1: note: struct declared here8// :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 {...@@ -19,3 +19,5 @@ export fn entry() void {
19//19//
20// :4:5: error: unreachable code20// :4:5: error: unreachable code
21// :4:25: note: control flow is diverted here21// :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 @@...@@ -1,22 +1,27 @@
1comptime {1comptime {
2 const a = "foo";2 const a = "foo";
3 if (a == "foo") unreachable;3 if (a != "foo") unreachable;
4}4}
5comptime {5comptime {
6 const a = "foo";6 const a = "foo";
7 if (a == ("foo")) unreachable; // intentionally allow7 if (a == "foo") {} else unreachable;
8}
9comptime {
10 const a = "foo";
11 if (a != ("foo")) {} // intentionally allow
12 if (a == ("foo")) {} // intentionally allow
8}13}
9comptime {14comptime {
10 const a = "foo";15 const a = "foo";
11 switch (a) {16 switch (a) {
12 "foo" => unreachable,17 "foo" => {},
13 else => {},18 else => unreachable,
14 }19 }
15}20}
16comptime {21comptime {
17 const a = "foo";22 const a = "foo";
18 switch (a) {23 switch (a) {
19 ("foo") => unreachable, // intentionally allow24 ("foo") => {}, // intentionally allow
20 else => {},25 else => {},
21 }26 }
22}27}
...@@ -25,5 +30,6 @@ comptime {...@@ -25,5 +30,6 @@ comptime {
25// backend=stage230// backend=stage2
26// target=native31// target=native
27//32//
28// :3:11: error: cannot compare strings with ==33// :3:11: error: cannot compare strings with !=
29// :12:9: error: cannot switch on strings34// :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 @@...@@ -1,6 +1,6 @@
1export fn foo() void {1export fn foo() void {
2 const a = 1;2 const a = 1;
3 struct {3 _ = struct {
4 test a {}4 test a {}
5 };5 };
6}6}
test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig-4
...@@ -28,10 +28,6 @@ fn foo() void {...@@ -28,10 +28,6 @@ fn foo() void {
28 _ = jd;28 _ = jd;
29}29}
3030
31export fn entry() usize {
32 return @sizeOf(@TypeOf(foo));
33}
34
35// error31// error
36// backend=stage232// backend=stage2
37// target=native33// target=native
test/cases/compile_errors/noreturn_builtins_divert_control_flow.zig+2-1
...@@ -7,7 +7,7 @@ export fn entry2() void {...@@ -7,7 +7,7 @@ export fn entry2() void {
7 @panic("");7 @panic("");
8}8}
9export fn entry3() void {9export fn entry3() void {
10 @compileError("");10 @compileError("expect to hit this");
11 @compileError("");11 @compileError("");
12}12}
1313
...@@ -21,3 +21,4 @@ export fn entry3() void {...@@ -21,3 +21,4 @@ export fn entry3() void {
21// :6:5: note: control flow is diverted here21// :6:5: note: control flow is diverted here
22// :11:5: error: unreachable code22// :11:5: error: unreachable code
23// :10:5: note: control flow is diverted here23// :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 @@...@@ -2,14 +2,8 @@
2fn entry() void {}2fn entry() void {}
3fn entry() void {}3fn entry() void {}
44
5fn foo() void {
6 var foo = 1234;
7}
8
9// error5// error
10//6//
11// :2:4: error: duplicate struct member name 'entry'7// :2:4: error: duplicate struct member name 'entry'
12// :3:4: note: duplicate name here8// :3:4: note: duplicate name here
13// :2:1: note: struct declared here9// :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 @@...@@ -1,6 +1,6 @@
1pub fn main() void {1pub fn main() void {
2 const x = 1;2 const x = 1;
3 const y, var z = .{ 2, 3 };3 const y, var z: u32 = .{ 2, 3 };
4}4}
55
6// error6// error