authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-06 16:36:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-06 16:36:49-07:00
log65a03c5859e57820d2c28ad2952dda3fd4ac7d9c
treecbf8162ed73547c27a535fe21ebea20888625639
parent34a7e6fdb362cb7be1067b7d1fc110eb2f323c51

implement %defer and ?defer

see #110

4 files changed, 113 insertions(+), 47 deletions(-)

src/all_types.hpp+10
...@@ -78,8 +78,18 @@ struct ConstExprValue {...@@ -78,8 +78,18 @@ struct ConstExprValue {
78 } data;78 } data;
79};79};
8080
81enum ReturnKnowledge {
82 ReturnKnowledgeUnknown,
83 ReturnKnowledgeKnownError,
84 ReturnKnowledgeKnownNonError,
85 ReturnKnowledgeKnownNull,
86 ReturnKnowledgeKnownNonNull,
87 ReturnKnowledgeSkipDefers,
88};
89
81struct Expr {90struct Expr {
82 TypeTableEntry *type_entry;91 TypeTableEntry *type_entry;
92 ReturnKnowledge return_knowledge;
8393
84 LLVMValueRef const_llvm_val;94 LLVMValueRef const_llvm_val;
85 ConstExprValue const_val;95 ConstExprValue const_val;
src/analyze.cpp+9-37
...@@ -3693,11 +3693,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -3693,11 +3693,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
3693 // explicit cast from child type of maybe type to maybe type3693 // explicit cast from child type of maybe type to maybe type
3694 if (wanted_type->id == TypeTableEntryIdMaybe) {3694 if (wanted_type->id == TypeTableEntryIdMaybe) {
3695 if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {3695 if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
3696 get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonNull;
3696 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);3697 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
3697 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||3698 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
3698 actual_type->id == TypeTableEntryIdNumLitFloat)3699 actual_type->id == TypeTableEntryIdNumLitFloat)
3699 {3700 {
3700 if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) {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 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);3703 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true);
3702 } else {3704 } else {
3703 return g->builtin_types.entry_invalid;3705 return g->builtin_types.entry_invalid;
...@@ -3708,11 +3710,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -3708,11 +3710,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
3708 // explicit cast from child type of error type to error type3710 // explicit cast from child type of error type to error type
3709 if (wanted_type->id == TypeTableEntryIdErrorUnion) {3711 if (wanted_type->id == TypeTableEntryIdErrorUnion) {
3710 if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {3712 if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
3713 get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNonError;
3711 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);3714 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
3712 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||3715 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
3713 actual_type->id == TypeTableEntryIdNumLitFloat)3716 actual_type->id == TypeTableEntryIdNumLitFloat)
3714 {3717 {
3715 if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) {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 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);3720 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true);
3717 } else {3721 } else {
3718 return g->builtin_types.entry_invalid;3722 return g->builtin_types.entry_invalid;
...@@ -3724,6 +3728,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -3724,6 +3728,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
3724 if (wanted_type->id == TypeTableEntryIdErrorUnion &&3728 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
3725 actual_type->id == TypeTableEntryIdPureError)3729 actual_type->id == TypeTableEntryIdPureError)
3726 {3730 {
3731 get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownError;
3727 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false);3732 return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false);
3728 }3733 }
37293734
...@@ -4602,44 +4607,11 @@ static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, Block...@@ -4602,44 +4607,11 @@ static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, Block
46024607
4603 node->data.defer.child_block = new_block_context(node, parent_context);4608 node->data.defer.child_block = new_block_context(node, parent_context);
46044609
4605 switch (node->data.defer.kind) {4610 TypeTableEntry *resolved_type = analyze_expression(g, import, parent_context, nullptr,
4606 case ReturnKindUnconditional:4611 node->data.defer.expr);
4607 {4612 validate_voided_expr(g, node->data.defer.expr, resolved_type);
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);
46114613
4612 return g->builtin_types.entry_void;4614 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 }
4643}4615}
46444616
4645static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,4617static 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,6 +483,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
483 }483 }
484 case CastOpPureErrorWrap:484 case CastOpPureErrorWrap:
485 assert(wanted_type->id == TypeTableEntryIdErrorUnion);485 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
486
486 if (!type_has_bits(wanted_type->data.error.child_type)) {487 if (!type_has_bits(wanted_type->data.error.child_type)) {
487 return expr_val;488 return expr_val;
488 } else {489 } else {
...@@ -1593,18 +1594,48 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {...@@ -1593,18 +1594,48 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
1593 return phi;1594 return phi;
1594}1595}
15951596
1596static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block) {1597static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block,
1598 bool gen_error_defers, bool gen_maybe_defers)
1599{
1597 while (inner_block != outer_block) {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 gen_expr(g, inner_block->node->data.defer.expr);1606 gen_expr(g, inner_block->node->data.defer.expr);
1600 }1607 }
1601 inner_block = inner_block->parent;1608 inner_block = inner_block->parent;
1602 }1609 }
1603}1610}
16041611
1605static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) {1612static int get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
1606 gen_defers_for_block(g, source_node->block_context,1613 int result = 0;
1607 source_node->block_context->fn_entry->fn_def_node->block_context);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
1626static 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 }
16081639
1609 TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;1640 TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
1610 if (handle_is_ptr(return_type)) {1641 if (handle_is_ptr(return_type)) {
...@@ -1628,7 +1659,23 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {...@@ -1628,7 +1659,23 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
1628 switch (node->data.return_expr.kind) {1659 switch (node->data.return_expr.kind) {
1629 case ReturnKindUnconditional: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 case ReturnKindError:1680 case ReturnKindError:
1634 {1681 {
...@@ -1653,7 +1700,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {...@@ -1653,7 +1700,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
1653 LLVMPositionBuilderAtEnd(g->builder, return_block);1700 LLVMPositionBuilderAtEnd(g->builder, return_block);
1654 TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;1701 TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
1655 if (return_type->id == TypeTableEntryIdPureError) {1702 if (return_type->id == TypeTableEntryIdPureError) {
1656 gen_return(g, node, err_val);1703 gen_return(g, node, err_val, ReturnKnowledgeKnownError);
1657 } else if (return_type->id == TypeTableEntryIdErrorUnion) {1704 } else if (return_type->id == TypeTableEntryIdErrorUnion) {
1658 if (type_has_bits(return_type->data.error.child_type)) {1705 if (type_has_bits(return_type->data.error.child_type)) {
1659 assert(g->cur_ret_ptr);1706 assert(g->cur_ret_ptr);
...@@ -1663,7 +1710,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {...@@ -1663,7 +1710,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
1663 LLVMBuildStore(g->builder, err_val, tag_ptr);1710 LLVMBuildStore(g->builder, err_val, tag_ptr);
1664 LLVMBuildRetVoid(g->builder);1711 LLVMBuildRetVoid(g->builder);
1665 } else {1712 } else {
1666 gen_return(g, node, err_val);1713 gen_return(g, node, err_val, ReturnKnowledgeKnownError);
1667 }1714 }
1668 } else {1715 } else {
1669 zig_unreachable();1716 zig_unreachable();
...@@ -1834,10 +1881,11 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i...@@ -1834,10 +1881,11 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i
1834 return nullptr;1881 return nullptr;
1835 }1882 }
18361883
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);
18381886
1839 if (implicit_return_type) {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 } else {1889 } else {
1842 return return_value;1890 return return_value;
1843 }1891 }
test/run_tests.cpp+36
...@@ -1545,6 +1545,42 @@ pub fn main(args: [][]u8) -> %void {...@@ -1545,6 +1545,42 @@ pub fn main(args: [][]u8) -> %void {
1545}1545}
1546 )SOURCE", "before\ndefer2\ndefer1\n");1546 )SOURCE", "before\ndefer2\ndefer1\n");
15471547
1548
1549 add_simple_case("%defer and it fails", R"SOURCE(
1550import "std.zig";
1551pub fn main(args: [][]u8) -> %void {
1552 do_test() %% return;
1553}
1554fn 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}
1562error IToldYouItWouldFail;
1563fn 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(
1570import "std.zig";
1571pub fn main(args: [][]u8) -> %void {
1572 do_test() %% return;
1573}
1574fn 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}
1582fn its_gonna_pass() -> %void { }
1583 )SOURCE", "before\nafter\ndefer3\ndefer1\n");
1548}1584}
15491585
15501586