aboutsummaryrefslogtreecommitdiff
path: root/src/AstGen.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-06-23 18:19:17 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-07-02 13:26:50 -0700
commit125b85d7375b96b4847f6ead51c853cdc0567506 (patch)
tree34a947e48f84ce6455d3ff133828a8e0e5614ce8 /src/AstGen.zig
parentd84b386f6034278c8a9e8c3d2b0975ac541584aa (diff)
downloadzig-125b85d7375b96b4847f6ead51c853cdc0567506.tar.gz
zig-125b85d7375b96b4847f6ead51c853cdc0567506.zip
move "unreachable code" error from stage1 to stage2
* AstGen: implement "unreachable code" error for blocks. This works at the statement level. * stage1: remove the "unreachable code" error implementation, which means removing the `is_gen` field from IrInstSrc. This is one small step towards a smaller memory footprint for stage1. The benefits won't be realized until a future commit because this flag took advantage of padding. There may be a regression here with "union has no associated enum" error, and there is a regression with the following code: ```zig const a = noreturn; ``` A future commit will address these regressions.
Diffstat (limited to 'src/AstGen.zig')
-rw-r--r--src/AstGen.zig70
1 files changed, 49 insertions, 21 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 4254481a55..6c5f2b5dae 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -1570,7 +1570,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index) Inn
const defer_scope = scope.cast(Scope.Defer).?;
scope = defer_scope.parent;
const expr_node = node_datas[defer_scope.defer_node].rhs;
- try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
+ _ = try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
},
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
.top => unreachable,
@@ -1623,7 +1623,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: ast.Node.Index)
const defer_scope = scope.cast(Scope.Defer).?;
scope = defer_scope.parent;
const expr_node = node_datas[defer_scope.defer_node].rhs;
- try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
+ _ = try unusedResultExpr(parent_gz, defer_scope.parent, expr_node);
},
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
.namespace => break,
@@ -1785,8 +1785,23 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod
var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
defer block_arena.deinit();
+ var noreturn_src_node: ast.Node.Index = 0;
var scope = parent_scope;
for (statements) |statement| {
+ if (noreturn_src_node != 0) {
+ return astgen.failNodeNotes(
+ statement,
+ "unreachable code",
+ .{},
+ &[_]u32{
+ try astgen.errNoteNode(
+ noreturn_src_node,
+ "control flow is diverted here",
+ .{},
+ ),
+ },
+ );
+ }
switch (node_tags[statement]) {
// zig fmt: off
.global_var_decl => scope = try varDecl(gz, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
@@ -1814,7 +1829,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod
.assign_mul => try assignOp(gz, scope, statement, .mul),
.assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
- else => try unusedResultExpr(gz, scope, statement),
+ else => noreturn_src_node = try unusedResultExpr(gz, scope, statement),
// zig fmt: on
}
}
@@ -1823,11 +1838,14 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod
try checkUsed(gz, parent_scope, scope);
}
-fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) InnerError!void {
+/// Returns AST source node of the thing that is noreturn if the statement is definitely `noreturn`.
+/// Otherwise returns 0.
+fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) InnerError!ast.Node.Index {
try emitDbgNode(gz, statement);
// We need to emit an error if the result is not `noreturn` or `void`, but
// we want to avoid adding the ZIR instruction if possible for performance.
const maybe_unused_result = try expr(gz, scope, .none, statement);
+ var noreturn_src_node: ast.Node.Index = 0;
const elide_check = if (gz.refToIndex(maybe_unused_result)) |inst| b: {
// Note that this array becomes invalid after appending more items to it
// in the above while loop.
@@ -2061,15 +2079,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
.extended,
=> break :b false,
- // ZIR instructions that are always either `noreturn` or `void`.
- .breakpoint,
- .fence,
- .dbg_stmt,
- .ensure_result_used,
- .ensure_result_non_error,
- .@"export",
- .set_eval_branch_quota,
- .ensure_err_payload_void,
+ // ZIR instructions that are always `noreturn`.
.@"break",
.break_inline,
.condbr,
@@ -2078,16 +2088,30 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
.ret_node,
.ret_coerce,
.@"unreachable",
+ .repeat,
+ .repeat_inline,
+ .panic,
+ => {
+ noreturn_src_node = statement;
+ break :b true;
+ },
+
+ // ZIR instructions that are always `void`.
+ .breakpoint,
+ .fence,
+ .dbg_stmt,
+ .ensure_result_used,
+ .ensure_result_non_error,
+ .@"export",
+ .set_eval_branch_quota,
+ .ensure_err_payload_void,
.store,
.store_node,
.store_to_block_ptr,
.store_to_inferred_ptr,
.resolve_inferred_alloc,
- .repeat,
- .repeat_inline,
.validate_struct_init_ptr,
.validate_array_init_ptr,
- .panic,
.set_align_stack,
.set_cold,
.set_float_mode,
@@ -2097,15 +2121,19 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
} else switch (maybe_unused_result) {
.none => unreachable,
- .void_value,
- .unreachable_value,
- => true,
+ .unreachable_value => b: {
+ noreturn_src_node = statement;
+ break :b true;
+ },
+
+ .void_value => true,
else => false,
};
if (!elide_check) {
_ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
}
+ return noreturn_src_node;
}
fn genDefers(
@@ -2132,7 +2160,7 @@ fn genDefers(
const prev_in_defer = gz.in_defer;
gz.in_defer = true;
defer gz.in_defer = prev_in_defer;
- try unusedResultExpr(gz, defer_scope.parent, expr_node);
+ _ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
},
.defer_error => {
const defer_scope = scope.cast(Scope.Defer).?;
@@ -2142,7 +2170,7 @@ fn genDefers(
const prev_in_defer = gz.in_defer;
gz.in_defer = true;
defer gz.in_defer = prev_in_defer;
- try unusedResultExpr(gz, defer_scope.parent, expr_node);
+ _ = try unusedResultExpr(gz, defer_scope.parent, expr_node);
},
.namespace => unreachable,
.top => unreachable,