Commit 4d7818a76a
Changed files (17)
lib
test
cases
compile_errors
lib/std/zig/AstGen.zig
@@ -172,9 +172,9 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
};
defer gz_instructions.deinit(gpa);
- // The AST -> ZIR lowering process assumes an AST that does not have any
- // parse errors.
- if (tree.errors.len == 0) {
+ // The AST -> ZIR lowering process assumes an AST that does not have any parse errors.
+ // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR.
+ const fatal = if (tree.errors.len == 0) fatal: {
if (AstGen.structDeclInner(
&gen_scope,
&gen_scope.base,
@@ -184,13 +184,15 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
0,
)) |struct_decl_ref| {
assert(struct_decl_ref.toIndex().? == .main_struct_inst);
+ break :fatal false;
} else |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {}, // Handled via compile_errors below.
+ error.AnalysisFail => break :fatal true, // Handled via compile_errors below.
}
- } else {
+ } else fatal: {
try lowerAstErrors(&astgen);
- }
+ break :fatal true;
+ };
const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
if (astgen.compile_errors.items.len == 0) {
@@ -228,8 +230,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
}
}
- return Zir{
- .instructions = astgen.instructions.toOwnedSlice(),
+ return .{
+ .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(),
.string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
.extra = try astgen.extra.toOwnedSlice(gpa),
};
@@ -2101,7 +2103,7 @@ fn comptimeExprAst(
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
if (gz.is_comptime) {
- return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
+ try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
}
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
@@ -3275,6 +3277,9 @@ fn varDecl(
try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
}
+ // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`.
+ const force_comptime = var_decl.comptime_token != null;
+
// Depending on the type of AST the initialization expression is, we may need an lvalue
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
// the variable, no memory location needed.
@@ -3288,7 +3293,7 @@ fn varDecl(
} else .{ .rl = .none, .ctx = .const_init };
const prev_anon_name_strategy = gz.anon_name_strategy;
gz.anon_name_strategy = .dbg_var;
- const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
+ const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, force_comptime);
gz.anon_name_strategy = prev_anon_name_strategy;
try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
@@ -3358,7 +3363,7 @@ fn varDecl(
const prev_anon_name_strategy = gz.anon_name_strategy;
gz.anon_name_strategy = .dbg_var;
defer gz.anon_name_strategy = prev_anon_name_strategy;
- const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node);
+ const init_inst = try reachableExprComptime(gz, scope, init_result_info, var_decl.ast.init_node, node, force_comptime);
// The const init expression may have modified the error return trace, so signal
// 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
const full = tree.assignDestructure(node);
if (full.comptime_token != null and gz.is_comptime) {
- return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
+ return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
}
// If this expression is marked comptime, we must wrap the whole thing in a comptime block.
@@ -3562,7 +3567,7 @@ fn assignDestructureMaybeDecls(
const full = tree.assignDestructure(node);
if (full.comptime_token != null and gz.is_comptime) {
- return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
+ try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
}
const is_comptime = full.comptime_token != null or gz.is_comptime;
@@ -3676,6 +3681,7 @@ fn assignDestructureMaybeDecls(
if (full.comptime_token != null and !any_non_const_variables) {
try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
+ // Note that this is non-fatal; we will still evaluate at comptime.
}
// If this expression is marked comptime, we must wrap it in a comptime block.
@@ -4125,8 +4131,8 @@ fn fnDecl(
// The source slice is added towards the *end* of this function.
astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
- // missing function name already happened in scanContainer()
- const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
+ // missing function name already checked in scanContainer()
+ const fn_name_token = fn_proto.name_token.?;
// We insert this at the beginning so that its instruction index marks the
// start of the top level declaration.
@@ -5167,8 +5173,7 @@ fn structDeclInner(
if (is_comptime) {
switch (layout) {
- .@"packed" => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
- .@"extern" => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
+ .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
.auto => any_comptime_fields = true,
}
} else {
@@ -5195,7 +5200,7 @@ fn structDeclInner(
if (have_align) {
if (layout == .@"packed") {
- try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
+ return astgen.failNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
}
any_aligned_fields = true;
const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
@@ -5289,8 +5294,7 @@ fn tupleDecl(
switch (layout) {
.auto => {},
- .@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),
- .@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
+ .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}),
}
if (backing_int_node != 0) {
@@ -5673,7 +5677,7 @@ fn containerDecl(
};
};
if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
- try astgen.appendErrorNodeNotes(
+ return astgen.failNodeNotes(
node,
"non-exhaustive enum missing integer tag type",
.{},
@@ -5896,9 +5900,19 @@ fn containerMember(
const full = tree.fullFnProto(&buf, member_node).?;
const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
+ const prev_decl_index = wip_members.decl_index;
astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {},
+ error.AnalysisFail => {
+ wip_members.decl_index = prev_decl_index;
+ try addFailedDeclaration(
+ wip_members,
+ gz,
+ .{ .named = full.name_token.? },
+ full.ast.proto_node,
+ full.visib_token != null,
+ );
+ },
};
},
@@ -5907,28 +5921,77 @@ fn containerMember(
.simple_var_decl,
.aligned_var_decl,
=> {
- astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
+ const full = tree.fullVarDecl(member_node).?;
+ const prev_decl_index = wip_members.decl_index;
+ astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {},
+ error.AnalysisFail => {
+ wip_members.decl_index = prev_decl_index;
+ try addFailedDeclaration(
+ wip_members,
+ gz,
+ .{ .named = full.ast.mut_token + 1 },
+ member_node,
+ full.visib_token != null,
+ );
+ },
};
},
.@"comptime" => {
+ const prev_decl_index = wip_members.decl_index;
astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {},
+ error.AnalysisFail => {
+ wip_members.decl_index = prev_decl_index;
+ try addFailedDeclaration(
+ wip_members,
+ gz,
+ .@"comptime",
+ member_node,
+ false,
+ );
+ },
};
},
.@"usingnamespace" => {
+ const prev_decl_index = wip_members.decl_index;
astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {},
+ error.AnalysisFail => {
+ wip_members.decl_index = prev_decl_index;
+ try addFailedDeclaration(
+ wip_members,
+ gz,
+ .@"usingnamespace",
+ member_node,
+ is_pub: {
+ const main_tokens = tree.nodes.items(.main_token);
+ const token_tags = tree.tokens.items(.tag);
+ const main_token = main_tokens[member_node];
+ break :is_pub main_token > 0 and token_tags[main_token - 1] == .keyword_pub;
+ },
+ );
+ },
};
},
.test_decl => {
+ const prev_decl_index = wip_members.decl_index;
+ // We need to have *some* decl here so that the decl count matches what's expected.
+ // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble
+ // of duplicating the test name logic, and just assume this is an unnamed test.
astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {},
+ error.AnalysisFail => {
+ wip_members.decl_index = prev_decl_index;
+ try addFailedDeclaration(
+ wip_members,
+ gz,
+ .unnamed_test,
+ member_node,
+ false,
+ );
+ },
};
},
else => unreachable,
@@ -6140,7 +6203,7 @@ fn orelseCatchExpr(
const payload = payload_token orelse break :blk &else_scope.base;
const err_str = tree.tokenSlice(payload);
if (mem.eql(u8, err_str, "_")) {
- return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
+ try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{});
}
const err_name = try astgen.identAsString(payload);
@@ -6599,7 +6662,7 @@ fn whileExpr(
const is_inline = while_full.inline_token != null;
if (parent_gz.is_comptime and is_inline) {
- return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
+ try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
}
const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
@@ -6889,7 +6952,7 @@ fn forExpr(
const is_inline = for_full.inline_token != null;
if (parent_gz.is_comptime and is_inline) {
- return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
+ try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
}
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
@@ -6950,7 +7013,7 @@ fn forExpr(
.none;
if (end_val == .none and is_discard) {
- return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
+ try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{});
}
const start_is_zero = nodeIsTriviallyZero(tree, start_node);
@@ -7467,6 +7530,7 @@ fn switchExprErrUnion(
const err_name = blk: {
const err_str = tree.tokenSlice(error_payload);
if (mem.eql(u8, err_str, "_")) {
+ // This is fatal because we already know we're switching on the captured error.
return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
}
const err_name = try astgen.identAsString(error_payload);
@@ -7521,7 +7585,7 @@ fn switchExprErrUnion(
const capture_slice = tree.tokenSlice(capture_token);
if (mem.eql(u8, capture_slice, "_")) {
- return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
+ try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{});
}
const tag_name = try astgen.identAsString(capture_token);
try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
@@ -8018,7 +8082,7 @@ fn switchExpr(
break :blk payload_sub_scope;
const tag_slice = tree.tokenSlice(tag_token);
if (mem.eql(u8, tag_slice, "_")) {
- return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
+ try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
} else if (case.inline_token == null) {
return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
}
@@ -13699,6 +13763,8 @@ fn scanContainer(
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
+ var any_invalid_declarations = false;
+
// This type forms a linked list of source tokens declaring the same name.
const NameEntry = struct {
tok: Ast.TokenIndex,
@@ -13758,6 +13824,7 @@ fn scanContainer(
const ident = main_tokens[member_node] + 1;
if (token_tags[ident] != .identifier) {
try astgen.appendErrorNode(member_node, "missing function name", .{});
+ any_invalid_declarations = true;
continue;
}
break :blk .{ .decl, ident };
@@ -13853,6 +13920,7 @@ fn scanContainer(
token_bytes,
}),
});
+ any_invalid_declarations = true;
continue;
}
@@ -13870,6 +13938,7 @@ fn scanContainer(
.{},
),
});
+ any_invalid_declarations = true;
break;
}
s = local_val.parent;
@@ -13886,6 +13955,7 @@ fn scanContainer(
.{},
),
});
+ any_invalid_declarations = true;
break;
}
s = local_ptr.parent;
@@ -13897,7 +13967,10 @@ fn scanContainer(
};
}
- if (!any_duplicates) return decl_count;
+ if (!any_duplicates) {
+ if (any_invalid_declarations) return error.AnalysisFail;
+ return decl_count;
+ }
for (names.keys(), names.values()) |name, first| {
if (first.next == null) continue;
@@ -13909,6 +13982,7 @@ fn scanContainer(
try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
+ any_invalid_declarations = true;
}
for (test_names.keys(), test_names.values()) |name, first| {
@@ -13921,6 +13995,7 @@ fn scanContainer(
try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
+ any_invalid_declarations = true;
}
for (decltest_names.keys(), decltest_names.values()) |name, first| {
@@ -13933,9 +14008,11 @@ fn scanContainer(
try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
+ any_invalid_declarations = true;
}
- return decl_count;
+ assert(any_invalid_declarations);
+ return error.AnalysisFail;
}
fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
@@ -14083,6 +14160,37 @@ const DeclarationName = union(enum) {
@"usingnamespace",
};
+fn addFailedDeclaration(
+ wip_members: *WipMembers,
+ gz: *GenZir,
+ name: DeclarationName,
+ src_node: Ast.Node.Index,
+ is_pub: bool,
+) !void {
+ const decl_inst = try gz.makeDeclaration(src_node);
+ wip_members.nextDecl(decl_inst);
+ var decl_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
+ _ = try decl_gz.add(.{
+ .tag = .extended,
+ .data = .{ .extended = .{
+ .opcode = .astgen_error,
+ .small = undefined,
+ .operand = undefined,
+ } },
+ });
+ try setDeclaration(
+ decl_inst,
+ @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
+ name,
+ gz.astgen.source_line,
+ is_pub,
+ false, // we don't care about exports since semantic analysis will fail
+ .empty,
+ &decl_gz,
+ null,
+ );
+}
+
/// Sets all extra data for a `declaration` instruction.
/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
fn setDeclaration(
lib/std/zig/Zir.zig
@@ -120,7 +120,21 @@ pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
}
pub fn hasCompileErrors(code: Zir) bool {
- return code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0;
+ if (code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0) {
+ return true;
+ } else {
+ assert(code.instructions.len != 0); // i.e. lowering did not fail
+ return false;
+ }
+}
+
+pub fn loweringFailed(code: Zir) bool {
+ if (code.instructions.len == 0) {
+ assert(code.hasCompileErrors());
+ return true;
+ } else {
+ return false;
+ }
}
pub fn deinit(code: *Zir, gpa: Allocator) void {
@@ -2089,7 +2103,14 @@ pub const Inst = struct {
/// `small` is an `Inst.InplaceOp`.
inplace_arith_result_ty,
/// Marks a statement that can be stepped to but produces no code.
+ /// `operand` and `small` are ignored.
dbg_empty_stmt,
+ /// At this point, AstGen encountered a fatal error which terminated ZIR lowering for this body.
+ /// A file-level error has been reported. Sema should terminate semantic analysis.
+ /// `operand` and `small` are ignored.
+ /// This instruction is always `noreturn`, however, it is not considered as such by ZIR-level queries. This allows AstGen to assume that
+ /// any code may have gone here, avoiding false-positive "unreachable code" errors.
+ astgen_error,
pub const InstData = struct {
opcode: Extended,
@@ -4065,6 +4086,7 @@ fn findDeclsInner(
.inplace_arith_result_ty,
.tuple_decl,
.dbg_empty_stmt,
+ .astgen_error,
=> return,
// `@TypeOf` has a body.
lib/std/multi_array_list.zig
@@ -74,6 +74,12 @@ pub fn MultiArrayList(comptime T: type) type {
len: usize,
capacity: usize,
+ pub const empty: Slice = .{
+ .ptrs = undefined,
+ .len = 0,
+ .capacity = 0,
+ };
+
pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
const F = FieldType(field);
if (self.capacity == 0) {
src/Zcu/PerThread.zig
@@ -185,11 +185,11 @@ pub fn astGenFile(
log.debug("AstGen cached success: {s}", .{file.sub_file_path});
if (file.zir.hasCompileErrors()) {
- {
- comp.mutex.lock();
- defer comp.mutex.unlock();
- try zcu.failed_files.putNoClobber(gpa, file, null);
- }
+ comp.mutex.lock();
+ defer comp.mutex.unlock();
+ try zcu.failed_files.putNoClobber(gpa, file, null);
+ }
+ if (file.zir.loweringFailed()) {
file.status = .astgen_failure;
return error.AnalysisFail;
}
@@ -226,7 +226,7 @@ pub fn astGenFile(
// single-threaded context, so we need to keep both versions around
// until that point in the pipeline. Previous ZIR data is freed after
// that.
- if (file.zir_loaded and !file.zir.hasCompileErrors()) {
+ if (file.zir_loaded and !file.zir.loweringFailed()) {
assert(file.prev_zir == null);
const prev_zir_ptr = try gpa.create(Zir);
file.prev_zir = prev_zir_ptr;
@@ -321,11 +321,11 @@ pub fn astGenFile(
};
if (file.zir.hasCompileErrors()) {
- {
- comp.mutex.lock();
- defer comp.mutex.unlock();
- try zcu.failed_files.putNoClobber(gpa, file, null);
- }
+ comp.mutex.lock();
+ defer comp.mutex.unlock();
+ try zcu.failed_files.putNoClobber(gpa, file, null);
+ }
+ if (file.zir.loweringFailed()) {
file.status = .astgen_failure;
return error.AnalysisFail;
}
@@ -363,7 +363,7 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
.file = file,
.inst_map = .{},
};
- if (!new_zir.hasCompileErrors()) {
+ if (!new_zir.loweringFailed()) {
try Zcu.mapOldZirToNew(gpa, old_zir.*, file.zir, &gop.value_ptr.inst_map);
}
}
@@ -379,20 +379,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
const file = updated_file.file;
- if (file.zir.hasCompileErrors()) {
- // If we mark this as outdated now, users of this inst will just get a transitive analysis failure.
- // Ultimately, they would end up throwing out potentially useful analysis results.
- // So, do nothing. We already have the file failure -- that's sufficient for now!
- continue;
- }
const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts
const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
.tid = @enumFromInt(tid),
.index = @intCast(tracked_inst_unwrapped_index),
}).wrap(ip);
const new_inst = updated_file.inst_map.get(old_inst) orelse {
- // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
- log.debug("tracking failed for %{d}", .{old_inst});
+ // Tracking failed for this instruction.
+ // This may be due to changes in the ZIR, or AstGen might have failed due to a very broken file.
+ // Either way, invalidate associated `src_hash` deps.
+ log.debug("tracking failed for %{d}{s}", .{
+ old_inst,
+ if (file.zir.loweringFailed()) " due to AstGen failure" else "",
+ });
tracked_inst.inst = .lost;
try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
continue;
@@ -494,8 +493,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
const file = updated_file.file;
- if (file.zir.hasCompileErrors()) {
- // Keep `prev_zir` around: it's the last non-error ZIR.
+ if (file.zir.loweringFailed()) {
+ // Keep `prev_zir` around: it's the last usable ZIR.
// Don't update the namespace, as we have no new data to update *to*.
} else {
const prev_zir = file.prev_zir.?;
src/main.zig
@@ -6457,7 +6457,7 @@ fn cmdChangelist(
file.zir_loaded = true;
defer file.zir.deinit(gpa);
- if (file.zir.hasCompileErrors()) {
+ if (file.zir.loweringFailed()) {
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
try wip_errors.init(gpa);
defer wip_errors.deinit();
@@ -6492,7 +6492,7 @@ fn cmdChangelist(
file.zir = try AstGen.generate(gpa, new_tree);
file.zir_loaded = true;
- if (file.zir.hasCompileErrors()) {
+ if (file.zir.loweringFailed()) {
var wip_errors: std.zig.ErrorBundle.Wip = undefined;
try wip_errors.init(gpa);
defer wip_errors.deinit();
src/print_zir.zig
@@ -623,6 +623,7 @@ const Writer = struct {
.inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),
.dbg_empty_stmt => try stream.writeAll("))"),
+ .astgen_error => try stream.writeAll("))"),
}
}
src/Sema.zig
@@ -1360,6 +1360,7 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .astgen_error => return error.AnalysisFail,
};
},
test/cases/compile_errors/access_invalid_typeInfo_decl.zig
@@ -1,11 +1,8 @@
-const A = B;
-test "Crash" {
+pub const A = B;
+export fn foo() void {
_ = @typeInfo(@This()).@"struct".decls[0];
}
// error
-// backend=stage2
-// target=native
-// is_test=true
//
-// :1:11: error: use of undeclared identifier 'B'
+// :1:15: error: use of undeclared identifier 'B'
test/cases/compile_errors/astgen_sema_errors_combined.zig
@@ -0,0 +1,17 @@
+const a = bogus; // astgen error (undeclared identifier)
+const b: u32 = "hi"; // sema error (type mismatch)
+
+comptime {
+ _ = b;
+ @compileError("not hit because 'b' failed");
+}
+
+comptime {
+ @compileError("this should be hit");
+}
+
+// error
+//
+// :1:11: error: use of undeclared identifier 'bogus'
+// :2:16: error: expected type 'u32', found '*const [2:0]u8'
+// :10:5: error: this should be hit
test/cases/compile_errors/colliding_invalid_top_level_functions.zig
@@ -1,13 +1,8 @@
-fn func() bogus {}
-fn func() bogus {}
-export fn entry() usize {
- return @sizeOf(@TypeOf(func));
-}
+fn func() void {}
+fn func() void {}
// error
//
// :1:4: error: duplicate struct member name 'func'
// :2:4: note: duplicate name here
// :1:1: note: struct declared here
-// :1:11: error: use of undeclared identifier 'bogus'
-// :2:11: error: use of undeclared identifier 'bogus'
test/cases/compile_errors/constant_inside_comptime_function_has_compile_error.zig
@@ -19,3 +19,5 @@ export fn entry() void {
//
// :4:5: error: unreachable code
// :4:25: note: control flow is diverted here
+// :4:25: error: aoeu
+// :1:36: note: called from here
test/cases/compile_errors/invalid_compare_string.zig
@@ -1,22 +1,27 @@
comptime {
const a = "foo";
- if (a == "foo") unreachable;
+ if (a != "foo") unreachable;
}
comptime {
const a = "foo";
- if (a == ("foo")) unreachable; // intentionally allow
+ if (a == "foo") {} else unreachable;
+}
+comptime {
+ const a = "foo";
+ if (a != ("foo")) {} // intentionally allow
+ if (a == ("foo")) {} // intentionally allow
}
comptime {
const a = "foo";
switch (a) {
- "foo" => unreachable,
- else => {},
+ "foo" => {},
+ else => unreachable,
}
}
comptime {
const a = "foo";
switch (a) {
- ("foo") => unreachable, // intentionally allow
+ ("foo") => {}, // intentionally allow
else => {},
}
}
@@ -25,5 +30,6 @@ comptime {
// backend=stage2
// target=native
//
-// :3:11: error: cannot compare strings with ==
-// :12:9: error: cannot switch on strings
+// :3:11: error: cannot compare strings with !=
+// :7:11: error: cannot compare strings with ==
+// :17:9: error: cannot switch on strings
test/cases/compile_errors/invalid_decltest.zig
@@ -1,6 +1,6 @@
export fn foo() void {
const a = 1;
- struct {
+ _ = struct {
test a {}
};
}
test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig
@@ -28,10 +28,6 @@ fn foo() void {
_ = jd;
}
-export fn entry() usize {
- return @sizeOf(@TypeOf(foo));
-}
-
// error
// backend=stage2
// target=native
test/cases/compile_errors/noreturn_builtins_divert_control_flow.zig
@@ -7,7 +7,7 @@ export fn entry2() void {
@panic("");
}
export fn entry3() void {
- @compileError("");
+ @compileError("expect to hit this");
@compileError("");
}
@@ -21,3 +21,4 @@ export fn entry3() void {
// :6:5: note: control flow is diverted here
// :11:5: error: unreachable code
// :10:5: note: control flow is diverted here
+// :10:5: error: expect to hit this
test/cases/function_redeclaration.zig
@@ -2,14 +2,8 @@
fn entry() void {}
fn entry() void {}
-fn foo() void {
- var foo = 1234;
-}
-
// error
//
// :2:4: error: duplicate struct member name 'entry'
// :3:4: note: duplicate name here
// :2:1: note: struct declared here
-// :6:9: error: local variable shadows declaration of 'foo'
-// :5:1: note: declared here
test/cases/unused_vars.zig
@@ -1,6 +1,6 @@
pub fn main() void {
const x = 1;
- const y, var z = .{ 2, 3 };
+ const y, var z: u32 = .{ 2, 3 };
}
// error