mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
AstGen: introduce 'reachableExpr' function
This function can be swapped out for calls to expr() to report a compile error when the expression results in control flow that does not return.
This commit is contained in:
parent
cffa22a658
commit
7f5560689a
@ -261,6 +261,23 @@ fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zi
|
||||
return expr(gz, scope, .{ .ty = .type_type }, type_node);
|
||||
}
|
||||
|
||||
/// Same as `expr` but fails with a compile error if the result type is `noreturn`.
|
||||
fn reachableExpr(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
node: ast.Node.Index,
|
||||
src_node: ast.Node.Index,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
const result_inst = try expr(gz, scope, rl, node);
|
||||
if (gz.refIsNoReturn(result_inst)) {
|
||||
return gz.astgen.failNodeNotes(src_node, "unreachable code", .{}, &[_]u32{
|
||||
try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
|
||||
});
|
||||
}
|
||||
return result_inst;
|
||||
}
|
||||
|
||||
fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref {
|
||||
const astgen = gz.astgen;
|
||||
const tree = astgen.tree;
|
||||
@ -2331,8 +2348,7 @@ fn varDecl(
|
||||
const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
|
||||
.ty = try typeExpr(gz, scope, var_decl.ast.type_node),
|
||||
} else .none;
|
||||
const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node);
|
||||
try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local constant");
|
||||
const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
|
||||
|
||||
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||
sub_scope.* = .{
|
||||
@ -2383,8 +2399,7 @@ fn varDecl(
|
||||
init_scope.rl_ptr = alloc;
|
||||
}
|
||||
const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
|
||||
const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node);
|
||||
try astgen.checkVarInitExpr(init_scope, node, var_decl.ast.init_node, init_inst, "local constant");
|
||||
const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node);
|
||||
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
@ -2486,8 +2501,7 @@ fn varDecl(
|
||||
resolve_inferred_alloc = alloc;
|
||||
break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
|
||||
};
|
||||
const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node);
|
||||
try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local variable");
|
||||
_ = try reachableExpr(gz, scope, var_data.result_loc, var_decl.ast.init_node, node);
|
||||
if (resolve_inferred_alloc != .none) {
|
||||
_ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
|
||||
}
|
||||
@ -6618,14 +6632,14 @@ fn as(
|
||||
const dest_type = try typeExpr(gz, scope, lhs);
|
||||
switch (rl) {
|
||||
.none, .none_or_ref, .discard, .ref, .ty => {
|
||||
const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);
|
||||
const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
|
||||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
.ptr, .inferred_ptr => |result_ptr| {
|
||||
return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type);
|
||||
return asRlPtr(gz, scope, rl, node, result_ptr, rhs, dest_type);
|
||||
},
|
||||
.block_ptr => |block_scope| {
|
||||
return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type);
|
||||
return asRlPtr(gz, scope, rl, node, block_scope.rl_ptr, rhs, dest_type);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -6679,6 +6693,7 @@ fn asRlPtr(
|
||||
parent_gz: *GenZir,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
src_node: ast.Node.Index,
|
||||
result_ptr: Zir.Inst.Ref,
|
||||
operand_node: ast.Node.Index,
|
||||
dest_type: Zir.Inst.Ref,
|
||||
@ -6692,7 +6707,7 @@ fn asRlPtr(
|
||||
defer as_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
|
||||
const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
|
||||
const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
|
||||
const parent_zir = &parent_gz.instructions;
|
||||
if (as_scope.rvalue_rl_count == 1) {
|
||||
// Busted! This expression didn't actually need a pointer.
|
||||
@ -9607,27 +9622,3 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
|
||||
astgen.source_line = line;
|
||||
astgen.source_column = column;
|
||||
}
|
||||
|
||||
fn checkVarInitExpr(
|
||||
astgen: *AstGen,
|
||||
gz: GenZir,
|
||||
var_node: ast.Node.Index,
|
||||
init_node: ast.Node.Index,
|
||||
init_inst: Zir.Inst.Ref,
|
||||
var_name_text: []const u8,
|
||||
) !void {
|
||||
if (gz.refIsNoReturn(init_inst)) {
|
||||
return astgen.failNodeNotes(
|
||||
var_node,
|
||||
"useless {s}",
|
||||
.{var_name_text},
|
||||
&[_]u32{
|
||||
try astgen.errNoteNode(
|
||||
init_node,
|
||||
"control flow is diverted here",
|
||||
.{},
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4831,7 +4831,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ const a = return;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:5: error: useless local constant",
|
||||
"tmp.zig:2:5: error: unreachable code",
|
||||
"tmp.zig:2:15: note: control flow is diverted here",
|
||||
});
|
||||
|
||||
@ -5058,6 +5058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\export fn entry() void { _ = f(); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:12: error: unreachable code",
|
||||
"tmp.zig:2:21: note: control flow is diverted here",
|
||||
});
|
||||
|
||||
cases.add("invalid builtin fn",
|
||||
@ -7143,9 +7144,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\export fn entry8() void {
|
||||
\\ var h = (Foo {}).bar;
|
||||
\\}
|
||||
\\export fn entry9() void {
|
||||
\\ var z: noreturn = return;
|
||||
\\}
|
||||
\\const Opaque = opaque {};
|
||||
\\const Foo = struct {
|
||||
\\ fn bar(self: *const Foo) void {}
|
||||
@ -7161,7 +7159,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
"tmp.zig:17:4: error: variable of type 'Opaque' not allowed",
|
||||
"tmp.zig:20:4: error: variable of type 'type' must be const or comptime",
|
||||
"tmp.zig:23:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",
|
||||
"tmp.zig:26:22: error: unreachable code",
|
||||
});
|
||||
|
||||
cases.add("variable with type 'noreturn'",
|
||||
\\export fn entry9() void {
|
||||
\\ var z: noreturn = return;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:5: error: unreachable code",
|
||||
"tmp.zig:2:23: note: control flow is diverted here",
|
||||
});
|
||||
|
||||
cases.add("wrong types given to atomic order args in cmpxchg",
|
||||
|
Loading…
Reference in New Issue
Block a user