diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2e86db1ee..dcbb57f50 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -847,6 +847,7 @@ typedef enum ACCESS_TYPEOFANY, ACCESS_ENUMNAME, ACCESS_FAULTNAME, + ACCESS_FAULTORDINAL, } BuiltinAccessKind; typedef struct diff --git a/src/compiler/expr.c b/src/compiler/expr.c index b6376b8e4..90994b0ee 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -185,6 +185,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case ACCESS_FAULTNAME: case ACCESS_LEN: case ACCESS_PTR: + case ACCESS_FAULTORDINAL: break; case ACCESS_TYPEOFANY: if (eval_kind != CONSTANT_EVAL_NO_SIDE_EFFECTS) return false; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 3ea5ece1e..644d5a03a 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -6047,6 +6047,37 @@ static inline void llvm_emit_builtin_access(GenContext *c, BEValue *be_value, Ex assert(be_value->type->type_kind == TYPE_SUBARRAY); llvm_emit_subarray_pointer(c, be_value, be_value); return; + case ACCESS_FAULTORDINAL: + { + LLVMBasicBlockRef current_block = llvm_get_current_block_if_in_use(c); + if (!current_block) + { + llvm_value_set(be_value, llvm_get_zero(c, type_usz), type_usz); + return; + } + Type *inner_type = type_flatten(inner->type); + assert(inner_type->type_kind == TYPE_FAULTTYPE); + llvm_value_rvalue(c, be_value); + BEValue zero; + LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "faultordinal_exit"); + LLVMBasicBlockRef ok_block = llvm_basic_block_new(c, "faultordinal_found"); + BEValue check; + llvm_emit_int_comp_zero(c, &check, be_value, BINARYOP_EQ); + llvm_emit_cond_br(c, &check, exit_block, ok_block); + llvm_emit_block(c, ok_block); + LLVMValueRef fault_data = LLVMBuildIntToPtr(c->builder, be_value->value, + c->ptr_type, ""); + LLVMValueRef ptr = LLVMBuildStructGEP2(c->builder, c->fault_type, fault_data, 2, ""); + LLVMValueRef ordinal = llvm_load_abi_alignment(c, type_usz, ptr, ""); + llvm_emit_br(c, exit_block); + llvm_emit_block(c, exit_block); + LLVMValueRef phi = LLVMBuildPhi(c->builder, c->size_type, "faultname"); + LLVMValueRef values[] = { llvm_const_int(c, type_usz, 0), ordinal }; + LLVMBasicBlockRef blocks[] = { current_block, ok_block }; + LLVMAddIncoming(phi, values, blocks, 2); + llvm_value_set(be_value, phi, type_usz); + return; + } case ACCESS_FAULTNAME: { Type *inner_type = type_no_optional(inner->type)->canonical; diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 983f26551..c599c1883 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -23,8 +23,8 @@ static inline LLVMTypeRef create_introspection_type(GenContext *c) static inline LLVMTypeRef create_fault_type(GenContext *c) { LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".fault"); - LLVMTypeRef fault_type[] = { c->typeid_type, c->chars_type }; - LLVMStructSetBody(type, fault_type, 2, false); + LLVMTypeRef fault_type[] = { c->typeid_type, c->chars_type, c->size_type }; + LLVMStructSetBody(type, fault_type, 3, false); return type; } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 8ee4672b9..c86034a69 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -602,10 +602,11 @@ static LLVMValueRef llvm_get_introspection_for_fault(GenContext *c, Type *type) LLVMSetAlignment(global_name, LLVMPreferredAlignmentOfGlobal(c->target_data, global_name)); LLVMSetGlobalConstant(global_name, 1); - LLVMValueRef vals[2] = { LLVMBuildPtrToInt(c->builder, ref, c->typeid_type, ""), - llvm_emit_string_const(c, val->name, ".fault") }; + LLVMValueRef vals[3] = { LLVMBuildPtrToInt(c->builder, ref, c->typeid_type, ""), + llvm_emit_string_const(c, val->name, ".fault"), + llvm_const_int(c, type_usz, val->enum_constant.ordinal + 1 )}; - LLVMSetInitializer(global_name, llvm_get_struct_named(c->fault_type, vals, 2)); + LLVMSetInitializer(global_name, llvm_get_struct_named(c->fault_type, vals, 3)); llvm_set_linkonce(c, global_name); val->backend_ref = LLVMBuildPtrToInt(c->builder, global_name, c->typeid_type, ""); } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 2865a7fad..fb3b5fc86 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -787,6 +787,7 @@ RETRY: case ACCESS_TYPEOFANY: case ACCESS_ENUMNAME: case ACCESS_FAULTNAME: + case ACCESS_FAULTORDINAL: // For the rest, just check size. goto CHECK_SIZE; } @@ -1003,6 +1004,13 @@ static bool cast_from_pointer(SemaContext *context, Expr *expr, Type *from, Type return cast_with_optional(expr, to_type, add_optional); } return sema_error_cannot_convert(expr, to_type, true, silent); + case TYPE_FAULTTYPE: + case TYPE_ANYERR: + if (expr_is_const_pointer(expr) && !expr->const_expr.ptr) + { + expr_rewrite_to_const_zero(expr, to_type); + return true; + } case TYPE_OPTIONAL_ANY: case TYPE_OPTIONAL: UNREACHABLE diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 84693eac6..8a47861b4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3652,12 +3652,28 @@ CHECK_DEEPER: expr_replace(expr, current_parent); return true; } + if (flat_type->type_kind == TYPE_FAULTTYPE) + { + if (expr_is_const(current_parent)) + { + if (current_parent->const_expr.const_kind == CONST_POINTER) + { + assert(!current_parent->const_expr.ptr); + expr_rewrite_const_int(expr, type_usz, 0); + return true; + } + expr_rewrite_const_int(expr, type_usz, current_parent->const_expr.enum_err_val->enum_constant.ordinal + 1); + return true; + } + expr_rewrite_to_builtin_access(expr, current_parent, ACCESS_FAULTORDINAL, type_usz); + return true; + } } if (kw == kw_nameof) { if (flat_type->type_kind == TYPE_ENUM) { - if (current_parent->expr_kind == EXPR_CONST) + if (expr_is_const(current_parent)) { expr_rewrite_to_string(expr, current_parent->const_expr.enum_err_val->name); return true; diff --git a/src/version.h b/src/version.h index 177712965..ca719259d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.109" \ No newline at end of file +#define COMPILER_VERSION "0.4.110" \ No newline at end of file diff --git a/test/test_suite/errors/error_introspect.c3t b/test/test_suite/errors/error_introspect.c3t index 62633f792..8610381f3 100644 --- a/test/test_suite/errors/error_introspect.c3t +++ b/test/test_suite/errors/error_introspect.c3t @@ -18,9 +18,9 @@ fn void main() /* #expect: foo.ll -@"foo.Foo$BAR" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), %"char[]" { ptr @.fault, i64 3 } }, align 8 +@"foo.Foo$BAR" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), %"char[]" { ptr @.fault, i64 3 }, i64 1 }, align 8 @.fault = internal constant [4 x i8] c"BAR\00", align 1 -@"foo.Foo$BAZ" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), %"char[]" { ptr @.fault.1, i64 3 } }, align 8 +@"foo.Foo$BAZ" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), %"char[]" { ptr @.fault.1, i64 3 }, i64 2 }, align 8 @.fault.1 = internal constant [4 x i8] c"BAZ\00", align 1 @"$ct.foo.Foo" = linkonce constant %.introspect { i8 9, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [4 x i8] c"BAR\00", align 1 @@ -35,7 +35,6 @@ fn void main() @.str.5 = private unnamed_addr constant [17 x i8] c"Foo.elements: %s\00", align 1 @"$ct.long" = linkonce constant %.introspect { i8 2, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 - entry: %x = alloca %"String[]", align 8 %literal = alloca [2 x %"char[]"], align 16 diff --git a/test/test_suite/errors/optional_taddr_and_access.c3t b/test/test_suite/errors/optional_taddr_and_access.c3t index c7c7c66db..5661426ef 100644 --- a/test/test_suite/errors/optional_taddr_and_access.c3t +++ b/test/test_suite/errors/optional_taddr_and_access.c3t @@ -28,7 +28,7 @@ fn void main() %Foo = type { i32, i32 } @"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"test.MyErr$FOO" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.test.MyErr" to i64), %"char[]" { ptr @.fault, i64 3 } }, align 8 +@"test.MyErr$FOO" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.test.MyErr" to i64), %"char[]" { ptr @.fault, i64 3 }, i64 1 }, align 8 @"$ct.test.MyErr" = linkonce constant %.introspect { i8 9, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @.str.1 = private unnamed_addr constant [17 x i8] c"Not visible: %d\0A\00", align 1 diff --git a/test/unit/regression/faults.c3 b/test/unit/regression/faults.c3 new file mode 100644 index 000000000..1cc3bfc8b --- /dev/null +++ b/test/unit/regression/faults.c3 @@ -0,0 +1,21 @@ +module faults @test; + +fault Foo +{ + ABC, + CDE +} + +fn void ordinals() +{ + Foo z = null; + assert(z.ordinal == 0); + $assert(Foo.ABC.ordinal == 1); + $assert(Foo.CDE.ordinal == 2); + $assert(((Foo)null).ordinal == 0); + Foo x = Foo.CDE; + assert(x.ordinal == 2); + x = Foo.ABC; + assert(x.ordinal == 1); +} +