IR: support error defers
This commit is contained in:
parent
14422e0312
commit
09d50e35a4
@ -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) {
|
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(instruction->value->type_entry);
|
||||||
TypeTableEntry *err_union_type = get_underlying_type(ptr_type->data.pointer.child_type);
|
|
||||||
TypeTableEntry *child_type = get_underlying_type(err_union_type->data.error.child_type);
|
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 = ir_llvm_value(g, instruction->value);
|
||||||
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type);
|
|
||||||
|
|
||||||
LLVMValueRef err_val;
|
LLVMValueRef err_val;
|
||||||
if (type_has_bits(child_type)) {
|
if (type_has_bits(child_type)) {
|
||||||
|
60
src/ir.cpp
60
src/ir.cpp
@ -1934,10 +1934,27 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
|
|||||||
size_t defer_counts[3];
|
size_t defer_counts[3];
|
||||||
ir_count_defers(irb, scope, outer_scope, defer_counts);
|
ir_count_defers(irb, scope, outer_scope, defer_counts);
|
||||||
if (defer_counts[ReturnKindError] > 0) {
|
if (defer_counts[ReturnKindError] > 0) {
|
||||||
// TODO in this situation we need to make a conditional
|
IrBasicBlock *err_block = ir_build_basic_block(irb, scope, "ErrRetErr");
|
||||||
// branch on the return value. we potentially must make multiple conditional branches,
|
IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "ErrRetOk");
|
||||||
// if unconditional defers are interleaved with error defers.
|
|
||||||
zig_panic("TODO handle error defers");
|
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) {
|
} else if (defer_counts[ReturnKindMaybe] > 0) {
|
||||||
// TODO in this situation we need to make a conditional
|
// TODO in this situation we need to make a conditional
|
||||||
// branch on the maybe value. we potentially must make multiple conditional branches,
|
// 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 {
|
} else {
|
||||||
// generate unconditional defers
|
// generate unconditional defers
|
||||||
ir_gen_defers_for_block(irb, scope, outer_scope, false, false);
|
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:
|
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);
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPurposeAddressOf);
|
||||||
if (err_union_ptr == irb->codegen->invalid_instruction)
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
||||||
return 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 *return_block = ir_build_basic_block(irb, scope, "ErrRetReturn");
|
||||||
IrBasicBlock *continue_block = ir_build_basic_block(irb, scope, "ErrRetContinue");
|
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)
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
||||||
return 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;
|
IrInstruction *is_comptime;
|
||||||
if (ir_should_inline(irb)) {
|
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)
|
if (value->type_entry->id == TypeTableEntryIdInvalid)
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
TypeTableEntry *ptr_type = value->type_entry;
|
TypeTableEntry *non_canon_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 = ptr_type->data.pointer.child_type;
|
|
||||||
TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
|
TypeTableEntry *canon_type = get_underlying_type(non_canon_type);
|
||||||
if (canon_type->id == TypeTableEntryIdInvalid) {
|
if (canon_type->id == TypeTableEntryIdInvalid) {
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
} else if (canon_type->id == TypeTableEntryIdErrorUnion) {
|
} else if (canon_type->id == TypeTableEntryIdErrorUnion) {
|
||||||
if (instr_is_comptime(value)) {
|
if (instr_is_comptime(value)) {
|
||||||
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
|
ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
|
||||||
if (!ptr_val)
|
if (!err_union_val)
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
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) {
|
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,
|
||||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
|
err_union_val->depends_on_compile_var);
|
||||||
out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
|
out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
|
||||||
return ira->codegen->builtin_types.entry_bool;
|
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);
|
ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
|
||||||
return ira->codegen->builtin_types.entry_bool;
|
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 {
|
} else {
|
||||||
ir_add_error(ira, value,
|
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, false);
|
||||||
buf_sprintf("expected error union type, found '%s'", buf_ptr(&non_canon_type->name)));
|
out_val->data.x_bool = false;
|
||||||
// TODO if this is a typedecl, add error note showing the declaration of the type decl
|
return ira->codegen->builtin_types.entry_bool;
|
||||||
return ira->codegen->builtin_types.entry_invalid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
test/cases3/defer.zig
Normal file
35
test/cases3/defer.zig
Normal file
@ -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();
|
||||||
|
}
|
@ -7,3 +7,4 @@ const test_atomics = @import("cases3/atomics.zig");
|
|||||||
const test_for = @import("cases3/for.zig");
|
const test_for = @import("cases3/for.zig");
|
||||||
const test_math = @import("cases3/math.zig");
|
const test_math = @import("cases3/math.zig");
|
||||||
const test_generics = @import("cases3/generics.zig");
|
const test_generics = @import("cases3/generics.zig");
|
||||||
|
const test_defer = @import("cases3/defer.zig");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user