authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-21 14:19:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-03-21 14:19:52-04:00
logbeea478acc2491289ec3e3bbdcec3b68f65d6e62
tree558ee509260855bbc521a11fec469220e185a2ae
parent153c6cf92e3459038c4ab8251a463163ac89b116
parent28dbc5883763fb2b87ef8d186a57c3971d3414bc
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4764 from LemonBoy/fix-1265

ir: Allow errdefer with payload

9 files changed, 145 insertions(+), 35 deletions(-)

lib/std/zig/ast.zig+3-4
......@@ -1032,6 +1032,7 @@ pub const Node = struct {
10321032 pub const Defer = struct {
10331033 base: Node = Node{ .id = .Defer },
10341034 defer_token: TokenIndex,
1035 payload: ?*Node,
10351036 expr: *Node,
10361037
10371038 pub fn iterate(self: *Defer, index: usize) ?*Node {
......@@ -1833,8 +1834,7 @@ pub const Node = struct {
18331834 var i = index;
18341835
18351836 switch (self.kind) {
1836 .Break,
1837 .Continue => |maybe_label| {
1837 .Break, .Continue => |maybe_label| {
18381838 if (maybe_label) |label| {
18391839 if (i < 1) return label;
18401840 i -= 1;
......@@ -1861,8 +1861,7 @@ pub const Node = struct {
18611861 }
18621862
18631863 switch (self.kind) {
1864 .Break,
1865 .Continue => |maybe_label| {
1864 .Break, .Continue => |maybe_label| {
18661865 if (maybe_label) |label| {
18671866 return label.lastToken();
18681867 }
lib/std/zig/parse.zig+6-1
......@@ -465,7 +465,7 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
465465/// / KEYWORD_noasync BlockExprStatement
466466/// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
467467/// / KEYWORD_defer BlockExprStatement
468/// / KEYWORD_errdefer BlockExprStatement
468/// / KEYWORD_errdefer Payload? BlockExprStatement
469469/// / IfStatement
470470/// / LabeledStatement
471471/// / SwitchExpr
......@@ -526,6 +526,10 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
526526
527527 const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer);
528528 if (defer_token) |token| {
529 const payload = if (tree.tokens.at(token).id == .Keyword_errdefer)
530 try parsePayload(arena, it, tree)
531 else
532 null;
529533 const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, .{
530534 .ExpectedBlockOrExpression = .{ .token = it.index },
531535 });
......@@ -533,6 +537,7 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
533537 node.* = .{
534538 .defer_token = token,
535539 .expr = expr_node,
540 .payload = payload,
536541 };
537542 return &node.base;
538543 }
lib/std/zig/parser_test.zig+13
......@@ -1,3 +1,16 @@
1test "zig fmt: errdefer with payload" {
2 try testCanonical(
3 \\pub fn main() anyerror!void {
4 \\ errdefer |a| x += 1;
5 \\ errdefer |a| {}
6 \\ errdefer |a| {
7 \\ x += 1;
8 \\ }
9 \\}
10 \\
11 );
12}
13
114test "zig fmt: noasync block" {
215 try testCanonical(
316 \\pub fn main() anyerror!void {
lib/std/zig/render.zig+3
......@@ -376,6 +376,9 @@ fn renderExpression(
376376 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
377377
378378 try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space);
379 if (defer_node.payload) |payload| {
380 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
381 }
379382 return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space);
380383 },
381384 .Comptime => {
src/all_types.hpp+1
......@@ -744,6 +744,7 @@ struct AstNodeReturnExpr {
744744
745745struct AstNodeDefer {
746746 ReturnKind kind;
747 AstNode *err_payload;
747748 AstNode *expr;
748749
749750 // temporary data used in IR generation
src/ir.cpp+78-27
......@@ -272,6 +272,10 @@ static ResultLoc *no_result_loc(void);
272272static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value);
273273static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr);
274274static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty);
275static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
276 bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime);
277static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
278 IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime);
275279
276280static void destroy_instruction_src(IrInstSrc *inst) {
277281 switch (inst->id) {
......@@ -5011,39 +5015,73 @@ static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) {
50115015 return instruction;
50125016}
50135017
5014static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {
5018static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) {
50155019 Scope *scope = inner_scope;
5016 bool is_noreturn = false;
5020 if (is_noreturn != nullptr) *is_noreturn = false;
50175021 while (scope != outer_scope) {
50185022 if (!scope)
5019 return is_noreturn;
5023 return true;
50205024
50215025 switch (scope->id) {
50225026 case ScopeIdDefer: {
50235027 AstNode *defer_node = scope->source_node;
50245028 assert(defer_node->type == NodeTypeDefer);
50255029 ReturnKind defer_kind = defer_node->data.defer.kind;
5026 if (defer_kind == ReturnKindUnconditional ||
5027 (gen_error_defers && defer_kind == ReturnKindError))
5028 {
5029 AstNode *defer_expr_node = defer_node->data.defer.expr;
5030 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
5031 IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
5032 if (defer_expr_value != irb->codegen->invalid_inst_src) {
5033 if (defer_expr_value->is_noreturn) {
5034 is_noreturn = true;
5035 } else {
5036 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
5037 defer_expr_value));
5038 }
5030 AstNode *defer_expr_node = defer_node->data.defer.expr;
5031 AstNode *defer_var_node = defer_node->data.defer.err_payload;
5032
5033 if (defer_kind == ReturnKindError && err_value == nullptr) {
5034 // This is an `errdefer` but we're generating code for a
5035 // `return` that doesn't return an error, skip it
5036 scope = scope->parent;
5037 continue;
5038 }
5039
5040 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
5041 if (defer_var_node != nullptr) {
5042 assert(defer_kind == ReturnKindError);
5043 assert(defer_var_node->type == NodeTypeSymbol);
5044 Buf *var_name = defer_var_node->data.symbol_expr.symbol;
5045
5046 if (defer_expr_node->type == NodeTypeUnreachable) {
5047 add_node_error(irb->codegen, defer_var_node,
5048 buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
5049 return false;
5050 }
5051
5052 IrInstSrc *is_comptime;
5053 if (ir_should_inline(irb->exec, defer_expr_scope)) {
5054 is_comptime = ir_build_const_bool(irb, defer_expr_scope,
5055 defer_expr_node, true);
5056 } else {
5057 is_comptime = ir_build_test_comptime(irb, defer_expr_scope,
5058 defer_expr_node, err_value);
50395059 }
5060
5061 ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope,
5062 var_name, true, true, false, is_comptime);
5063 build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value,
5064 buf_ptr(var_name), is_comptime);
5065
5066 defer_expr_scope = err_var->child_scope;
5067 }
5068
5069 IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
5070 if (defer_expr_value == irb->codegen->invalid_inst_src)
5071 return irb->codegen->invalid_inst_src;
5072
5073 if (defer_expr_value->is_noreturn) {
5074 if (is_noreturn != nullptr) *is_noreturn = true;
5075 } else {
5076 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
5077 defer_expr_value));
50405078 }
50415079 scope = scope->parent;
50425080 continue;
50435081 }
50445082 case ScopeIdDecls:
50455083 case ScopeIdFnDef:
5046 return is_noreturn;
5084 return true;
50475085 case ScopeIdBlock:
50485086 case ScopeIdVarDecl:
50495087 case ScopeIdLoop:
......@@ -5060,7 +5098,7 @@ static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope
50605098 zig_unreachable();
50615099 }
50625100 }
5063 return is_noreturn;
5101 return true;
50645102}
50655103
50665104static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) {
......@@ -5146,7 +5184,8 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node,
51465184 bool have_err_defers = defer_counts[ReturnKindError] > 0;
51475185 if (!have_err_defers && !irb->codegen->have_err_ret_tracing) {
51485186 // only generate unconditional defers
5149 ir_gen_defers_for_block(irb, scope, outer_scope, false);
5187 if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
5188 return irb->codegen->invalid_inst_src;
51505189 IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
51515190 result_loc_ret->base.source_instruction = result;
51525191 return result;
......@@ -5169,14 +5208,16 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node,
51695208 IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
51705209
51715210 ir_set_cursor_at_end_and_append_block(irb, err_block);
5172 ir_gen_defers_for_block(irb, scope, outer_scope, true);
5211 if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value))
5212 return irb->codegen->invalid_inst_src;
51735213 if (irb->codegen->have_err_ret_tracing && !should_inline) {
51745214 ir_build_save_err_ret_addr_src(irb, scope, node);
51755215 }
51765216 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
51775217
51785218 ir_set_cursor_at_end_and_append_block(irb, ok_block);
5179 ir_gen_defers_for_block(irb, scope, outer_scope, false);
5219 if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
5220 return irb->codegen->invalid_inst_src;
51805221 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
51815222
51825223 ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
......@@ -5213,7 +5254,12 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node,
52135254 result_loc_ret->base.id = ResultLocIdReturn;
52145255 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
52155256 ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
5216 if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
5257
5258 bool is_noreturn = false;
5259 if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) {
5260 return irb->codegen->invalid_inst_src;
5261 }
5262 if (!is_noreturn) {
52175263 if (irb->codegen->have_err_ret_tracing && !should_inline) {
52185264 ir_build_save_err_ret_addr_src(irb, scope, node);
52195265 }
......@@ -5415,7 +5461,8 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
54155461
54165462 bool is_return_from_fn = block_node == irb->main_block_node;
54175463 if (!is_return_from_fn) {
5418 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
5464 if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
5465 return irb->codegen->invalid_inst_src;
54195466 }
54205467
54215468 IrInstSrc *result;
......@@ -5440,7 +5487,8 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *
54405487 result_loc_ret->base.id = ResultLocIdReturn;
54415488 ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
54425489 ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
5443 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
5490 if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
5491 return irb->codegen->invalid_inst_src;
54445492 return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result));
54455493}
54465494
......@@ -9240,7 +9288,8 @@ static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope
92409288 }
92419289
92429290 IrBasicBlockSrc *dest_block = block_scope->end_block;
9243 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
9291 if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
9292 return irb->codegen->invalid_inst_src;
92449293
92459294 block_scope->incoming_blocks->append(irb->current_basic_block);
92469295 block_scope->incoming_values->append(result_value);
......@@ -9314,7 +9363,8 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n
93149363 }
93159364
93169365 IrBasicBlockSrc *dest_block = loop_scope->break_block;
9317 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
9366 if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
9367 return irb->codegen->invalid_inst_src;
93189368
93199369 loop_scope->incoming_blocks->append(irb->current_basic_block);
93209370 loop_scope->incoming_values->append(result_value);
......@@ -9373,7 +9423,8 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN
93739423 }
93749424
93759425 IrBasicBlockSrc *dest_block = loop_scope->continue_block;
9376 ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
9426 if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr))
9427 return irb->codegen->invalid_inst_src;
93779428 return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
93789429}
93799430
src/parser.cpp+9-2
......@@ -879,7 +879,7 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
879879// / KEYWORD_noasync BlockExprStatement
880880// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
881881// / KEYWORD_defer BlockExprStatement
882// / KEYWORD_errdefer BlockExprStatement
882// / KEYWORD_errdefer Payload? BlockExprStatement
883883// / IfStatement
884884// / LabeledStatement
885885// / SwitchExpr
......@@ -923,12 +923,18 @@ static AstNode *ast_parse_statement(ParseContext *pc) {
923923 if (defer == nullptr)
924924 defer = eat_token_if(pc, TokenIdKeywordErrdefer);
925925 if (defer != nullptr) {
926 Token *payload = (defer->id == TokenIdKeywordErrdefer) ?
927 ast_parse_payload(pc) : nullptr;
926928 AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
927929 AstNode *res = ast_create_node(pc, NodeTypeDefer, defer);
930
928931 res->data.defer.kind = ReturnKindUnconditional;
929932 res->data.defer.expr = statement;
930 if (defer->id == TokenIdKeywordErrdefer)
933 if (defer->id == TokenIdKeywordErrdefer) {
931934 res->data.defer.kind = ReturnKindError;
935 if (payload != nullptr)
936 res->data.defer.err_payload = token_symbol(pc, payload);
937 }
932938 return res;
933939 }
934940
......@@ -3032,6 +3038,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
30323038 break;
30333039 case NodeTypeDefer:
30343040 visit_field(&node->data.defer.expr, visit, context);
3041 visit_field(&node->data.defer.err_payload, visit, context);
30353042 break;
30363043 case NodeTypeVariableDeclaration:
30373044 visit_field(&node->data.variable_declaration.type, visit, context);
test/compile_errors.zig+12
......@@ -2,6 +2,18 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("unused variable error on errdefer",
6 \\fn foo() !void {
7 \\ errdefer |a| unreachable;
8 \\ return error.A;
9 \\}
10 \\export fn entry() void {
11 \\ foo() catch unreachable;
12 \\}
13 , &[_][]const u8{
14 "tmp.zig:2:15: error: unused variable: 'a'",
15 });
16
517 cases.addTest("shift on type with non-power-of-two size",
618 \\export fn entry() void {
719 \\ const S = struct {
test/stage1/behavior/defer.zig+20-1
......@@ -1,4 +1,7 @@
1const expect = @import("std").testing.expect;
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const expectError = std.testing.expectError;
25
36var result: [3]u8 = undefined;
47var index: usize = undefined;
......@@ -93,3 +96,19 @@ test "return variable while defer expression in scope to modify it" {
9396 S.doTheTest();
9497 comptime S.doTheTest();
9598}
99
100test "errdefer with payload" {
101 const S = struct {
102 fn foo() !i32 {
103 errdefer |a| {
104 expectEqual(error.One, a);
105 }
106 return error.One;
107 }
108 fn doTheTest() void {
109 expectError(error.One, foo());
110 }
111 };
112 S.doTheTest();
113 comptime S.doTheTest();
114}