Remove implicit cast from enum to int. Allow enums to use distinct types as the backing type. enum += 1 returns enum type.

This commit is contained in:
Christoffer Lerno
2023-03-03 18:12:53 +01:00
parent 488472ecbb
commit f9923de7f9
10 changed files with 107 additions and 32 deletions

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.95"
#define COMPILER_VERSION "0.4.96"

View File

@@ -1,3 +1,4 @@
// #target: macos-x64
enum MyEnum : short
{
HELO,

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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()