authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-17 22:40:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-20 16:25:52-07:00
log1fe0142d1f40a0051c6a41c12640db443f635ec8
treec3ec24dabd5d5f312ac7bd053cf0f41185d3dda8
parent76102ea41f6369b2254b4a281001061e48d81ffb

AstGen: properly generate errdefer expressions when returning

`return` statements use a new function `nodeMayEvalToError` which does some basic checks on the AST node to return never, always, or maybe. Depending on this result, AstGen skips the errdefers, always includes the errdefers, or emits a conditional branch to check whether the return value is an error that Sema will have to evaluate. Closes #8821 Unblocks #9047

1 files changed, 259 insertions(+), 13 deletions(-)

src/AstGen.zig+259-13
...@@ -5998,22 +5998,55 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -5998,22 +5998,55 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
5998 if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});5998 if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
59995999
6000 const operand_node = node_datas[node].lhs;6000 const operand_node = node_datas[node].lhs;
6001 if (operand_node != 0) {6001 if (operand_node == 0) {
6002 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{6002 // Returning a void value; skip error defers.
6003 .ptr = try gz.addNodeExtended(.ret_ptr, node),
6004 } else .{
6005 .ty = try gz.addNodeExtended(.ret_type, node),
6006 };
6007 const operand = try expr(gz, scope, rl, operand_node);
6008 // TODO check operand to see if we need to generate errdefers
6009 try genDefers(gz, &astgen.fn_block.?.base, scope, .none);6003 try genDefers(gz, &astgen.fn_block.?.base, scope, .none);
6010 _ = try gz.addUnNode(.ret_node, operand, node);6004 _ = try gz.addUnNode(.ret_node, .void_value, node);
6011 return Zir.Inst.Ref.unreachable_value;6005 return Zir.Inst.Ref.unreachable_value;
6012 }6006 }
6013 // Returning a void value; skip error defers.6007
6014 try genDefers(gz, &astgen.fn_block.?.base, scope, .none);6008 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
6015 _ = try gz.addUnNode(.ret_node, .void_value, node);6009 .ptr = try gz.addNodeExtended(.ret_ptr, node),
6016 return Zir.Inst.Ref.unreachable_value;6010 } else .{
6011 .ty = try gz.addNodeExtended(.ret_type, node),
6012 };
6013 const operand = try expr(gz, scope, rl, operand_node);
6014
6015 switch (nodeMayEvalToError(tree, operand_node)) {
6016 .never => {
6017 // Returning a value that cannot be an error; skip error defers.
6018 try genDefers(gz, &astgen.fn_block.?.base, scope, .none);
6019 _ = try gz.addUnNode(.ret_node, operand, node);
6020 return Zir.Inst.Ref.unreachable_value;
6021 },
6022 .always => {
6023 // Value is always an error. Emit both error defers and regular defers.
6024 const err_code = try gz.addUnNode(.err_union_code, operand, node);
6025 try genDefers(gz, &astgen.fn_block.?.base, scope, err_code);
6026 _ = try gz.addUnNode(.ret_node, operand, node);
6027 return Zir.Inst.Ref.unreachable_value;
6028 },
6029 .maybe => {
6030 // Emit conditional branch for generating errdefers.
6031 const is_err = try gz.addUnNode(.is_err, operand, node);
6032 const condbr = try gz.addCondBr(.condbr, node);
6033
6034 var then_scope = gz.makeSubBlock(scope);
6035 defer then_scope.instructions.deinit(astgen.gpa);
6036 const err_code = try then_scope.addUnNode(.err_union_code, operand, node);
6037 try genDefers(&then_scope, &astgen.fn_block.?.base, scope, err_code);
6038 _ = try then_scope.addUnNode(.ret_node, operand, node);
6039
6040 var else_scope = gz.makeSubBlock(scope);
6041 defer else_scope.instructions.deinit(astgen.gpa);
6042 try genDefers(&else_scope, &astgen.fn_block.?.base, scope, .none);
6043 _ = try else_scope.addUnNode(.ret_node, operand, node);
6044
6045 try setCondBrPayload(condbr, is_err, &then_scope, &else_scope);
6046
6047 return Zir.Inst.Ref.unreachable_value;
6048 },
6049 }
6017}6050}
60186051
6019fn identifier(6052fn identifier(
...@@ -7555,6 +7588,219 @@ fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index)...@@ -7555,6 +7588,219 @@ fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index)
7555 }7588 }
7556}7589}
75577590
7591fn nodeMayEvalToError(tree: *const ast.Tree, start_node: ast.Node.Index) enum { never, always, maybe } {
7592 const node_tags = tree.nodes.items(.tag);
7593 const node_datas = tree.nodes.items(.data);
7594 const main_tokens = tree.nodes.items(.main_token);
7595 const token_tags = tree.tokens.items(.tag);
7596
7597 var node = start_node;
7598 while (true) {
7599 switch (node_tags[node]) {
7600 .root,
7601 .@"usingnamespace",
7602 .test_decl,
7603 .switch_case,
7604 .switch_case_one,
7605 .container_field_init,
7606 .container_field_align,
7607 .container_field,
7608 .asm_output,
7609 .asm_input,
7610 => unreachable,
7611
7612 .error_value => return .always,
7613
7614 .@"asm",
7615 .asm_simple,
7616 .identifier,
7617 .field_access,
7618 .deref,
7619 .array_access,
7620 .while_simple,
7621 .while_cont,
7622 .for_simple,
7623 .if_simple,
7624 .@"while",
7625 .@"if",
7626 .@"for",
7627 .@"switch",
7628 .switch_comma,
7629 .call_one,
7630 .call_one_comma,
7631 .async_call_one,
7632 .async_call_one_comma,
7633 .call,
7634 .call_comma,
7635 .async_call,
7636 .async_call_comma,
7637 => return .maybe,
7638
7639 .@"return",
7640 .@"break",
7641 .@"continue",
7642 .bit_not,
7643 .bool_not,
7644 .global_var_decl,
7645 .local_var_decl,
7646 .simple_var_decl,
7647 .aligned_var_decl,
7648 .@"defer",
7649 .@"errdefer",
7650 .address_of,
7651 .optional_type,
7652 .negation,
7653 .negation_wrap,
7654 .@"resume",
7655 .array_type,
7656 .array_type_sentinel,
7657 .ptr_type_aligned,
7658 .ptr_type_sentinel,
7659 .ptr_type,
7660 .ptr_type_bit_range,
7661 .@"suspend",
7662 .@"anytype",
7663 .fn_proto_simple,
7664 .fn_proto_multi,
7665 .fn_proto_one,
7666 .fn_proto,
7667 .fn_decl,
7668 .anyframe_type,
7669 .anyframe_literal,
7670 .integer_literal,
7671 .float_literal,
7672 .enum_literal,
7673 .string_literal,
7674 .multiline_string_literal,
7675 .char_literal,
7676 .true_literal,
7677 .false_literal,
7678 .null_literal,
7679 .undefined_literal,
7680 .unreachable_literal,
7681 .error_set_decl,
7682 .container_decl,
7683 .container_decl_trailing,
7684 .container_decl_two,
7685 .container_decl_two_trailing,
7686 .container_decl_arg,
7687 .container_decl_arg_trailing,
7688 .tagged_union,
7689 .tagged_union_trailing,
7690 .tagged_union_two,
7691 .tagged_union_two_trailing,
7692 .tagged_union_enum_tag,
7693 .tagged_union_enum_tag_trailing,
7694 .add,
7695 .add_wrap,
7696 .array_cat,
7697 .array_mult,
7698 .assign,
7699 .assign_bit_and,
7700 .assign_bit_or,
7701 .assign_bit_shift_left,
7702 .assign_bit_shift_right,
7703 .assign_bit_xor,
7704 .assign_div,
7705 .assign_sub,
7706 .assign_sub_wrap,
7707 .assign_mod,
7708 .assign_add,
7709 .assign_add_wrap,
7710 .assign_mul,
7711 .assign_mul_wrap,
7712 .bang_equal,
7713 .bit_and,
7714 .bit_or,
7715 .bit_shift_left,
7716 .bit_shift_right,
7717 .bit_xor,
7718 .bool_and,
7719 .bool_or,
7720 .div,
7721 .equal_equal,
7722 .error_union,
7723 .greater_or_equal,
7724 .greater_than,
7725 .less_or_equal,
7726 .less_than,
7727 .merge_error_sets,
7728 .mod,
7729 .mul,
7730 .mul_wrap,
7731 .switch_range,
7732 .sub,
7733 .sub_wrap,
7734 .slice,
7735 .slice_open,
7736 .slice_sentinel,
7737 .array_init_one,
7738 .array_init_one_comma,
7739 .array_init_dot_two,
7740 .array_init_dot_two_comma,
7741 .array_init_dot,
7742 .array_init_dot_comma,
7743 .array_init,
7744 .array_init_comma,
7745 .struct_init_one,
7746 .struct_init_one_comma,
7747 .struct_init_dot_two,
7748 .struct_init_dot_two_comma,
7749 .struct_init_dot,
7750 .struct_init_dot_comma,
7751 .struct_init,
7752 .struct_init_comma,
7753 => return .never,
7754
7755 // Forward the question to the LHS sub-expression.
7756 .grouped_expression,
7757 .@"try",
7758 .@"await",
7759 .@"comptime",
7760 .@"nosuspend",
7761 .unwrap_optional,
7762 => node = node_datas[node].lhs,
7763
7764 // Forward the question to the RHS sub-expression.
7765 .@"catch",
7766 .@"orelse",
7767 => node = node_datas[node].rhs,
7768
7769 .block_two,
7770 .block_two_semicolon,
7771 .block,
7772 .block_semicolon,
7773 => {
7774 const lbrace = main_tokens[node];
7775 if (token_tags[lbrace - 1] == .colon) {
7776 // Labeled blocks may need a memory location to forward
7777 // to their break statements.
7778 return .maybe;
7779 } else {
7780 return .never;
7781 }
7782 },
7783
7784 .builtin_call,
7785 .builtin_call_comma,
7786 .builtin_call_two,
7787 .builtin_call_two_comma,
7788 => {
7789 const builtin_token = main_tokens[node];
7790 const builtin_name = tree.tokenSlice(builtin_token);
7791 // If the builtin is an invalid name, we don't cause an error here; instead
7792 // let it pass, and the error will be "invalid builtin function" later.
7793 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
7794 if (builtin_info.tag == .err_set_cast) {
7795 return .always;
7796 } else {
7797 return .never;
7798 }
7799 },
7800 }
7801 }
7802}
7803
7558/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of7804/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
7559/// result locations must call this function on their result.7805/// result locations must call this function on their result.
7560/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.7806/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.