| author | |
| committer | |
| log | 65a03c5859e57820d2c28ad2952dda3fd4ac7d9c |
| tree | cbf8162ed73547c27a535fe21ebea20888625639 |
| parent | 34a7e6fdb362cb7be1067b7d1fc110eb2f323c51 |
see #1104 files changed, 113 insertions(+), 47 deletions(-)
src/all_types.hpp+10| ... | ... | @@ -78,8 +78,18 @@ struct ConstExprValue { |
| 78 | 78 | } data; |
| 79 | 79 | }; |
| 80 | 80 | |
| 81 | enum ReturnKnowledge { | |
| 82 | ReturnKnowledgeUnknown, | |
| 83 | ReturnKnowledgeKnownError, | |
| 84 | ReturnKnowledgeKnownNonError, | |
| 85 | ReturnKnowledgeKnownNull, | |
| 86 | ReturnKnowledgeKnownNonNull, | |
| 87 | ReturnKnowledgeSkipDefers, | |
| 88 | }; | |
| 89 | ||
| 81 | 90 | struct Expr { |
| 82 | 91 | TypeTableEntry *type_entry; |
| 92 | ReturnKnowledge return_knowledge; | |
| 83 | 93 | |
| 84 | 94 | LLVMValueRef const_llvm_val; |
| 85 | 95 | ConstExprValue const_val; |
src/analyze.cpp+9-37| ... | ... | @@ -3693,11 +3693,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 3693 | 3693 | // explicit cast from child type of maybe type to maybe type |
| 3694 | 3694 | if (wanted_type->id == TypeTableEntryIdMaybe) { |
| 3695 | 3695 | if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { |
| 3696 | get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; | |
| 3696 | 3697 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); |
| 3697 | 3698 | } else if (actual_type->id == TypeTableEntryIdNumLitInt || |
| 3698 | 3699 | actual_type->id == TypeTableEntryIdNumLitFloat) |
| 3699 | 3700 | { |
| 3700 | 3701 | if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) { |
| 3702 | get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull; | |
| 3701 | 3703 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); |
| 3702 | 3704 | } else { |
| 3703 | 3705 | return g->builtin_types.entry_invalid; |
| ... | ... | @@ -3708,11 +3710,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 3708 | 3710 | // explicit cast from child type of error type to error type |
| 3709 | 3711 | if (wanted_type->id == TypeTableEntryIdErrorUnion) { |
| 3710 | 3712 | if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { |
| 3713 | get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; | |
| 3711 | 3714 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); |
| 3712 | 3715 | } else if (actual_type->id == TypeTableEntryIdNumLitInt || |
| 3713 | 3716 | actual_type->id == TypeTableEntryIdNumLitFloat) |
| 3714 | 3717 | { |
| 3715 | 3718 | if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { |
| 3719 | get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError; | |
| 3716 | 3720 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); |
| 3717 | 3721 | } else { |
| 3718 | 3722 | return g->builtin_types.entry_invalid; |
| ... | ... | @@ -3724,6 +3728,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 3724 | 3728 | if (wanted_type->id == TypeTableEntryIdErrorUnion && |
| 3725 | 3729 | actual_type->id == TypeTableEntryIdPureError) |
| 3726 | 3730 | { |
| 3731 | get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownError; | |
| 3727 | 3732 | return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false); |
| 3728 | 3733 | } |
| 3729 | 3734 | |
| ... | ... | @@ -4602,44 +4607,11 @@ static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, Block |
| 4602 | 4607 | |
| 4603 | 4608 | node->data.defer.child_block = new_block_context(node, parent_context); |
| 4604 | 4609 | |
| 4605 | switch (node->data.defer.kind) { | |
| 4606 | case ReturnKindUnconditional: | |
| 4607 | { | |
| 4608 | TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr, | |
| 4609 | node->data.defer.expr); | |
| 4610 | validate_voided_expr(g, node->data.defer.expr, resolved_type); | |
| 4610 | TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr, | |
| 4611 | node->data.defer.expr); | |
| 4612 | validate_voided_expr(g, node->data.defer.expr, resolved_type); | |
| 4611 | 4613 | |
| 4612 | return g->builtin_types.entry_void; | |
| 4613 | } | |
| 4614 | case ReturnKindError: | |
| 4615 | { | |
| 4616 | TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr, | |
| 4617 | node->data.defer.expr); | |
| 4618 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 4619 | // OK | |
| 4620 | } else if (resolved_type->id == TypeTableEntryIdErrorUnion) { | |
| 4621 | // OK | |
| 4622 | } else { | |
| 4623 | add_node_error(g, node->data.defer.expr, | |
| 4624 | buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name))); | |
| 4625 | } | |
| 4626 | return g->builtin_types.entry_void; | |
| 4627 | } | |
| 4628 | case ReturnKindMaybe: | |
| 4629 | { | |
| 4630 | TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr, | |
| 4631 | node->data.defer.expr); | |
| 4632 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 4633 | // OK | |
| 4634 | } else if (resolved_type->id == TypeTableEntryIdMaybe) { | |
| 4635 | // OK | |
| 4636 | } else { | |
| 4637 | add_node_error(g, node->data.defer.expr, | |
| 4638 | buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); | |
| 4639 | } | |
| 4640 | return g->builtin_types.entry_void; | |
| 4641 | } | |
| 4642 | } | |
| 4614 | return g->builtin_types.entry_void; | |
| 4643 | 4615 | } |
| 4644 | 4616 | |
| 4645 | 4617 | static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
src/codegen.cpp+58-10| ... | ... | @@ -483,6 +483,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 483 | 483 | } |
| 484 | 484 | case CastOpPureErrorWrap: |
| 485 | 485 | assert(wanted_type->id == TypeTableEntryIdErrorUnion); |
| 486 | ||
| 486 | 487 | if (!type_has_bits(wanted_type->data.error.child_type)) { |
| 487 | 488 | return expr_val; |
| 488 | 489 | } else { |
| ... | ... | @@ -1593,18 +1594,48 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { |
| 1593 | 1594 | return phi; |
| 1594 | 1595 | } |
| 1595 | 1596 | |
| 1596 | static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block) { | |
| 1597 | static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block, | |
| 1598 | bool gen_error_defers, bool gen_maybe_defers) | |
| 1599 | { | |
| 1597 | 1600 | while (inner_block != outer_block) { |
| 1598 | if (inner_block->node->type == NodeTypeDefer) { | |
| 1601 | if (inner_block->node->type == NodeTypeDefer && | |
| 1602 | ((inner_block->node->data.defer.kind == ReturnKindUnconditional) || | |
| 1603 | (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) || | |
| 1604 | (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe))) | |
| 1605 | { | |
| 1599 | 1606 | gen_expr(g, inner_block->node->data.defer.expr); |
| 1600 | 1607 | } |
| 1601 | 1608 | inner_block = inner_block->parent; |
| 1602 | 1609 | } |
| 1603 | 1610 | } |
| 1604 | 1611 | |
| 1605 | static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { | |
| 1606 | gen_defers_for_block(g, source_node->block_context, | |
| 1607 | source_node->block_context->fn_entry->fn_def_node->block_context); | |
| 1612 | static int get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { | |
| 1613 | int result = 0; | |
| 1614 | while (inner_block != outer_block) { | |
| 1615 | if (inner_block->node->type == NodeTypeDefer && | |
| 1616 | (inner_block->node->data.defer.kind == ReturnKindError || | |
| 1617 | inner_block->node->data.defer.kind == ReturnKindMaybe)) | |
| 1618 | { | |
| 1619 | result += 1; | |
| 1620 | } | |
| 1621 | inner_block = inner_block->parent; | |
| 1622 | } | |
| 1623 | return result; | |
| 1624 | } | |
| 1625 | ||
| 1626 | static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value, ReturnKnowledge rk) { | |
| 1627 | BlockContext *defer_inner_block = source_node->block_context; | |
| 1628 | BlockContext *defer_outer_block = source_node->block_context->fn_entry->fn_def_node->block_context; | |
| 1629 | if (rk == ReturnKnowledgeUnknown) { | |
| 1630 | if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) { | |
| 1631 | // generate branching code that checks the return value and generates defers | |
| 1632 | // if the return value is error | |
| 1633 | zig_panic("TODO"); | |
| 1634 | } | |
| 1635 | } else if (rk != ReturnKnowledgeSkipDefers) { | |
| 1636 | gen_defers_for_block(g, defer_inner_block, defer_outer_block, | |
| 1637 | rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull); | |
| 1638 | } | |
| 1608 | 1639 | |
| 1609 | 1640 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; |
| 1610 | 1641 | if (handle_is_ptr(return_type)) { |
| ... | ... | @@ -1628,7 +1659,23 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { |
| 1628 | 1659 | switch (node->data.return_expr.kind) { |
| 1629 | 1660 | case ReturnKindUnconditional: |
| 1630 | 1661 | { |
| 1631 | return gen_return(g, node, value); | |
| 1662 | Expr *expr = get_resolved_expr(param_node); | |
| 1663 | if (expr->const_val.ok) { | |
| 1664 | if (value_type->id == TypeTableEntryIdErrorUnion) { | |
| 1665 | if (expr->const_val.data.x_err.err) { | |
| 1666 | expr->return_knowledge = ReturnKnowledgeKnownError; | |
| 1667 | } else { | |
| 1668 | expr->return_knowledge = ReturnKnowledgeKnownNonError; | |
| 1669 | } | |
| 1670 | } else if (value_type->id == TypeTableEntryIdMaybe) { | |
| 1671 | if (expr->const_val.data.x_maybe) { | |
| 1672 | expr->return_knowledge = ReturnKnowledgeKnownNonNull; | |
| 1673 | } else { | |
| 1674 | expr->return_knowledge = ReturnKnowledgeKnownNull; | |
| 1675 | } | |
| 1676 | } | |
| 1677 | } | |
| 1678 | return gen_return(g, node, value, expr->return_knowledge); | |
| 1632 | 1679 | } |
| 1633 | 1680 | case ReturnKindError: |
| 1634 | 1681 | { |
| ... | ... | @@ -1653,7 +1700,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { |
| 1653 | 1700 | LLVMPositionBuilderAtEnd(g->builder, return_block); |
| 1654 | 1701 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; |
| 1655 | 1702 | if (return_type->id == TypeTableEntryIdPureError) { |
| 1656 | gen_return(g, node, err_val); | |
| 1703 | gen_return(g, node, err_val, ReturnKnowledgeKnownError); | |
| 1657 | 1704 | } else if (return_type->id == TypeTableEntryIdErrorUnion) { |
| 1658 | 1705 | if (type_has_bits(return_type->data.error.child_type)) { |
| 1659 | 1706 | assert(g->cur_ret_ptr); |
| ... | ... | @@ -1663,7 +1710,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { |
| 1663 | 1710 | LLVMBuildStore(g->builder, err_val, tag_ptr); |
| 1664 | 1711 | LLVMBuildRetVoid(g->builder); |
| 1665 | 1712 | } else { |
| 1666 | gen_return(g, node, err_val); | |
| 1713 | gen_return(g, node, err_val, ReturnKnowledgeKnownError); | |
| 1667 | 1714 | } |
| 1668 | 1715 | } else { |
| 1669 | 1716 | zig_unreachable(); |
| ... | ... | @@ -1834,10 +1881,11 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i |
| 1834 | 1881 | return nullptr; |
| 1835 | 1882 | } |
| 1836 | 1883 | |
| 1837 | gen_defers_for_block(g, block_node->data.block.nested_block, block_node->data.block.child_block); | |
| 1884 | gen_defers_for_block(g, block_node->data.block.nested_block, block_node->data.block.child_block, | |
| 1885 | false, false); | |
| 1838 | 1886 | |
| 1839 | 1887 | if (implicit_return_type) { |
| 1840 | return gen_return(g, block_node, return_value); | |
| 1888 | return gen_return(g, block_node, return_value, ReturnKnowledgeSkipDefers); | |
| 1841 | 1889 | } else { |
| 1842 | 1890 | return return_value; |
| 1843 | 1891 | } |
test/run_tests.cpp+36| ... | ... | @@ -1545,6 +1545,42 @@ pub fn main(args: [][]u8) -> %void { |
| 1545 | 1545 | } |
| 1546 | 1546 | )SOURCE", "before\ndefer2\ndefer1\n"); |
| 1547 | 1547 | |
| 1548 | ||
| 1549 | add_simple_case("%defer and it fails", R"SOURCE( | |
| 1550 | import "std.zig"; | |
| 1551 | pub fn main(args: [][]u8) -> %void { | |
| 1552 | do_test() %% return; | |
| 1553 | } | |
| 1554 | fn do_test() -> %void { | |
| 1555 | %%stdout.printf("before\n"); | |
| 1556 | defer %%stdout.printf("defer1\n"); | |
| 1557 | %defer %%stdout.printf("deferErr\n"); | |
| 1558 | %return its_gonna_fail(); | |
| 1559 | defer %%stdout.printf("defer3\n"); | |
| 1560 | %%stdout.printf("after\n"); | |
| 1561 | } | |
| 1562 | error IToldYouItWouldFail; | |
| 1563 | fn its_gonna_fail() -> %void { | |
| 1564 | return error.IToldYouItWouldFail; | |
| 1565 | } | |
| 1566 | )SOURCE", "before\ndeferErr\ndefer1\n"); | |
| 1567 | ||
| 1568 | ||
| 1569 | add_simple_case("%defer and it passes", R"SOURCE( | |
| 1570 | import "std.zig"; | |
| 1571 | pub fn main(args: [][]u8) -> %void { | |
| 1572 | do_test() %% return; | |
| 1573 | } | |
| 1574 | fn do_test() -> %void { | |
| 1575 | %%stdout.printf("before\n"); | |
| 1576 | defer %%stdout.printf("defer1\n"); | |
| 1577 | %defer %%stdout.printf("deferErr\n"); | |
| 1578 | %return its_gonna_pass(); | |
| 1579 | defer %%stdout.printf("defer3\n"); | |
| 1580 | %%stdout.printf("after\n"); | |
| 1581 | } | |
| 1582 | fn its_gonna_pass() -> %void { } | |
| 1583 | )SOURCE", "before\nafter\ndefer3\ndefer1\n"); | |
| 1548 | 1584 | } |
| 1549 | 1585 | |
| 1550 | 1586 |