mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
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:
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.95"
|
||||
#define COMPILER_VERSION "0.4.96"
|
||||
@@ -1,3 +1,4 @@
|
||||
// #target: macos-x64
|
||||
enum MyEnum : short
|
||||
{
|
||||
HELO,
|
||||
|
||||
49
test/test_suite/enumerations/enum_add_sub.c3t
Normal file
49
test/test_suite/enumerations/enum_add_sub.c3t
Normal 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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user