mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Merge pull request #21 from c3lang/develop
Enum, unions, struct and traps on overflow added.
This commit is contained in:
72
missing.txt
Normal file
72
missing.txt
Normal file
@@ -0,0 +1,72 @@
|
||||
Things missing:
|
||||
|
||||
* Attributes
|
||||
- All types: @noreflect, @deprecated
|
||||
- Struct: @packed, @aligned, @opaque
|
||||
- Enums: @distinct, @noreflect
|
||||
- Unions: @packed, @aligned, @opaque
|
||||
- Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention()
|
||||
- Calls: @noinline, @inline
|
||||
- Variables, parameters: @unused
|
||||
- Constants, globals: @unused, @used, @section
|
||||
- Labels: @unused
|
||||
|
||||
* Designated initializer
|
||||
- Array initializer
|
||||
- Array range initializer { [1..2] = 2 }
|
||||
|
||||
* Initializers
|
||||
- Array initializers
|
||||
- Union initializers
|
||||
- Initializers with anonymous members
|
||||
|
||||
* Asserts
|
||||
- assert, $assert
|
||||
- @unreachable
|
||||
|
||||
* Types
|
||||
- Vararrays
|
||||
- Strings
|
||||
- Array
|
||||
- Slice
|
||||
- Values: size, alignment, name, qualifiedName
|
||||
- Functions: offsetof
|
||||
- Distinct types
|
||||
- Simd types?
|
||||
- Complex types?
|
||||
- Subtype casts
|
||||
- Bitstruct
|
||||
- Enumset
|
||||
- Typeid
|
||||
|
||||
* Struct / union
|
||||
- Cast to union?
|
||||
- Structural typed anonymous structs and casts to them.
|
||||
|
||||
* Expressions
|
||||
- Disallow x >= 0 and x < 0 on unsigned types unless in a macro.
|
||||
- Range check arrays on debug
|
||||
- Allow negating int if assigned to a larger type. E.g short x = 1; int y = -x;
|
||||
|
||||
* Switch
|
||||
- String switch
|
||||
- Range case
|
||||
|
||||
* Functions
|
||||
- Varargs
|
||||
- C ABI
|
||||
- Safe varargs
|
||||
|
||||
* Pre-post conditions
|
||||
- Breakdown here
|
||||
|
||||
* Error handling
|
||||
- Error unions
|
||||
- Catch/try
|
||||
- Function return channel
|
||||
|
||||
* Enum
|
||||
- Values: min, max, array
|
||||
- Functions: fomOrdinal, ordinal, fromName, name, fromFullName, fullName, fromQualifiedName, qualifiedName, <value>(), fromValue()
|
||||
|
||||
|
||||
@@ -2,16 +2,6 @@ module bar;
|
||||
|
||||
typedef int as Bob;
|
||||
|
||||
/* hello *//* there */
|
||||
/+ why /+ you /* lucky +/ +/
|
||||
// Whut
|
||||
// Here
|
||||
//
|
||||
//---
|
||||
/*
|
||||
Hello
|
||||
*/
|
||||
|
||||
struct Test
|
||||
{
|
||||
int a;
|
||||
@@ -38,30 +28,6 @@ struct Teob
|
||||
int oekfeo;
|
||||
}
|
||||
|
||||
enum EnumTest : long
|
||||
{
|
||||
VALUE1 = 4,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestDefault
|
||||
{
|
||||
VALUE,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestNoOverflowAfterLong : long
|
||||
{
|
||||
VALUE = 0x7FFF_FFFF_FFFF_FFFE,
|
||||
VALUE_NO_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestSmall : ushort
|
||||
{
|
||||
VALUE = 0xFF,
|
||||
VALUE2 = 0xFFFF
|
||||
}
|
||||
|
||||
enum EnumWithData : ushort (int a, char[] x, long b = 4)
|
||||
{
|
||||
// Currently the args are ignored TODO!
|
||||
@@ -69,37 +35,15 @@ enum EnumWithData : ushort (int a, char[] x, long b = 4)
|
||||
TEST2(12, "world")
|
||||
}
|
||||
|
||||
|
||||
/* ERRORS
|
||||
|
||||
enum EnumWithErrorData : int (int
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
enum EnumWithErrorWithMissingName : int (int)
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
/*
|
||||
enum EnumTestNoOverflowAfterULong : ulong
|
||||
{
|
||||
VALUE = 0xFFFF_FFFF_FFFF_FFFE,
|
||||
VALUE_NO_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestOverflow
|
||||
{
|
||||
VALUE = 0x80000000,
|
||||
}
|
||||
|
||||
|
||||
enum EnumTestOverflowAfter
|
||||
{
|
||||
VALUE = 0x80000000 - 1,
|
||||
VALUE_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestOverflowAfterLong : long
|
||||
{
|
||||
VALUE = 0x7FFF_FFFF_FFFF_FFFF,
|
||||
@@ -112,24 +56,153 @@ enum EnumTestOverflowAfterULong : ulong
|
||||
VALUE_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestErrorType : float
|
||||
enum EnumTestOverflowAfter
|
||||
{
|
||||
VALUE_BOOM
|
||||
}
|
||||
VALUE = 0x80000000 - 1,
|
||||
VALUE_EXCEED
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
error Error
|
||||
{
|
||||
BLURB,
|
||||
NO_SUCH_FILE,
|
||||
|
||||
}
|
||||
|
||||
error OtherError
|
||||
{
|
||||
FOO_BAR
|
||||
}
|
||||
|
||||
enum Inf
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C = 10000
|
||||
}
|
||||
|
||||
enum Inf2 : byte
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C = 129,
|
||||
}
|
||||
|
||||
typedef Inf as BooInf;
|
||||
|
||||
struct TestStruct
|
||||
{
|
||||
int a;
|
||||
}
|
||||
|
||||
struct TestStruct2
|
||||
{
|
||||
TestStruct a;
|
||||
char xx;
|
||||
TestStruct b;
|
||||
int c;
|
||||
}
|
||||
|
||||
union TestUnion
|
||||
{
|
||||
int a;
|
||||
double f;
|
||||
TestStruct2 e;
|
||||
}
|
||||
union SimpleUnion
|
||||
{
|
||||
int a;
|
||||
double f;
|
||||
}
|
||||
|
||||
struct AnonStruct
|
||||
{
|
||||
int a;
|
||||
struct sune
|
||||
{
|
||||
int b;
|
||||
int c;
|
||||
}
|
||||
struct
|
||||
{
|
||||
int b1;
|
||||
int c1;
|
||||
}
|
||||
union
|
||||
{
|
||||
int b2;
|
||||
int c2;
|
||||
}
|
||||
int x;
|
||||
}
|
||||
|
||||
|
||||
func void testAnonStruct()
|
||||
{
|
||||
|
||||
AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 };
|
||||
AnonStruct foo;
|
||||
|
||||
s.sune.b = 1;
|
||||
s.b1 = 2;
|
||||
s.b2 = 3;
|
||||
s.c2 = 4;
|
||||
|
||||
}
|
||||
|
||||
func void testUnion()
|
||||
{
|
||||
SimpleUnion s;
|
||||
s.a = 1;
|
||||
s.f = 1.0;
|
||||
s = { 1 };
|
||||
int x = 2;
|
||||
s = { (x = 2) };
|
||||
//s = { f = 1.0 };
|
||||
TestUnion tu = { e = TestStruct2 { c = 1 } };
|
||||
tu.e = TestStruct2 { c = 1 };
|
||||
}
|
||||
|
||||
func TestStruct2 structTest(int i)
|
||||
{
|
||||
TestStruct foo = { i };
|
||||
TestStruct foo2 = { a = i };
|
||||
TestStruct foo3 = TestStruct { i };
|
||||
TestStruct2 bar = { c = 2 };
|
||||
int x = 3 * i;
|
||||
TestStruct2 bar2 = { b.a = x, a.a = x + 1 };
|
||||
return bar2;
|
||||
}
|
||||
|
||||
func void enumInferenceTest()
|
||||
{
|
||||
OtherError e = OtherError.FOO_BAR;
|
||||
Inf x = Inf.A;
|
||||
x = BooInf.B;
|
||||
x = A;
|
||||
int x1 = 0;
|
||||
bool y = x1 == x1;
|
||||
Inf2 z = C;
|
||||
if (z == Inf2.A) return;
|
||||
if (z == 1) return;
|
||||
z = 2;
|
||||
switch (z)
|
||||
{
|
||||
case Inf2.A:
|
||||
x1++;
|
||||
return;
|
||||
case B:
|
||||
return;
|
||||
case 111:
|
||||
x1 += 1;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
func int jumptest()
|
||||
{
|
||||
if (1) goto LABELX;
|
||||
@@ -144,6 +217,7 @@ func int borok() throws
|
||||
func void testNoReturn()
|
||||
{
|
||||
int i = 0;
|
||||
i = -i;
|
||||
}
|
||||
|
||||
func int testReturn()
|
||||
@@ -440,7 +514,10 @@ func int testPointers(int x)
|
||||
}
|
||||
func int main(int x)
|
||||
{
|
||||
printf("Helo!\n");
|
||||
int efd = 9;
|
||||
uint fefoek = 1;
|
||||
long fefoek = -fefoek;
|
||||
int okfe = 1;
|
||||
return 1;
|
||||
switch (int bobe = okfe > 0 ? 1 : 0)
|
||||
|
||||
13
resources/tests/comments_ok.c3
Normal file
13
resources/tests/comments_ok.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
module comments;
|
||||
/* Span *//* style */
|
||||
|
||||
/+ Nested /+ Errors /* Inside +/ +/
|
||||
// Single line
|
||||
/*
|
||||
Multiline span style
|
||||
*/
|
||||
|
||||
func void test()
|
||||
{
|
||||
return;
|
||||
}
|
||||
24
resources/tests/enum_errors.c3
Normal file
24
resources/tests/enum_errors.c3
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
enum EnumWithErrorWithMissingName : int (int)
|
||||
// @error The function parameter must be named
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
enum EnumWithErrorData : int (int
|
||||
// @error Unexpected end of parameter list
|
||||
{
|
||||
TEST
|
||||
}
|
||||
|
||||
enum EnumTestOverflow
|
||||
{
|
||||
VALUE = 0x80000000,
|
||||
// @error does not fit into 'int'
|
||||
}
|
||||
|
||||
enum EnumTestErrorType : float
|
||||
// @error The enum type must be an integer type not 'float'
|
||||
{
|
||||
VALUE_BOOM
|
||||
}
|
||||
31
resources/tests/enum_ok.c3
Normal file
31
resources/tests/enum_ok.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
enum EnumTest : long
|
||||
{
|
||||
VALUE1 = 4,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
typedef long as Frob;
|
||||
|
||||
enum EnumTestAlias : Frob
|
||||
{
|
||||
VALUE1 = 4,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestDefault
|
||||
{
|
||||
VALUE,
|
||||
VALUE2
|
||||
}
|
||||
|
||||
enum EnumTestNoOverflowAfterLong : long
|
||||
{
|
||||
VALUE = 0x7FFF_FFFF_FFFF_FFFE,
|
||||
VALUE_NO_EXCEED
|
||||
}
|
||||
|
||||
enum EnumTestSmall : ushort
|
||||
{
|
||||
VALUE = 0xFF,
|
||||
VALUE2 = 0xFFFF
|
||||
}
|
||||
12
resources/tests/error_decl_ok.c3
Normal file
12
resources/tests/error_decl_ok.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
module errors;
|
||||
|
||||
error TheError
|
||||
{
|
||||
FOO_MISSING,
|
||||
NO_SUCH_FILE,
|
||||
}
|
||||
|
||||
error OtherError
|
||||
{
|
||||
BAR_OVERFLOWED
|
||||
}
|
||||
0
resources/tests/structs_ok.c3
Normal file
0
resources/tests/structs_ok.c3
Normal file
5
resources/tests/typedef_errors.c3
Normal file
5
resources/tests/typedef_errors.c3
Normal file
@@ -0,0 +1,5 @@
|
||||
module typedefs;
|
||||
|
||||
typedef Loop as Loop2;
|
||||
typedef Loop2 as Loop3;
|
||||
typedef Loop3 as Loop;
|
||||
14
resources/tests/typedefs_ok.c3
Normal file
14
resources/tests/typedefs_ok.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module typedefs;
|
||||
|
||||
// Standard case
|
||||
typedef int as Foo;
|
||||
|
||||
// Nested resolution
|
||||
typedef AType as BType;
|
||||
typedef int as AType;
|
||||
|
||||
enum Bar : BType
|
||||
{
|
||||
A,
|
||||
B
|
||||
}
|
||||
@@ -316,6 +316,7 @@ void parse_arguments(int argc, const char *argv[])
|
||||
build_options.optimization_level = OPTIMIZATION_NOT_SET;
|
||||
build_options.size_optimization_level = SIZE_OPTIMIZATION_NOT_SET;
|
||||
build_options.debug_info = false;
|
||||
build_options.debug_mode = false;
|
||||
build_options.command = COMMAND_MISSING;
|
||||
build_options.symtab_size = DEFAULT_SYMTAB_SIZE;
|
||||
build_options.files = NULL;
|
||||
|
||||
@@ -103,6 +103,7 @@ typedef struct
|
||||
OptimizationLevel optimization_level;
|
||||
SizeOptimizationLevel size_optimization_level;
|
||||
bool debug_info;
|
||||
bool debug_mode;
|
||||
bool emit_llvm;
|
||||
bool emit_bitcode;
|
||||
} BuildOptions;
|
||||
|
||||
@@ -8,7 +8,6 @@ static void fprint_asts_recursive(FILE *file, Ast **asts, int indent);
|
||||
|
||||
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility)
|
||||
{
|
||||
assert(name.string);
|
||||
Decl *decl = CALLOCS(Decl);
|
||||
decl->decl_kind = decl_kind;
|
||||
decl->name_span = name.span;
|
||||
@@ -551,12 +550,6 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
fprint_type_info_recursive(file, expr->type_access.type, indent + 1);
|
||||
break;
|
||||
case EXPR_STRUCT_VALUE:
|
||||
fprintf_indented(file, indent, "(structvalue\n");
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
fprint_type_info_recursive(file, expr->struct_value_expr.type, indent + 1);
|
||||
fprint_expr_recursive(file, expr->struct_value_expr.init_expr, indent + 1);
|
||||
break;
|
||||
case EXPR_ACCESS:
|
||||
fprintf_indented(file, indent, "(access .%s\n", expr->access_expr.sub_element.string);
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
@@ -567,6 +560,10 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
fprint_type_info_recursive(file, expr->type_expr.type, indent + 1);
|
||||
break;
|
||||
case EXPR_GROUP:
|
||||
fprintf_indented(file, indent, "(group\n");
|
||||
fprint_expr_recursive(file, expr->group_expr, indent + 1);
|
||||
break;
|
||||
case EXPR_CALL:
|
||||
fprintf_indented(file, indent, "(call\n");
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
@@ -595,12 +592,27 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
fprint_expr_recursive(file, expr->ternary_expr.else_expr, indent + 1);
|
||||
break;
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
fprintf_indented(file, indent, "(initializerlist\n");
|
||||
fprintf_indented(file, indent, "(initializerlist ");
|
||||
switch (expr->expr_initializer.init_type)
|
||||
{
|
||||
case INITIALIZER_UNKNOWN:
|
||||
fprintf(file, "not-analyzed\n");
|
||||
break;
|
||||
case INITIALIZER_ZERO:
|
||||
fprintf(file, "zero\n");
|
||||
break;
|
||||
case INITIALIZER_NORMAL:
|
||||
fprintf(file, "normal\n");
|
||||
break;
|
||||
case INITIALIZER_DESIGNATED:
|
||||
fprintf(file, "designated\n");
|
||||
break;
|
||||
}
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
{
|
||||
VECEACH(expr->initializer_expr, i)
|
||||
VECEACH(expr->expr_initializer.initializer_expr, i)
|
||||
{
|
||||
fprint_expr_recursive(file, expr->initializer_expr[i], indent + 1);
|
||||
fprint_expr_recursive(file, expr->expr_initializer.initializer_expr[i], indent + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -302,13 +302,24 @@ bool ixxxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from compile time int to any signed or unsigned int
|
||||
* @return true unless the conversion was lossy.
|
||||
*/
|
||||
bool ixxen(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
assert(canonical->type_kind == TYPE_ENUM);
|
||||
canonical = canonical->decl->enums.type_info->type->canonical;
|
||||
return ixxxi(left, canonical, type, cast_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast signed int -> signed int
|
||||
* @return true if this is a widening, an explicit cast or if it is an implicit assign add
|
||||
*/
|
||||
bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
bool sisi(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize;
|
||||
bool is_narrowing = from_canonical->builtin.bytesize > canonical->builtin.bytesize;
|
||||
|
||||
if (is_narrowing && cast_type != CAST_TYPE_EXPLICIT)
|
||||
{
|
||||
@@ -330,9 +341,9 @@ bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
* Cast unsigned int -> unsigned int
|
||||
* @return true if this was not a narrowing implicit assign or narrowing implicit assign add
|
||||
*/
|
||||
bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
bool uiui(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_narrowing = from->builtin.bytesize > canonical->builtin.bytesize;
|
||||
bool is_narrowing = from_canonical->builtin.bytesize > canonical->builtin.bytesize;
|
||||
|
||||
if (is_narrowing && cast_type != CAST_TYPE_EXPLICIT)
|
||||
{
|
||||
@@ -355,9 +366,9 @@ bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
* Cast unsigned int -> signed int
|
||||
* @return true if this is an explicit cast or if it is an implicit assign add or if it is a widening cast.
|
||||
*/
|
||||
bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
bool uisi(Expr* left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
bool is_widening = from->builtin.bytesize < canonical->builtin.bytesize;
|
||||
bool is_widening = from_canonical->builtin.bytesize < canonical->builtin.bytesize;
|
||||
|
||||
if (!is_widening && cast_type != CAST_TYPE_EXPLICIT)
|
||||
{
|
||||
@@ -505,9 +516,45 @@ bool usus(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xixi(Expr *left, Type *from_canonical, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
assert(from_canonical->canonical == from_canonical);
|
||||
switch (from_canonical->type_kind)
|
||||
{
|
||||
case TYPE_IXX:
|
||||
return ixxxi(left, canonical, type, cast_type);
|
||||
case ALL_SIGNED_INTS:
|
||||
if (type_is_unsigned(canonical)) return siui(left, canonical, type, cast_type);
|
||||
return sisi(left, from_canonical, canonical, type, cast_type);
|
||||
case ALL_UNSIGNED_INTS:
|
||||
if (type_is_unsigned(canonical)) return uiui(left, from_canonical, canonical, type, cast_type);
|
||||
return uisi(left, from_canonical, canonical, type, cast_type);
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
bool enxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
Type *enum_type = from->decl->enums.type_info->type;
|
||||
Type *enum_type_canonical = enum_type->canonical;
|
||||
// 1. If the underlying type is the same, this is just setting the type.
|
||||
if (canonical == enum_type_canonical)
|
||||
{
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
// 2. See if we can convert to the target type.
|
||||
if (cast_type != CAST_TYPE_EXPLICIT && type_find_max_type(enum_type_canonical, canonical) != canonical)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
SEMA_ERROR(left, "Cannot implictly convert '%s' with underlying type of '%s' to '%s',"
|
||||
" use an explicit cast if this is what you want.", type_to_error_string(from),
|
||||
type_to_error_string(enum_type_canonical), type_to_error_string(canonical));
|
||||
return false;
|
||||
}
|
||||
// 3. Dispatch to the right cast:
|
||||
return xixi(left, enum_type_canonical, canonical, type, cast_type);
|
||||
}
|
||||
bool erxi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
@@ -632,6 +679,7 @@ CastKind cast_to_bool_kind(Type *type)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
{
|
||||
Type *from_type = expr->type->canonical;
|
||||
@@ -656,6 +704,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (type_is_float(canonical)) return ixxfp(expr, canonical, to_type, cast_type);
|
||||
if (canonical == type_bool) return ixxbo(expr, to_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return xipt(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_ENUM) return ixxen(expr, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
@@ -726,5 +775,6 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (canonical->type_kind == TYPE_POINTER) return sapt(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
}
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
return sema_type_mismatch(expr, canonical, cast_type);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ void compiler_compile(BuildTarget *target)
|
||||
}
|
||||
if (diagnostics.errors > 0) exit(EXIT_FAILURE);
|
||||
|
||||
llvm_codegen_setup();
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
Context *context = contexts[i];
|
||||
|
||||
@@ -30,8 +30,6 @@ typedef struct _Expr Expr;
|
||||
typedef struct _Module Module;
|
||||
typedef struct _Type Type;
|
||||
|
||||
typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type);
|
||||
|
||||
typedef struct _BigInt
|
||||
{
|
||||
unsigned digit_count;
|
||||
@@ -55,7 +53,11 @@ typedef struct
|
||||
char* chars;
|
||||
int len;
|
||||
} string;
|
||||
Decl *enum_constant;
|
||||
Decl *error_constant;
|
||||
};
|
||||
// Valid type kinds:
|
||||
// bool, ints, floats, string
|
||||
TypeKind kind;
|
||||
} ExprConst;
|
||||
|
||||
@@ -135,7 +137,7 @@ typedef struct
|
||||
{
|
||||
unsigned char bitsize;
|
||||
unsigned char bytesize;
|
||||
unsigned char min_alignment;
|
||||
unsigned char abi_alignment;
|
||||
unsigned char pref_alignment;
|
||||
} TypeBuiltin;
|
||||
|
||||
@@ -224,7 +226,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t alignment;
|
||||
uint32_t abi_alignment;
|
||||
uint32_t id;
|
||||
uint64_t size;
|
||||
Decl **members;
|
||||
} StructDecl;
|
||||
@@ -235,7 +238,11 @@ typedef struct _VarDecl
|
||||
unsigned id : 16;
|
||||
VarDeclKind kind : 3;
|
||||
TypeInfo *type_info;
|
||||
Expr *init_expr;
|
||||
union
|
||||
{
|
||||
Expr *init_expr;
|
||||
Decl *parent;
|
||||
};
|
||||
void *backend_ref;
|
||||
void *backend_debug_ref;
|
||||
} VarDecl;
|
||||
@@ -315,7 +322,6 @@ typedef struct
|
||||
{
|
||||
FunctionSignature function_signature;
|
||||
TypeInfo *type_info;
|
||||
Type *type;
|
||||
};
|
||||
} TypedefDecl;
|
||||
|
||||
@@ -363,7 +369,11 @@ typedef struct _Decl
|
||||
{
|
||||
struct
|
||||
{
|
||||
Decl** method_functions;
|
||||
union
|
||||
{
|
||||
Decl* parent_struct;
|
||||
Decl** method_functions;
|
||||
};
|
||||
union
|
||||
{
|
||||
ErrorDecl error;
|
||||
@@ -459,8 +469,6 @@ typedef struct
|
||||
Token sub_element;
|
||||
Decl *ref;
|
||||
};
|
||||
// TODO cleanup
|
||||
int index;
|
||||
} ExprAccess;
|
||||
|
||||
typedef struct
|
||||
@@ -499,6 +507,32 @@ typedef struct
|
||||
Ast **stmts;
|
||||
} ExprFuncBlock;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INITIALIZER_UNKNOWN,
|
||||
INITIALIZER_ZERO,
|
||||
INITIALIZER_DESIGNATED,
|
||||
INITIALIZER_NORMAL
|
||||
} InitializerType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
InitializerType init_type;
|
||||
Expr** initializer_expr;
|
||||
} ExprInitializer;
|
||||
|
||||
typedef struct _DesignatedInitPath
|
||||
{
|
||||
Decl *decl;
|
||||
struct _DesignatedInitPath *sub_path;
|
||||
} DesignatedInitPath;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DesignatedInitPath path;
|
||||
Expr *value;
|
||||
} ExprDesignatedInit;
|
||||
|
||||
struct _Expr
|
||||
{
|
||||
ExprKind expr_kind : 8;
|
||||
@@ -506,6 +540,7 @@ struct _Expr
|
||||
SourceRange span;
|
||||
Type *type;
|
||||
union {
|
||||
ExprDesignatedInit designated_init_expr;
|
||||
ExprCast cast_expr;
|
||||
ExprConst const_expr;
|
||||
ExprStructValue struct_value_expr;
|
||||
@@ -521,7 +556,7 @@ struct _Expr
|
||||
ExprAccess access_expr;
|
||||
ExprIdentifier identifier_expr;
|
||||
ExprType type_expr;
|
||||
Expr** initializer_expr;
|
||||
ExprInitializer expr_initializer;
|
||||
Expr** expression_list;
|
||||
ExprScope expr_scope;
|
||||
ExprFuncBlock expr_block;
|
||||
@@ -809,6 +844,7 @@ typedef struct _Context
|
||||
Decl **ct_ifs;
|
||||
Ast **defers;
|
||||
Decl *active_function_for_analysis;
|
||||
Decl *active_type_for_analysis;
|
||||
Decl **last_local;
|
||||
Ast **labels;
|
||||
Ast **gotos;
|
||||
@@ -884,14 +920,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
||||
extern Type *type_compint, *type_compfloat;
|
||||
extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong;
|
||||
extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong;
|
||||
|
||||
extern Type t_i8, t_i16, t_i32, t_i64, t_isz, t_ixx;
|
||||
extern Type t_u1, t_u8, t_u16, t_u32, t_u64, t_usz, t_uxx;
|
||||
extern Type t_f32, t_f64, t_fxx;
|
||||
extern Type t_u0, t_str;
|
||||
extern Type t_cus, t_cui, t_cul, t_cull;
|
||||
extern Type t_cs, t_ci, t_cl, t_cll;
|
||||
extern Type t_voidstar;
|
||||
extern Type *type_typeid, *type_error;
|
||||
|
||||
extern const char *main_name;
|
||||
|
||||
@@ -976,7 +1005,7 @@ bool cast_to_runtime(Expr *expr);
|
||||
void cast_to_smallest_runtime(Expr *expr);
|
||||
|
||||
void llvm_codegen(Context *context);
|
||||
void llvm_set_struct_size_alignment(Decl *decl);
|
||||
void llvm_codegen_setup();
|
||||
|
||||
|
||||
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr);
|
||||
@@ -1139,17 +1168,48 @@ bool type_is_subtype(Type *type, Type *possible_subtype);
|
||||
Type *type_find_common_ancestor(Type *left, Type *right);
|
||||
const char *type_to_error_string(Type *type);
|
||||
size_t type_size(Type *canonical);
|
||||
size_t type_abi_alignment(Type *canonical);
|
||||
void type_append_signature_name(Type *type, char *dst, size_t *offset);
|
||||
Type *type_find_max_type(Type *type, Type *other);
|
||||
|
||||
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
|
||||
static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; }
|
||||
static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; }
|
||||
static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_IXX; }
|
||||
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; }
|
||||
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; }
|
||||
static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; }
|
||||
static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; }
|
||||
bool type_may_have_method_functions(Type *type);
|
||||
|
||||
static inline Type *type_reduced(Type *type)
|
||||
{
|
||||
Type *canonical = type->canonical;
|
||||
if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical;
|
||||
if (canonical->type_kind == TYPE_ERROR) return type_error->canonical;
|
||||
return canonical;
|
||||
}
|
||||
|
||||
static inline bool type_is_structlike(Type *type)
|
||||
{
|
||||
assert(type->canonical = type);
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_UNION:
|
||||
case TYPE_STRUCT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static inline Type *type_reduced_from_expr(Expr *expr)
|
||||
{
|
||||
return type_reduced(expr->type);
|
||||
}
|
||||
|
||||
|
||||
static inline bool type_is_integer(Type *type)
|
||||
{
|
||||
assert(type == type->canonical);
|
||||
|
||||
@@ -220,11 +220,10 @@ typedef enum
|
||||
EXPR_IDENTIFIER,
|
||||
EXPR_TYPE_ACCESS,
|
||||
EXPR_CALL,
|
||||
EXPR_GROUP,
|
||||
EXPR_SIZEOF,
|
||||
EXPR_SUBSCRIPT,
|
||||
EXPR_ACCESS,
|
||||
EXPR_STRUCT_VALUE,
|
||||
EXPR_STRUCT_INIT_VALUES,
|
||||
EXPR_INITIALIZER_LIST,
|
||||
EXPR_EXPRESSION_LIST,
|
||||
EXPR_CAST,
|
||||
|
||||
@@ -46,7 +46,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
|
||||
}
|
||||
|
||||
// TODO fix name
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, decl->type->backend_type, decl->name);
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name);
|
||||
|
||||
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
|
||||
|
||||
@@ -155,8 +155,39 @@ static int get_inlining_threshold(void)
|
||||
return 250;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline unsigned lookup_intrinsic(const char *name)
|
||||
{
|
||||
return LLVMLookupIntrinsicID(name, strlen(name));
|
||||
}
|
||||
|
||||
static bool intrinsics_setup = false;
|
||||
unsigned ssub_overflow_intrinsic_id;
|
||||
unsigned usub_overflow_intrinsic_id;
|
||||
unsigned sadd_overflow_intrinsic_id;
|
||||
unsigned uadd_overflow_intrinsic_id;
|
||||
unsigned smul_overflow_intrinsic_id;
|
||||
unsigned umul_overflow_intrinsic_id;
|
||||
unsigned trap_intrinsic_id;
|
||||
|
||||
void llvm_codegen_setup()
|
||||
{
|
||||
assert(intrinsics_setup == false);
|
||||
ssub_overflow_intrinsic_id = lookup_intrinsic("llvm.ssub.with.overflow");
|
||||
usub_overflow_intrinsic_id = lookup_intrinsic("llvm.usub.with.overflow");
|
||||
sadd_overflow_intrinsic_id = lookup_intrinsic("llvm.sadd.with.overflow");
|
||||
uadd_overflow_intrinsic_id = lookup_intrinsic("llvm.uadd.with.overflow");
|
||||
smul_overflow_intrinsic_id = lookup_intrinsic("llvm.smul.with.overflow");
|
||||
umul_overflow_intrinsic_id = lookup_intrinsic("llvm.umul.with.overflow");
|
||||
trap_intrinsic_id = lookup_intrinsic("llvm.trap");
|
||||
|
||||
intrinsics_setup = true;
|
||||
}
|
||||
|
||||
void llvm_codegen(Context *context)
|
||||
{
|
||||
assert(intrinsics_setup);
|
||||
GenContext gen_context;
|
||||
gencontext_init(&gen_context, context);
|
||||
gencontext_begin_module(&gen_context);
|
||||
|
||||
@@ -13,7 +13,26 @@ static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *ty
|
||||
return LLVMBuildAdd(context->builder, left, right, "add_mod");
|
||||
}
|
||||
|
||||
// TODO insert trap
|
||||
if (build_options.debug_mode)
|
||||
{
|
||||
LLVMTypeRef type_to_use = llvm_type(type->canonical);
|
||||
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
||||
LLVMValueRef args[2] = { left, right };
|
||||
assert(type->canonical == type);
|
||||
LLVMValueRef add_res;
|
||||
if (type_is_unsigned(type))
|
||||
{
|
||||
add_res = gencontext_emit_call_intrinsic(context, uadd_overflow_intrinsic_id, types, args, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_res = gencontext_emit_call_intrinsic(context, sadd_overflow_intrinsic_id, types, args, 2);
|
||||
}
|
||||
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
|
||||
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
|
||||
gencontext_emit_panic_on_true(context, ok, "Addition overflow");
|
||||
return result;
|
||||
}
|
||||
return type_is_unsigned_integer(type)
|
||||
? LLVMBuildNUWAdd(context->builder, left, right, "uadd")
|
||||
: LLVMBuildNSWAdd(context->builder, left, right, "add");
|
||||
@@ -26,25 +45,43 @@ static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *ty
|
||||
return LLVMBuildSub(context->builder, left, right, "sub_mod");
|
||||
}
|
||||
|
||||
// TODO insert trap
|
||||
if (build_options.debug_mode)
|
||||
{
|
||||
LLVMTypeRef type_to_use = llvm_type(type);
|
||||
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
||||
LLVMValueRef args[2] = { left, right };
|
||||
assert(type->canonical == type);
|
||||
LLVMValueRef add_res;
|
||||
if (type_is_unsigned(type))
|
||||
{
|
||||
add_res = gencontext_emit_call_intrinsic(context, usub_overflow_intrinsic_id, types, args, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2);
|
||||
}
|
||||
LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, "");
|
||||
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, "");
|
||||
gencontext_emit_panic_on_true(context, ok, "Subtraction overflow");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return type_is_unsigned_integer(type)
|
||||
? LLVMBuildNUWSub(context->builder, left, right, "usub")
|
||||
: LLVMBuildNSWSub(context->builder, left, right, "sub");
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr)
|
||||
static inline LLVMValueRef gencontext_emit_subscript_addr_from_value(GenContext *context, LLVMValueRef parent, Type *parent_type, Expr *index_expr)
|
||||
{
|
||||
LLVMValueRef index = gencontext_emit_expr(context, expr->subscript_expr.index);
|
||||
Type *type = expr->subscript_expr.expr->type->canonical;
|
||||
switch (type->type_kind)
|
||||
assert(parent_type->canonical == parent_type);
|
||||
LLVMValueRef index = gencontext_emit_expr(context, index_expr);
|
||||
switch (parent_type->type_kind)
|
||||
{
|
||||
case TYPE_ARRAY:
|
||||
TODO
|
||||
case TYPE_POINTER:
|
||||
return LLVMBuildGEP2(context->builder,
|
||||
llvm_type(type->pointer),
|
||||
gencontext_emit_expr(context, expr->subscript_expr.expr),
|
||||
&index, 1, "[]");
|
||||
llvm_type(parent_type->pointer),
|
||||
parent, &index, 1, "[]");
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
case TYPE_STRING:
|
||||
@@ -52,12 +89,48 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
}
|
||||
static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef parent = gencontext_emit_expr(context, expr->subscript_expr.expr);
|
||||
return gencontext_emit_subscript_addr_from_value(context, parent, expr->subscript_expr.expr->type->canonical, expr->subscript_expr.index);
|
||||
}
|
||||
|
||||
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
|
||||
{
|
||||
unsigned index;
|
||||
Decl *current_parent;
|
||||
if (decl_is_struct_type(member))
|
||||
{
|
||||
index = member->strukt.id;
|
||||
current_parent = member->parent_struct;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = member->var.id;
|
||||
current_parent = member->var.parent;
|
||||
}
|
||||
assert(current_parent);
|
||||
if (parent != current_parent)
|
||||
{
|
||||
value = gencontext_emit_member_addr(context, value, parent, current_parent);
|
||||
}
|
||||
|
||||
if (current_parent->decl_kind == DECL_UNION)
|
||||
{
|
||||
return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), "unionref");
|
||||
}
|
||||
return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, "structref");
|
||||
}
|
||||
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
|
||||
return LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
Expr *parent = expr->access_expr.parent;
|
||||
LLVMValueRef value = gencontext_emit_address(context, parent);
|
||||
Decl *member = expr->access_expr.ref;
|
||||
return gencontext_emit_member_addr(context, value, parent->type->canonical->decl, member);
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr)
|
||||
@@ -91,6 +164,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
return gencontext_emit_subscript_addr(context, expr);
|
||||
case EXPR_SCOPED_EXPR:
|
||||
return gencontext_emit_scoped_expr_address(context, expr);
|
||||
case EXPR_GROUP:
|
||||
return gencontext_emit_address(context, expr->group_expr);
|
||||
case EXPR_CONST:
|
||||
case EXPR_TYPE:
|
||||
case EXPR_POISONED:
|
||||
@@ -101,12 +176,11 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
case EXPR_POST_UNARY:
|
||||
case EXPR_TYPE_ACCESS:
|
||||
case EXPR_CALL:
|
||||
case EXPR_STRUCT_VALUE:
|
||||
case EXPR_STRUCT_INIT_VALUES:
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
case EXPR_CAST:
|
||||
case EXPR_MACRO_EXPR:
|
||||
case EXPR_DESIGNATED_INIT:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -178,9 +252,93 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *
|
||||
return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical);
|
||||
}
|
||||
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_designated_initializer(GenContext *context, Type *parent_type, LLVMValueRef parent, Expr *expr)
|
||||
{
|
||||
assert(parent_type == parent_type->canonical);
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_SUBSCRIPT:
|
||||
if (expr->subscript_expr.expr)
|
||||
{
|
||||
parent = gencontext_emit_designated_initializer(context, parent_type, parent, expr->subscript_expr.expr);
|
||||
parent_type = expr->subscript_expr.expr->type->canonical;
|
||||
}
|
||||
return gencontext_emit_subscript_addr_from_value(context, parent, parent_type, expr->subscript_expr.index);
|
||||
case EXPR_ACCESS:
|
||||
parent = gencontext_emit_designated_initializer(context, parent_type, parent, expr->access_expr.parent);
|
||||
parent_type = expr->subscript_expr.expr->type->canonical;
|
||||
return gencontext_emit_member_addr(context, parent, parent_type->decl, expr->access_expr.ref);
|
||||
case EXPR_IDENTIFIER:
|
||||
return gencontext_emit_member_addr(context, parent, parent_type->decl, expr->identifier_expr.decl);
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a Foo { .... } literal.
|
||||
*
|
||||
* Improve: Direct assign in the case where this is assigning to a variable.
|
||||
* Improve: Create constant initializer for the constant case and do a memcopy
|
||||
*/
|
||||
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
LLVMValueRef ref = gencontext_emit_alloca(context, type, "literal");
|
||||
|
||||
if (expr->expr_initializer.init_type == INITIALIZER_ZERO)
|
||||
{
|
||||
LLVMBuildMemSet(context->builder,
|
||||
ref,
|
||||
LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false),
|
||||
expr->type->decl->strukt.abi_alignment);
|
||||
return ref;
|
||||
}
|
||||
|
||||
Expr **elements = expr->expr_initializer.initializer_expr;
|
||||
|
||||
bool is_union = expr->type->canonical->type_kind == TYPE_UNION;
|
||||
if (expr->expr_initializer.init_type == INITIALIZER_NORMAL)
|
||||
{
|
||||
if (is_union)
|
||||
{
|
||||
assert(vec_size(elements) == 1);
|
||||
LLVMValueRef init_value = gencontext_emit_expr(context, elements[0]);
|
||||
LLVMValueRef u = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(elements[0]->type->canonical), 0), "");
|
||||
LLVMBuildStore(context->builder, init_value, u);
|
||||
return ref;
|
||||
}
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *element = elements[i];
|
||||
LLVMValueRef init_value = gencontext_emit_expr(context, element);
|
||||
LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, "");
|
||||
LLVMBuildStore(context->builder, init_value, subref);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
||||
// Clear the temp.
|
||||
LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment);
|
||||
|
||||
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *element = elements[i];
|
||||
LLVMValueRef sub_value = gencontext_emit_expr(context, element->binary_expr.right);
|
||||
LLVMValueRef sub_ref = gencontext_emit_designated_initializer(context, expr->type->canonical, ref, element->binary_expr.left);
|
||||
LLVMBuildStore(context->builder, sub_value, sub_ref);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff)
|
||||
{
|
||||
Type *type = expr->type->canonical;
|
||||
Type *type = type_reduced_from_expr(expr);
|
||||
LLVMTypeRef llvm_type = llvm_type(type);
|
||||
|
||||
if (type->type_kind == TYPE_POINTER)
|
||||
@@ -221,32 +379,43 @@ static inline LLVMValueRef gencontext_emit_post_inc_dec(GenContext *context, Exp
|
||||
|
||||
LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
Type *type = type_reduced_from_expr(expr->unary_expr.expr);
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_ERROR:
|
||||
FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator);
|
||||
case UNARYOP_NOT:
|
||||
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not");
|
||||
return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(llvm_type(type_bool), 1, 0), "not");
|
||||
case UNARYOP_BITNEG:
|
||||
return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot");
|
||||
case UNARYOP_NEGMOD:
|
||||
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "negmod");
|
||||
case UNARYOP_NEG:
|
||||
// TODO improve how unsigned numbers are negated.
|
||||
if (type_is_float(expr->unary_expr.expr->type->canonical))
|
||||
if (type_is_float(type))
|
||||
{
|
||||
return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg");
|
||||
}
|
||||
if (type_is_unsigned(expr->unary_expr.expr->type->canonical))
|
||||
assert(!type_is_unsigned(type));
|
||||
{
|
||||
return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||
LLVMValueRef to_negate = gencontext_emit_expr(context, expr->unary_expr.expr);
|
||||
LLVMValueRef zero = LLVMConstInt(llvm_type(expr->unary_expr.expr->type->canonical), 0, false);
|
||||
if (build_options.debug_mode)
|
||||
{
|
||||
LLVMTypeRef type_to_use = llvm_type(type->canonical);
|
||||
LLVMValueRef args[2] = { zero, to_negate };
|
||||
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
||||
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, ssub_overflow_intrinsic_id, types, args, 2);
|
||||
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
|
||||
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
|
||||
gencontext_emit_panic_on_true(context, ok, "Signed negation overflow");
|
||||
return result;
|
||||
}
|
||||
return LLVMBuildNSWSub(context->builder, zero, to_negate, "neg");
|
||||
}
|
||||
// TODO insert trap
|
||||
return LLVMBuildNSWNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg");
|
||||
case UNARYOP_ADDR:
|
||||
return gencontext_emit_address(context, expr->unary_expr.expr);
|
||||
case UNARYOP_DEREF:
|
||||
return LLVMBuildLoad2(context->builder, llvm_type(expr->unary_expr.expr->type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||
return LLVMBuildLoad2(context->builder, llvm_type(type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||
case UNARYOP_INC:
|
||||
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false);
|
||||
case UNARYOP_DEC:
|
||||
@@ -285,7 +454,7 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex
|
||||
|
||||
// Generate phi
|
||||
gencontext_emit_block(context, phi_block);
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, type_bool->backend_type, "val");
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(type_bool), "val");
|
||||
|
||||
// Simplify for LLVM by entering the constants we already know of.
|
||||
LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false);
|
||||
@@ -440,7 +609,6 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
{
|
||||
return gencontext_emit_logical_and_or(context, expr, binary_op);
|
||||
}
|
||||
Type *type = expr->type->canonical;
|
||||
Expr *lhs = expr->binary_expr.left;
|
||||
Expr *rhs = expr->binary_expr.right;
|
||||
|
||||
@@ -456,10 +624,10 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
}
|
||||
|
||||
rhs_value = gencontext_emit_expr(context, rhs);
|
||||
Type *lhs_type = expr->binary_expr.left->type->canonical;
|
||||
Type *lhs_type = type_reduced_from_expr(lhs);
|
||||
if (type_is_integer(lhs_type) && binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)
|
||||
{
|
||||
return gencontext_emit_int_comparison(context, lhs_type, rhs->type->canonical, lhs_value, rhs_value, binary_op);
|
||||
return gencontext_emit_int_comparison(context, lhs_type, type_reduced_from_expr(rhs), lhs_value, rhs_value, binary_op);
|
||||
}
|
||||
bool is_float = type_is_float(lhs_type);
|
||||
switch (binary_op)
|
||||
@@ -468,15 +636,33 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
UNREACHABLE
|
||||
case BINARYOP_MULT:
|
||||
if (is_float) return LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul");
|
||||
// TODO insert trap
|
||||
if (type_is_unsigned_integer(lhs_type))
|
||||
{
|
||||
if (build_options.debug_mode)
|
||||
{
|
||||
LLVMTypeRef type_to_use = llvm_type(lhs_type);
|
||||
LLVMValueRef args[2] = { lhs_value, rhs_value };
|
||||
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
||||
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, umul_overflow_intrinsic_id, types, args, 2);
|
||||
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
|
||||
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
|
||||
gencontext_emit_panic_on_true(context, ok, "Unsigned multiplication overflow");
|
||||
return result;
|
||||
}
|
||||
return LLVMBuildNUWMul(context->builder, lhs_value, rhs_value, "umul");
|
||||
}
|
||||
else
|
||||
if (build_options.debug_mode)
|
||||
{
|
||||
return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul");
|
||||
LLVMTypeRef type_to_use = llvm_type(lhs_type);
|
||||
LLVMValueRef args[2] = { lhs_value, rhs_value };
|
||||
LLVMTypeRef types[2] = { type_to_use, type_to_use };
|
||||
LLVMValueRef call_res = gencontext_emit_call_intrinsic(context, smul_overflow_intrinsic_id, types, args, 2);
|
||||
LLVMValueRef result = LLVMBuildExtractValue(context->builder, call_res, 0, "");
|
||||
LLVMValueRef ok = LLVMBuildExtractValue(context->builder, call_res, 1, "");
|
||||
gencontext_emit_panic_on_true(context, ok, "Signed multiplication overflow");
|
||||
return result;
|
||||
}
|
||||
return LLVMBuildNSWMul(context->builder, lhs_value, rhs_value, "mul");
|
||||
case BINARYOP_MULT_MOD:
|
||||
return LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul");
|
||||
case BINARYOP_SUB:
|
||||
@@ -520,28 +706,27 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
case BINARYOP_BIT_XOR:
|
||||
return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor");
|
||||
case BINARYOP_EQ:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
// Unordered?
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq");
|
||||
case BINARYOP_NE:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
// Unordered?
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq");
|
||||
case BINARYOP_GE:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge");
|
||||
case BINARYOP_GT:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt");
|
||||
case BINARYOP_LE:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le");
|
||||
case BINARYOP_LT:
|
||||
assert(!type_is_integer(lhs_type));
|
||||
assert(type_is_float(lhs_type));
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt");
|
||||
case BINARYOP_AND:
|
||||
case BINARYOP_OR:
|
||||
UNREACHABLE
|
||||
case BINARYOP_ASSIGN:
|
||||
case BINARYOP_MULT_ASSIGN:
|
||||
case BINARYOP_MULT_MOD_ASSIGN:
|
||||
@@ -581,10 +766,6 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr)
|
||||
if (binary_op == BINARYOP_ASSIGN)
|
||||
{
|
||||
LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left);
|
||||
if (expr->binary_expr.right->expr_kind == EXPR_INITIALIZER_LIST)
|
||||
{
|
||||
return gencontext_emit_initialization_from_expr(context, addr, expr->binary_expr.right);
|
||||
}
|
||||
LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right);
|
||||
LLVMBuildStore(context->builder, value, addr);
|
||||
return value;
|
||||
@@ -616,7 +797,7 @@ LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
|
||||
|
||||
// Generate phi
|
||||
gencontext_emit_block(context, phi_block);
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val");
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
|
||||
|
||||
LLVMValueRef logic_values[2] = { lhs, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { current_block, rhs_block };
|
||||
@@ -649,24 +830,19 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr)
|
||||
|
||||
// Generate phi
|
||||
gencontext_emit_block(context, phi_block);
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val");
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, llvm_type(expr->type), "val");
|
||||
|
||||
LLVMValueRef logicValues[2] = { lhs, rhs };
|
||||
LLVMValueRef logic_values[2] = { lhs, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { lhs_block, rhs_block };
|
||||
LLVMAddIncoming(phi, logicValues, blocks, 2);
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
|
||||
static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->type->canonical->backend_type,
|
||||
expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name);
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
LLVMTypeRef type = llvm_type(type_reduced_from_expr(expr));
|
||||
switch (expr->const_expr.kind)
|
||||
{
|
||||
case ALL_INTS:
|
||||
@@ -731,7 +907,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr
|
||||
{
|
||||
// Improve, add string description to the access?
|
||||
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
|
||||
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, expr->access_expr.ref->var.id, "");
|
||||
return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, "");
|
||||
}
|
||||
|
||||
@@ -745,31 +921,6 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
LLVMValueRef value = LLVMGetUndef(type);
|
||||
|
||||
if (!vec_size(expr->initializer_expr))
|
||||
{
|
||||
LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp");
|
||||
value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.alignment);
|
||||
return ref;
|
||||
}
|
||||
|
||||
VECEACH(expr->initializer_expr, i)
|
||||
{
|
||||
LLVMValueRef init_value = gencontext_emit_expr(context, expr->initializer_expr[i]);
|
||||
value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr)
|
||||
{
|
||||
@@ -802,12 +953,24 @@ static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr
|
||||
return return_out;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types,
|
||||
LLVMValueRef *values, unsigned arg_count)
|
||||
{
|
||||
LLVMValueRef decl = LLVMGetIntrinsicDeclaration(context->module, intrinsic_id, types, arg_count);
|
||||
LLVMTypeRef type = LLVMIntrinsicGetType(context->context, intrinsic_id, types, arg_count);
|
||||
return LLVMBuildCall2(context->builder, type, decl, values, arg_count, "");
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_DESIGNATED_INIT:
|
||||
// This is handled inside of initializer setup
|
||||
UNREACHABLE
|
||||
case EXPR_EXPR_BLOCK:
|
||||
return gencontext_emit_expr_block(context, expr);
|
||||
case EXPR_SCOPED_EXPR:
|
||||
@@ -834,12 +997,10 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||
return gencontext_load_expr(context, gencontext_emit_address(context, expr));
|
||||
case EXPR_CALL:
|
||||
return gencontext_emit_call_expr(context, expr);
|
||||
case EXPR_GROUP:
|
||||
return gencontext_emit_expr(context, expr->group_expr);
|
||||
case EXPR_ACCESS:
|
||||
return gencontext_emit_access_expr(context, expr);
|
||||
case EXPR_STRUCT_VALUE:
|
||||
return gencontext_emit_struct_value_expr(context, expr);
|
||||
case EXPR_STRUCT_INIT_VALUES:
|
||||
return gencontext_emit_struct_init_values_expr(context, expr);
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
return gencontext_emit_initializer_list_expr(context, expr);
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
|
||||
@@ -68,10 +68,20 @@ typedef struct
|
||||
bool did_call_stack_save : 1;
|
||||
} GenContext;
|
||||
|
||||
extern unsigned sadd_overflow_intrinsic_id;
|
||||
extern unsigned uadd_overflow_intrinsic_id;
|
||||
extern unsigned ssub_overflow_intrinsic_id;
|
||||
extern unsigned usub_overflow_intrinsic_id;
|
||||
extern unsigned smul_overflow_intrinsic_id;
|
||||
extern unsigned umul_overflow_intrinsic_id;
|
||||
extern unsigned trap_intrinsic_id;
|
||||
|
||||
void gencontext_begin_module(GenContext *context);
|
||||
void gencontext_end_module(GenContext *context);
|
||||
void gencontext_emit_stmt(GenContext *context, Ast *ast);
|
||||
LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types,
|
||||
LLVMValueRef *values, unsigned arg_count);
|
||||
void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name);
|
||||
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end);
|
||||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
|
||||
LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr);
|
||||
|
||||
@@ -23,7 +23,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
{
|
||||
Decl *decl = ast->declare_stmt;
|
||||
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name);
|
||||
// TODO NRVO
|
||||
// TODO debug info
|
||||
/*
|
||||
@@ -44,11 +44,11 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
{
|
||||
Expr *expr = decl->var.init_expr;
|
||||
// Quick path for empty initializer list
|
||||
if (expr->expr_kind == EXPR_INITIALIZER_LIST && vec_size(expr->initializer_expr) == 0)
|
||||
if (expr->expr_kind == EXPR_INITIALIZER_LIST && expr->expr_initializer.init_type == INITIALIZER_ZERO)
|
||||
{
|
||||
LLVMBuildMemSet(context->builder, decl->var.backend_ref, LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false),
|
||||
expr->type->decl->strukt.alignment);
|
||||
expr->type->decl->strukt.abi_alignment);
|
||||
return decl->var.backend_ref;
|
||||
}
|
||||
|
||||
@@ -600,6 +600,18 @@ void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end);
|
||||
}
|
||||
|
||||
void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name)
|
||||
{
|
||||
LLVMBasicBlockRef panic_block = gencontext_create_free_block(context, "panic");
|
||||
LLVMBasicBlockRef ok_block = gencontext_create_free_block(context, "checkok");
|
||||
gencontext_emit_cond_br(context, value, panic_block, ok_block);
|
||||
gencontext_emit_block(context, panic_block);
|
||||
gencontext_emit_call_intrinsic(context, trap_intrinsic_id, NULL, NULL, 0);
|
||||
gencontext_emit_br(context, ok_block);
|
||||
gencontext_emit_block(context, ok_block);
|
||||
}
|
||||
|
||||
|
||||
void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
|
||||
@@ -38,7 +38,7 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
|
||||
|
||||
}
|
||||
case DECL_TYPEDEF:
|
||||
return llvm_get_type(context, decl->typedef_decl.type);
|
||||
return llvm_get_type(context, decl->typedef_decl.type_info->type);
|
||||
case DECL_STRUCT:
|
||||
{
|
||||
LLVMTypeRef *types = NULL;
|
||||
@@ -52,20 +52,28 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl
|
||||
}
|
||||
case DECL_UNION:
|
||||
{
|
||||
LLVMTypeRef max_type = NULL;
|
||||
Decl *max_type = NULL;
|
||||
unsigned long long max_size = 0;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
LLVMTypeRef type = llvm_get_type(context, decl->strukt.members[i]->type);
|
||||
unsigned long long size = LLVMStoreSizeOfType(target_data_layout(), type);
|
||||
Decl *member = decl->strukt.members[i];
|
||||
unsigned size = type_size(member->type);
|
||||
if (size > max_size || !max_type)
|
||||
{
|
||||
max_size = size;
|
||||
max_type = type;
|
||||
max_type = member;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name);
|
||||
LLVMStructSetBody(type, &max_type, 1, false);
|
||||
if (max_type)
|
||||
{
|
||||
LLVMTypeRef type_ref = llvm_get_type(context, max_type->type);
|
||||
LLVMStructSetBody(type, &type_ref, 1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMStructSetBody(type, NULL, 0, true);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
case DECL_ENUM:
|
||||
@@ -155,14 +163,20 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_META_TYPE:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERROR:
|
||||
UNREACHABLE;
|
||||
case TYPE_TYPEDEF:
|
||||
return type->backend_type = llvm_get_type(context, type->canonical);
|
||||
case TYPE_ERROR_UNION:
|
||||
{
|
||||
LLVMTypeRef types[2];
|
||||
types[0] = llvm_get_type(context, type_typeid->canonical);
|
||||
types[1] = llvm_get_type(context, type_error->canonical);
|
||||
return type->backend_type = LLVMStructType(types, 2, false);
|
||||
}
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERROR:
|
||||
case TYPE_ERROR_UNION:
|
||||
return type->backend_type = llvm_type_from_decl(context, type->decl);
|
||||
case TYPE_FUNC:
|
||||
return type->backend_type = llvm_func_type(context, type);
|
||||
@@ -216,9 +230,3 @@ LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
||||
return llvm_get_type(context->context, type);
|
||||
}
|
||||
|
||||
void llvm_set_struct_size_alignment(Decl *decl)
|
||||
{
|
||||
LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type);
|
||||
decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type);
|
||||
decl->strukt.alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type);
|
||||
}
|
||||
|
||||
@@ -34,19 +34,6 @@ static int type_bits[TYPE_U64 + 1] = {
|
||||
[TYPE_I64] = 64,
|
||||
};
|
||||
|
||||
static int64_t int_type_max[TYPE_I64 + 1] = {
|
||||
[TYPE_I8] = 0x7F,
|
||||
[TYPE_I16] = 0x7FFF,
|
||||
[TYPE_I32] = 0x7FFFFFFFLL,
|
||||
[TYPE_I64] = 0x7FFFFFFFFFFFFFFFLL,
|
||||
};
|
||||
|
||||
static int64_t int_type_min[TYPE_I64 + 1] = {
|
||||
[TYPE_I8] = -0x80,
|
||||
[TYPE_I16] = -0x8000L,
|
||||
[TYPE_I32] = -0x80000000L,
|
||||
[TYPE_I64] = -0x8000000000000000LL,
|
||||
};
|
||||
|
||||
void expr_const_fprint(FILE *__restrict file, ExprConst *expr)
|
||||
{
|
||||
@@ -74,6 +61,12 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr)
|
||||
case TYPE_FXX:
|
||||
fprintf(file, "%Lf", expr->f);
|
||||
break;
|
||||
case TYPE_ENUM:
|
||||
fprintf(file, "%s", expr->enum_constant->name);
|
||||
break;
|
||||
case TYPE_ERROR:
|
||||
fprintf(file, "%s", expr->error_constant->name);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -172,6 +165,7 @@ static inline bool compare_fps(long double left, long double right, BinaryOp op)
|
||||
|
||||
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op)
|
||||
{
|
||||
bool is_eq;
|
||||
switch (left->kind)
|
||||
{
|
||||
case TYPE_BOOL:
|
||||
@@ -183,10 +177,23 @@ bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp
|
||||
case TYPE_POINTER:
|
||||
return true;
|
||||
case TYPE_STRING:
|
||||
TODO
|
||||
if (left->string.len != right->string.len)
|
||||
{
|
||||
is_eq = false;
|
||||
break;
|
||||
}
|
||||
if (right->string.chars == left->string.chars)
|
||||
{
|
||||
is_eq = true;
|
||||
break;
|
||||
}
|
||||
is_eq = strncmp(left->string.chars, right->string.chars, left->string.len);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
assert(op == BINARYOP_EQ || op == BINARYOP_NE);
|
||||
return op == BINARYOP_EQ == is_eq;
|
||||
}
|
||||
|
||||
bool expr_const_int_overflowed(const ExprConst *expr)
|
||||
@@ -239,6 +246,15 @@ const char *expr_const_to_error_string(const ExprConst *expr)
|
||||
case TYPE_FXX:
|
||||
asprintf(&buff, "%Lf", expr->f);
|
||||
return buff;
|
||||
case TYPE_ENUM:
|
||||
asprintf(&buff, "%s.%s", expr->enum_constant->type->name, expr->enum_constant->name);
|
||||
return buff;
|
||||
case TYPE_ERROR:
|
||||
asprintf(&buff, "%s.%s", expr->error_constant->type->name, expr->error_constant->name);
|
||||
return buff;
|
||||
case TYPE_STRING:
|
||||
asprintf(&buff, "\"%*.s\"", expr->string.len, expr->string.chars);
|
||||
return buff;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
@@ -208,10 +208,12 @@ static Expr *parse_ternary_expr(Context *context, Expr *left_side)
|
||||
static Expr *parse_grouping_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *expr = expr_new(EXPR_GROUP, context->tok.span);
|
||||
advance_and_verify(context, TOKEN_LPAREN);
|
||||
Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
expr->group_expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_expr);
|
||||
return right;
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,8 +251,9 @@ Expr *parse_initializer(Context *context)
|
||||
Expr *parse_initializer_list(Context *context)
|
||||
{
|
||||
Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok);
|
||||
initializer_list->expr_initializer.init_type = INITIALIZER_UNKNOWN;
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_expr);
|
||||
if (!parse_param_list(context, &initializer_list->initializer_expr, false)) return &poisoned_expr;
|
||||
if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false)) return &poisoned_expr;
|
||||
CONSUME_OR(TOKEN_RBRACE, &poisoned_expr);
|
||||
return initializer_list;
|
||||
}
|
||||
@@ -324,6 +327,8 @@ static Expr *parse_access_expr(Context *context, Expr *left)
|
||||
access_expr->access_expr.parent = left;
|
||||
access_expr->access_expr.sub_element = context->tok;
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr);
|
||||
access_expr->span = left->span;
|
||||
access_expr->span.end_loc = access_expr->access_expr.sub_element.span.end_loc;
|
||||
return access_expr;
|
||||
}
|
||||
|
||||
|
||||
@@ -891,7 +891,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili
|
||||
{
|
||||
if (context->tok.type != TOKEN_COMMA && context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Unexpected end of the parameter list, did you forget an ')'?");
|
||||
sema_error_at(context->prev_tok_end, "Unexpected end of the parameter list, did you forget an ')'?");
|
||||
return false;
|
||||
}
|
||||
SEMA_ERROR(type, "The function parameter must be named.");
|
||||
@@ -989,11 +989,11 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
Decl *member;
|
||||
if (context->next_tok.type != TOKEN_IDENT)
|
||||
{
|
||||
Token name_replacement = context->tok;
|
||||
name_replacement.string = NULL;
|
||||
member = decl_new_with_type(name_replacement, decl_kind, parent->visibility);
|
||||
advance(context);
|
||||
}
|
||||
Token name_replacement = context->tok;
|
||||
name_replacement.string = NULL;
|
||||
member = decl_new_with_type(name_replacement, decl_kind, parent->visibility);
|
||||
advance(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
advance(context);
|
||||
@@ -1010,6 +1010,8 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
advance_and_verify(context, TOKEN_IDENT);
|
||||
}
|
||||
if (!parse_attributes(context, member)) return false;
|
||||
member->parent_struct = parent;
|
||||
member->strukt.id = vec_size(parent->strukt.members);
|
||||
parent->strukt.members = VECADD(parent->strukt.members, member);
|
||||
if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent))
|
||||
{
|
||||
@@ -1033,7 +1035,10 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent)
|
||||
decl_poison(other);
|
||||
decl_poison(member);
|
||||
}
|
||||
unsigned index = vec_size(parent->strukt.members);
|
||||
parent->strukt.members = VECADD(parent->strukt.members, member);
|
||||
member->var.id = index;
|
||||
member->var.parent = parent;
|
||||
advance(context);
|
||||
if (context->tok.type != TOKEN_COMMA) break;
|
||||
}
|
||||
@@ -1955,6 +1960,7 @@ void parse_file(Context *context)
|
||||
static Expr *parse_type_access(Context *context, TypeInfo *type)
|
||||
{
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE_ACCESS, context->tok);
|
||||
expr->span = type->span;
|
||||
expr->type_access.type = type;
|
||||
|
||||
advance_and_verify(context, TOKEN_DOT);
|
||||
@@ -1966,6 +1972,7 @@ static Expr *parse_type_access(Context *context, TypeInfo *type)
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_CONST_IDENT:
|
||||
advance(context);
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
return expr;
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected a function name, macro, or constant.");
|
||||
@@ -1992,11 +1999,7 @@ Expr *parse_type_identifier_with_path(Context *context, Path *path)
|
||||
RANGE_EXTEND_PREV(type);
|
||||
if (context->tok.type == TOKEN_LBRACE)
|
||||
{
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok);
|
||||
expr->struct_value_expr.type = type;
|
||||
expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr);
|
||||
|
||||
return expr;
|
||||
return TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr);
|
||||
}
|
||||
EXPECT_OR(TOKEN_DOT, &poisoned_expr);
|
||||
return parse_type_access(context, type);
|
||||
|
||||
@@ -31,15 +31,64 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
|
||||
break;
|
||||
}
|
||||
}
|
||||
constant->error_constant.value = i;
|
||||
constant->error_constant.value = i + 1;
|
||||
constant->resolve_status = RESOLVE_DONE;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static inline void sema_set_struct_size(Decl *decl)
|
||||
{
|
||||
// TODO packed
|
||||
uint64_t size = 0;
|
||||
uint64_t alignment = 0;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Decl *member = decl->strukt.members[i];
|
||||
Type *canonical = member->type->canonical;
|
||||
uint64_t member_size = type_size(canonical);
|
||||
uint64_t member_alignment = type_abi_alignment(canonical);
|
||||
assert(member_size > 0);
|
||||
// Add padding.
|
||||
if (member_alignment && (size % member_alignment))
|
||||
{
|
||||
size += member_alignment - size % member_alignment;
|
||||
}
|
||||
// Add size.
|
||||
size += member_size;
|
||||
if (member_alignment > alignment) alignment = member_alignment;
|
||||
}
|
||||
decl->strukt.abi_alignment = alignment;
|
||||
if (alignment && size % alignment)
|
||||
{
|
||||
size += alignment - size % alignment;
|
||||
}
|
||||
decl->strukt.size = size;
|
||||
}
|
||||
|
||||
static inline void sema_set_union_size(Decl *decl)
|
||||
{
|
||||
uint64_t size = 0;
|
||||
uint64_t alignment = 0;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Decl *member = decl->strukt.members[i];
|
||||
Type *canonical = member->type->canonical;
|
||||
uint64_t member_size = type_size(canonical);
|
||||
uint64_t member_alignment = type_abi_alignment(canonical);
|
||||
if (member_size > size) size = member_size;
|
||||
if (member_alignment > alignment) alignment = member_alignment;
|
||||
}
|
||||
decl->strukt.abi_alignment = alignment;
|
||||
decl->strukt.size = size;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
{
|
||||
assert(decl->resolve_status == RESOLVE_NOT_DONE);
|
||||
decl->resolve_status = RESOLVE_RUNNING;
|
||||
if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION)
|
||||
{
|
||||
DEBUG_LOG("Beginning analysis of inner struct/union");
|
||||
@@ -61,12 +110,19 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
decl_poison(decl);
|
||||
}
|
||||
}
|
||||
if (decl->decl_kind == DECL_UNION)
|
||||
{
|
||||
sema_set_union_size(decl);
|
||||
}
|
||||
else
|
||||
{
|
||||
sema_set_struct_size(decl);
|
||||
}
|
||||
DEBUG_LOG("Analysis complete.");
|
||||
return decl_ok(decl);
|
||||
}
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
assert(decl->var.kind == VARDECL_MEMBER);
|
||||
assert(!decl->var.init_expr);
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info))
|
||||
{
|
||||
decl_poison(decl);
|
||||
@@ -74,6 +130,7 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
}
|
||||
decl->type = decl->var.type_info->type;
|
||||
assert(decl->var.type_info->type);
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -89,7 +146,7 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl)
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
if (!sema_analyse_struct_member(context, decl->strukt.members[i]))
|
||||
if (!sema_analyse_struct_member(context, member))
|
||||
{
|
||||
if (decl_ok(decl))
|
||||
{
|
||||
@@ -235,7 +292,7 @@ static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false;
|
||||
decl->type->canonical = decl->typedef_decl.type_info->type;
|
||||
decl->type->canonical = decl->typedef_decl.type_info->type->canonical;
|
||||
// Do we need anything else?
|
||||
return true;
|
||||
}
|
||||
@@ -246,7 +303,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
||||
if (!sema_resolve_type_info(context, decl->enums.type_info)) return false;
|
||||
|
||||
Type *type = decl->enums.type_info->type;
|
||||
Type *canonical = decl->enums.type_info->type;
|
||||
Type *canonical = type->canonical;
|
||||
|
||||
// Require an integer type
|
||||
if (!type_is_integer(canonical))
|
||||
@@ -430,6 +487,7 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl);
|
||||
@@ -450,9 +508,13 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
if (!sema_analyse_throws(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_STRUCT:
|
||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||
sema_set_struct_size(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_UNION:
|
||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||
llvm_set_struct_size_alignment(decl);
|
||||
sema_set_union_size(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_FUNC:
|
||||
|
||||
@@ -49,6 +49,8 @@ static bool expr_is_ltype(Expr *expr)
|
||||
return expr->unary_expr.operator == UNARYOP_DEREF;
|
||||
case EXPR_ACCESS:
|
||||
return expr_is_ltype(expr->access_expr.parent);
|
||||
case EXPR_GROUP:
|
||||
return expr_is_ltype(expr->group_expr);
|
||||
case EXPR_SUBSCRIPT:
|
||||
return true;
|
||||
default:
|
||||
@@ -114,15 +116,72 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl)
|
||||
{
|
||||
VECEACH(decl->enums.values, i)
|
||||
{
|
||||
Decl *enum_constant = decl->enums.values[i];
|
||||
if (enum_constant->name == name)
|
||||
{
|
||||
assert(enum_constant->resolve_status == RESOLVE_DONE);
|
||||
expr->type = enum_constant->type;
|
||||
expr->const_expr = enum_constant->enum_constant.expr->const_expr;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_error_constant(Expr *expr, const char *name, Decl *decl)
|
||||
{
|
||||
VECEACH(decl->error.error_constants, i)
|
||||
{
|
||||
Decl *error_constant = decl->error.error_constants[i];
|
||||
if (error_constant->name == name)
|
||||
{
|
||||
assert(error_constant->resolve_status == RESOLVE_DONE);
|
||||
expr->type = decl->type;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr_const_set_int(&expr->const_expr, error_constant->error_constant.value, type_error->canonical->type_kind);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool find_possible_inferred_identifier(Type *to, Expr *expr)
|
||||
{
|
||||
if (to->canonical->type_kind != TYPE_ENUM && to->canonical->type_kind != TYPE_ERROR) return false;
|
||||
Decl *parent_decl = to->canonical->decl;
|
||||
switch (parent_decl->decl_kind)
|
||||
{
|
||||
case DECL_ENUM:
|
||||
return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl);
|
||||
case DECL_ERROR:
|
||||
return sema_expr_analyse_error_constant(expr, expr->identifier_expr.identifier, parent_decl);
|
||||
case DECL_UNION:
|
||||
case DECL_STRUCT:
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
}
|
||||
static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
// TODO what about struct functions
|
||||
Decl *ambiguous_decl;
|
||||
Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl);
|
||||
|
||||
if (!decl && !expr->identifier_expr.path && to)
|
||||
{
|
||||
if (find_possible_inferred_identifier(to, expr)) return true;
|
||||
}
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_ERROR(expr, "Unknown symbol '%s'.", expr->identifier_expr.identifier);
|
||||
SEMA_ERROR(expr, "The symbol '%s' could not be found.", expr->identifier_expr.identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -163,6 +222,7 @@ static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to,
|
||||
static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr) { TODO }
|
||||
static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO };
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl)
|
||||
{
|
||||
Expr **args =expr->call_expr.arguments;
|
||||
@@ -221,15 +281,6 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_struct_value(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_struct_init_values(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
@@ -284,58 +335,33 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr, Decl *decl)
|
||||
{
|
||||
const char *name = expr->type_access.name.string;
|
||||
VECEACH(decl->enums.values, i)
|
||||
{
|
||||
Decl *enum_constant = decl->enums.values[i];
|
||||
if (enum_constant->name == name)
|
||||
{
|
||||
assert(enum_constant->resolve_status == RESOLVE_DONE);
|
||||
expr_replace(expr, enum_constant->enum_constant.expr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr, Decl *decl)
|
||||
{
|
||||
const char *name = expr->type_access.name.string;
|
||||
VECEACH(decl->error.error_constants, i)
|
||||
{
|
||||
Decl *error_constant = decl->error.error_constants[i];
|
||||
if (error_constant->name == name)
|
||||
{
|
||||
assert(error_constant->resolve_status == RESOLVE_DONE);
|
||||
expr->type = decl->type;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr_const_set_int(&expr->const_expr, decl->error_constant.value, TYPE_U32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int *index)
|
||||
static Decl *strukt_recursive_search_member(Decl *strukt, const char *name)
|
||||
{
|
||||
VECEACH(strukt->strukt.members, i)
|
||||
{
|
||||
(*index)++;
|
||||
Decl *member = strukt->strukt.members[i];
|
||||
if (member->name == name) return member;
|
||||
if (!member->name && decl_is_struct_type(member))
|
||||
if (!member->name && type_is_structlike(member->type->canonical))
|
||||
{
|
||||
Decl *result = strukt_recursive_search_member(member, name, index);
|
||||
if (result) return result;
|
||||
Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_group(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_expr(context, to, expr->group_expr)) return false;
|
||||
*expr = *expr->group_expr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_expr(context, NULL, expr->access_expr.parent)) return false;
|
||||
@@ -364,8 +390,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
int index = -1;
|
||||
Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string, &index);
|
||||
Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string);
|
||||
if (!member)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string);
|
||||
@@ -381,7 +406,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex
|
||||
expr->access_expr.parent = deref;
|
||||
}
|
||||
expr->type = member->type;
|
||||
expr->access_expr.index = index;
|
||||
expr->access_expr.ref = member;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -389,21 +414,38 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp
|
||||
{
|
||||
TypeInfo *type_info = expr->type_access.type;
|
||||
if (!sema_resolve_type_info(context, type_info)) return false;
|
||||
if (!type_may_have_method_functions(type_info->type))
|
||||
Type *canonical = type_info->type->canonical;
|
||||
if (!type_may_have_method_functions(canonical))
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' does not have method functions.", type_to_error_string(type_info->type));
|
||||
return false;
|
||||
}
|
||||
Decl *decl = type_info->type->decl;
|
||||
Decl *decl = canonical->decl;
|
||||
// TODO add more constants that can be inspected?
|
||||
// e.g. SomeEnum.values, MyUnion.x.offset etc?
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_ENUM:
|
||||
if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_enum_constant(context, expr, decl);
|
||||
if (expr->type_access.name.type == TOKEN_CONST_IDENT)
|
||||
{
|
||||
if (!sema_expr_analyse_enum_constant(expr, expr->type_access.name.string, decl))
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, expr->type_access.name.string);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DECL_ERROR:
|
||||
if (expr->type_access.name.type == TOKEN_CONST_IDENT) return sema_expr_analyse_error_constant(context, expr, decl);
|
||||
if (expr->type_access.name.type == TOKEN_CONST_IDENT)
|
||||
{
|
||||
if (!sema_expr_analyse_error_constant(expr, expr->type_access.name.string, decl))
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, expr->type_access.name.string);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DECL_UNION:
|
||||
case DECL_STRUCT:
|
||||
@@ -448,9 +490,12 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr *
|
||||
{
|
||||
assert(expr->resolve_status == RESOLVE_NOT_DONE);
|
||||
expr->resolve_status = RESOLVE_RUNNING;
|
||||
expr->identifier_expr.decl = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier);
|
||||
Decl *res = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier);
|
||||
if (!res) return NULL;
|
||||
expr->identifier_expr.decl = res;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr->identifier_expr.decl;
|
||||
expr->type = res->type;
|
||||
return res;
|
||||
}
|
||||
|
||||
static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *access_expr)
|
||||
@@ -465,6 +510,7 @@ static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *acce
|
||||
}
|
||||
decl = access_expr->access_expr.ref = sema_analyse_init_identifier_string(context, decl->type->decl, access_expr->access_expr.sub_element.string);
|
||||
access_expr->resolve_status = RESOLVE_DONE;
|
||||
access_expr->type = decl->type;
|
||||
return decl;
|
||||
}
|
||||
|
||||
@@ -492,77 +538,94 @@ static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef enum
|
||||
static bool sema_expr_analyse_struct_designated_initializer(Context *context, Decl *assigned, Expr *initializer)
|
||||
{
|
||||
INIT_SEMA_ERROR,
|
||||
INIT_SEMA_NOT_FOUND,
|
||||
INIT_SEMA_OK
|
||||
} InitSemaResult;
|
||||
Expr **init_expressions = initializer->expr_initializer.initializer_expr;
|
||||
|
||||
static InitSemaResult sema_expr_analyse_struct_named_initializer_list(Context *context, Decl *assigned, Expr *expr_list)
|
||||
{
|
||||
VECEACH(expr_list->initializer_expr, i)
|
||||
VECEACH(init_expressions, i)
|
||||
{
|
||||
Expr *expr = expr_list->initializer_expr[i];
|
||||
if (expr->expr_kind != EXPR_BINARY && expr->binary_expr.operator != BINARYOP_ASSIGN)
|
||||
Expr *expr = init_expressions[i];
|
||||
|
||||
// 1. Ensure that're seeing expr = expr on the top level.
|
||||
if (expr->expr_kind != EXPR_BINARY || expr->binary_expr.operator != BINARYOP_ASSIGN)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
SEMA_ERROR(expr, "Named and non-named initializers are not allowed together, please choose one or the other.");
|
||||
return INIT_SEMA_ERROR;
|
||||
}
|
||||
// If there is an unexpected expression and no previous element then this is a normal initializer list.
|
||||
return INIT_SEMA_NOT_FOUND;
|
||||
SEMA_ERROR(expr, "Expected an initializer on the format 'foo = 123' here.");
|
||||
return false;
|
||||
}
|
||||
Expr *path = expr->binary_expr.left;
|
||||
Expr *value = expr->binary_expr.right;
|
||||
Decl *result = sema_analyse_init_path(context, assigned, path);
|
||||
if (!result)
|
||||
if (!sema_analyse_init_path(context, assigned, path))
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
SEMA_ERROR(path, "Unexpected element when initializing '%s', did you get the name right?", assigned->name);
|
||||
return INIT_SEMA_ERROR;
|
||||
}
|
||||
return INIT_SEMA_NOT_FOUND;
|
||||
SEMA_ERROR(path, "This is not a valid member of '%s'.", type_to_error_string(assigned->type));
|
||||
return false;
|
||||
}
|
||||
if (!sema_analyse_expr_of_required_type(context, result->type, value)) return INIT_SEMA_ERROR;
|
||||
Expr *value = expr->binary_expr.right;
|
||||
if (!sema_analyse_expr_of_required_type(context, path->type, value)) return false;
|
||||
expr->type = path->type;
|
||||
}
|
||||
return INIT_SEMA_OK;
|
||||
initializer->expr_initializer.init_type = INITIALIZER_DESIGNATED;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_struct_initializer_list(Context *context, Type *assigned, Expr *expr)
|
||||
static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, Decl *assigned, Expr *initializer)
|
||||
{
|
||||
Decl **members = assigned->decl->strukt.members;
|
||||
unsigned size = vec_size(members);
|
||||
// Zero size init will initialize to empty.
|
||||
if (size == 0) return true;
|
||||
Expr **elements = initializer->expr_initializer.initializer_expr;
|
||||
Decl **members = assigned->strukt.members;
|
||||
initializer->expr_initializer.init_type = INITIALIZER_NORMAL;
|
||||
unsigned size = vec_size(elements);
|
||||
unsigned expected_members = vec_size(members);
|
||||
|
||||
InitSemaResult result = sema_expr_analyse_struct_named_initializer_list(context, assigned->decl, expr);
|
||||
if (result == INIT_SEMA_ERROR) return false;
|
||||
if (result == INIT_SEMA_OK)
|
||||
// For struct number of members must be the same as the size of the struct.
|
||||
|
||||
assert(size > 0);
|
||||
if (expected_members == 0)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
if (assigned->type_kind == TYPE_UNION)
|
||||
{
|
||||
SEMA_ERROR(expr->initializer_expr[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }");
|
||||
SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty.");
|
||||
return false;
|
||||
}
|
||||
if (size < vec_size(expr->initializer_expr))
|
||||
|
||||
bool is_union = assigned->decl_kind == DECL_UNION;
|
||||
expected_members = is_union ? 1 : expected_members;
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
SEMA_ERROR(expr->initializer_expr[size], "Too many elements in initializer, expected only %d.", size);
|
||||
if (i >= expected_members)
|
||||
{
|
||||
SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members);
|
||||
return false;
|
||||
}
|
||||
if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false;
|
||||
}
|
||||
if (expected_members > size)
|
||||
{
|
||||
SEMA_ERROR(elements[size - 1], "Few elements in initializer, there should be elements after this one.");
|
||||
return false;
|
||||
}
|
||||
VECEACH(expr->initializer_expr, i)
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, members[i]->type, expr->initializer_expr[i])) return false;
|
||||
}
|
||||
expr->type = assigned;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_struct_initializer(Context *context, Type *assigned, Expr *expr)
|
||||
{
|
||||
expr->type = assigned;
|
||||
|
||||
Expr **init_expressions = expr->expr_initializer.initializer_expr;
|
||||
|
||||
// 1. Zero size init will initialize to empty.
|
||||
if (vec_size(init_expressions) == 0)
|
||||
{
|
||||
expr->expr_initializer.init_type = INITIALIZER_ZERO;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Check if we might have a designated initializer
|
||||
// this means that in this case we're actually not resolving macros here.
|
||||
if (init_expressions[0]->expr_kind == EXPR_BINARY && init_expressions[0]->binary_expr.operator == BINARYOP_ASSIGN)
|
||||
{
|
||||
return sema_expr_analyse_struct_designated_initializer(context, assigned->decl, expr);
|
||||
}
|
||||
|
||||
// 3. Otherwise use the plain initializer.
|
||||
return sema_expr_analyse_struct_plain_initializer(context, assigned->decl, expr);
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
assert(to);
|
||||
@@ -572,7 +635,9 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to
|
||||
{
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
if (decl_is_struct_type(assigned->decl)) return sema_expr_analyse_struct_initializer_list(context, assigned, expr);
|
||||
if (decl_is_struct_type(assigned->decl)) return sema_expr_analyse_struct_initializer(context,
|
||||
assigned,
|
||||
expr);
|
||||
break;
|
||||
case TYPE_ARRAY:
|
||||
TODO
|
||||
@@ -831,6 +896,8 @@ static bool binary_arithmetic_promotion(Expr *left, Expr *right, Type *left_type
|
||||
*/
|
||||
static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
{
|
||||
// TODO enums
|
||||
|
||||
bool is_mod = expr->binary_expr.operator == BINARYOP_SUB_MOD;
|
||||
|
||||
// 1. Analyse a and b. Do not push down if this is a -%
|
||||
@@ -926,6 +993,8 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *
|
||||
*/
|
||||
static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr *left, Expr *right)
|
||||
{
|
||||
// TODO enums
|
||||
|
||||
bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD;
|
||||
|
||||
// 1. Promote everything to the recipient type – if possible
|
||||
@@ -1446,11 +1515,15 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp
|
||||
Type *max = type_find_max_type(left_type, right_type);
|
||||
|
||||
// 4. If no common type, then that's an error:
|
||||
if (!max) goto ERR;
|
||||
if (!max)
|
||||
{
|
||||
SEMA_ERROR(expr, "'%s' and '%s' are different types and cannot be compared.",
|
||||
type_to_error_string(left->type), type_to_error_string(right->type));
|
||||
};
|
||||
|
||||
// 5. Most types can do equality, but not all can do comparison,
|
||||
// so we need to check that as well.
|
||||
if (is_equality_type_op)
|
||||
if (!is_equality_type_op)
|
||||
{
|
||||
switch (max->type_kind)
|
||||
{
|
||||
@@ -1496,21 +1569,7 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp
|
||||
// 7. Do constant folding.
|
||||
if (both_const(left, right))
|
||||
{
|
||||
switch (left->const_expr.kind)
|
||||
{
|
||||
case TYPE_BOOL:
|
||||
SEMA_ERROR(expr, "Cannot compare booleans, convert them into integers first.");
|
||||
return false;
|
||||
case ALL_FLOATS:
|
||||
case ALL_INTS:
|
||||
expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator);
|
||||
return true;
|
||||
case TYPE_STRING:
|
||||
SEMA_ERROR(expr, "Cannot compare strings.");
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr->const_expr.b = expr_const_compare(&left->const_expr, &right->const_expr, expr->binary_expr.operator);
|
||||
expr->const_expr.kind = TYPE_BOOL;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
}
|
||||
@@ -1678,6 +1737,9 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *
|
||||
case TYPE_STRING:
|
||||
expr->const_expr.b = !inner->const_expr.string.len;
|
||||
break;
|
||||
case TYPE_ERROR:
|
||||
case TYPE_ENUM:
|
||||
TODO
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -1871,6 +1933,7 @@ static Expr *expr_shallow_copy(Expr *source)
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list);
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr);
|
||||
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source);
|
||||
@@ -1929,6 +1992,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex
|
||||
Expr *expr = expr_shallow_copy(source_expr);
|
||||
switch (source_expr->expr_kind)
|
||||
{
|
||||
case EXPR_DESIGNATED_INIT:
|
||||
// This type of expression is only created after analysis.
|
||||
UNREACHABLE
|
||||
case EXPR_EXPR_BLOCK:
|
||||
ast_copy_list_from_macro(context, macro, &expr->expr_block.stmts);
|
||||
return expr;
|
||||
@@ -1975,18 +2041,14 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex
|
||||
EXPR_COPY(expr->subscript_expr.expr);
|
||||
EXPR_COPY(expr->subscript_expr.index);
|
||||
return expr;
|
||||
case EXPR_GROUP:
|
||||
EXPR_COPY(expr->group_expr->group_expr);
|
||||
return expr;
|
||||
case EXPR_ACCESS:
|
||||
EXPR_COPY(expr->access_expr.parent);
|
||||
return expr;
|
||||
case EXPR_STRUCT_VALUE:
|
||||
expr->struct_value_expr.type = type_info_copy_from_macro(context, macro, expr->struct_value_expr.type);
|
||||
EXPR_COPY(expr->struct_value_expr.init_expr);
|
||||
return expr;
|
||||
case EXPR_STRUCT_INIT_VALUES:
|
||||
TODO
|
||||
return expr;
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
expr->initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->initializer_expr);
|
||||
expr->expr_initializer.initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->expr_initializer.initializer_expr);
|
||||
return expr;
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
expr->expression_list = expr_copy_expr_list_from_macro(context, macro, expr->expression_list);
|
||||
@@ -2387,6 +2449,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
case EXPR_POISONED:
|
||||
return false;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
case EXPR_DESIGNATED_INIT:
|
||||
UNREACHABLE
|
||||
case EXPR_EXPR_BLOCK:
|
||||
return sema_expr_analyse_expr_block(context, to, expr);
|
||||
@@ -2416,12 +2479,10 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
return sema_expr_analyse_sizeof(context, to, expr);
|
||||
case EXPR_SUBSCRIPT:
|
||||
return sema_expr_analyse_subscript(context, to, expr);
|
||||
case EXPR_GROUP:
|
||||
return sema_expr_analyse_group(context, to, expr);
|
||||
case EXPR_ACCESS:
|
||||
return sema_expr_analyse_access(context, to, expr);
|
||||
case EXPR_STRUCT_VALUE:
|
||||
return sema_expr_analyse_struct_value(context, to, expr);
|
||||
case EXPR_STRUCT_INIT_VALUES:
|
||||
return sema_expr_analyse_struct_init_values(context, to, expr);
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
return sema_expr_analyse_initializer_list(context, to, expr);
|
||||
case EXPR_CAST:
|
||||
|
||||
@@ -20,7 +20,7 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
||||
context->current_scope->local_decl_start = context->last_local;
|
||||
context->current_scope->defers.start = parent_defer;
|
||||
context->current_scope->defers.end = parent_defer;
|
||||
if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK))
|
||||
if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK | SCOPE_NEXT))
|
||||
{
|
||||
context->current_scope->flags = flags;
|
||||
}
|
||||
@@ -550,29 +550,40 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cast the case expression to the switch type and ensure it is constant.
|
||||
*
|
||||
* @return true if the analysis succeeds.
|
||||
*/
|
||||
static bool sema_analyse_case_expr(Context *context, Type* to_type, Ast *case_stmt)
|
||||
{
|
||||
assert(to_type);
|
||||
Expr *case_expr = case_stmt->case_stmt.expr;
|
||||
// TODO handle enums
|
||||
// TODO string expr
|
||||
if (!sema_analyse_expr_of_required_type(context, to_type, case_expr)) return false;
|
||||
|
||||
// 1. Try to do implicit conversion to the correct type.
|
||||
if (!sema_analyse_expr(context, to_type, case_expr)) return false;
|
||||
|
||||
// 2. Skip continued analysis if it's not constant.
|
||||
if (case_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "This must be a constant expression.");
|
||||
SEMA_ERROR(case_expr, "A case value must always be constant at compile time.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cast_to_runtime(case_expr)) return false;
|
||||
Type *case_type = case_expr->type->canonical;
|
||||
Type *to_type_canonical = to_type->canonical;
|
||||
|
||||
if (case_expr->const_expr.kind == TYPE_BOOL) return true;
|
||||
// 3. If we already have the same type we're done.
|
||||
if (to_type_canonical == case_type) return true;
|
||||
|
||||
if (!type_is_integer(case_expr->type))
|
||||
// 4. Otherwise check if we have an enum receiving type and a number on
|
||||
// in the case. In that case we do an implicit conversion.
|
||||
if (to_type_canonical->type_kind == TYPE_ENUM && type_is_any_integer(case_expr->type))
|
||||
{
|
||||
SEMA_ERROR(case_expr, "The 'case' value must be a boolean or integer constant.");
|
||||
return false;
|
||||
return cast(case_expr, to_type, CAST_TYPE_EXPLICIT);
|
||||
}
|
||||
return true;
|
||||
|
||||
return cast_implicit(case_expr, to_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -614,15 +625,23 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
bool success = sema_analyse_cond(context, cond, false);
|
||||
|
||||
Type *switch_type = ast_cond_type(cond)->canonical;
|
||||
if (switch_type == type_bool || !type_is_integer(switch_type))
|
||||
switch (switch_type->type_kind)
|
||||
{
|
||||
SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type));
|
||||
return false;
|
||||
case ALL_INTS:
|
||||
assert(switch_type->type_kind != TYPE_IXX);
|
||||
case TYPE_BOOL:
|
||||
case TYPE_ERROR:
|
||||
case TYPE_META_TYPE:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_STRING:
|
||||
break;
|
||||
default:
|
||||
SEMA_ERROR(cond, "It is not possible to switch over '%s'.", type_to_error_string(switch_type));
|
||||
return false;
|
||||
}
|
||||
Ast *default_case = NULL;
|
||||
assert(context->current_scope->defers.start == context->current_scope->defers.end);
|
||||
|
||||
// TODO enum, exhaustive cases.
|
||||
ExitType prev_exit = context->current_scope->exit;
|
||||
bool exhaustive = false;
|
||||
ExitType lowest_exit = EXIT_NONE;
|
||||
@@ -693,15 +712,10 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
if (lowest_exit <= EXIT_BREAK) lowest_exit = prev_exit;
|
||||
// Check exhaustive use.
|
||||
context->current_scope->exit = exhaustive ? lowest_exit : EXIT_NONE;
|
||||
context_pop_scope(context);
|
||||
if (!success) return false;
|
||||
// Is this a typeless switch value?
|
||||
if (switch_type->type_kind == TYPE_IXX)
|
||||
{
|
||||
|
||||
TODO
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string);
|
||||
type_info->type = decl->type;
|
||||
type_info->type = decl->typedef_decl.type_info->type;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
case DECL_POISONED:
|
||||
|
||||
@@ -109,6 +109,7 @@ void target_setup()
|
||||
LLVMTypeRef float_type = LLVMFloatType();
|
||||
LLVMTypeRef double_type = LLVMDoubleType();
|
||||
LLVMTypeRef quad_type = LLVMFP128Type();
|
||||
LLVMTypeRef pointer_type = LLVMPointerType(int_type, 0);
|
||||
build_target.align_byte = LLVMABIAlignmentOfType(build_target.llvm_data_layout, byte_type);
|
||||
build_target.align_short = LLVMABIAlignmentOfType(build_target.llvm_data_layout, short_type);
|
||||
build_target.align_int = LLVMABIAlignmentOfType(build_target.llvm_data_layout, int_type);
|
||||
@@ -116,6 +117,7 @@ void target_setup()
|
||||
build_target.align_f128 = LLVMABIAlignmentOfType(build_target.llvm_data_layout, quad_type);
|
||||
build_target.align_double = LLVMABIAlignmentOfType(build_target.llvm_data_layout, double_type);
|
||||
build_target.align_float = LLVMABIAlignmentOfType(build_target.llvm_data_layout, float_type);
|
||||
build_target.align_pointer = LLVMABIAlignmentOfType(build_target.llvm_data_layout, pointer_type);
|
||||
build_target.little_endian = LLVMByteOrder(build_target.llvm_data_layout) == LLVMLittleEndian;
|
||||
build_target.width_c_short = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_SHORT);
|
||||
build_target.width_c_int = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_INT);
|
||||
|
||||
@@ -151,15 +151,15 @@ typedef struct
|
||||
bool asm_supported;
|
||||
bool float_128;
|
||||
bool float_16;
|
||||
unsigned align_min_pointer;
|
||||
unsigned align_min_byte;
|
||||
unsigned align_min_short;
|
||||
unsigned align_min_int;
|
||||
unsigned align_min_long;
|
||||
unsigned align_min_half;
|
||||
unsigned align_min_float;
|
||||
unsigned align_min_double;
|
||||
unsigned align_min_f128;
|
||||
unsigned align_pref_pointer;
|
||||
unsigned align_pref_byte;
|
||||
unsigned align_pref_short;
|
||||
unsigned align_pref_int;
|
||||
unsigned align_pref_long;
|
||||
unsigned align_pref_half;
|
||||
unsigned align_pref_float;
|
||||
unsigned align_pref_double;
|
||||
unsigned align_pref_f128;
|
||||
unsigned align_pointer;
|
||||
unsigned align_byte;
|
||||
unsigned align_short;
|
||||
|
||||
@@ -4,22 +4,44 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
Type *type_bool, *type_void, *type_string, *type_voidptr;
|
||||
Type *type_float, *type_double;
|
||||
Type *type_char, *type_short, *type_int, *type_long, *type_isize;
|
||||
Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
||||
Type *type_compint, *type_compfloat;
|
||||
Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong;
|
||||
Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong;
|
||||
static Type t_u0, t_str, t_u1, t_i8, t_i16, t_i32, t_i64, t_ixx;
|
||||
static Type t_u8, t_u16, t_u32, t_u64;
|
||||
static Type t_f32, t_f64, t_fxx;
|
||||
static Type t_usz, t_isz;
|
||||
static Type t_cus, t_cui, t_cul, t_cull;
|
||||
static Type t_cs, t_ci, t_cl, t_cll;
|
||||
static Type t_voidstar, t_typeid;
|
||||
static Type t_err;
|
||||
|
||||
Type *type_bool = &t_u1;
|
||||
Type *type_void = &t_u0;
|
||||
Type *type_string = &t_str;
|
||||
Type *type_voidptr = &t_voidstar;
|
||||
Type *type_float = &t_f32;
|
||||
Type *type_double = &t_f64;
|
||||
Type *type_error = &t_err;
|
||||
Type *type_typeid = &t_typeid;
|
||||
Type *type_char = &t_i8;
|
||||
Type *type_short = &t_i16;
|
||||
Type *type_int = &t_i32;
|
||||
Type *type_long = &t_i64;
|
||||
Type *type_isize = &t_isz;
|
||||
Type *type_byte = &t_u8;
|
||||
Type *type_ushort = &t_u16;
|
||||
Type *type_uint = &t_u32;
|
||||
Type *type_ulong = &t_u64;
|
||||
Type *type_usize = &t_usz;
|
||||
Type *type_compint = &t_ixx;
|
||||
Type *type_compfloat = &t_fxx;
|
||||
Type *type_c_short = &t_cs;
|
||||
Type *type_c_int = &t_ci;
|
||||
Type *type_c_long = &t_cl;
|
||||
Type *type_c_longlong = &t_cll;
|
||||
Type *type_c_ushort = &t_cus;
|
||||
Type *type_c_uint = &t_cui;
|
||||
Type *type_c_ulong = &t_cul;
|
||||
Type *type_c_ulonglong = &t_cull;
|
||||
|
||||
Type t_u0, t_str;
|
||||
Type t_u1, t_i8, t_i16, t_i32, t_i64, t_ixx;
|
||||
Type t_u8, t_u16, t_u32, t_u64, t_uxx;
|
||||
Type t_f32, t_f64, t_fxx;
|
||||
Type t_usz, t_isz;
|
||||
Type t_cus, t_cui, t_cul, t_cull;
|
||||
Type t_cs, t_ci, t_cl, t_cll;
|
||||
Type t_voidstar;
|
||||
|
||||
#define META_OFFSET 0
|
||||
#define PTR_OFFSET 1
|
||||
@@ -176,29 +198,18 @@ size_t type_size(Type *canonical)
|
||||
case TYPE_META_TYPE:
|
||||
return 0;
|
||||
case TYPE_ENUM:
|
||||
return type_size(canonical->decl->enums.type_info->type->canonical);
|
||||
return canonical->decl->enums.type_info->type->canonical->builtin.bytesize;
|
||||
case TYPE_ERROR:
|
||||
return type_error->canonical->builtin.bytesize;
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_ERROR:
|
||||
TODO
|
||||
return canonical->decl->strukt.size;
|
||||
case TYPE_VOID:
|
||||
return 1;
|
||||
case TYPE_BOOL:
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case ALL_INTS:
|
||||
case ALL_FLOATS:
|
||||
return canonical->builtin.bytesize;
|
||||
case TYPE_IXX:
|
||||
return 8;
|
||||
case TYPE_FXX:
|
||||
return 8;
|
||||
case TYPE_FUNC:
|
||||
case TYPE_POINTER:
|
||||
case TYPE_VARARRAY:
|
||||
@@ -211,7 +222,44 @@ size_t type_size(Type *canonical)
|
||||
case TYPE_ERROR_UNION:
|
||||
TODO
|
||||
}
|
||||
TODO
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
size_t type_abi_alignment(Type *canonical)
|
||||
{
|
||||
assert(canonical && canonical->canonical == canonical);
|
||||
switch (canonical->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_VOID:
|
||||
UNREACHABLE;
|
||||
case TYPE_META_TYPE:
|
||||
return 0;
|
||||
case TYPE_ENUM:
|
||||
return canonical->decl->enums.type_info->type->canonical->builtin.abi_alignment;
|
||||
case TYPE_ERROR:
|
||||
return type_error->canonical->builtin.abi_alignment;
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
return canonical->decl->strukt.abi_alignment;
|
||||
case TYPE_BOOL:
|
||||
case ALL_INTS:
|
||||
case ALL_FLOATS:
|
||||
return canonical->builtin.abi_alignment;
|
||||
case TYPE_FUNC:
|
||||
case TYPE_POINTER:
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_STRING:
|
||||
return t_usz.canonical->builtin.abi_alignment;
|
||||
case TYPE_ARRAY:
|
||||
return type_abi_alignment(canonical->array.base);
|
||||
case TYPE_SUBARRAY:
|
||||
TODO
|
||||
case TYPE_ERROR_UNION:
|
||||
TODO
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline void create_type_cache(Type *canonical_type)
|
||||
@@ -346,31 +394,29 @@ Type *type_get_array(Type *arr_type, uint64_t len)
|
||||
return type_create_array(arr_type, len, false);
|
||||
}
|
||||
|
||||
static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bitsize,
|
||||
static void type_create(const char *name, Type *location, TypeKind kind, unsigned bitsize,
|
||||
unsigned align, unsigned pref_align)
|
||||
{
|
||||
*location = (Type) {
|
||||
.type_kind = kind,
|
||||
.builtin.bytesize = (bitsize + 7) / 8,
|
||||
.builtin.bitsize = bitsize,
|
||||
.builtin.min_alignment = align,
|
||||
.builtin.abi_alignment = align,
|
||||
.builtin.pref_alignment = pref_align,
|
||||
.name = name,
|
||||
.canonical = location,
|
||||
};
|
||||
location->name = name;
|
||||
location->canonical = location;
|
||||
*ptr = location;
|
||||
}
|
||||
|
||||
static void type_create_alias(const char *name, Type *location, Type **ptr, Type *canonical)
|
||||
static void type_create_alias(const char *name, Type *location, Type *canonical)
|
||||
{
|
||||
*location = (Type) {
|
||||
.type_kind = TYPE_TYPEDEF,
|
||||
.name = name,
|
||||
.canonical = canonical
|
||||
};
|
||||
*ptr = location;
|
||||
}
|
||||
|
||||
|
||||
@@ -383,7 +429,7 @@ void builtin_setup(Target *target)
|
||||
type_string.type_kind = TYPE_STRING;
|
||||
*/
|
||||
#define DEF_TYPE(_name, _shortname, _type, _bits, _align) \
|
||||
type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_min_ ## _align, target->align_ ## _align)
|
||||
type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target->align_pref_ ## _align)
|
||||
|
||||
DEF_TYPE(bool, t_u1, TYPE_BOOL, 1, byte);
|
||||
DEF_TYPE(float, t_f32, TYPE_F32, 32, float);
|
||||
@@ -404,26 +450,28 @@ type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_mi
|
||||
|
||||
#undef DEF_TYPE
|
||||
|
||||
type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer);
|
||||
type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_pref_pointer, target->align_pointer);
|
||||
create_type_cache(type_void);
|
||||
type_void->type_cache[0] = &t_voidstar;
|
||||
t_voidstar.pointer = type_void;
|
||||
type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 32, 0, 0);
|
||||
type_create("compfloat", &t_fxx, &type_compfloat, TYPE_FXX, 64, 0, 0);
|
||||
type_create("compint", &t_ixx, TYPE_IXX, 32, 0, 0);
|
||||
type_create("compfloat", &t_fxx, TYPE_FXX, 64, 0, 0);
|
||||
|
||||
type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_bitsize(target->width_pointer));
|
||||
type_create_alias("isize", &t_isz, &type_isize, type_signed_int_by_bitsize(target->width_pointer));
|
||||
type_create_alias("usize", &t_usz, type_unsigned_int_by_bitsize(target->width_pointer));
|
||||
type_create_alias("isize", &t_isz, type_signed_int_by_bitsize(target->width_pointer));
|
||||
|
||||
type_create_alias("c_ushort", &t_cus, &type_c_ushort, type_unsigned_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_uint", &t_cui, &type_c_uint, type_unsigned_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("c_ulong", &t_cul, &type_c_ulong, type_unsigned_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_ulonglong", &t_cull, &type_c_ulonglong, type_unsigned_int_by_bitsize(target->width_c_long_long));
|
||||
|
||||
type_create_alias("c_short", &t_cs, &type_c_short, type_signed_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_int", &t_ci, &type_c_int, type_signed_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("c_long", &t_cl, &type_c_long, type_signed_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_longlong", &t_cll, &type_c_longlong, type_signed_int_by_bitsize(target->width_c_long_long));
|
||||
type_create_alias("c_ushort", &t_cus, type_unsigned_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_uint", &t_cui, type_unsigned_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("c_ulong", &t_cul, type_unsigned_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_ulonglong", &t_cull, type_unsigned_int_by_bitsize(target->width_c_long_long));
|
||||
|
||||
type_create_alias("c_short", &t_cs, type_signed_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_int", &t_ci, type_signed_int_by_bitsize(target->width_c_int));
|
||||
// TODO fix error size
|
||||
type_create_alias("error", &t_err, type_signed_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("typeid", &t_typeid, type_signed_int_by_bitsize(target->width_pointer));
|
||||
type_create_alias("c_long", &t_cl, type_signed_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_longlong", &t_cll, type_signed_int_by_bitsize(target->width_c_long_long));
|
||||
|
||||
}
|
||||
|
||||
@@ -621,6 +669,7 @@ Type *type_find_max_type(Type *type, Type *other)
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
if (other->type_kind == TYPE_ENUM) return type_find_max_type(type, other->decl->enums.type_info->type->canonical);
|
||||
case TYPE_F32:
|
||||
case TYPE_F64:
|
||||
case TYPE_FXX:
|
||||
|
||||
@@ -42,6 +42,7 @@ TargetInfo target_info_new()
|
||||
.asm_supported = false,
|
||||
.float_128 = false,
|
||||
.float_16 = false,
|
||||
.align_pointer = 8,
|
||||
.align_byte = 8,
|
||||
.align_c_int = 32,
|
||||
.align_c_long = 32,
|
||||
|
||||
@@ -267,6 +267,7 @@ static inline void* _expand(void *vec, size_t element_size)
|
||||
#define VECEACH(_vec, _index) \
|
||||
for (unsigned _index = 0, __vecsize = vec_size(_vec); _index < __vecsize; _index++)
|
||||
|
||||
|
||||
#define VECNEW(_type, _capacity) ((_type *)(_vec_new(sizeof(_type), _capacity) + 1))
|
||||
#define VECADD(_vec, _value) \
|
||||
({ \
|
||||
|
||||
Reference in New Issue
Block a user