authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-19 17:25:09-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-19 17:25:09-05:00
log2419f0c91436106f85ea8b6ec183cdaea438a1d0
tree18c4d72142c50137fff9ace7f8a2139ed2ff6aec
parent09d50e35a4555d9af2c794390a4375c7fc0e48f7

IR: support maybe defers


5 files changed, 107 insertions(+), 73 deletions(-)

src/all_types.hpp+2-2
......@@ -1410,7 +1410,7 @@ enum IrInstructionId {
14101410 IrInstructionIdAsm,
14111411 IrInstructionIdCompileVar,
14121412 IrInstructionIdSizeOf,
1413 IrInstructionIdTestNull,
1413 IrInstructionIdTestNonNull,
14141414 IrInstructionIdUnwrapMaybe,
14151415 IrInstructionIdMaybeWrap,
14161416 IrInstructionIdEnumTag,
......@@ -1787,7 +1787,7 @@ struct IrInstructionSizeOf {
17871787
17881788// returns true if nonnull, returns false if null
17891789// this is so that `zeroes` sets maybe values to null
1790struct IrInstructionTestNull {
1790struct IrInstructionTestNonNull {
17911791 IrInstruction base;
17921792
17931793 IrInstruction *value;
src/codegen.cpp+13-18
......@@ -1554,26 +1554,20 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
15541554 return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
15551555}
15561556
1557// 0 - null, 1 - non null
1558static LLVMValueRef gen_null_bit(CodeGen *g, TypeTableEntry *ptr_type, LLVMValueRef maybe_ptr) {
1559 assert(ptr_type->id == TypeTableEntryIdPointer);
1560 TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;
1561 assert(maybe_type->id == TypeTableEntryIdMaybe);
1562 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
1563 LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type);
1564 bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn);
1557static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) {
1558 bool maybe_is_ptr = (maybe_type->id == TypeTableEntryIdPointer || maybe_type->id == TypeTableEntryIdFn);
15651559 if (maybe_is_ptr) {
1566 return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, LLVMConstNull(child_type->type_ref), "");
1560 return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), "");
15671561 } else {
1568 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, maybe_null_index, "");
1562 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
15691563 return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
15701564 }
15711565}
15721566
1573static LLVMValueRef ir_render_test_null(CodeGen *g, IrExecutable *executable, IrInstructionTestNull *instruction) {
1574 TypeTableEntry *ptr_type = instruction->value->type_entry;
1575 assert(ptr_type->id == TypeTableEntryIdPointer);
1576 return gen_null_bit(g, ptr_type, ir_llvm_value(g, instruction->value));
1567static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable,
1568 IrInstructionTestNonNull *instruction)
1569{
1570 return gen_non_null_bit(g, instruction->value->type_entry, ir_llvm_value(g, instruction->value));
15771571}
15781572
15791573static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
......@@ -1586,11 +1580,12 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
15861580 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
15871581 bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn);
15881582 LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
1583 LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type);
15891584 if (ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on) {
1590 LLVMValueRef nonnull_bit = gen_null_bit(g, ptr_type, maybe_ptr);
1585 LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
15911586 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk");
15921587 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeFail");
1593 LLVMBuildCondBr(g->builder, nonnull_bit, ok_block, fail_block);
1588 LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);
15941589
15951590 LLVMPositionBuilderAtEnd(g->builder, fail_block);
15961591 gen_debug_safety_crash(g);
......@@ -2227,8 +2222,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
22272222 return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
22282223 case IrInstructionIdAsm:
22292224 return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
2230 case IrInstructionIdTestNull:
2231 return ir_render_test_null(g, executable, (IrInstructionTestNull *)instruction);
2225 case IrInstructionIdTestNonNull:
2226 return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
22322227 case IrInstructionIdUnwrapMaybe:
22332228 return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction);
22342229 case IrInstructionIdClz:
src/ir.cpp+64-47
......@@ -275,8 +275,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
275275 return IrInstructionIdSizeOf;
276276}
277277
278static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNull *) {
279 return IrInstructionIdTestNull;
278static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {
279 return IrInstructionIdTestNonNull;
280280}
281281
282282static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) {
......@@ -1200,7 +1200,7 @@ static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *so
12001200}
12011201
12021202static IrInstruction *ir_build_test_null(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
1203 IrInstructionTestNull *instruction = ir_build_instruction<IrInstructionTestNull>(irb, scope, source_node);
1203 IrInstructionTestNonNull *instruction = ir_build_instruction<IrInstructionTestNonNull>(irb, scope, source_node);
12041204 instruction->value = value;
12051205
12061206 ir_ref_instruction(value);
......@@ -1956,10 +1956,27 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
19561956 ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
19571957 return ir_build_return(irb, scope, node, return_value);
19581958 } else if (defer_counts[ReturnKindMaybe] > 0) {
1959 // TODO in this situation we need to make a conditional
1960 // branch on the maybe value. we potentially must make multiple conditional branches,
1961 // if unconditional defers are interleaved with error defers.
1962 zig_panic("TODO handle maybe defers");
1959 IrBasicBlock *null_block = ir_build_basic_block(irb, scope, "MaybeRetNull");
1960 IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "MaybeRetOk");
1961
1962 IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, return_value);
1963
1964 IrInstruction *is_comptime;
1965 if (ir_should_inline(irb)) {
1966 is_comptime = ir_build_const_bool(irb, scope, node, true);
1967 } else {
1968 is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
1969 }
1970
1971 ir_build_cond_br(irb, scope, node, is_non_null, ok_block, null_block, is_comptime);
1972
1973 ir_set_cursor_at_end(irb, null_block);
1974 ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
1975 ir_build_return(irb, scope, node, return_value);
1976
1977 ir_set_cursor_at_end(irb, ok_block);
1978 ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
1979 return ir_build_return(irb, scope, node, return_value);
19631980 } else {
19641981 // generate unconditional defers
19651982 ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
......@@ -1998,12 +2015,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
19982015 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
19992016 if (maybe_val_ptr == irb->codegen->invalid_instruction)
20002017 return irb->codegen->invalid_instruction;
2001 IrInstruction *is_nonnull_val = ir_build_test_null(irb, scope, node, maybe_val_ptr);
2018 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
2019 IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, maybe_val);
20022020
20032021 IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "MaybeRetReturn");
20042022 IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "MaybeRetContinue");
20052023 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb));
2006 ir_build_cond_br(irb, scope, node, is_nonnull_val, continue_block, return_block, is_comptime);
2024 ir_build_cond_br(irb, scope, node, is_non_null, continue_block, return_block, is_comptime);
20072025
20082026 ir_set_cursor_at_end(irb, return_block);
20092027 ir_gen_defers_for_block(irb, scope, outer_scope, false, true);
......@@ -2247,7 +2265,8 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
22472265 if (maybe_ptr == irb->codegen->invalid_instruction)
22482266 return irb->codegen->invalid_instruction;
22492267
2250 IrInstruction *is_non_null = ir_build_test_null(irb, parent_scope, node, maybe_ptr);
2268 IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
2269 IrInstruction *is_non_null = ir_build_test_null(irb, parent_scope, node, maybe_val);
22512270
22522271 IrInstruction *is_comptime;
22532272 if (ir_should_inline(irb)) {
......@@ -3514,11 +3533,12 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *
35143533 AstNode *else_node = node->data.if_var_expr.else_node;
35153534 bool var_is_ptr = node->data.if_var_expr.var_is_ptr;
35163535
3517 IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
3518 if (expr_value == irb->codegen->invalid_instruction)
3519 return expr_value;
3536 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
3537 if (maybe_val_ptr == irb->codegen->invalid_instruction)
3538 return maybe_val_ptr;
35203539
3521 IrInstruction *is_nonnull_value = ir_build_test_null(irb, scope, node, expr_value);
3540 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
3541 IrInstruction *is_non_null = ir_build_test_null(irb, scope, node, maybe_val);
35223542
35233543 IrBasicBlock *then_block = ir_build_basic_block(irb, scope, "MaybeThen");
35243544 IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "MaybeElse");
......@@ -3528,9 +3548,9 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *
35283548 if (ir_should_inline(irb) || node->data.if_var_expr.is_inline) {
35293549 is_comptime = ir_build_const_bool(irb, scope, node, true);
35303550 } else {
3531 is_comptime = ir_build_test_comptime(irb, scope, node, is_nonnull_value);
3551 is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
35323552 }
3533 ir_build_cond_br(irb, scope, node, is_nonnull_value, then_block, else_block, is_comptime);
3553 ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime);
35343554
35353555 ir_set_cursor_at_end(irb, then_block);
35363556 IrInstruction *var_type = nullptr;
......@@ -3544,7 +3564,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode *
35443564 VariableTableEntry *var = ir_create_var(irb, node, scope,
35453565 var_decl->symbol, is_const, is_const, is_shadowable, is_comptime);
35463566
3547 IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, expr_value, false);
3567 IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false);
35483568 IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value);
35493569 ir_build_var_decl(irb, scope, node, var, var_type, var_value);
35503570 IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var->child_scope);
......@@ -4440,7 +4460,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
44404460 }
44414461
44424462 // implicitly take a const pointer to something
4443 {
4463 if (!type_requires_comptime(actual_type)) {
44444464 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
44454465 if (types_match_const_cast_only(expected_type, const_ptr_actual)) {
44464466 return ImplicitCastMatchResultYes;
......@@ -5230,7 +5250,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
52305250 }
52315251
52325252 // explicit cast from something to const pointer of it
5233 {
5253 if (!type_requires_comptime(actual_type)) {
52345254 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
52355255 if (types_match_const_cast_only(wanted_type, const_ptr_actual)) {
52365256 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
......@@ -7763,39 +7783,36 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
77637783 zig_unreachable();
77647784}
77657785
7766static TypeTableEntry *ir_analyze_instruction_test_null(IrAnalyze *ira,
7767 IrInstructionTestNull *test_null_instruction)
7768{
7769 IrInstruction *value = test_null_instruction->value->other;
7786static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) {
7787 IrInstruction *value = instruction->value->other;
77707788 if (value->type_entry->id == TypeTableEntryIdInvalid)
77717789 return ira->codegen->builtin_types.entry_invalid;
77727790
7773 // This will be a pointer type because test null IR instruction operates on a pointer to a thing.
7774 TypeTableEntry *ptr_type = value->type_entry;
7775 assert(ptr_type->id == TypeTableEntryIdPointer);
7776
7777 TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
7778 if (type_entry->id != TypeTableEntryIdMaybe) {
7779 add_node_error(ira->codegen, test_null_instruction->base.source_node,
7780 buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name)));
7781 return ira->codegen->builtin_types.entry_invalid;
7782 }
7791 TypeTableEntry *type_entry = value->type_entry;
77837792
7784 if (value->static_value.special != ConstValSpecialRuntime) {
7785 ConstExprValue *maybe_val = value->static_value.data.x_ptr.base_ptr;
7786 assert(value->static_value.data.x_ptr.index == SIZE_MAX);
7793 if (type_entry->id == TypeTableEntryIdMaybe) {
7794 if (instr_is_comptime(value)) {
7795 ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
7796 if (!maybe_val)
7797 return ira->codegen->builtin_types.entry_invalid;
77877798
7788 if (maybe_val->special != ConstValSpecialRuntime) {
7789 bool depends_on_compile_var = maybe_val->depends_on_compile_var;
7790 ConstExprValue *out_val = ir_build_const_from(ira, &test_null_instruction->base,
7791 depends_on_compile_var);
7792 out_val->data.x_bool = (maybe_val->data.x_maybe == nullptr);
7799 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
7800 maybe_val->depends_on_compile_var);
7801 out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr);
77937802 return ira->codegen->builtin_types.entry_bool;
77947803 }
7795 }
77967804
7797 ir_build_test_null_from(&ira->new_irb, &test_null_instruction->base, value);
7798 return ira->codegen->builtin_types.entry_bool;
7805 ir_build_test_null_from(&ira->new_irb, &instruction->base, value);
7806 return ira->codegen->builtin_types.entry_bool;
7807 } else if (type_entry->id == TypeTableEntryIdNullLit) {
7808 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
7809 out_val->data.x_bool = false;
7810 return ira->codegen->builtin_types.entry_bool;
7811 } else {
7812 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
7813 out_val->data.x_bool = true;
7814 return ira->codegen->builtin_types.entry_bool;
7815 }
77997816}
78007817
78017818static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
......@@ -9689,8 +9706,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
96899706 return ir_analyze_instruction_compile_var(ira, (IrInstructionCompileVar *)instruction);
96909707 case IrInstructionIdSizeOf:
96919708 return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
9692 case IrInstructionIdTestNull:
9693 return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction);
9709 case IrInstructionIdTestNonNull:
9710 return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction);
96949711 case IrInstructionIdUnwrapMaybe:
96959712 return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction);
96969713 case IrInstructionIdClz:
......@@ -9908,7 +9925,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
99089925 case IrInstructionIdSliceType:
99099926 case IrInstructionIdCompileVar:
99109927 case IrInstructionIdSizeOf:
9911 case IrInstructionIdTestNull:
9928 case IrInstructionIdTestNonNull:
99129929 case IrInstructionIdUnwrapMaybe:
99139930 case IrInstructionIdClz:
99149931 case IrInstructionIdCtz:
src/ir_print.cpp+3-3
......@@ -579,7 +579,7 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) {
579579 fprintf(irp->f, ")");
580580}
581581
582static void ir_print_test_null(IrPrint *irp, IrInstructionTestNull *instruction) {
582static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) {
583583 fprintf(irp->f, "*");
584584 ir_print_other_instruction(irp, instruction->value);
585585 fprintf(irp->f, " != null");
......@@ -1012,8 +1012,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
10121012 case IrInstructionIdSizeOf:
10131013 ir_print_size_of(irp, (IrInstructionSizeOf *)instruction);
10141014 break;
1015 case IrInstructionIdTestNull:
1016 ir_print_test_null(irp, (IrInstructionTestNull *)instruction);
1015 case IrInstructionIdTestNonNull:
1016 ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);
10171017 break;
10181018 case IrInstructionIdUnwrapMaybe:
10191019 ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction);
test/cases3/defer.zig+25-3
......@@ -3,7 +3,7 @@ var index: usize = undefined;
33
44error FalseNotAllowed;
55
6fn runSomeDefers(x: bool) -> %bool {
6fn runSomeErrorDefers(x: bool) -> %bool {
77 index = 0;
88 defer {result[index] = 'a'; index += 1;};
99 %defer {result[index] = 'b'; index += 1;};
......@@ -11,14 +11,22 @@ fn runSomeDefers(x: bool) -> %bool {
1111 return if (x) x else error.FalseNotAllowed;
1212}
1313
14fn runSomeMaybeDefers(x: bool) -> ?bool {
15 index = 0;
16 defer {result[index] = 'a'; index += 1;};
17 ?defer {result[index] = 'b'; index += 1;};
18 defer {result[index] = 'c'; index += 1;};
19 return if (x) x else null;
20}
21
1422fn mixingNormalAndErrorDefers() {
1523 @setFnTest(this);
1624
17 assert(%%runSomeDefers(true));
25 assert(%%runSomeErrorDefers(true));
1826 assert(result[0] == 'c');
1927 assert(result[1] == 'a');
2028
21 const ok = runSomeDefers(false) %% |err| {
29 const ok = runSomeErrorDefers(false) %% |err| {
2230 assert(err == error.FalseNotAllowed);
2331 true
2432 };
......@@ -28,6 +36,20 @@ fn mixingNormalAndErrorDefers() {
2836 assert(result[2] == 'a');
2937}
3038
39fn mixingNormalAndMaybeDefers() {
40 @setFnTest(this);
41
42 assert(??runSomeMaybeDefers(true));
43 assert(result[0] == 'c');
44 assert(result[1] == 'a');
45
46 const ok = runSomeMaybeDefers(false) ?? true;
47 assert(ok);
48 assert(result[0] == 'c');
49 assert(result[1] == 'b');
50 assert(result[2] == 'a');
51}
52
3153// TODO const assert = @import("std").debug.assert;
3254fn assert(ok: bool) {
3355 if (!ok)