diff --git a/releasenotes.md b/releasenotes.md index 6c51ff445..348da060f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -26,6 +26,7 @@ - Asserts are now correctly included and traced in when running tests. - Use atexit to fix finalizers on Windows #1361. - Fix bugs in "trap-on-wrap" #1434. +- Bug with casting anyfault to error. ### Stdlib changes - Additional init functions for hashmap. diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index b5029895b..39ae4a46c 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -1127,6 +1127,22 @@ static bool rule_explicit_ok(CastContext *cc, bool is_explicit, bool silent) } +static bool rule_anyfault_to_fault(CastContext *cc, bool is_explicit, bool is_silent) +{ + if (!is_explicit) + { + if (is_silent) return false; + return sema_cast_error(cc, rule_anyfault_to_fault(cc, true, true), false); + } + Expr *expr = cc->expr; + if (!expr_is_const_fault(expr)) return true; + if (type_flatten(cc->to) == expr->const_expr.enum_err_val->type) return true; + if (is_silent) return false; + RETURN_CAST_ERROR(expr, "This expression is known at compile time to be a fault of type %s, so it cannot be cast to %s.", + type_quoted_error_string(expr->const_expr.enum_err_val->type), + type_quoted_error_string(cc->to_type)); +} + static bool rule_int_to_float(CastContext *cc, bool is_explicit, bool is_silent) { if (is_explicit) return true; @@ -1810,14 +1826,9 @@ static void cast_untyped_list_to_other(SemaContext *context, Expr *expr, Type *t static void cast_anyfault_to_fault(SemaContext *context, Expr *expr, Type *type) { - if (insert_runtime_cast_unless_const(expr, CAST_EUER, type) && expr->const_expr.const_kind == CONST_ERR) return; + if (insert_runtime_cast_unless_const(expr, CAST_EUER, type) || !expr_is_const_fault(expr)) return; Decl *value = expr->const_expr.enum_err_val; - if (value->type != type) - { - expr->const_expr.const_kind = CONST_POINTER; - expr->const_expr.ptr = 0; - } - assert(value->type == type); + assert(value->type != type); expr->type = type; } @@ -2134,6 +2145,7 @@ static void cast_typeid_to_bool(SemaContext *context, Expr *expr, Type *to_type) #define RWIDE &rule_widen_narrow /* Widen / narrow conversion of int/float */ #define RINFL &rule_int_to_float /* Simple expressions, check sizes */ #define ROKOK &rule_all_ok /* Always works */ +#define RAFFA &rule_anyfault_to_fault /* Runtime check that it's valid, otherwise ok if explicit */ #define RINPT &rule_int_to_ptr /* Int -> ptr (explicit + size match) */ #define RPTIN &rule_ptr_to_int /* Ptr -> int (explicit + size match) */ #define RINBS &rule_int_to_bits /* Int -> bits (explicit + int + size match) */ @@ -2185,7 +2197,7 @@ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { {REXPL, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // ENUM {REXPL, _NO__, REXPL, RPTIN, _NO__, _NO__, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RPTPT, _NO__, _NO__, ROKOK, _NO__, _NO__, _NO__}, // FUNC {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NA__, _NO__, REXPL, REXPL, _NO__, _NO__}, // TYPEID - {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, REXPL, _NO__, _NO__, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__}, // ANYFAULT + {REXPL, _NO__, REXPL, RPTIN, _NO__, REXPL, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, _NO__, _NO__, RAFFA, _NO__, _NO__, _NO__, _NA__, REXPL, REXPL, _NO__, _NO__}, // ANYFAULT {REXPL, _NO__, REXPL, RPTIN, _NO__, ROKOK, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, ROKOK, _NO__, _NO__, _NA__, ROKOK, _NO__, _NO__}, // VOIDPTR {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, RAPSL, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, ROKOK, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE, _NO__}, // ARRPTR {_NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // INFERRED diff --git a/test/test_suite/errors/fault_conv.c3t b/test/test_suite/errors/fault_conv.c3t new file mode 100644 index 000000000..e0d4dfac0 --- /dev/null +++ b/test/test_suite/errors/fault_conv.c3t @@ -0,0 +1,123 @@ +// #target: macos-x64 +module test; +import std::io; + +fault HadError +{ + BAD_STUFF, + WORSE_STUFF, + THE_WORST_STUFF +} + +fn int exitcode(anyfault error) +{ + switch ((HadError)error) + { + case BAD_STUFF: return 64; + case WORSE_STUFF: return 65; + case THE_WORST_STUFF: return 66; + default: return 70; + } +} + +fn void! canFail() +{ + if (34 + 35 == 69) + { + return HadError.BAD_STUFF?; + } +} + +fn int main(String[] args) +{ + if (catch err = canFail()) + { + return exitcode(err); + } + + return 0; +} + +/* #expect: test.ll + + +define i32 @test.exitcode(i64 %0) #0 { +entry: + %switch = alloca i64, align 8 + store i64 %0, ptr %switch, align 8 + br label %switch.entry + +switch.entry: ; preds = %entry + %1 = load i64, ptr %switch, align 8 + %eq = icmp eq i64 ptrtoint (ptr @"test.HadError$BAD_STUFF" to i64), %1 + br i1 %eq, label %switch.case, label %next_if + +switch.case: ; preds = %switch.entry + ret i32 64 + +next_if: ; preds = %switch.entry + %eq1 = icmp eq i64 ptrtoint (ptr @"test.HadError$WORSE_STUFF" to i64), %1 + br i1 %eq1, label %switch.case2, label %next_if3 + +switch.case2: ; preds = %next_if + ret i32 65 + +next_if3: ; preds = %next_if + %eq4 = icmp eq i64 ptrtoint (ptr @"test.HadError$THE_WORST_STUFF" to i64), %1 + br i1 %eq4, label %switch.case5, label %next_if6 + +switch.case5: ; preds = %next_if3 + ret i32 66 + +next_if6: ; preds = %next_if3 + br label %switch.default + +switch.default: ; preds = %next_if6 + ret i32 70 +} + +define i64 @test.canFail() #0 { +entry: + br label %if.then + +if.then: ; preds = %entry + ret i64 ptrtoint (ptr @"test.HadError$BAD_STUFF" to i64) +} + +define i32 @test.main(ptr %0, i64 %1) #0 { +entry: + %args = alloca %"char[][]", align 8 + %err = alloca i64, align 8 + store ptr %0, ptr %args, align 8 + %ptradd = getelementptr inbounds i8, ptr %args, i64 8 + store i64 %1, ptr %ptradd, align 8 + br label %testblock + +testblock: ; preds = %entry + %2 = call i64 @test.canFail() + %not_err = icmp eq i64 %2, 0 + %3 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %3, label %after_check, label %assign_optional + +assign_optional: ; preds = %testblock + store i64 %2, ptr %err, align 8 + br label %end_block + +after_check: ; preds = %testblock + store i64 0, ptr %err, align 8 + br label %end_block + +end_block: ; preds = %after_check, %assign_optional + %4 = load i64, ptr %err, align 8 + %neq = icmp ne i64 %4, 0 + br i1 %neq, label %if.then, label %if.exit + +if.then: ; preds = %end_block + %5 = load i64, ptr %err, align 8 + %6 = call i32 @test.exitcode(i64 %5) + ret i32 %6 + +if.exit: ; preds = %end_block + ret i32 0 +} + diff --git a/test/test_suite/errors/invalid_cast_ct.c3 b/test/test_suite/errors/invalid_cast_ct.c3 new file mode 100644 index 000000000..55c14ebb2 --- /dev/null +++ b/test/test_suite/errors/invalid_cast_ct.c3 @@ -0,0 +1,16 @@ +module exitcodes; +import std::io; + +fault HadError +{ + BAD_STUFF, + WORSE_STUFF, + THE_WORST_STUFF +} + +fn int main(String[] args) +{ + HadError err = HadError.BAD_STUFF; + SearchResult res = (SearchResult)(anyfault)HadError.BAD_STUFF; // #error: This expression is known at compile time + return 0; +}