Skip to content

Commit d77b368

Browse files
committed
copy elision: catch with identifier expression
This commit also makes `@sizeOf(?error) == @sizeof(error)`. Since 0 is not a valid error code, 0 can be used for the null value, in the same way that 0 is used for the null value of pointers. ```zig export fn entry() void { var x: error!Bar = fail(); var y = x catch bar2(); } ``` ```llvm define void @entry() #2 !dbg !42 { Entry: %error_return_trace_addresses = alloca [30 x i64], align 8 %error_return_trace = alloca %StackTrace, align 8 %x = alloca { i16, %Bar }, align 4 %y = alloca %Bar, align 4 %0 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %StackTrace, %StackTrace* %error_return_trace, i32 0, i32 1 %2 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 0 %3 = getelementptr inbounds [30 x i64], [30 x i64]* %error_return_trace_addresses, i64 0, i64 0 store i64* %3, i64** %2, align 8 %4 = getelementptr inbounds %"[]usize", %"[]usize"* %1, i32 0, i32 1 store i64 30, i64* %4, align 8 %5 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !59 %6 = call fastcc i16 @fail(%StackTrace* %error_return_trace), !dbg !60 store i16 %6, i16* %5, align 2, !dbg !60 call void @llvm.dbg.declare(metadata { i16, %Bar }* %x, metadata !46, metadata !DIExpression()), !dbg !59 %7 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 1, !dbg !61 %8 = bitcast %Bar* %7 to i8*, !dbg !61 %9 = bitcast %Bar* %y to i8*, !dbg !61 call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %9, i8* align 4 %8, i64 8, i1 false), !dbg !61 %10 = getelementptr inbounds { i16, %Bar }, { i16, %Bar }* %x, i32 0, i32 0, !dbg !61 %11 = load i16, i16* %10, align 2, !dbg !62 %12 = icmp ne i16 %11, 0, !dbg !62 br i1 %12, label %CatchError, label %CatchEnd, !dbg !62 CatchError: ; preds = %Entry call fastcc void @bar2(%Bar* sret %y), !dbg !63 br label %CatchEnd, !dbg !62 CatchEnd: ; preds = %CatchError, %Entry call void @llvm.dbg.declare(metadata %Bar* %y, metadata !57, metadata !DIExpression()), !dbg !64 ret void, !dbg !65 } ```
1 parent 952da3c commit d77b368

File tree

5 files changed

+121
-24
lines changed

5 files changed

+121
-24
lines changed

src/all_types.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,7 @@ enum IrInstructionId {
21862186
IrInstructionIdAllocaSrc,
21872187
IrInstructionIdAllocaGen,
21882188
IrInstructionIdAssertNonError,
2189+
IrInstructionIdErrorUnionFieldErrorSet,
21892190
};
21902191

21912192
struct IrInstruction {
@@ -2882,6 +2883,12 @@ struct IrInstructionUnwrapErrCode {
28822883
IrInstruction *value;
28832884
};
28842885

2886+
struct IrInstructionErrorUnionFieldErrorSet {
2887+
IrInstruction base;
2888+
2889+
IrInstruction *ptr;
2890+
};
2891+
28852892
struct IrInstructionUnwrapErrPayload {
28862893
IrInstruction base;
28872894

src/analyze.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
589589
if (child_type->zero_bits) {
590590
entry->type_ref = LLVMInt1Type();
591591
entry->di_type = g->builtin_types.entry_bool->di_type;
592-
} else if (type_is_codegen_pointer(child_type)) {
592+
} else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) {
593593
assert(child_type->di_type);
594594
// this is an optimization but also is necessary for calling C
595595
// functions where all pointers are maybe pointers
@@ -4490,7 +4490,8 @@ bool handle_is_ptr(ZigType *type_entry) {
44904490
return type_has_bits(type_entry->data.error_union.payload_type);
44914491
case ZigTypeIdOptional:
44924492
return type_has_bits(type_entry->data.maybe.child_type) &&
4493-
!type_is_codegen_pointer(type_entry->data.maybe.child_type);
4493+
!type_is_codegen_pointer(type_entry->data.maybe.child_type) &&
4494+
type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet;
44944495
case ZigTypeIdUnion:
44954496
assert(type_entry->data.unionation.zero_bits_known);
44964497
if (type_entry->data.unionation.gen_field_count == 0)

src/codegen.cpp

+27-5
Original file line numberDiff line numberDiff line change
@@ -3734,8 +3734,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR
37343734
if (child_type->zero_bits) {
37353735
return maybe_handle;
37363736
} else {
3737-
bool maybe_is_ptr = type_is_codegen_pointer(child_type);
3738-
if (maybe_is_ptr) {
3737+
bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
3738+
if (is_scalar) {
37393739
return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), "");
37403740
} else {
37413741
LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, "");
@@ -3774,8 +3774,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
37743774
if (child_type->zero_bits) {
37753775
return nullptr;
37763776
} else {
3777-
bool maybe_is_ptr = type_is_codegen_pointer(child_type);
3778-
if (maybe_is_ptr) {
3777+
bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet;
3778+
if (is_scalar) {
37793779
return maybe_ptr;
37803780
} else {
37813781
LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
@@ -4552,6 +4552,7 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
45524552
ZigType *ptr_type = instruction->value->value.type;
45534553
assert(ptr_type->id == ZigTypeIdPointer);
45544554
ZigType *err_union_type = ptr_type->data.pointer.child_type;
4555+
assert(err_union_type->id == ZigTypeIdErrorUnion);
45554556
ZigType *payload_type = err_union_type->data.error_union.payload_type;
45564557
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
45574558
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
@@ -4564,6 +4565,23 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
45644565
}
45654566
}
45664567

4568+
static LLVMValueRef ir_render_error_union_field_error_set(CodeGen *g, IrExecutable *executable,
4569+
IrInstructionErrorUnionFieldErrorSet *instruction)
4570+
{
4571+
ZigType *ptr_type = instruction->ptr->value.type;
4572+
assert(ptr_type->id == ZigTypeIdPointer);
4573+
ZigType *err_union_type = ptr_type->data.pointer.child_type;
4574+
assert(err_union_type->id == ZigTypeIdErrorUnion);
4575+
ZigType *payload_type = err_union_type->data.error_union.payload_type;
4576+
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->ptr);
4577+
4578+
if (type_has_bits(payload_type)) {
4579+
return LLVMBuildStructGEP(g->builder, err_union_ptr, err_union_err_index, "");
4580+
} else {
4581+
return err_union_ptr;
4582+
}
4583+
}
4584+
45674585
static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) {
45684586
ZigType *ptr_type = instruction->value->value.type;
45694587
assert(ptr_type->id == ZigTypeIdPointer);
@@ -4615,7 +4633,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I
46154633
}
46164634

46174635
LLVMValueRef payload_val = ir_llvm_value(g, instruction->value);
4618-
if (type_is_codegen_pointer(child_type)) {
4636+
if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) {
46194637
return payload_val;
46204638
}
46214639

@@ -5301,6 +5319,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
53015319
return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
53025320
case IrInstructionIdTestErr:
53035321
return ir_render_test_err(g, executable, (IrInstructionTestErr *)instruction);
5322+
case IrInstructionIdErrorUnionFieldErrorSet:
5323+
return ir_render_error_union_field_error_set(g, executable, (IrInstructionErrorUnionFieldErrorSet *)instruction);
53045324
case IrInstructionIdUnwrapErrCode:
53055325
return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction);
53065326
case IrInstructionIdUnwrapErrPayload:
@@ -5735,6 +5755,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
57355755
return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false);
57365756
} else if (type_is_codegen_pointer(child_type)) {
57375757
return gen_const_val_ptr(g, const_val, name);
5758+
} else if (child_type->id == ZigTypeIdErrorSet) {
5759+
zig_panic("TODO");
57385760
} else {
57395761
LLVMValueRef child_val;
57405762
LLVMValueRef maybe_val;

src/ir.cpp

+75-17
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonError *
903903
return IrInstructionIdAssertNonError;
904904
}
905905

906+
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldErrorSet *) {
907+
return IrInstructionIdErrorUnionFieldErrorSet;
908+
}
909+
906910
template<typename T>
907911
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
908912
T *special_instruction = allocate<T>(1);
@@ -2921,6 +2925,17 @@ static IrInstruction *ir_build_assert_non_error(IrBuilder *irb, Scope *scope, As
29212925
return &instruction->base;
29222926
}
29232927

2928+
static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope *scope, AstNode *source_node,
2929+
IrInstruction *ptr)
2930+
{
2931+
IrInstructionErrorUnionFieldErrorSet *instruction = ir_build_instruction<IrInstructionErrorUnionFieldErrorSet>(irb, scope, source_node);
2932+
instruction->ptr = ptr;
2933+
2934+
ir_ref_instruction(ptr, irb->current_basic_block);
2935+
2936+
return &instruction->base;
2937+
}
2938+
29242939
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
29252940
results[ReturnKindUnconditional] = 0;
29262941
results[ReturnKindError] = 0;
@@ -3153,7 +3168,7 @@ static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LV
31533168
// must return the error code
31543169
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, node, ptr, false);
31553170
ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc);
3156-
return ir_build_unwrap_err_code(irb, scope, node, ptr);
3171+
return ir_build_error_union_field_error_set(irb, scope, node, ptr);
31573172
}
31583173
case LValOptional:
31593174
zig_panic("TODO");
@@ -5109,11 +5124,12 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode
51095124
static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node,
51105125
AstNode *expr_node, LVal lval, IrInstruction *result_loc)
51115126
{
5112-
IrInstruction *err_val = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc);
5113-
if (err_val == irb->codegen->invalid_instruction)
5127+
IrInstruction *err_code_ptr = ir_gen_node(irb, expr_node, scope, LValErrorUnion, result_loc);
5128+
if (err_code_ptr == irb->codegen->invalid_instruction)
51145129
return irb->codegen->invalid_instruction;
51155130

5116-
ir_build_assert_non_error(irb, scope, source_node, err_val);
5131+
IrInstruction *err_code = ir_build_load_ptr(irb, scope, source_node, err_code_ptr, nullptr);
5132+
ir_build_assert_non_error(irb, scope, source_node, err_code);
51175133

51185134
return ir_gen_lval_ptr(irb, scope, source_node, lval, result_loc);
51195135
}
@@ -6453,11 +6469,12 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode
64536469
return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc);
64546470
}
64556471

6456-
IrInstruction *err_val = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc);
6457-
if (err_val == irb->codegen->invalid_instruction)
6472+
IrInstruction *err_code_ptr = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnion, result_loc);
6473+
if (err_code_ptr == irb->codegen->invalid_instruction)
64586474
return irb->codegen->invalid_instruction;
64596475

6460-
IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_val);
6476+
IrInstruction *opt_err_code = ir_build_load_ptr(irb, parent_scope, node, err_code_ptr, nullptr);
6477+
IrInstruction *is_err = ir_build_test_nonnull(irb, parent_scope, node, opt_err_code);
64616478

64626479
IrInstruction *is_comptime;
64636480
if (ir_should_inline(irb->exec, parent_scope)) {
@@ -6480,7 +6497,9 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode
64806497
ZigVar *var = ir_create_var(irb, node, parent_scope, var_name,
64816498
is_const, is_const, is_shadowable, is_comptime);
64826499
err_scope = var->child_scope;
6483-
ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val);
6500+
IrInstruction *unwrapped_err_code_ptr = ir_build_unwrap_maybe(irb, err_scope, var_node,
6501+
err_code_ptr, false);
6502+
ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, unwrapped_err_code_ptr);
64846503
} else {
64856504
err_scope = parent_scope;
64866505
}
@@ -19573,6 +19592,40 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct
1957319592
}
1957419593
}
1957519594

19595+
static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnalyze *ira,
19596+
IrInstructionErrorUnionFieldErrorSet *instruction)
19597+
{
19598+
IrInstruction *ptr = instruction->ptr->child;
19599+
if (type_is_invalid(ptr->value.type))
19600+
return ira->codegen->invalid_instruction;
19601+
ZigType *ptr_type = ptr->value.type;
19602+
19603+
assert(ptr_type->id == ZigTypeIdPointer);
19604+
19605+
ZigType *type_entry = ptr_type->data.pointer.child_type;
19606+
if (type_is_invalid(type_entry))
19607+
return ira->codegen->invalid_instruction;
19608+
19609+
if (type_entry->id != ZigTypeIdErrorUnion) {
19610+
ir_add_error(ira, ptr,
19611+
buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
19612+
return ira->codegen->invalid_instruction;
19613+
}
19614+
19615+
ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type);
19616+
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, opt_err_set,
19617+
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0);
19618+
19619+
if (instr_is_comptime(ptr)) {
19620+
zig_panic("TODO need to change ConstExprValue x_err_union to have a ConstExprValue for error set value");
19621+
}
19622+
19623+
IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb,
19624+
instruction->base.scope, instruction->base.source_node, ptr);
19625+
result->value.type = result_type;
19626+
return result;
19627+
}
19628+
1957619629
static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
1957719630
IrInstructionUnwrapErrCode *instruction)
1957819631
{
@@ -19681,19 +19734,21 @@ static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira,
1968119734
if (type_is_invalid(err_code->value.type))
1968219735
return ira->codegen->invalid_instruction;
1968319736

19684-
assert(err_code->value.type->id == ZigTypeIdErrorSet);
19737+
assert(err_code->value.type->id == ZigTypeIdOptional);
19738+
assert(err_code->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet);
1968519739

1968619740
if (instr_is_comptime(err_code)) {
19687-
ConstExprValue *err_val = ir_resolve_const(ira, err_code, UndefBad);
19688-
if (err_val == nullptr)
19741+
ConstExprValue *opt_val = ir_resolve_const(ira, err_code, UndefBad);
19742+
if (opt_val == nullptr)
1968919743
return ira->codegen->invalid_instruction;
19744+
ConstExprValue *err_val = opt_val->data.x_optional;
19745+
if (err_val == nullptr)
19746+
return ir_const_void(ira, &instruction->base);
1969019747
ErrorTableEntry *err = err_val->data.x_err_set;
19691-
if (err != nullptr) {
19692-
ir_add_error(ira, &instruction->base,
19693-
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
19694-
return ira->codegen->invalid_instruction;
19695-
}
19696-
return ir_const_void(ira, &instruction->base);
19748+
assert(err != nullptr);
19749+
ir_add_error(ira, &instruction->base,
19750+
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
19751+
return ira->codegen->invalid_instruction;
1969719752
}
1969819753

1969919754
ir_build_assert_non_error(&ira->new_irb, instruction->base.scope,
@@ -21593,6 +21648,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2159321648
return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction);
2159421649
case IrInstructionIdAllocaSrc:
2159521650
return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction);
21651+
case IrInstructionIdErrorUnionFieldErrorSet:
21652+
return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction);
2159621653
case IrInstructionIdResultBytesToSlice:
2159721654
zig_panic("TODO");
2159821655
case IrInstructionIdResultSliceToBytes:
@@ -21829,6 +21886,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2182921886
case IrInstructionIdResultBytesToSlice:
2183021887
case IrInstructionIdAllocaSrc:
2183121888
case IrInstructionIdAllocaGen:
21889+
case IrInstructionIdErrorUnionFieldErrorSet:
2183221890
return false;
2183321891

2183421892
case IrInstructionIdLoadPtr:

src/ir_print.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1393,6 +1393,12 @@ static void ir_print_assert_non_error(IrPrint *irp, IrInstructionAssertNonError
13931393
fprintf(irp->f, ")");
13941394
}
13951395

1396+
static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErrorUnionFieldErrorSet *instruction) {
1397+
fprintf(irp->f, "ErrorUnionFieldErrorSet(");
1398+
ir_print_other_instruction(irp, instruction->ptr);
1399+
fprintf(irp->f, ")");
1400+
}
1401+
13961402
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
13971403
ir_print_prefix(irp, instruction);
13981404
switch (instruction->id) {
@@ -1854,6 +1860,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
18541860
case IrInstructionIdAssertNonError:
18551861
ir_print_assert_non_error(irp, (IrInstructionAssertNonError *)instruction);
18561862
break;
1863+
case IrInstructionIdErrorUnionFieldErrorSet:
1864+
ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction);
1865+
break;
18571866
}
18581867
fprintf(irp->f, "\n");
18591868
}

0 commit comments

Comments
 (0)