authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-13 19:36:56-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-13 19:36:56-05:00
log647d13168a810936a778cc02e4cc99bd460c1f55
treec8c1f90b7bfc2fcb2c9369ad8636b39831b456af
parent3f3630d7e349361116416dce36d2f69d7c3e318c

IR: implement maybe return expression


4 files changed, 78 insertions(+), 85 deletions(-)

src/ast_render.cpp+1-1
......@@ -759,7 +759,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
759759 fprintf(ar->f, ": ");
760760 render_node_ungrouped(ar, var_decl->type);
761761 }
762 fprintf(ar->f, " = ");
762 fprintf(ar->f, " ?= ");
763763 render_node_grouped(ar, var_decl->expr);
764764 fprintf(ar->f, ") ");
765765 render_node_grouped(ar, node->data.if_var_expr.then_block);
src/ir.cpp+63-83
......@@ -1731,7 +1731,13 @@ static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
17311731 }
17321732}
17331733
1734static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node) {
1734static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
1735 assert(basic_block);
1736
1737 irb->current_basic_block = basic_block;
1738}
1739
1740static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
17351741 assert(node->type == NodeTypeReturnExpr);
17361742
17371743 FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
......@@ -1740,6 +1746,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node)
17401746 return irb->codegen->invalid_instruction;
17411747 }
17421748
1749 Scope *outer_scope = fn_entry->child_scope;
1750 bool is_inline = ir_should_inline(irb);
1751
17431752 AstNode *expr_node = node->data.return_expr.expr;
17441753 switch (node->data.return_expr.kind) {
17451754 case ReturnKindUnconditional:
......@@ -1747,28 +1756,46 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node)
17471756 IrInstruction *return_value;
17481757 if (expr_node) {
17491758 return_value = ir_gen_node(irb, expr_node, scope);
1759 if (return_value == irb->codegen->invalid_instruction)
1760 return irb->codegen->invalid_instruction;
17501761 } else {
17511762 return_value = ir_build_const_void(irb, scope, node);
17521763 }
17531764
1754 Scope *outer_scope = fn_entry->child_scope;
1765 // TODO conditionally gen maybe defers and error defers
17551766 ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
17561767 return ir_build_return(irb, scope, node, return_value);
17571768 }
17581769 case ReturnKindError:
17591770 zig_panic("TODO gen IR for %%return");
17601771 case ReturnKindMaybe:
1761 zig_panic("TODO gen IR for ?return");
1772 {
1773 assert(expr_node);
1774 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
1775 if (maybe_val_ptr == irb->codegen->invalid_instruction)
1776 return irb->codegen->invalid_instruction;
1777 IrInstruction *is_nonnull_val = ir_build_test_null(irb, scope, node, maybe_val_ptr);
1778
1779 IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
1780 IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
1781 ir_build_cond_br(irb, scope, node, is_nonnull_val, continue_block, return_block, is_inline);
1782
1783 ir_set_cursor_at_end(irb, return_block);
1784 ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
1785 IrInstruction *null = ir_build_const_null(irb, scope, node);
1786 ir_build_return(irb, scope, node, null);
1787
1788 ir_set_cursor_at_end(irb, continue_block);
1789 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false);
1790 if (lval != LValPurposeNone)
1791 return unwrapped_ptr;
1792 else
1793 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
1794 }
17621795 }
17631796 zig_unreachable();
17641797}
17651798
1766static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
1767 assert(basic_block);
1768
1769 irb->current_basic_block = basic_block;
1770}
1771
17721799static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
17731800 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, bool is_inline)
17741801{
......@@ -3606,7 +3633,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
36063633 case NodeTypeArrayAccessExpr:
36073634 return ir_gen_array_access(irb, scope, node, lval);
36083635 case NodeTypeReturnExpr:
3609 return ir_lval_wrap(irb, scope, ir_gen_return(irb, scope, node), lval);
3636 return ir_gen_return(irb, scope, node, lval);
36103637 case NodeTypeFieldAccessExpr:
36113638 return ir_gen_field_access(irb, scope, node, lval);
36123639 case NodeTypeThisLiteral:
......@@ -3827,6 +3854,7 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
38273854 return ira->codegen->builtin_types.entry_invalid;
38283855 }
38293856 bool any_are_pure_error = (prev_inst->type_entry->id == TypeTableEntryIdPureError);
3857 bool any_are_null = (prev_inst->type_entry->id == TypeTableEntryIdNullLit);
38303858 for (size_t i = 1; i < instruction_count; i += 1) {
38313859 IrInstruction *cur_inst = instructions[i];
38323860 TypeTableEntry *cur_type = cur_inst->type_entry;
......@@ -3836,9 +3864,15 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
38363864 } else if (prev_type->id == TypeTableEntryIdPureError) {
38373865 prev_inst = cur_inst;
38383866 continue;
3867 } else if (prev_type->id == TypeTableEntryIdNullLit) {
3868 prev_inst = cur_inst;
3869 continue;
38393870 } else if (cur_type->id == TypeTableEntryIdPureError) {
38403871 any_are_pure_error = true;
38413872 continue;
3873 } else if (cur_type->id == TypeTableEntryIdNullLit) {
3874 any_are_null = true;
3875 continue;
38423876 } else if (types_match_const_cast_only(prev_type, cur_type)) {
38433877 continue;
38443878 } else if (types_match_const_cast_only(cur_type, prev_type)) {
......@@ -3889,9 +3923,13 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
38893923 return ira->codegen->builtin_types.entry_invalid;
38903924 }
38913925 } else {
3892 add_node_error(ira->codegen, source_node,
3926 ErrorMsg *msg = add_node_error(ira->codegen, source_node,
38933927 buf_sprintf("incompatible types: '%s' and '%s'",
38943928 buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
3929 add_error_note(ira->codegen, msg, prev_inst->source_node,
3930 buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
3931 add_error_note(ira->codegen, msg, cur_inst->source_node,
3932 buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
38953933
38963934 return ira->codegen->builtin_types.entry_invalid;
38973935 }
......@@ -3903,9 +3941,23 @@ static TypeTableEntry *ir_determine_peer_types(IrAnalyze *ira, AstNode *source_n
39033941 add_node_error(ira->codegen, source_node,
39043942 buf_sprintf("unable to make error union out of number literal"));
39053943 return ira->codegen->builtin_types.entry_invalid;
3944 } else if (prev_inst->type_entry->id == TypeTableEntryIdNullLit) {
3945 add_node_error(ira->codegen, source_node,
3946 buf_sprintf("unable to make error union out of null literal"));
3947 return ira->codegen->builtin_types.entry_invalid;
39063948 } else {
39073949 return get_error_type(ira->codegen, prev_inst->type_entry);
39083950 }
3951 } else if (any_are_null && prev_inst->type_entry->id != TypeTableEntryIdNullLit) {
3952 if (prev_inst->type_entry->id == TypeTableEntryIdNumLitInt ||
3953 prev_inst->type_entry->id == TypeTableEntryIdNumLitFloat)
3954 {
3955 add_node_error(ira->codegen, source_node,
3956 buf_sprintf("unable to make maybe out of number literal"));
3957 return ira->codegen->builtin_types.entry_invalid;
3958 } else {
3959 return get_maybe_type(ira->codegen, prev_inst->type_entry);
3960 }
39093961 } else {
39103962 return prev_inst->type_entry;
39113963 }
......@@ -9053,16 +9105,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
90539105//static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
90549106// TypeTableEntry *expected_type, AstNode *node)
90559107//{
9056// if (!node->data.return_expr.expr) {
9057// node->data.return_expr.expr = create_ast_void_node(g, import, node);
9058// normalize_parent_ptrs(node);
9059// }
9060//
90619108// TypeTableEntry *expected_return_type = get_return_type(context);
90629109//
90639110// switch (node->data.return_expr.kind) {
9064// case ReturnKindUnconditional:
9065// zig_panic("TODO moved to ir.cpp");
90669111// case ReturnKindError:
90679112// {
90689113// TypeTableEntry *expected_err_type;
......@@ -9093,34 +9138,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
90939138// return g->builtin_types.entry_invalid;
90949139// }
90959140// }
9096// case ReturnKindMaybe:
9097// {
9098// TypeTableEntry *expected_maybe_type;
9099// if (expected_type) {
9100// expected_maybe_type = get_maybe_type(g, expected_type);
9101// } else {
9102// expected_maybe_type = nullptr;
9103// }
9104// TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type,
9105// node->data.return_expr.expr);
9106// if (resolved_type->id == TypeTableEntryIdInvalid) {
9107// return resolved_type;
9108// } else if (resolved_type->id == TypeTableEntryIdMaybe) {
9109// if (expected_return_type->id != TypeTableEntryIdMaybe) {
9110// ErrorMsg *msg = add_node_error(g, node,
9111// buf_sprintf("?return statement in function with return type '%s'",
9112// buf_ptr(&expected_return_type->name)));
9113// AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
9114// add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
9115// }
9116//
9117// return resolved_type->data.maybe.child_type;
9118// } else {
9119// add_node_error(g, node->data.return_expr.expr,
9120// buf_sprintf("expected maybe type, found '%s'", buf_ptr(&resolved_type->name)));
9121// return g->builtin_types.entry_invalid;
9122// }
9123// }
91249141// }
91259142// zig_unreachable();
91269143//}
......@@ -9344,43 +9361,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
93449361// return nullptr;
93459362// }
93469363// }
9347// case ReturnKindMaybe:
9348// {
9349// assert(value_type->id == TypeTableEntryIdMaybe);
9350// TypeTableEntry *child_type = value_type->data.maybe.child_type;
9351//
9352// LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn");
9353// LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue");
9354//
9355// LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
9356// LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, "");
9357//
9358// LLVMValueRef zero = LLVMConstNull(LLVMInt1Type());
9359// LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, "");
9360// LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
9361//
9362// LLVMPositionBuilderAtEnd(g->builder, return_block);
9363// TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
9364// assert(return_type->id == TypeTableEntryIdMaybe);
9365// if (handle_is_ptr(return_type)) {
9366// assert(g->cur_ret_ptr);
9367//
9368// LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, "");
9369// LLVMBuildStore(g->builder, zero, maybe_bit_ptr);
9370// LLVMBuildRetVoid(g->builder);
9371// } else {
9372// LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref);
9373// gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull);
9374// }
9375//
9376// LLVMPositionBuilderAtEnd(g->builder, continue_block);
9377// if (type_has_bits(child_type)) {
9378// LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
9379// return get_handle_value(g, val_ptr, child_type);
9380// } else {
9381// return nullptr;
9382// }
9383// }
93849364// }
93859365// zig_unreachable();
93869366//}
src/ir_print.cpp+1-1
......@@ -585,7 +585,7 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) {
585585static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) {
586586 fprintf(irp->f, "*");
587587 ir_print_other_instruction(irp, instruction->value);
588 fprintf(irp->f, " == null");
588 fprintf(irp->f, " != null");
589589}
590590
591591static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {
test/self_hosted2.zig+13
......@@ -343,6 +343,18 @@ fn shlWithOverflow() {
343343 assert(result == 0b1011111111111100);
344344}
345345
346fn assignToIfVarPtr() {
347
348 var maybe_bool: ?bool = true;
349
350 if (const *b ?= maybe_bool) {
351 *b = false;
352 }
353
354 assert(??maybe_bool == false);
355}
356
357
346358fn assert(ok: bool) {
347359 if (!ok)
348360 @unreachable();
......@@ -377,6 +389,7 @@ fn runAllTests() {
377389 intTypeBuiltin();
378390 overflowIntrinsics();
379391 shlWithOverflow();
392 assignToIfVarPtr();
380393}
381394
382395export nakedcc fn _start() -> unreachable {