diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 6e2424eb1..48f7c2cb4 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -1256,6 +1256,25 @@ static bool cast_from_vector(SemaContext *context, Expr *expr, Type *from, Type } +/** + * Cast an enum. + */ +static bool cast_from_enum(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) +{ + if (!is_explicit) + { + bool may_cast = type_is_integer(type_flatten(to)); + sema_error_cannot_convert(expr, to_type, may_cast, silent); + return false; + } + if (!type_is_integer(to)) + { + sema_error_cannot_convert(expr, to_type, false, silent); + return false; + } + return cast_with_optional(expr, to_type, add_optional); +} + /** * Cast an integer. Note here that "from" may be an enum. * 1. Floats -> always works @@ -1282,12 +1301,8 @@ RETRY: to = to->array.base->canonical; goto RETRY; case TYPE_ENUM: - // Enum <-> enum is not allowed - if (from->type_kind == TYPE_ENUM) break; - // Look at the underlying int, then use int conversion. - to = to->decl->enums.type_info->type->canonical; - if (is_explicit) to = type_flatten_distinct(to); - FALLTHROUGH; + if (is_explicit) goto CAST; + goto REQUIRE_CAST; case ALL_INTS: { // All explicit casts work. @@ -1565,6 +1580,7 @@ static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, boo case TYPE_ARRAY: return cast_from_array(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_ENUM: + return cast_from_enum(context, expr, from, to, to_type, add_optional, is_explicit, silent); case ALL_INTS: return cast_from_integer(context, expr, from, to, to_type, add_optional, is_explicit, silent); case ALL_FLOATS: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 72d3a4fcd..07919e3ee 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1014,10 +1014,9 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) if (!sema_resolve_type_info(context, decl->enums.type_info)) return false; Type *type = decl->enums.type_info->type; - Type *canonical = type->canonical; - + Type *flat_underlying_type = type_flatten_distinct(type); // Require an integer type - if (!type_is_integer(canonical)) + if (!type_is_integer(flat_underlying_type)) { SEMA_ERROR(decl->enums.type_info, "The enum type must be an integer type not '%s'.", type_to_error_string(type)); return false; @@ -1067,6 +1066,7 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) Int128 value = { 0, 0 }; Decl **enum_values = decl->enums.values; + for (unsigned i = 0; i < enums; i++) { Decl *enum_value = enum_values[i]; @@ -1082,12 +1082,13 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl) // Create a "fake" expression. // This will be evaluated later to catch the case - Int val = (Int){ value, canonical->type_kind }; - if (!int_fits(val, canonical->type_kind)) + + Int val = (Int){ value, flat_underlying_type->type_kind }; + if (!int_fits(val, flat_underlying_type->type_kind)) { SEMA_ERROR(enum_value, "The enum value would implicitly be %s which does not fit in %s.", - i128_to_string(value, 10, type_is_signed(canonical)), + i128_to_string(value, 10, type_is_signed(flat_underlying_type)), type_quoted_error_string(type)); return false; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 8b18b3374..36ae218a5 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -4338,6 +4338,14 @@ static bool sema_expr_analyse_add_sub_assign(SemaContext *context, Expr *expr, E return true; } + Type *lhs_flat = type_flatten_distinct(left_type_canonical); + if (lhs_flat->type_kind == TYPE_ENUM) + { + if (!cast_implicit(context, right, lhs_flat->decl->enums.type_info->type)) return false; + expr->type = type_add_optional(expr->type, optional); + return true; + } + // 8. Otherwise we cast rhs to lhs if (!cast_implicit(context, right, left->type)) return false; diff --git a/src/version.h b/src/version.h index 203045d47..cecbcd7d2 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.95" \ No newline at end of file +#define COMPILER_VERSION "0.4.96" \ No newline at end of file diff --git a/test/test_suite/enumerations/compile_time.c3t b/test/test_suite/enumerations/compile_time.c3t index bf18c25b4..ccea48d73 100644 --- a/test/test_suite/enumerations/compile_time.c3t +++ b/test/test_suite/enumerations/compile_time.c3t @@ -1,3 +1,4 @@ +// #target: macos-x64 enum MyEnum : short { HELO, diff --git a/test/test_suite/enumerations/enum_add_sub.c3t b/test/test_suite/enumerations/enum_add_sub.c3t new file mode 100644 index 000000000..d493ce176 --- /dev/null +++ b/test/test_suite/enumerations/enum_add_sub.c3t @@ -0,0 +1,49 @@ +// #target: macos-x64 +module test; +enum Foo +{ + ABC +} +typedef Abc = distinct Foo; +typedef BarInt = distinct int; +enum Bar : BarInt +{ + ABC +} +fn int main() +{ + Foo a; + a += 1; + $assert($typeof(a++).typeid == Foo.typeid); + $assert($typeof(a += 1).typeid == Foo.typeid); + Abc b; + b += 1; + $assert($typeof(b++).typeid == Abc.typeid); + $assert($typeof(b += 1).typeid == Abc.typeid); + $assert(!$checks(a += Foo.ABC)); + Bar c; + c += 1; + $assert($typeof(c++).typeid == Bar.typeid); + $assert($typeof(c += 1).typeid == Bar.typeid); + return 0; +} + + +/* #expect: test.ll + + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 128 + store i32 0, ptr %a, align 4 + %0 = load i32, ptr %a, align 4 + %add = add i32 %0, 1 + store i32 %add, ptr %a, align 4 + store i32 0, ptr %b, align 4 + %1 = load i32, ptr %b, align 4 + %add1 = add i32 %1, 1 + store i32 %add1, ptr %b, align 4 + store i32 0, ptr %c, align 128 + %2 = load i32, ptr %c, align 128 + %add2 = add i32 %2, 1 + store i32 %add2, ptr %c, align 128 + ret i32 0 diff --git a/test/test_suite/enumerations/enum_cast.c3t b/test/test_suite/enumerations/enum_cast.c3t index cdcb9e417..802292ffa 100644 --- a/test/test_suite/enumerations/enum_cast.c3t +++ b/test/test_suite/enumerations/enum_cast.c3t @@ -16,20 +16,20 @@ fn void test2() fn void test() { - char b = MyEnum.FOO; - int z = (int)(MyEnum.BAR); - var $foo = (int)(MyEnum.FOO); + char b = (char)MyEnum.FOO; + int z = (int)MyEnum.BAR; + var $foo = (int)MyEnum.FOO; var $baz = MyEnum.BAR; MyEnum x = MyEnum.BAR; - char b2 = x; - int z2 = (int)(x); - float d = (float)(MyEnum.FOO); - bool hello = (bool)(MyEnum.FOO); - var $d = (float)(MyEnum.FOO); - var $hello = (bool)(MyEnum.FOO); + char b2 = (char)x; + int z2 = (int)x; + float d = (float)(char)MyEnum.FOO; + bool hello = (bool)(char)MyEnum.FOO; + var $d = (float)(char)MyEnum.FOO; + var $hello = (bool)(char)MyEnum.FOO; MyEnum! xf = MyEnum.BAR; - float! e = (float)(x); - e = (float)(xf); + float! e = (float)(char)x; + e = (float)(char)xf; } diff --git a/test/test_suite/expressions/casts/cast_enum_const_to_distinct.c3 b/test/test_suite/expressions/casts/cast_enum_const_to_distinct.c3 index e51f5df7c..195bb263c 100644 --- a/test/test_suite/expressions/casts/cast_enum_const_to_distinct.c3 +++ b/test/test_suite/expressions/casts/cast_enum_const_to_distinct.c3 @@ -5,5 +5,5 @@ enum Foo typedef Abc = distinct int; fn void main() { - Abc d = Foo.ABC; // #error: cannot implicitly be converted + Abc d = Foo.ABC; // #error: Implicitly casting } diff --git a/test/test_suite/expressions/casts/cast_enum_to_various.c3 b/test/test_suite/expressions/casts/cast_enum_to_various.c3 index e589f4f62..8a3380169 100644 --- a/test/test_suite/expressions/casts/cast_enum_to_various.c3 +++ b/test/test_suite/expressions/casts/cast_enum_to_various.c3 @@ -17,11 +17,11 @@ typedef Func = fn void(Enum); fn void test1(Enum e) { - bool a = (bool)(e); - char b = (char)(e); - uint c = (uint)(e); - float d = (float)(e); - uint* f = (uint*)(e); // #error: 'uint*' + bool a = (bool)e; // #error: bool + char b = (char)e; + uint c = (uint)e; + float d = (float)e; // #error: float + uint* f = (uint*)e; // #error: 'uint*' } fn void test2(Enum e) diff --git a/test/test_suite/symbols/various.c3 b/test/test_suite/symbols/various.c3 index fc4937313..ec81a4e8a 100644 --- a/test/test_suite/symbols/various.c3 +++ b/test/test_suite/symbols/various.c3 @@ -81,8 +81,8 @@ enum Enum : int fn void test11() { - int a = Enum.A; - ichar b = Enum.B; + int a = Enum.A; // #error: Implicitly casting + ichar b = Enum.B; // #error: Implicitly casting } fn void test12()