AstGen: properly restore previous state after temporary changes

Before this, if a compile error occurred, it would cause the previous
value for e.g. the function scope to not get reset. If the AstGen
process continued, it would result in a violation of the data
guarantees that it relies on.

This commit takes advantage of defer to ensure the previous value is
always reset, even in the case of an error.

Closes #8920
This commit is contained in:
Andrew Kelley 2021-05-28 17:29:56 -07:00
parent 54f774f796
commit 3f5ca3920a
2 changed files with 24 additions and 14 deletions

View File

@ -249,9 +249,9 @@ pub const bool_rl: ResultLoc = .{ .ty = .bool_type };
fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zir.Inst.Ref {
const prev_force_comptime = gz.force_comptime;
gz.force_comptime = true;
const e = expr(gz, scope, .{ .ty = .type_type }, type_node);
gz.force_comptime = prev_force_comptime;
return e;
defer gz.force_comptime = prev_force_comptime;
return expr(gz, scope, .{ .ty = .type_type }, type_node);
}
fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref {
@ -1465,9 +1465,9 @@ fn comptimeExpr(
) InnerError!Zir.Inst.Ref {
const prev_force_comptime = gz.force_comptime;
gz.force_comptime = true;
const result = try expr(gz, scope, rl, node);
gz.force_comptime = prev_force_comptime;
return result;
defer gz.force_comptime = prev_force_comptime;
return expr(gz, scope, rl, node);
}
/// This one is for an actual `comptime` syntax, and will emit a compile error if
@ -2121,8 +2121,8 @@ fn genDefers(
const expr_node = node_datas[defer_scope.defer_node].rhs;
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);
gz.in_defer = prev_in_defer;
},
.defer_error => {
const defer_scope = scope.cast(Scope.Defer).?;
@ -2131,8 +2131,8 @@ fn genDefers(
const expr_node = node_datas[defer_scope.defer_node].rhs;
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);
gz.in_defer = prev_in_defer;
},
.namespace => unreachable,
.top => unreachable,
@ -2887,6 +2887,7 @@ fn fnDecl(
const prev_fn_block = astgen.fn_block;
astgen.fn_block = &fn_gz;
defer astgen.fn_block = prev_fn_block;
// Iterate over the parameters. We put the param names as the first N
// items inside `extra` so that debug info later can refer to the parameter names
@ -2938,8 +2939,6 @@ fn fnDecl(
_ = try fn_gz.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node));
}
astgen.fn_block = prev_fn_block;
break :func try decl_gz.addFunc(.{
.src_node = decl_node,
.ret_ty = return_type_inst,
@ -3276,6 +3275,7 @@ fn testDecl(
const prev_fn_block = astgen.fn_block;
astgen.fn_block = &fn_block;
defer astgen.fn_block = prev_fn_block;
const block_result = try expr(&fn_block, &fn_block.base, .none, body_node);
if (fn_block.instructions.items.len == 0 or !fn_block.refIsNoReturn(block_result)) {
@ -3284,8 +3284,6 @@ fn testDecl(
_ = try fn_block.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node));
}
astgen.fn_block = prev_fn_block;
const func_inst = try decl_block.addFunc(.{
.src_node = node,
.ret_ty = .void_type,

View File

@ -938,8 +938,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\pub fn b() !void {
\\ defer return a();
\\}
, &[_][]const u8{
, &[_][]const u8{
":7:8: error: try is not allowed inside defer expression",
":10:8: error: cannot return from defer expression",
});
@ -979,6 +978,19 @@ pub fn addCases(ctx: *TestContext) !void {
":3:9: error: local shadows declaration of 'testing'",
":1:1: note: declared here",
});
case.addError(
\\fn a() type {
\\ return struct {
\\ pub fn b() void {
\\ const c = 6;
\\ const c = 69;
\\ }
\\ };
\\}
, &[_][]const u8{
":5:19: error: redeclaration of 'c'",
":4:19: note: previously declared here",
});
}
{