Commit 09d50e35a4

Andrew Kelley <superjoe30@gmail.com>
2016-12-19 22:45:15
IR: support error defers
1 parent 14422e0
Changed files (4)
src/codegen.cpp
@@ -2001,11 +2001,9 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,
 }
 
 static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {
-    TypeTableEntry *ptr_type = get_underlying_type(instruction->value->type_entry);
-    TypeTableEntry *err_union_type = get_underlying_type(ptr_type->data.pointer.child_type);
+    TypeTableEntry *err_union_type = get_underlying_type(instruction->value->type_entry);
     TypeTableEntry *child_type = get_underlying_type(err_union_type->data.error.child_type);
-    LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
-    LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type);
+    LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value);
 
     LLVMValueRef err_val;
     if (type_has_bits(child_type)) {
src/ir.cpp
@@ -1934,10 +1934,27 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 size_t defer_counts[3];
                 ir_count_defers(irb, scope, outer_scope, defer_counts);
                 if (defer_counts[ReturnKindError] > 0) {
-                    // TODO in this situation we need to make a conditional
-                    // branch on the return value. we potentially must make multiple conditional branches,
-                    // if unconditional defers are interleaved with error defers.
-                    zig_panic("TODO handle error defers");
+                    IrBasicBlock *err_block = ir_build_basic_block(irb, scope, "ErrRetErr");
+                    IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "ErrRetOk");
+
+                    IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
+
+                    IrInstruction *is_comptime;
+                    if (ir_should_inline(irb)) {
+                        is_comptime = ir_build_const_bool(irb, scope, node, true);
+                    } else {
+                        is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
+                    }
+
+                    ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime);
+
+                    ir_set_cursor_at_end(irb, err_block);
+                    ir_gen_defers_for_block(irb, scope, outer_scope, true, false);
+                    ir_build_return(irb, scope, node, return_value);
+
+                    ir_set_cursor_at_end(irb, ok_block);
+                    ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
+                    return ir_build_return(irb, scope, node, return_value);
                 } else if (defer_counts[ReturnKindMaybe] > 0) {
                     // TODO in this situation we need to make a conditional
                     // branch on the maybe value. we potentially must make multiple conditional branches,
@@ -1946,8 +1963,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 } else {
                     // generate unconditional defers
                     ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
+                    return ir_build_return(irb, scope, node, return_value);
                 }
-                return ir_build_return(irb, scope, node, return_value);
             }
         case ReturnKindError:
             {
@@ -1955,7 +1972,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
                 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
                 if (err_union_ptr == irb->codegen->invalid_instruction)
                     return irb->codegen->invalid_instruction;
-                IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_ptr);
+                IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr);
+                IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val);
 
                 IrBasicBlock *return_block = ir_build_basic_block(irb, scope, "ErrRetReturn");
                 IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "ErrRetContinue");
@@ -3932,7 +3950,8 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
     if (err_union_ptr == irb->codegen->invalid_instruction)
         return irb->codegen->invalid_instruction;
 
-    IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_ptr);
+    IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr);
+    IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val);
 
     IrInstruction *is_comptime;
     if (ir_should_inline(irb)) {
@@ -9443,26 +9462,20 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
     if (value->type_entry->id == TypeTableEntryIdInvalid)
         return ira->codegen->builtin_types.entry_invalid;
 
-    TypeTableEntry *ptr_type = value->type_entry;
-
-    // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
-    assert(ptr_type->id == TypeTableEntryIdPointer);
+    TypeTableEntry *non_canon_type = value->type_entry;
 
-    TypeTableEntry *non_canon_type = ptr_type->data.pointer.child_type;
     TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
     if (canon_type->id == TypeTableEntryIdInvalid) {
         return ira->codegen->builtin_types.entry_invalid;
     } else if (canon_type->id == TypeTableEntryIdErrorUnion) {
         if (instr_is_comptime(value)) {
-            ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
-            if (!ptr_val)
+            ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
+            if (!err_union_val)
                 return ira->codegen->builtin_types.entry_invalid;
-            ConstExprValue *err_union_val = ptr_val->data.x_ptr.base_ptr;
-            assert(ptr_val->data.x_ptr.index == SIZE_MAX);
 
             if (err_union_val->special != ConstValSpecialRuntime) {
-                bool depends_on_compile_var = ptr_val->depends_on_compile_var || err_union_val->depends_on_compile_var;
-                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+                ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
+                    err_union_val->depends_on_compile_var);
                 out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
                 return ira->codegen->builtin_types.entry_bool;
             }
@@ -9470,11 +9483,14 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
 
         ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
         return ira->codegen->builtin_types.entry_bool;
+    } else if (canon_type->id == TypeTableEntryIdPureError) {
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
+        out_val->data.x_bool = true;
+        return ira->codegen->builtin_types.entry_bool;
     } else {
-        ir_add_error(ira, value,
-            buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
-        // TODO if this is a typedecl, add error note showing the declaration of the type decl
-        return ira->codegen->builtin_types.entry_invalid;
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
+        out_val->data.x_bool = false;
+        return ira->codegen->builtin_types.entry_bool;
     }
 }
 
test/cases3/defer.zig
@@ -0,0 +1,35 @@
+var result: [3]u8 = undefined;
+var index: usize = undefined;
+
+error FalseNotAllowed;
+
+fn runSomeDefers(x: bool) -> %bool {
+    index = 0;
+    defer {result[index] = 'a'; index += 1;};
+    %defer {result[index] = 'b'; index += 1;};
+    defer {result[index] = 'c'; index += 1;};
+    return if (x) x else error.FalseNotAllowed;
+}
+
+fn mixingNormalAndErrorDefers() {
+    @setFnTest(this);
+
+    assert(%%runSomeDefers(true));
+    assert(result[0] == 'c');
+    assert(result[1] == 'a');
+
+    const ok = runSomeDefers(false) %% |err| {
+        assert(err == error.FalseNotAllowed);
+        true
+    };
+    assert(ok);
+    assert(result[0] == 'c');
+    assert(result[1] == 'b');
+    assert(result[2] == 'a');
+}
+
+// TODO const assert = @import("std").debug.assert;
+fn assert(ok: bool) {
+    if (!ok)
+        @unreachable();
+}
test/self_hosted3.zig
@@ -7,3 +7,4 @@ const test_atomics = @import("cases3/atomics.zig");
 const test_for = @import("cases3/for.zig");
 const test_math = @import("cases3/math.zig");
 const test_generics = @import("cases3/generics.zig");
+const test_defer = @import("cases3/defer.zig");