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-17 22:44:17-07:00
log6ce84409875e92d69da1fd88409e2f00d4adee9f
tree023c203932be96ed0040d6b869f03f28f8194214
parentccb6e1627ec76ffeccef9bdcce321c21d1e72bfd

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