mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Started work on ranges. Added weak/noreturn/inline/cname/stdcall/section/alignment attributes. Some work on subarrays. Fixes to throws. Function pointers work.
This commit is contained in:
@@ -70,7 +70,7 @@ add_executable(c3c
|
||||
src/compiler/context.c
|
||||
src/compiler/sema_expr.c
|
||||
src/compiler/enums.h
|
||||
src/compiler/casts.c
|
||||
src/compiler/sema_casts.c
|
||||
src/compiler/target.c
|
||||
src/compiler/compiler.h
|
||||
src/compiler/types.c
|
||||
|
||||
10
README.md
10
README.md
@@ -18,24 +18,28 @@ C3 tries to be an alternative in the the C/C++ niche: fast and close to the meta
|
||||
|
||||
### Current status
|
||||
|
||||
Most work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss C3 on the r/ProgrammingLanguages Discord server: https://discord.gg/cfu4wdk
|
||||
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/?1EFo – this is courtesy of the
|
||||
developer of Judge0.
|
||||
|
||||
Design work is still being done in the design draft here: https://c3lang.github.io/c3docs/. If you have suggestions, send a mail to [christoffer@aegik.com](mailto:christoffer@aegik.com), [file an issue](https://github.com/c3lang/c3c/issues) or discuss C3 on the r/ProgrammingLanguages Discord server: https://discord.gg/cfu4wdk
|
||||
|
||||
There are some small work being done on the parser here, but most of the structure is still missing:
|
||||
|
||||
#### What's missing in the parser
|
||||
|
||||
- `asm` sections.
|
||||
- bitstructs
|
||||
- array range initializers e.g. `{ [1..2] = 2 }`
|
||||
- assert/$assert as keywords
|
||||
- Docs not linked to statements/functions/declarations.
|
||||
|
||||
#### What's missing in the semantic analyser
|
||||
|
||||
- Incomplete handling of imports.
|
||||
- `next` not correct
|
||||
- Function signatures incomplete.
|
||||
- Function typedef not done.
|
||||
- `asm` not done.
|
||||
- `generic` not analysed.
|
||||
- `attribute` not analysed.
|
||||
- `$switch` and `$for` not handled.
|
||||
- Enums not correctly handled.
|
||||
- Type resolution not complete for all types.
|
||||
|
||||
@@ -2,10 +2,10 @@ Things missing:
|
||||
|
||||
* Attributes
|
||||
- All types: @noreflect, @deprecated
|
||||
- Struct: @packed, @aligned, @opaque
|
||||
- Struct: @packed, @opaque
|
||||
- Enums: @distinct, @noreflect
|
||||
- Unions: @packed, @aligned, @opaque
|
||||
- Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention()
|
||||
- Unions: @packed, @opaque
|
||||
- Functions: @reflect, @noreturn, @unused, @used, @interrupt, @naked, @convention()
|
||||
- Calls: @noinline, @inline
|
||||
- Variables, parameters: @unused
|
||||
- Constants, globals: @unused, @used, @section
|
||||
@@ -62,6 +62,7 @@ Things missing:
|
||||
- C ABI
|
||||
- Safe varargs
|
||||
- Malloc/free
|
||||
- Check that structs are transferred the right way.
|
||||
|
||||
* Pre-post conditions
|
||||
- Breakdown here
|
||||
|
||||
@@ -372,6 +372,7 @@ label_statement
|
||||
labeled_statement
|
||||
: label_statement
|
||||
| CASE constant_expression ':'
|
||||
| CASE constant_expression ELLIPSIS constant_expression ':'
|
||||
| DEFAULT ':'
|
||||
;
|
||||
|
||||
|
||||
@@ -659,11 +659,7 @@ func int testThrow(int x) throws Err
|
||||
return x * x;
|
||||
}
|
||||
|
||||
func int testThrowAny(int x) throws
|
||||
{
|
||||
if (x < 0) throw Err.TEST_ERR1;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
|
||||
func int oekt() throws
|
||||
{
|
||||
@@ -671,9 +667,46 @@ func int oekt() throws
|
||||
printf("Skipped.\n");
|
||||
NEXT:
|
||||
int x = try testThrow(-3);
|
||||
catch (error e)
|
||||
{
|
||||
printf("An error %p\n", cast(e, usize));
|
||||
throw e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
errset Xan
|
||||
{
|
||||
EE,
|
||||
FF,
|
||||
DD,
|
||||
}
|
||||
|
||||
func int testThrowAny(int x) throws
|
||||
{
|
||||
if (x < 0) throw Err.TEST_ERR1;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
func void testErrorBug()
|
||||
{
|
||||
printf("Test error multi\n");
|
||||
{
|
||||
printf("Will call\n");
|
||||
try oekt();
|
||||
}
|
||||
catch (Err e)
|
||||
{
|
||||
printf("Expected particular error.\n");
|
||||
}
|
||||
catch (error e)
|
||||
{
|
||||
error foo = Err.TEST_ERR1;
|
||||
printf("Not expected %p != %p\n", cast(e, usize), cast(foo, usize));
|
||||
printf("Unexpected any error error.\n");
|
||||
}
|
||||
printf("End\n");
|
||||
}
|
||||
func void testErrorMulti()
|
||||
{
|
||||
defer printf("End defer\n");
|
||||
@@ -692,6 +725,8 @@ func void testErrorMulti()
|
||||
catch (error e)
|
||||
{
|
||||
defer printf("Left any error\n");
|
||||
error foo = Err.TEST_ERR1;
|
||||
printf("Not expected %p != %p\n", cast(e, usize), cast(foo, usize));
|
||||
printf("Unexpected any error error.\n");
|
||||
}
|
||||
printf("End\n");
|
||||
@@ -771,15 +806,50 @@ func void testErrors()
|
||||
printf("Throws: %d\n", throwsDone.times);
|
||||
printf("End of errors\n");
|
||||
}
|
||||
macro void arrayCallMacro($x)
|
||||
{
|
||||
printf("Array call: %d, %d\n", $x[0], $x[1]);
|
||||
}
|
||||
|
||||
typedef func int(int) as IntInt;
|
||||
|
||||
func int funcTest(int i)
|
||||
{
|
||||
return i * i;
|
||||
}
|
||||
|
||||
func void testFuncPointer()
|
||||
{
|
||||
IntInt bobd = &funcTest;
|
||||
int x = bobd(2);
|
||||
printf("Func test: %d\n", x);
|
||||
printf("Func test: %d\n", bobd(100));
|
||||
}
|
||||
|
||||
func void arrayCall(int[2] x) @inline @weak @align(16) @cname("Foo")
|
||||
{
|
||||
printf("Array call: %d, %d\n", x[0], x[1]);
|
||||
}
|
||||
func void testArray()
|
||||
{
|
||||
int[4] zebra = { [2] = 1 };
|
||||
int[4] deok = { 1, 2, 3, 4 };
|
||||
int[4] deok = { 1, 5, 9, 16 };
|
||||
int[] sa = &deok;
|
||||
int* sp = sa;
|
||||
WithArray boo;
|
||||
boo.x[0] = 2;
|
||||
printf("boo.x[0] = %d\n", boo.x[0]);
|
||||
printf("boo.x[2] = %d\n", boo.x[2]);
|
||||
printf("sa[2] = %d\n", sa[2]);
|
||||
printf("sa[3] = %d\n", sa[3]);
|
||||
deok[2] = 100;
|
||||
sa[1] = 999;
|
||||
printf("sa[2] = %d\n", sa[2]);
|
||||
printf("sp[2] = %d\n", sp[2]);
|
||||
printf("deok[1] = %d\n", deok[1]);
|
||||
int[2] two = { 142, 2 };
|
||||
arrayCall(two);
|
||||
//@arrayCallMacro(two);
|
||||
int[4] x;
|
||||
x[1] = 1;
|
||||
x[0] = 3;
|
||||
@@ -838,10 +908,38 @@ func void testType()
|
||||
typeid structSize = typeof(b);
|
||||
}
|
||||
|
||||
struct Big
|
||||
{
|
||||
long x;
|
||||
long y;
|
||||
long z;
|
||||
}
|
||||
func Big testStructReturn()
|
||||
{
|
||||
return Big { 1, 2, 3};
|
||||
}
|
||||
|
||||
func void testExprBlock()
|
||||
{
|
||||
printf("ExprBlock\n");
|
||||
int x = ({
|
||||
int y = 1;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
printf("%d\n", i);
|
||||
if (y % 3 == 0 && y % 2 == 0) return y;
|
||||
y++;
|
||||
}
|
||||
return 100;
|
||||
});
|
||||
printf("Was %d\n", x);
|
||||
}
|
||||
|
||||
func int main(int x)
|
||||
{
|
||||
|
||||
printf("Helo!\n");
|
||||
testErrorBug();
|
||||
testErrors();
|
||||
testDefault(y = 99);
|
||||
testPointers(2, 3);
|
||||
@@ -851,6 +949,8 @@ func int main(int x)
|
||||
testSimpleStruct(0);
|
||||
testUnion();
|
||||
testType();
|
||||
testExprBlock();
|
||||
testFuncPointer();
|
||||
int efd = 9;
|
||||
uint fefoek = 1;
|
||||
printf("Helo: %d\n", efd + cast(fefoek, int));
|
||||
|
||||
@@ -428,6 +428,16 @@ void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent)
|
||||
}
|
||||
DUMPF("(unresolved %s)", type_info->unresolved.name_loc.string);
|
||||
break;
|
||||
case TYPE_INFO_VARARRAY:
|
||||
DUMP("(vararray");
|
||||
DUMPTI(type_info->array.base);
|
||||
DUMPE();
|
||||
break;
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
DUMP("(subarray");
|
||||
DUMPTI(type_info->array.base);
|
||||
DUMPE();
|
||||
break;
|
||||
case TYPE_INFO_ARRAY:
|
||||
DUMP("(unresolved-array");
|
||||
DUMPTI(type_info->array.base);
|
||||
@@ -435,7 +445,7 @@ void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent)
|
||||
DUMPE();
|
||||
break;
|
||||
case TYPE_INFO_POINTER:
|
||||
DUMP("pointer");
|
||||
DUMP("(pointer");
|
||||
DUMPTI(type_info->pointer);
|
||||
DUMPE();
|
||||
break;
|
||||
@@ -821,7 +831,6 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
|
||||
DUMPEND();
|
||||
case DECL_IMPORT:
|
||||
DUMPF("(import %s", decl->name);
|
||||
|
||||
// TODO
|
||||
DUMPEND();
|
||||
case DECL_ATTRIBUTE:
|
||||
|
||||
@@ -19,9 +19,11 @@ typedef uint32_t SourceLoc;
|
||||
#define MAX_SCOPE_DEPTH 0xFF
|
||||
#define MAX_PATH 1024
|
||||
#define MAX_DEFERS 0xFFFF
|
||||
#define MAX_MACRO_NESTING 1024
|
||||
#define MAX_FUNCTION_SIGNATURE_SIZE 2048
|
||||
#define MAX_PARAMS 512
|
||||
#define MAX_ERRORS 0xFFFF
|
||||
#define MAX_ALIGNMENT (1U << 29U)
|
||||
|
||||
typedef struct _Ast Ast;
|
||||
typedef struct _Decl Decl;
|
||||
@@ -226,6 +228,7 @@ typedef struct
|
||||
union
|
||||
{
|
||||
Expr *expr;
|
||||
uint32_t alignment;
|
||||
};
|
||||
} Attr;
|
||||
|
||||
@@ -261,7 +264,6 @@ typedef struct _VarDecl
|
||||
Expr *init_expr;
|
||||
Decl *parent;
|
||||
};
|
||||
void *backend_ref;
|
||||
void *backend_debug_ref;
|
||||
} VarDecl;
|
||||
|
||||
@@ -301,6 +303,7 @@ typedef enum
|
||||
ERROR_RETURN_MANY = 2,
|
||||
ERROR_RETURN_ANY = 3,
|
||||
} ErrorReturn;
|
||||
|
||||
typedef struct _FunctionSignature
|
||||
{
|
||||
CallConvention convention : 4;
|
||||
@@ -322,18 +325,27 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool attr_weak : 1;
|
||||
bool attr_noreturn : 1;
|
||||
bool attr_inline : 1;
|
||||
bool attr_noinline : 1;
|
||||
bool attr_cname : 1;
|
||||
bool attr_stdcall : 1;
|
||||
};
|
||||
|
||||
TypeInfo *type_parent;
|
||||
FunctionSignature function_signature;
|
||||
Ast *body;
|
||||
FuncAnnotations *annotations;
|
||||
Decl **locals;
|
||||
Ast **labels;
|
||||
void *backend_value;
|
||||
} FuncDecl;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AttributeDomains domains;
|
||||
AttributeDomain domains;
|
||||
FunctionSignature attr_signature;
|
||||
} AttrDecl;
|
||||
|
||||
@@ -378,7 +390,11 @@ typedef struct _Decl
|
||||
Visibility visibility : 2;
|
||||
ResolveStatus resolve_status : 2;
|
||||
bool is_packed : 1;
|
||||
/* bool is_exported : 1;
|
||||
void *ref;
|
||||
const char *cname;
|
||||
uint32_t alignment;
|
||||
const char *section;
|
||||
/* bool is_exported : 1;
|
||||
bool is_used : 1;
|
||||
bool is_used_public : 1;
|
||||
bool has_cname : 1;
|
||||
@@ -523,6 +539,7 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
bool is_struct_function : 1;
|
||||
bool is_pointer_call : 1;
|
||||
Expr *function;
|
||||
Expr **arguments;
|
||||
ThrowInfo *throw_info;
|
||||
@@ -963,6 +980,14 @@ typedef struct _Context
|
||||
};
|
||||
Type *rtype;
|
||||
int in_volatile_section;
|
||||
struct
|
||||
{
|
||||
bool in_macro_call : 1;
|
||||
bool in_macro : 1;
|
||||
int macro_counter;
|
||||
int macro_nesting;
|
||||
Expr *first_macro_call;
|
||||
};
|
||||
Decl *locals[MAX_LOCALS];
|
||||
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
||||
char path_scratch[MAX_PATH];
|
||||
@@ -1014,8 +1039,9 @@ 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 *type_typeid, *type_error_union, *type_error_base;
|
||||
|
||||
extern const char *attribute_list[NUMBER_OF_ATTRIBUTES];
|
||||
|
||||
extern const char *main_name;
|
||||
extern const char *main_kw;
|
||||
|
||||
#define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, _token.span)
|
||||
#define AST_NEW(_kind, _loc) new_ast(_kind, _loc)
|
||||
@@ -1257,6 +1283,8 @@ static inline Token wrap(const char *string)
|
||||
}
|
||||
|
||||
Type *type_get_ptr(Type *ptr_type);
|
||||
Type *type_get_subarray(Type *arr_type);
|
||||
Type *type_get_vararray(Type *arr_type);
|
||||
Type *type_get_meta(Type *meta_type);
|
||||
Type *type_get_indexed_type(Type *type);
|
||||
Type *type_get_array(Type *arr_type, uint64_t len);
|
||||
|
||||
@@ -189,8 +189,7 @@ bool context_add_import(Context *context, Path *path, Token token, Token alias)
|
||||
}
|
||||
|
||||
vec_add(context->imports, import);
|
||||
printf("Added import %s\n", path->module);
|
||||
|
||||
DEBUG_LOG("Added import %s\n", path->module);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,8 @@ typedef enum
|
||||
CAST_UIUI,
|
||||
CAST_UIFP,
|
||||
CAST_ENUMSI,
|
||||
CAST_APTSA,
|
||||
CAST_SAPTR,
|
||||
/*
|
||||
CAST_NONE,
|
||||
CAST_INLINE,
|
||||
@@ -242,9 +244,10 @@ typedef enum
|
||||
typedef enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, // =, *=, /=, %=, ...
|
||||
PREC_ASSIGNMENT, // =, *=, /=, %=, +=, etc
|
||||
PREC_TRY, // try
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_RANGE, // ...
|
||||
PREC_LOGICAL, // && ||
|
||||
PREC_RELATIONAL, // < > <= >= == !=
|
||||
PREC_ADDITIVE, // + -
|
||||
@@ -279,6 +282,8 @@ typedef enum
|
||||
TYPE_INFO_EXPRESSION,
|
||||
TYPE_INFO_ARRAY,
|
||||
TYPE_INFO_INC_ARRAY,
|
||||
TYPE_INFO_VARARRAY,
|
||||
TYPE_INFO_SUBARRAY,
|
||||
TYPE_INFO_POINTER,
|
||||
} TypeInfoKind;
|
||||
|
||||
@@ -345,7 +350,7 @@ typedef enum
|
||||
TOKEN_SHL, // <<
|
||||
|
||||
// Three or more
|
||||
TOKEN_ELIPSIS, // ...
|
||||
TOKEN_ELLIPSIS, // ...
|
||||
TOKEN_MINUS_MOD_ASSIGN, // -%=
|
||||
TOKEN_MULT_MOD_ASSIGN, // *%=
|
||||
TOKEN_PLUS_MOD_ASSIGN, // +%=
|
||||
@@ -561,7 +566,23 @@ typedef enum
|
||||
ATTR_CONST = 1 << 5,
|
||||
ATTR_ERROR = 1 << 6,
|
||||
ATTR_TYPEDEF = 1 << 7
|
||||
} AttributeDomains;
|
||||
} AttributeDomain;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ATTRIBUTE_INLINE,
|
||||
ATTRIBUTE_NOINLINE,
|
||||
ATTRIBUTE_STDCALL,
|
||||
ATTRIBUTE_OPAQUE,
|
||||
ATTRIBUTE_NORETURN,
|
||||
ATTRIBUTE_SECTION,
|
||||
ATTRIBUTE_CNAME,
|
||||
ATTRIBUTE_WEAK,
|
||||
ATTRIBUTE_ALIGN,
|
||||
ATTRIBUTE_PACKED,
|
||||
NUMBER_OF_ATTRIBUTES = ATTRIBUTE_PACKED + 1,
|
||||
ATTRIBUTE_NONE,
|
||||
} AttributeType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
@@ -333,7 +333,7 @@ static inline Token scan_hex(Lexer *lexer)
|
||||
}
|
||||
while (is_hex_or_(peek(lexer))) next(lexer);
|
||||
bool is_float = false;
|
||||
if (peek(lexer) == '.')
|
||||
if (peek(lexer) == '.' && peek_next(lexer) != '.')
|
||||
{
|
||||
is_float = true;
|
||||
next(lexer);
|
||||
@@ -360,7 +360,7 @@ static inline Token scan_dec(Lexer *lexer)
|
||||
{
|
||||
while (is_digit_or_(peek(lexer))) next(lexer);
|
||||
bool is_float = false;
|
||||
if (peek(lexer) == '.')
|
||||
if (peek(lexer) == '.' && peek_next(lexer) != '.')
|
||||
{
|
||||
is_float = true;
|
||||
next(lexer);
|
||||
@@ -495,7 +495,7 @@ Token lexer_scan_token(Lexer *lexer)
|
||||
case '#':
|
||||
return make_token(lexer, TOKEN_HASH, "#");
|
||||
case '$':
|
||||
return scan_ident(lexer, TOKEN_CT_TYPE_IDENT, TOKEN_CT_CONST_IDENT, TOKEN_CT_TYPE_IDENT, '$');
|
||||
return scan_ident(lexer, TOKEN_CT_IDENT, TOKEN_CT_CONST_IDENT, TOKEN_CT_TYPE_IDENT, '$');
|
||||
case ',':
|
||||
return make_token(lexer, TOKEN_COMMA, ",");
|
||||
case ';':
|
||||
@@ -513,7 +513,7 @@ Token lexer_scan_token(Lexer *lexer)
|
||||
case ']':
|
||||
return make_token(lexer, TOKEN_RBRACKET, "]");
|
||||
case '.':
|
||||
if (match(lexer, '.')) return match(lexer, '.') ? make_token(lexer, TOKEN_ELIPSIS, "...") : make_token(lexer, TOKEN_DOTDOT, "..");
|
||||
if (match(lexer, '.')) return match(lexer, '.') ? make_token(lexer, TOKEN_ELLIPSIS, "...") : make_token(lexer, TOKEN_DOTDOT, "..");
|
||||
return make_token(lexer, TOKEN_DOT, ".");
|
||||
case '~':
|
||||
return make_token(lexer, TOKEN_BIT_NOT, "~");
|
||||
|
||||
@@ -66,29 +66,29 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
|
||||
assert(decl->var.kind == VARDECL_GLOBAL);
|
||||
|
||||
// TODO fix name
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name);
|
||||
decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name);
|
||||
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
LLVMSetInitializer(decl->var.backend_ref, gencontext_emit_expr(context, decl->var.init_expr));
|
||||
LLVMSetInitializer(decl->ref, gencontext_emit_expr(context, decl->var.init_expr));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMSetInitializer(decl->var.backend_ref, LLVMConstInt(llvm_type(type_bool), 0, false));
|
||||
LLVMSetInitializer(decl->ref, LLVMConstInt(llvm_type(type_bool), 0, false));
|
||||
}
|
||||
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
|
||||
|
||||
switch (decl->visibility)
|
||||
{
|
||||
case VISIBLE_MODULE:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMProtectedVisibility);
|
||||
LLVMSetVisibility(decl->ref, LLVMProtectedVisibility);
|
||||
break;
|
||||
case VISIBLE_PUBLIC:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||
LLVMSetVisibility(decl->ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_EXTERN:
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
||||
LLVMSetVisibility(decl->ref, LLVMHiddenVisibility);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -212,6 +212,8 @@ unsigned nounwind_attribute;
|
||||
unsigned writeonly_attribute;
|
||||
unsigned readonly_attribute;
|
||||
unsigned optnone_attribute;
|
||||
unsigned noalias_attribute;
|
||||
unsigned sret_attribute;
|
||||
|
||||
void llvm_codegen_setup()
|
||||
{
|
||||
@@ -232,7 +234,8 @@ void llvm_codegen_setup()
|
||||
writeonly_attribute = lookup_attribute("writeonly");
|
||||
readonly_attribute = lookup_attribute("readonly");
|
||||
optnone_attribute = lookup_attribute("optnone");
|
||||
|
||||
sret_attribute = lookup_attribute("sret");
|
||||
noalias_attribute = lookup_attribute("noalias");
|
||||
intrinsics_setup = true;
|
||||
}
|
||||
|
||||
@@ -248,14 +251,14 @@ void gencontext_emit_struct_decl(GenContext *context, Decl *decl)
|
||||
switch (decl->visibility)
|
||||
{
|
||||
case VISIBLE_MODULE:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMProtectedVisibility);
|
||||
LLVMSetVisibility(global_name, LLVMProtectedVisibility);
|
||||
break;
|
||||
case VISIBLE_PUBLIC:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_EXTERN:
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility);
|
||||
LLVMSetVisibility(global_name, LLVMHiddenVisibility);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -278,13 +281,14 @@ void gencontext_emit_error_decl(GenContext *context, Decl *decl)
|
||||
LLVMTypeRef reserved_type = LLVMArrayType(llvm_type(type_char), slots);
|
||||
char *buffer = strcat_arena(decl->external_name, "_DOMAIN");
|
||||
LLVMValueRef global_name = LLVMAddGlobal(context->module, reserved_type, buffer);
|
||||
LLVMSetLinkage(global_name, LLVMInternalLinkage);
|
||||
LLVMSetLinkage(global_name, LLVMExternalLinkage);
|
||||
LLVMSetGlobalConstant(global_name, 1);
|
||||
LLVMSetInitializer(global_name, llvm_int(type_char, 1));
|
||||
decl->error.start_value = global_name;
|
||||
LLVMSetInitializer(global_name, LLVMConstAllOnes(reserved_type));
|
||||
decl->error.start_value = LLVMBuildPtrToInt(context->builder, global_name, llvm_type(type_usize), "");
|
||||
uint32_t min_align = upper_power_of_two(slots);
|
||||
uint32_t pointer_align = type_abi_alignment(type_voidptr);
|
||||
LLVMSetAlignment(global_name, pointer_align > min_align ? pointer_align : min_align);
|
||||
LLVMSetVisibility(global_name, LLVMDefaultVisibility);
|
||||
switch (decl->visibility)
|
||||
{
|
||||
case VISIBLE_MODULE:
|
||||
@@ -422,8 +426,9 @@ void llvm_codegen(Context *context)
|
||||
gencontext_destroy(&gen_context);
|
||||
}
|
||||
|
||||
void gencontext_add_attribute(GenContext context, unsigned attribute_id, LLVMValueRef value_to_add_attribute_to)
|
||||
void
|
||||
gencontext_add_attribute(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index)
|
||||
{
|
||||
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context.context, attribute_id, 0);
|
||||
LLVMAddAttributeAtIndex(value_to_add_attribute_to, -1, llvm_attr);
|
||||
LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute_id, 0);
|
||||
LLVMAddAttributeAtIndex(value_to_add_attribute_to, index, llvm_attr);
|
||||
}
|
||||
@@ -76,6 +76,7 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
{
|
||||
Expr *parent = expr->subscript_expr.expr;
|
||||
Expr *index = expr->subscript_expr.index;
|
||||
if (index->expr_kind == EXPR_RANGE) TODO;
|
||||
LLVMValueRef index_value = gencontext_emit_expr(context, index);
|
||||
LLVMValueRef parent_value;
|
||||
Type *type = parent->type->canonical;
|
||||
@@ -99,8 +100,19 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
llvm_type(type),
|
||||
parent_value, indices, 2, "arridx");
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
{
|
||||
// TODO insert trap on overflow.
|
||||
LLVMTypeRef subarray_type = llvm_type(type);
|
||||
parent_value = gencontext_emit_address(context, expr->subscript_expr.expr);
|
||||
LLVMValueRef pointer_addr = LLVMBuildStructGEP2(context->builder, subarray_type, parent_value, 0, "");
|
||||
LLVMTypeRef pointer_type = llvm_type(type_get_ptr(type->array.base));
|
||||
LLVMValueRef pointer = LLVMBuildLoad2(context->builder, pointer_type, pointer_addr, "");
|
||||
return LLVMBuildInBoundsGEP2(context->builder,
|
||||
llvm_type(type->array.base),
|
||||
pointer, &index_value, 1, "sarridx");
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_STRING:
|
||||
TODO
|
||||
default:
|
||||
@@ -175,7 +187,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
UNREACHABLE
|
||||
|
||||
case EXPR_IDENTIFIER:
|
||||
return expr->identifier_expr.decl->var.backend_ref;
|
||||
return expr->identifier_expr.decl->ref;
|
||||
case EXPR_UNARY:
|
||||
assert(expr->unary_expr.operator == UNARYOP_DEREF);
|
||||
return gencontext_emit_expr(context, expr->unary_expr.expr);
|
||||
@@ -211,12 +223,26 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
static inline LLVMValueRef gencontext_emit_error_cast(GenContext *context, LLVMValueRef value, Type *type)
|
||||
{
|
||||
LLVMValueRef global = type->decl->error.start_value;
|
||||
LLVMValueRef val = LLVMBuildBitCast(context->builder, global, llvm_type(type_usize), "");
|
||||
LLVMValueRef extend = LLVMBuildZExtOrBitCast(context->builder, value, llvm_type(type_usize), "");
|
||||
return LLVMBuildAdd(context->builder, val, extend, "");
|
||||
return LLVMBuildAdd(context->builder, global, extend, "");
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_type)
|
||||
LLVMValueRef gencontext_emit_arr_to_subarray_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
|
||||
{
|
||||
size_t size = from_type->pointer->array.len;
|
||||
LLVMTypeRef subarray_type = llvm_type(to_type);
|
||||
LLVMValueRef result = LLVMGetUndef(subarray_type);
|
||||
value = gencontext_emit_bitcast(context, value, type_get_ptr(from_type->pointer->array.base));
|
||||
result = LLVMBuildInsertValue(context->builder, result, value, 0, "");
|
||||
return LLVMBuildInsertValue(context->builder, result, llvm_int(type_usize, size), 1, "");
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_subarray_to_ptr_cast(GenContext *context, LLVMValueRef value, Type *to_type, Type *from_type)
|
||||
{
|
||||
return LLVMBuildExtractValue(context->builder, value, 0, "");
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type)
|
||||
{
|
||||
switch (cast_kind)
|
||||
{
|
||||
@@ -226,9 +252,13 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV
|
||||
case CAST_ERROR:
|
||||
UNREACHABLE
|
||||
case CAST_PTRPTR:
|
||||
return LLVMBuildPointerCast(context->builder, value, llvm_type(type), "ptrptr");
|
||||
return LLVMBuildPointerCast(context->builder, value, llvm_type(to_type), "ptrptr");
|
||||
case CAST_PTRXI:
|
||||
return LLVMBuildPtrToInt(context->builder, value, llvm_type(type), "ptrxi");
|
||||
return LLVMBuildPtrToInt(context->builder, value, llvm_type(to_type), "ptrxi");
|
||||
case CAST_APTSA:
|
||||
return gencontext_emit_arr_to_subarray_cast(context, value, to_type, from_type);
|
||||
case CAST_SAPTR:
|
||||
return gencontext_emit_subarray_to_ptr_cast(context, value, to_type, from_type);
|
||||
case CAST_VARRPTR:
|
||||
TODO
|
||||
case CAST_ARRPTR:
|
||||
@@ -236,51 +266,51 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV
|
||||
case CAST_STRPTR:
|
||||
TODO
|
||||
case CAST_ERREU:
|
||||
return gencontext_emit_error_cast(context, value, target_type);
|
||||
return gencontext_emit_error_cast(context, value, from_type);
|
||||
case CAST_EUERR:
|
||||
TODO
|
||||
case CAST_EUBOOL:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, llvm_int(type_usize, 0), "eubool");
|
||||
case CAST_PTRBOOL:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(type->canonical->pointer)), "ptrbool");
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(to_type->canonical->pointer)), "ptrbool");
|
||||
case CAST_BOOLINT:
|
||||
return LLVMBuildTrunc(context->builder, value, llvm_type(type), "boolsi");
|
||||
return LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "boolsi");
|
||||
case CAST_FPBOOL:
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUNE, value, LLVMConstNull(LLVMTypeOf(value)), "fpbool");
|
||||
case CAST_BOOLFP:
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(type), "boolfp");
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(to_type), "boolfp");
|
||||
case CAST_INTBOOL:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool");
|
||||
case CAST_FPFP:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildFPTrunc(context->builder, value, llvm_type(type), "fpfptrunc")
|
||||
: LLVMBuildFPExt(context->builder, value, llvm_type(type), "fpfpext");
|
||||
return type_convert_will_trunc(to_type, from_type)
|
||||
? LLVMBuildFPTrunc(context->builder, value, llvm_type(to_type), "fpfptrunc")
|
||||
: LLVMBuildFPExt(context->builder, value, llvm_type(to_type), "fpfpext");
|
||||
case CAST_FPSI:
|
||||
return LLVMBuildFPToSI(context->builder, value, llvm_type(type), "fpsi");
|
||||
return LLVMBuildFPToSI(context->builder, value, llvm_type(to_type), "fpsi");
|
||||
case CAST_FPUI:
|
||||
return LLVMBuildFPToUI(context->builder, value, llvm_type(type), "fpui");
|
||||
return LLVMBuildFPToUI(context->builder, value, llvm_type(to_type), "fpui");
|
||||
case CAST_SISI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "sisitrunc")
|
||||
: LLVMBuildSExt(context->builder, value, llvm_type(type), "sisiext");
|
||||
return type_convert_will_trunc(to_type, from_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "sisitrunc")
|
||||
: LLVMBuildSExt(context->builder, value, llvm_type(to_type), "sisiext");
|
||||
case CAST_SIUI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "siuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "siuiext");
|
||||
return type_convert_will_trunc(to_type, from_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "siuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "siuiext");
|
||||
case CAST_SIFP:
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(type), "sifp");
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(to_type), "sifp");
|
||||
case CAST_XIPTR:
|
||||
return LLVMBuildIntToPtr(context->builder, value, llvm_type(type), "xiptr");
|
||||
return LLVMBuildIntToPtr(context->builder, value, llvm_type(to_type), "xiptr");
|
||||
case CAST_UISI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uisitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uisiext");
|
||||
return type_convert_will_trunc(to_type, from_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "uisitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "uisiext");
|
||||
case CAST_UIUI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uiuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uiuiext");
|
||||
return type_convert_will_trunc(to_type, from_type)
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(to_type), "uiuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(to_type), "uiuiext");
|
||||
case CAST_UIFP:
|
||||
return LLVMBuildUIToFP(context->builder, value, llvm_type(type), "uifp");
|
||||
return LLVMBuildUIToFP(context->builder, value, llvm_type(to_type), "uifp");
|
||||
case CAST_ENUMSI:
|
||||
TODO
|
||||
}
|
||||
@@ -834,12 +864,14 @@ LLVMValueRef gencontext_emit_try_expr(GenContext *context, Expr *expr)
|
||||
if (expr->try_expr.type == TRY_EXPR_ELSE_JUMP)
|
||||
{
|
||||
LLVMBasicBlockRef else_block = gencontext_get_try_target(context, expr);
|
||||
LLVMValueRef val = gencontext_emit_expr(context, expr->try_expr.expr);
|
||||
LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch");
|
||||
gencontext_emit_br(context, after_catch);
|
||||
gencontext_emit_block(context, else_block);
|
||||
gencontext_emit_stmt(context, expr->try_expr.else_stmt);
|
||||
gencontext_emit_br(context, after_catch);
|
||||
gencontext_emit_block(context, after_catch);
|
||||
return val;
|
||||
}
|
||||
return gencontext_emit_expr(context, expr->try_expr.expr);
|
||||
}
|
||||
@@ -953,7 +985,7 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||
return llvm_int(type, expr->const_expr.b ? 1 : 0);
|
||||
case TYPE_STRING:
|
||||
{
|
||||
LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type), "string");
|
||||
LLVMValueRef global_name = LLVMAddGlobal(context->module, LLVMArrayType(llvm_type(type_char), expr->const_expr.string.len + 1), "");
|
||||
LLVMSetLinkage(global_name, LLVMInternalLinkage);
|
||||
LLVMSetGlobalConstant(global_name, 1);
|
||||
LLVMSetInitializer(global_name, LLVMConstStringInContext(context->context,
|
||||
@@ -1024,113 +1056,6 @@ static inline void gencontext_emit_throw_union_branch(GenContext *context, Catch
|
||||
*/
|
||||
|
||||
|
||||
static inline bool gencontext_emit_throw_branch_for_single_throw_catch(GenContext *context, Decl *throw, LLVMValueRef value, Ast *catch, bool single_throw)
|
||||
{
|
||||
gencontext_generate_catch_block_if_needed(context, catch);
|
||||
|
||||
// Catch any is simple.
|
||||
if (catch->catch_stmt.error_param->type == type_error_union)
|
||||
{
|
||||
// If this was a single throw, then we do a cast first.
|
||||
if (single_throw)
|
||||
{
|
||||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, throw->type);
|
||||
}
|
||||
// Store the value and jump to the catch.
|
||||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we catch a single, the single throw is easy, it *must* be the same type.
|
||||
if (single_throw)
|
||||
{
|
||||
assert(catch->catch_stmt.error_param->type->decl == throw);
|
||||
// Store and jump.
|
||||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Here instead we more than one throw and we're catching a single error type
|
||||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, catch->catch_stmt.error_param->type->decl->error.start_value, llvm_type(type_error_union), "");
|
||||
LLVMValueRef check = LLVMBuildAnd(context->builder, offset, value, "");
|
||||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntEQ, offset, check, "");
|
||||
LLVMBasicBlockRef catch_block_set = gencontext_create_free_block(context, "setval");
|
||||
LLVMBasicBlockRef continue_block = gencontext_create_free_block(context, "");
|
||||
gencontext_emit_cond_br(context, match, catch_block_set, continue_block);
|
||||
value = LLVMBuildAnd(context->builder, LLVMBuildNeg(context->builder, offset, ""), value, "");
|
||||
LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||||
gencontext_emit_br(context, catch->catch_stmt.block);
|
||||
gencontext_emit_block(context, continue_block);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool gencontext_emit_throw_branch_for_single_throw(GenContext *context, Decl *throw, LLVMValueRef value, CatchInfo *catch_infos, bool single_throw, bool union_return)
|
||||
{
|
||||
TODO
|
||||
/*
|
||||
VECEACH(catch_infos, i)
|
||||
{
|
||||
CatchInfo *info = &catch_infos[i];
|
||||
switch (info->kind)
|
||||
{
|
||||
case CATCH_REGULAR:
|
||||
if (gencontext_emit_throw_branch_for_single_throw_catch(context, throw, value, info->catch, single_throw))
|
||||
{
|
||||
// Catching all should only happen on the last branch.
|
||||
assert(i == vec_size(catch_infos));
|
||||
return true;
|
||||
}
|
||||
break;;
|
||||
case CATCH_TRY_ELSE:
|
||||
// Try else should only happen on the last branch.
|
||||
assert(i == vec_size(catch_infos));
|
||||
gencontext_emit_br(context, info->try_else->try_expr.jump_target);
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
// If this is not a single throw, then we need to check first.
|
||||
if (!single_throw)
|
||||
{
|
||||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, throw->error.start_value, llvm_type(type_error_union), "");
|
||||
LLVMValueRef check = LLVMBuildAnd(context->builder, offset, value, "");
|
||||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntEQ, offset, check, "");
|
||||
LLVMBasicBlockRef catch_block_set = gencontext_create_free_block(context, "setval");
|
||||
LLVMBasicBlockRef continue_block = gencontext_create_free_block(context, "");
|
||||
gencontext_emit_cond_br(context, match, catch_block_set, continue_block);
|
||||
value = LLVMBuildAnd(context->builder, LLVMBuildNeg(context->builder, offset, ""), value, "");
|
||||
//LLVMBuildStore(context->builder, value, catch->catch_stmt.error_param->var.backend_ref);
|
||||
//gencontext_emit_br(context, catch->catch_stmt.block);
|
||||
gencontext_emit_block(context, continue_block);
|
||||
|
||||
}
|
||||
// Case (1) the error return is a normal return, in that case we send the unadorned type.
|
||||
/* if (union_return)
|
||||
{
|
||||
|
||||
|
||||
assert(error_type == type_error_base);
|
||||
gencontext_emit_cond_br(context, comparison, return_block, after_block);
|
||||
gencontext_emit_block(context, return_block);
|
||||
gencontext_emit_return_value(context, value);
|
||||
gencontext_emit_block(context, after_block);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(context->cur_func_decl->func.function_signature.error_return == ERROR_RETURN_UNION);
|
||||
if (error_type == type_error_base)
|
||||
{
|
||||
value = gencontext_emit_cast(context, CAST_EREU, value, cuf, error_type)
|
||||
|
||||
gencontext_emit_cast_expr
|
||||
|
||||
|
||||
return false;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRef value, TypeInfo** errors, ThrowInfo *throw_info, ErrorReturn error_return)
|
||||
@@ -1231,7 +1156,7 @@ static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRe
|
||||
value = gencontext_emit_cast(context, CAST_ERREU, value, type_error_union, errors[0]->type);
|
||||
}
|
||||
}
|
||||
LLVMBuildStore(context->builder, value, error_param->var.backend_ref);
|
||||
LLVMBuildStore(context->builder, value, error_param->ref);
|
||||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||||
gencontext_emit_block(context, after_block);
|
||||
@@ -1263,8 +1188,7 @@ static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRe
|
||||
{
|
||||
// This is always the last catch, so we can assume that we have the correct error
|
||||
// type already.
|
||||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, catch->error->error.start_value, llvm_type(type_error_union), "");
|
||||
LLVMValueRef negated = LLVMBuildNeg(context->builder, offset, "");
|
||||
LLVMValueRef negated = LLVMBuildNeg(context->builder, catch->error->error.start_value, "");
|
||||
LLVMValueRef final_value = LLVMBuildAnd(context->builder, negated, value, "");
|
||||
gencontext_emit_defer(context, throw_info->defer, NULL);
|
||||
gencontext_emit_return_value(context, final_value);
|
||||
@@ -1294,15 +1218,15 @@ static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRe
|
||||
}
|
||||
case CATCH_REGULAR:
|
||||
{
|
||||
Decl *param = catch->catch->catch_stmt.error_param;
|
||||
gencontext_generate_catch_block_if_needed(context, catch->catch);
|
||||
Decl *error_param = catch->catch->catch_stmt.error_param;
|
||||
Type *error_type = error_param->type->canonical;
|
||||
|
||||
// The wildcard catch is always the last one.
|
||||
if (param->type == type_error_union)
|
||||
if (error_type == type_error_union)
|
||||
{
|
||||
// Store the value, then jump
|
||||
LLVMBuildStore(context->builder, value, error_param->var.backend_ref);
|
||||
LLVMBuildStore(context->builder, value, error_param->ref);
|
||||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||||
gencontext_emit_block(context, after_block);
|
||||
@@ -1312,21 +1236,19 @@ static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRe
|
||||
|
||||
// Here we have a normal catch.
|
||||
|
||||
// Find the offset.
|
||||
LLVMValueRef offset = LLVMBuildBitCast(context->builder, param->type->decl->error.start_value, llvm_type(type_error_union), "");
|
||||
|
||||
// wrapping(value - offset) < entries + 1 – this handles both cases since wrapping will make
|
||||
// values below the offset big.
|
||||
LLVMValueRef comp_value = LLVMBuildSub(context->builder, value, offset, "");
|
||||
LLVMValueRef entries_value = llvm_int(type_error_union, vec_size(param->type->decl->error.error_constants) + 1);
|
||||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntULT, comp_value, entries_value, "matcherr");
|
||||
Decl *error_decl = error_type->decl;
|
||||
LLVMValueRef comp_value = LLVMBuildSub(context->builder, value, error_decl->error.start_value, "");
|
||||
LLVMValueRef entries_value = llvm_int(type_error_union, vec_size(error_decl->error.error_constants) + 1);
|
||||
LLVMValueRef match = LLVMBuildICmp(context->builder, LLVMIntULT, comp_value, entries_value, "regmatch");
|
||||
|
||||
LLVMBasicBlockRef match_block = gencontext_create_free_block(context, "match");
|
||||
gencontext_emit_cond_br(context, match, match_block, err_handling_block);
|
||||
gencontext_emit_block(context, match_block);
|
||||
gencontext_emit_defer(context, throw_info->defer, catch->defer);
|
||||
|
||||
LLVMBuildStore(context->builder, comp_value, error_param->var.backend_ref);
|
||||
LLVMBuildStore(context->builder, comp_value, error_param->ref);
|
||||
gencontext_emit_br(context, catch->catch->catch_stmt.block);
|
||||
gencontext_emit_block(context, err_handling_block);
|
||||
err_handling_block = NULL;
|
||||
@@ -1341,8 +1263,25 @@ static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRe
|
||||
LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
size_t args = vec_size(expr->call_expr.arguments);
|
||||
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
|
||||
FunctionSignature *signature = &function_decl->func.function_signature;
|
||||
FunctionSignature *signature;
|
||||
LLVMValueRef func;
|
||||
LLVMTypeRef func_type;
|
||||
|
||||
if (expr->call_expr.is_pointer_call)
|
||||
{
|
||||
signature = expr->call_expr.function->type->canonical->pointer->func.signature;
|
||||
func = gencontext_emit_expr(context, expr->call_expr.function);
|
||||
func_type = llvm_type(expr->call_expr.function->type->canonical->pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Decl *function_decl = expr->call_expr.function->identifier_expr.decl;
|
||||
signature = &function_decl->func.function_signature;
|
||||
func = function_decl->ref;
|
||||
func_type = llvm_type(function_decl->type);
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef return_param = NULL;
|
||||
if (signature->return_param)
|
||||
{
|
||||
@@ -1360,13 +1299,9 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||||
values[param_index++] = gencontext_emit_expr(context, expr->call_expr.arguments[i]);
|
||||
}
|
||||
|
||||
Decl *function = expr->call_expr.function->identifier_expr.decl;
|
||||
|
||||
LLVMValueRef func = function->func.backend_value;
|
||||
LLVMTypeRef func_type = llvm_type(function->type);
|
||||
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call");
|
||||
|
||||
gencontext_emit_throw_branch(context, call, function->func.function_signature.throws, expr->call_expr.throw_info, signature->error_return);
|
||||
gencontext_emit_throw_branch(context, call, signature->throws, expr->call_expr.throw_info, signature->error_return);
|
||||
|
||||
// If we used a return param, then load that info here.
|
||||
if (return_param)
|
||||
@@ -1423,7 +1358,7 @@ static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr
|
||||
context->return_out = old_ret_out;
|
||||
context->expr_block_exit = saved_expr_block;
|
||||
|
||||
return return_out;
|
||||
return return_out ? gencontext_emit_load(context, expr->type, return_out) : NULL;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types,
|
||||
@@ -1457,7 +1392,6 @@ NESTED_RETRY:
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_RANGE:
|
||||
TODO
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_DESIGNATED_INITIALIZER:
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#include "bigint.h"
|
||||
|
||||
bool gencontext_check_block_branch_emit(GenContext *context)
|
||||
{
|
||||
@@ -78,8 +75,8 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un
|
||||
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||
|
||||
// Allocate room on stack and copy.
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref);
|
||||
decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->ref);
|
||||
}
|
||||
|
||||
void gencontext_emit_implicit_return(GenContext *context)
|
||||
@@ -103,12 +100,12 @@ void gencontext_emit_implicit_return(GenContext *context)
|
||||
void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("Generating function %s.", decl->external_name);
|
||||
assert(decl->func.backend_value);
|
||||
assert(decl->ref);
|
||||
|
||||
LLVMValueRef prev_function = context->function;
|
||||
LLVMBuilderRef prev_builder = context->builder;
|
||||
|
||||
context->function = decl->func.backend_value;
|
||||
context->function = decl->ref;
|
||||
context->cur_func_decl = decl;
|
||||
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
||||
@@ -182,22 +179,61 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
// Resolve function backend type for function.
|
||||
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||
llvm_type(decl->type));
|
||||
|
||||
|
||||
// Specify appropriate storage class, visibility and call convention
|
||||
// extern functions (linkedited in separately):
|
||||
/*
|
||||
if (glofn->flags & FlagSystem) {
|
||||
LLVMSetFunctionCallConv(glofn->llvmvar, LLVMX86StdcallCallConv);
|
||||
LLVMSetDLLStorageClass(glofn->llvmvar, LLVMDLLImportStorageClass);
|
||||
}*/
|
||||
if (decl->visibility == VISIBLE_LOCAL)
|
||||
LLVMValueRef function = LLVMAddFunction(context->module, decl->cname ?: decl->external_name, llvm_type(decl->type));
|
||||
decl->ref = function;
|
||||
if (decl->func.function_signature.return_param)
|
||||
{
|
||||
LLVMSetVisibility(decl->func.backend_value, LLVMHiddenVisibility);
|
||||
if (decl->func.function_signature.error_return == ERROR_RETURN_NONE)
|
||||
{
|
||||
gencontext_add_attribute(context, function, sret_attribute, 1);
|
||||
}
|
||||
gencontext_add_attribute(context, function, noalias_attribute, 1);
|
||||
}
|
||||
if (decl->func.attr_inline)
|
||||
{
|
||||
gencontext_add_attribute(context, function, alwaysinline_attribute, -1);
|
||||
}
|
||||
if (decl->func.attr_noinline)
|
||||
{
|
||||
gencontext_add_attribute(context, function, noinline_attribute, -1);
|
||||
}
|
||||
if (decl->func.attr_noreturn)
|
||||
{
|
||||
gencontext_add_attribute(context, function, noreturn_attribute, -1);
|
||||
}
|
||||
if (decl->alignment)
|
||||
{
|
||||
LLVMSetAlignment(function, decl->alignment);
|
||||
}
|
||||
if (decl->section)
|
||||
{
|
||||
LLVMSetSection(function, decl->section);
|
||||
}
|
||||
gencontext_add_attribute(context, function, nounwind_attribute, -1);
|
||||
|
||||
// TODO only for windows.
|
||||
if (decl->func.attr_stdcall)
|
||||
{
|
||||
LLVMSetFunctionCallConv(function, LLVMX86StdcallCallConv);
|
||||
LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass);
|
||||
}
|
||||
|
||||
switch (decl->visibility)
|
||||
{
|
||||
case VISIBLE_EXTERN:
|
||||
LLVMSetLinkage(function, decl->func.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage);
|
||||
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_PUBLIC:
|
||||
case VISIBLE_MODULE:
|
||||
if (decl->func.attr_weak) LLVMSetLinkage(function, LLVMWeakAnyLinkage);
|
||||
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetLinkage(function, decl->func.attr_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage);
|
||||
LLVMSetVisibility(function, LLVMDefaultVisibility);
|
||||
break;;
|
||||
}
|
||||
if (context->debug.builder)
|
||||
{
|
||||
LLVMDIFlags flags = LLVMDIFlagZero;
|
||||
@@ -241,13 +277,13 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
|
||||
case DECL_POISONED:
|
||||
UNREACHABLE;
|
||||
case DECL_FUNC:
|
||||
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||
decl->ref = LLVMAddFunction(context->module, decl->cname ?: decl->external_name,
|
||||
llvm_type(decl->type));
|
||||
LLVMSetVisibility(decl->func.backend_value, LLVMDefaultVisibility);
|
||||
LLVMSetVisibility(decl->ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case DECL_VAR:
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->external_name);
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||
decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->cname ?: decl->external_name);
|
||||
LLVMSetVisibility(decl->ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
UNREACHABLE
|
||||
|
||||
@@ -100,13 +100,17 @@ extern unsigned writeonly_attribute;
|
||||
extern unsigned readonly_attribute;
|
||||
// Disable optimization.
|
||||
extern unsigned optnone_attribute;
|
||||
|
||||
// Sret (pointer)
|
||||
extern unsigned sret_attribute;
|
||||
// Noalias (pointer)
|
||||
extern unsigned noalias_attribute;
|
||||
|
||||
void gencontext_begin_module(GenContext *context);
|
||||
void gencontext_end_module(GenContext *context);
|
||||
|
||||
|
||||
void gencontext_add_attribute(GenContext context, unsigned attribute_id, LLVMValueRef value_to_add_attribute_to);
|
||||
void
|
||||
gencontext_add_attribute(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index);
|
||||
void gencontext_emit_stmt(GenContext *context, Ast *ast);
|
||||
|
||||
void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast);
|
||||
@@ -149,9 +153,13 @@ static inline void gencontext_emit_return_value(GenContext *context, LLVMValueRe
|
||||
context->current_block = NULL;
|
||||
context->current_block_is_target = false;
|
||||
}
|
||||
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_type);
|
||||
LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *to_type, Type *from_type);
|
||||
static inline bool gencontext_func_pass_return_by_param(GenContext *context, Type *first_param_type) { return false; };
|
||||
static inline bool gencontext_func_pass_param_by_reference(GenContext *context, Type *param_type) { return false; }
|
||||
static inline LLVMValueRef gencontext_emit_bitcast(GenContext *context, LLVMValueRef value, Type *type)
|
||||
{
|
||||
return LLVMBuildBitCast(context->builder, value, gencontext_get_llvm_type(context, type), "");
|
||||
}
|
||||
|
||||
static inline bool gencontext_use_debug(GenContext *context)
|
||||
{
|
||||
|
||||
@@ -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(type_reduced(decl->type)), decl->name);
|
||||
decl->ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name);
|
||||
// TODO NRVO
|
||||
// TODO debug info
|
||||
/*
|
||||
@@ -42,10 +42,10 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
*/
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
gencontext_emit_assign_expr(context, decl->var.backend_ref, decl->var.init_expr);
|
||||
return decl->var.backend_ref;
|
||||
gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr);
|
||||
return decl->ref;
|
||||
}
|
||||
return decl->var.backend_ref;
|
||||
return decl->ref;
|
||||
}
|
||||
|
||||
void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Ast *ast)
|
||||
@@ -602,7 +602,7 @@ void gencontext_generate_catch_block_if_needed(GenContext *context, Ast *ast)
|
||||
{
|
||||
type = llvm_type(type_error_base);
|
||||
}
|
||||
ast->catch_stmt.error_param->var.backend_ref = gencontext_emit_alloca(context, type, "");
|
||||
ast->catch_stmt.error_param->ref = gencontext_emit_alloca(context, type, "");
|
||||
}
|
||||
|
||||
void gencontext_emit_catch_stmt(GenContext *context, Ast *ast)
|
||||
|
||||
@@ -152,8 +152,7 @@ LLVMTypeRef llvm_func_type(LLVMContextRef context, Type *type)
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
LLVMTypeRef functype = LLVMFunctionType(ret_type, params, parameters, signature->variadic);
|
||||
return functype;
|
||||
return LLVMFunctionType(ret_type, params, parameters, signature->variadic);
|
||||
}
|
||||
|
||||
|
||||
@@ -213,16 +212,15 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
return type->backend_type = llvm_type_from_array(context, type);
|
||||
case TYPE_SUBARRAY:
|
||||
{
|
||||
LLVMTypeRef base_type = llvm_get_type(context, type->array.base);
|
||||
LLVMTypeRef base_type = llvm_get_type(context, type_get_ptr(type->array.base));
|
||||
LLVMTypeRef size_type = llvm_get_type(context, type_usize);
|
||||
assert(type->array.base->canonical->type_kind == TYPE_POINTER);
|
||||
LLVMTypeRef array_type = LLVMStructCreateNamed(context, type->name);
|
||||
LLVMTypeRef types[2] = { base_type, size_type };
|
||||
LLVMStructSetBody(array_type, types, 2, false);
|
||||
return type->backend_type = array_type;
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
return type->backend_type = LLVMPointerType(llvm_get_type(context, type->array.base), 0);
|
||||
return type->backend_type = llvm_get_type(context, type_get_ptr(type->array.base));
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
@@ -198,6 +198,18 @@ static Expr *parse_post_unary(Context *context, Expr *left)
|
||||
return unary;
|
||||
}
|
||||
|
||||
static Expr *parse_range_expr(Context *context, Expr *left_side)
|
||||
{
|
||||
assert(expr_ok(left_side));
|
||||
advance_and_verify(context, TOKEN_ELLIPSIS);
|
||||
Expr *right = TRY_EXPR_OR(parse_precedence(context, PREC_RANGE + 1), poisoned_expr);
|
||||
Expr *range = expr_new(EXPR_RANGE, left_side->span);
|
||||
range->range_expr.left = left_side;
|
||||
range->range_expr.right = right;
|
||||
RANGE_EXTEND_PREV(range);
|
||||
return range;
|
||||
}
|
||||
|
||||
static Expr *parse_ternary_expr(Context *context, Expr *left_side)
|
||||
{
|
||||
assert(expr_ok(left_side));
|
||||
@@ -819,6 +831,7 @@ static Expr* parse_expr_block(Context *context, Expr *left)
|
||||
}
|
||||
|
||||
ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_ELLIPSIS] = { NULL, parse_range_expr, PREC_RANGE },
|
||||
[TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
|
||||
|
||||
@@ -180,6 +180,7 @@ static inline Ast *parse_case_stmts(Context *context)
|
||||
/**
|
||||
* case_stmt
|
||||
* : CASE constant_expression ':' case_stmts
|
||||
* | CASE constant_expression ELLIPSIS constant_expression ':' cast_stmts
|
||||
*/
|
||||
static inline Ast* parse_case_stmt(Context *context)
|
||||
{
|
||||
@@ -800,7 +801,7 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_MULT_ASSIGN:
|
||||
case TOKEN_NOT_EQUAL:
|
||||
case TOKEN_PLUS_ASSIGN:
|
||||
case TOKEN_ELIPSIS:
|
||||
case TOKEN_ELLIPSIS:
|
||||
case TOKEN_SCOPE:
|
||||
case TOKEN_SHR:
|
||||
case TOKEN_SHL:
|
||||
|
||||
@@ -425,6 +425,7 @@ static inline TypeInfo *parse_base_type(Context *context)
|
||||
* : '[' constant_expression ']'
|
||||
* | '[' ']'
|
||||
* | '[' '+' ']'
|
||||
* | '[' '*' ']'
|
||||
* ;
|
||||
*
|
||||
* @param type the type to wrap, may not be poisoned.
|
||||
@@ -443,13 +444,22 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type)
|
||||
RANGE_EXTEND_PREV(incr_array);
|
||||
return incr_array;
|
||||
}
|
||||
if (try_consume(context, TOKEN_STAR))
|
||||
{
|
||||
CONSUME_OR(TOKEN_RBRACKET, poisoned_type_info);
|
||||
TypeInfo *vararray = type_info_new(TYPE_INFO_VARARRAY, type->span);
|
||||
vararray->array.base = type;
|
||||
vararray->array.len = NULL;
|
||||
RANGE_EXTEND_PREV(vararray);
|
||||
return vararray;
|
||||
}
|
||||
if (try_consume(context, TOKEN_RBRACKET))
|
||||
{
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span);
|
||||
array->array.base = type;
|
||||
array->array.len = NULL;
|
||||
RANGE_EXTEND_PREV(array);
|
||||
return array;
|
||||
TypeInfo *subarray = type_info_new(TYPE_INFO_SUBARRAY, type->span);
|
||||
subarray->array.base = type;
|
||||
subarray->array.len = NULL;
|
||||
RANGE_EXTEND_PREV(subarray);
|
||||
return subarray;
|
||||
}
|
||||
TypeInfo *array = type_info_new(TYPE_INFO_ARRAY, type->span);
|
||||
array->array.base = type;
|
||||
@@ -927,7 +937,7 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa
|
||||
SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list.");
|
||||
return false;
|
||||
}
|
||||
if (try_consume(context, TOKEN_ELIPSIS))
|
||||
if (try_consume(context, TOKEN_ELLIPSIS))
|
||||
{
|
||||
signature->variadic = true;
|
||||
}
|
||||
@@ -1170,7 +1180,7 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi
|
||||
|
||||
|
||||
|
||||
static AttributeDomains TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
|
||||
static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
|
||||
[TOKEN_FUNC] = ATTR_FUNC,
|
||||
[TOKEN_VAR] = ATTR_VAR,
|
||||
[TOKEN_ENUM] = ATTR_ENUM,
|
||||
@@ -1209,8 +1219,8 @@ static AttributeDomains TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
|
||||
static inline Decl *parse_attribute_declaration(Context *context, Visibility visibility)
|
||||
{
|
||||
advance_and_verify(context, TOKEN_ATTRIBUTE);
|
||||
AttributeDomains domains = 0;
|
||||
AttributeDomains last_domain;
|
||||
AttributeDomain domains = 0;
|
||||
AttributeDomain last_domain;
|
||||
last_domain = TOKEN_TO_ATTR[context->tok.type];
|
||||
while (last_domain)
|
||||
{
|
||||
@@ -1255,7 +1265,7 @@ static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility v
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return parse_opt_throw_declaration(context, VISIBLE_PUBLIC, &(decl->typedef_decl.function_signature));
|
||||
return parse_opt_throw_declaration(context, visibility, &(decl->typedef_decl.function_signature));
|
||||
|
||||
}
|
||||
|
||||
@@ -1290,7 +1300,6 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
|
||||
{
|
||||
rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl);
|
||||
}
|
||||
|
||||
Decl *decl = decl_new(DECL_MACRO, context->tok, visibility);
|
||||
decl->macro_decl.rtype = rtype;
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here", poisoned_decl);
|
||||
@@ -1531,6 +1540,8 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit
|
||||
|
||||
if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return poisoned_decl;
|
||||
|
||||
if (!parse_attributes(context, func)) return poisoned_decl;
|
||||
|
||||
// TODO remove
|
||||
is_interface = context->tok.type == TOKEN_EOS;
|
||||
|
||||
|
||||
@@ -617,7 +617,14 @@ bool vava(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
|
||||
bool sapt(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
bool is_subtype = type_is_subtype(canonical->pointer, from->array.base);
|
||||
if (!is_subtype)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
return sema_type_mismatch(left, type, cast_type);
|
||||
}
|
||||
insert_cast(left, CAST_SAPTR, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vasa(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
@@ -625,10 +632,7 @@ bool vasa(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
TODO
|
||||
}
|
||||
|
||||
bool usui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
|
||||
bool euxi(Expr *left, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
@@ -686,6 +690,24 @@ bool ptva(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ
|
||||
TODO
|
||||
}
|
||||
|
||||
bool ptsa(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
if (from->pointer->type_kind != TYPE_ARRAY)
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
sema_type_mismatch(left, type, CAST_TYPE_EXPLICIT);
|
||||
return false;
|
||||
}
|
||||
if (!type_is_subtype(canonical->array.base, from->pointer->array.base))
|
||||
{
|
||||
if (cast_type == CAST_TYPE_OPTIONAL_IMPLICIT) return true;
|
||||
sema_type_mismatch(left, type, CAST_TYPE_EXPLICIT);
|
||||
return false;
|
||||
}
|
||||
insert_cast(left, CAST_APTSA, canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool usbo(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
TODO
|
||||
@@ -845,6 +867,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (canonical->type_kind == TYPE_POINTER) return ptpt(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_FUNC) return ptfu(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_VARARRAY) return ptva(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_SUBARRAY) return ptsa(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
case TYPE_ENUM:
|
||||
if (type_is_integer(canonical)) return enxi(expr, from_type, canonical, to_type, cast_type);
|
||||
@@ -854,9 +877,8 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
if (canonical == type_error_union) return ereu(expr);
|
||||
break;
|
||||
case TYPE_FUNC:
|
||||
if (type_is_integer(canonical)) return ptxi(expr, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_POINTER) return fupt(expr, from_type, canonical, to_type, cast_type);
|
||||
break;
|
||||
SEMA_ERROR(expr, "The function call is missing (...), if you want to take the address of a function it must be prefixed with '&'.");
|
||||
return false;
|
||||
case TYPE_STRUCT:
|
||||
if (canonical->type_kind == TYPE_STRUCT) return stst(expr, from_type, canonical, to_type, cast_type);
|
||||
if (canonical->type_kind == TYPE_UNION) return stun(expr, from_type, canonical, to_type, cast_type);
|
||||
@@ -318,7 +318,7 @@ static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
||||
{
|
||||
Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false);
|
||||
if (!func_type) return false;
|
||||
decl->type->canonical = func_type;
|
||||
decl->type->canonical = type_get_ptr(func_type);
|
||||
return true;
|
||||
}
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false;
|
||||
@@ -437,7 +437,123 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline AttributeType attribute_by_name(Attr *attr)
|
||||
{
|
||||
const char *attribute = attr->name.string;
|
||||
for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++)
|
||||
{
|
||||
if (attribute_list[i] == attribute) return (AttributeType)i;
|
||||
}
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
static const char *attribute_domain_to_string(AttributeDomain domain)
|
||||
{
|
||||
switch (domain)
|
||||
{
|
||||
case ATTR_FUNC:
|
||||
return "function";
|
||||
case ATTR_VAR:
|
||||
return "variable";
|
||||
case ATTR_ENUM:
|
||||
return "enum";
|
||||
case ATTR_STRUCT:
|
||||
return "struct";
|
||||
case ATTR_UNION:
|
||||
return "union";
|
||||
case ATTR_CONST:
|
||||
return "constant";
|
||||
case ATTR_ERROR:
|
||||
return "error type";
|
||||
case ATTR_TYPEDEF:
|
||||
return "typedef";
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
static AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain)
|
||||
{
|
||||
AttributeType type = attribute_by_name(attr);
|
||||
if (type == ATTRIBUTE_NONE)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "There is no attribute with the name '%s', did you mistype?", attr->name.string);
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = {
|
||||
[ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_CNAME] = 0xFF,
|
||||
[ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_NORETURN] = ATTR_FUNC,
|
||||
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_INLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION
|
||||
};
|
||||
|
||||
if ((attribute_domain[type] & domain) != domain)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "'%s' is not a valid %s attribute.", attr->name.string, attribute_domain_to_string(domain));
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case ATTRIBUTE_ALIGN:
|
||||
if (!attr->expr)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "'align' requires an power-of-2 argument, e.g. align(8).");
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
if (!sema_analyse_expr(context, type_usize, attr->expr)) return false;
|
||||
if (attr->expr->expr_kind != EXPR_CONST || !type_is_any_integer(attr->expr->type->canonical))
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "Expected a constant integer value as argument.");
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
{
|
||||
BigInt comp;
|
||||
bigint_init_unsigned(&comp, MAX_ALIGNMENT);
|
||||
if (bigint_cmp(&attr->expr->const_expr.i, &comp) == CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "Alignment must be less or equal to %ull.", MAX_ALIGNMENT);
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
if (bigint_cmp_zero(&attr->expr->const_expr.i) != CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "Alignment must be greater than zero.");
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
uint64_t align = bigint_as_unsigned(&attr->expr->const_expr.i);
|
||||
if (!is_power_of_two(align))
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "Alignment must be a power of two.");
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
attr->alignment = align;
|
||||
}
|
||||
return type;
|
||||
case ATTRIBUTE_SECTION:
|
||||
case ATTRIBUTE_CNAME:
|
||||
if (!attr->expr)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "'%s' requires a string argument, e.g. %s(\"foo\").", attr->name.string, attr->name.string);
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
if (!sema_analyse_expr(context, NULL, attr->expr)) return false;
|
||||
if (attr->expr->expr_kind != EXPR_CONST || attr->expr->type->canonical != type_string)
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "Expected a constant string value as argument.");
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
return type;
|
||||
default:
|
||||
if (attr->expr)
|
||||
{
|
||||
SEMA_ERROR(attr->expr, "'%s' should not have any arguments.", attr->name.string);
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
{
|
||||
@@ -449,7 +565,50 @@ static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
{
|
||||
if (!sema_analyse_method_function(context, decl)) return decl_poison(decl);
|
||||
}
|
||||
if (decl->name == main_name)
|
||||
VECEACH(decl->attributes, i)
|
||||
{
|
||||
Attr *attr = decl->attributes[i];
|
||||
|
||||
AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_FUNC);
|
||||
if (attribute == ATTRIBUTE_NONE) return decl_poison(decl);
|
||||
|
||||
bool had = false;
|
||||
#define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break
|
||||
switch (attribute)
|
||||
{
|
||||
case ATTRIBUTE_CNAME:
|
||||
had = decl->cname != NULL;
|
||||
decl->cname = attr->expr->const_expr.string.chars;
|
||||
break;
|
||||
case ATTRIBUTE_SECTION:
|
||||
had = decl->section != NULL;
|
||||
decl->section = attr->expr->const_expr.string.chars;
|
||||
break;
|
||||
case ATTRIBUTE_ALIGN:
|
||||
had = decl->alignment != 0;
|
||||
decl->alignment = attr->alignment;
|
||||
break;
|
||||
case ATTRIBUTE_NOINLINE: SET_ATTR(attr_noinline);
|
||||
case ATTRIBUTE_STDCALL: SET_ATTR(attr_stdcall);
|
||||
case ATTRIBUTE_INLINE: SET_ATTR(attr_inline);
|
||||
case ATTRIBUTE_NORETURN: SET_ATTR(attr_noreturn);
|
||||
case ATTRIBUTE_WEAK: SET_ATTR(attr_weak);
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
#undef SET_ATTR
|
||||
if (had)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "Attribute occurred twice, please remove one.");
|
||||
return decl_poison(decl);
|
||||
}
|
||||
if (decl->func.attr_inline && decl->func.attr_noinline)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "A function cannot be 'inline' and 'noinline' at the same time.");
|
||||
return decl_poison(decl);
|
||||
}
|
||||
}
|
||||
if (decl->name == main_kw)
|
||||
{
|
||||
if (decl->visibility == VISIBLE_LOCAL)
|
||||
{
|
||||
@@ -492,6 +651,42 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AttributeDomain domain = decl->var.kind == VARDECL_CONST ? ATTR_CONST : ATTR_FUNC;
|
||||
VECEACH(decl->attributes, i)
|
||||
{
|
||||
Attr *attr = decl->attributes[i];
|
||||
|
||||
AttributeType attribute = sema_analyse_attribute(context, attr, domain);
|
||||
if (attribute == ATTRIBUTE_NONE) return decl_poison(decl);
|
||||
|
||||
bool had = false;
|
||||
#define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break
|
||||
switch (attribute)
|
||||
{
|
||||
case ATTRIBUTE_CNAME:
|
||||
had = decl->cname != NULL;
|
||||
decl->cname = attr->expr->const_expr.string.chars;
|
||||
break;
|
||||
case ATTRIBUTE_SECTION:
|
||||
had = decl->section != NULL;
|
||||
decl->section = attr->expr->const_expr.string.chars;
|
||||
break;
|
||||
case ATTRIBUTE_ALIGN:
|
||||
had = decl->alignment != 0;
|
||||
decl->alignment = attr->alignment;
|
||||
break;
|
||||
case ATTRIBUTE_WEAK: SET_ATTR(attr_weak);
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
#undef SET_ATTR
|
||||
if (had)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(attr->name, "Attribute occurred twice, please remove one.");
|
||||
return decl_poison(decl);
|
||||
}
|
||||
}
|
||||
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
|
||||
@@ -23,6 +23,7 @@ static Ast **ast_copy_list_from_macro(Context *context, Expr *macro, Ast **to_co
|
||||
#define MACRO_COPY_AST_LIST(x) x = ast_copy_list_from_macro(context, macro, x)
|
||||
#define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, macro, x)
|
||||
|
||||
bool sema_analyse_expr_may_be_function(Context *context, Expr *expr);
|
||||
|
||||
static inline bool is_const(Expr *expr)
|
||||
{
|
||||
@@ -39,6 +40,30 @@ static inline bool both_any_integer(Expr *left, Expr *right)
|
||||
return type_is_any_integer(left->type->canonical) && type_is_any_integer(right->type->canonical);
|
||||
}
|
||||
|
||||
static inline void context_pop_returns(Context *context, Ast **restore)
|
||||
{
|
||||
if (!context->returns_cache && context->returns)
|
||||
{
|
||||
context->returns_cache = context->returns;
|
||||
}
|
||||
context->returns = restore;
|
||||
}
|
||||
static inline Ast **context_push_returns(Context *context)
|
||||
{
|
||||
Ast** old_returns = context->returns;
|
||||
if (context->returns_cache)
|
||||
{
|
||||
context->returns = context->returns_cache;
|
||||
context->returns_cache = NULL;
|
||||
vec_resize(context->returns, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->returns = NULL;
|
||||
}
|
||||
return old_returns;
|
||||
}
|
||||
|
||||
int sema_check_comp_time_bool(Context *context, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, type_bool, expr)) return -1;
|
||||
@@ -55,9 +80,10 @@ static bool expr_is_ltype(Expr *expr)
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_IDENTIFIER:
|
||||
return expr->identifier_expr.decl->decl_kind == DECL_VAR && (expr->identifier_expr.decl->var.kind == VARDECL_LOCAL
|
||||
return
|
||||
(expr->identifier_expr.decl->decl_kind == DECL_VAR && (expr->identifier_expr.decl->var.kind == VARDECL_LOCAL
|
||||
|| expr->identifier_expr.decl->var.kind == VARDECL_GLOBAL
|
||||
|| expr->identifier_expr.decl->var.kind == VARDECL_PARAM);
|
||||
|| expr->identifier_expr.decl->var.kind == VARDECL_PARAM));
|
||||
case EXPR_UNARY:
|
||||
return expr->unary_expr.operator == UNARYOP_DEREF;
|
||||
case EXPR_ACCESS:
|
||||
@@ -84,6 +110,15 @@ static inline bool sema_type_error_on_binop(Expr *expr)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool expr_cast_to_index(Expr *index)
|
||||
{
|
||||
if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true;
|
||||
if (index->expr_kind != EXPR_RANGE) return cast_implicit(index, type_isize);
|
||||
if (!cast_implicit(index->range_expr.left, type_isize)) return false;
|
||||
if (!cast_implicit(index->range_expr.right, type_isize)) return false;
|
||||
index->type = type_isize;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
@@ -219,8 +254,14 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
|
||||
}
|
||||
if (decl->decl_kind == DECL_MACRO)
|
||||
{
|
||||
SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name);
|
||||
return false;
|
||||
if (!context->in_macro_call)
|
||||
{
|
||||
SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name);
|
||||
return false;
|
||||
}
|
||||
expr->identifier_expr.decl = decl;
|
||||
expr->type = type_void;
|
||||
return true;
|
||||
}
|
||||
assert(decl->type);
|
||||
expr->identifier_expr.decl = decl;
|
||||
@@ -233,10 +274,6 @@ static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to,
|
||||
return sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right);
|
||||
}
|
||||
|
||||
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 int find_index_of_named_parameter(Decl** func_params, Expr *expr)
|
||||
{
|
||||
if (expr->expr_kind != EXPR_IDENTIFIER || expr->identifier_expr.path)
|
||||
@@ -253,14 +290,12 @@ static inline int find_index_of_named_parameter(Decl** func_params, Expr *expr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl)
|
||||
static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
FunctionSignature *signature = &decl->func.function_signature;
|
||||
Decl **func_params = signature->params;
|
||||
expr->call_expr.throw_info = CALLOCS(ThrowInfo);
|
||||
expr->call_expr.throw_info->defer = context->current_scope->defers.start;
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
unsigned error_params = signature->throw_any || signature->throws;
|
||||
if (error_params)
|
||||
{
|
||||
@@ -277,8 +312,8 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr
|
||||
else
|
||||
{
|
||||
vec_add(context->error_calls, throw_new(expr->span,
|
||||
vec_size(signature->throws) == 1 ? THROW_TYPE_CALL_THROW_ONE : THROW_TYPE_CALL_THROW_MANY,
|
||||
expr->call_expr.throw_info, signature->throws));
|
||||
vec_size(signature->throws) == 1 ? THROW_TYPE_CALL_THROW_ONE : THROW_TYPE_CALL_THROW_MANY,
|
||||
expr->call_expr.throw_info, signature->throws));
|
||||
}
|
||||
}
|
||||
unsigned func_param_count = vec_size(func_params);
|
||||
@@ -341,17 +376,103 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr
|
||||
SEMA_ERROR(expr, "Parameter '%s' was not set.", func_params[i]->name);
|
||||
return false;
|
||||
}
|
||||
expr->type = decl->func.function_signature.rtype->type;
|
||||
expr->type = signature->rtype->type;
|
||||
expr->call_expr.arguments = actual_args;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr, Decl *var_decl)
|
||||
{
|
||||
Type *func_ptr_type = var_decl->type->canonical;
|
||||
if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC)
|
||||
{
|
||||
SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(var_decl->type));
|
||||
return false;
|
||||
}
|
||||
expr->call_expr.is_pointer_call = true;
|
||||
return sema_expr_analyse_func_invocation(context,
|
||||
func_ptr_type->pointer->func.signature,
|
||||
expr,
|
||||
func_ptr_type->decl,
|
||||
to);
|
||||
|
||||
}
|
||||
static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO };
|
||||
|
||||
|
||||
|
||||
|
||||
static inline Type *unify_returns(Context *context, Type *to)
|
||||
{
|
||||
bool all_returns_need_casts = false;
|
||||
// Let's unify the return statements.
|
||||
VECEACH(context->returns, i)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
bool last_expr_was_void = to == type_void;
|
||||
Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void;
|
||||
bool current_expr_was_void = right_canonical == type_void;
|
||||
if (i > 0 && last_expr_was_void != current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns.");
|
||||
SEMA_PREV(context->returns[i - 1], "Previous return was here.");
|
||||
return NULL;
|
||||
}
|
||||
if (to)
|
||||
{
|
||||
if (current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to));
|
||||
return NULL;
|
||||
}
|
||||
if (!cast_implicit(ret_expr, to))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The simple case.
|
||||
if (to == right_canonical) continue;
|
||||
|
||||
// Try to find a common type:
|
||||
Type *max = type_find_max_type(to, right_canonical);
|
||||
if (!max)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'",
|
||||
type_to_error_string(to), type_to_error_string(right_canonical));
|
||||
SEMA_PREV(context->returns[i - 1], "The previous return was here.");
|
||||
return false;
|
||||
}
|
||||
to = max;
|
||||
all_returns_need_casts = true;
|
||||
}
|
||||
if (all_returns_need_casts)
|
||||
{
|
||||
VECEACH(context->returns, i)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
if (!cast_implicit(ret_expr, to))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl)
|
||||
{
|
||||
expr->call_expr.is_pointer_call = false;
|
||||
return sema_expr_analyse_func_invocation(context, &decl->func.function_signature, expr, decl, to);
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
// TODO
|
||||
Expr *func_expr = expr->call_expr.function;
|
||||
// TODO check
|
||||
if (!sema_analyse_expr(context, to, func_expr)) return false;
|
||||
if (!sema_analyse_expr_may_be_function(context, func_expr)) return false;
|
||||
Decl *decl;
|
||||
switch (func_expr->expr_kind)
|
||||
{
|
||||
@@ -367,7 +488,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_VAR:
|
||||
return sema_expr_analyse_var_call(context, to, expr);
|
||||
return sema_expr_analyse_var_call(context, to, expr, decl);
|
||||
case DECL_FUNC:
|
||||
return sema_expr_analyse_func_call(context, to, expr, decl);
|
||||
case DECL_MACRO:
|
||||
@@ -383,6 +504,83 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_range(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *left = expr->range_expr.left;
|
||||
Expr *right = expr->range_expr.right;
|
||||
bool success = sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right);
|
||||
if (!success) return expr_poison(expr);
|
||||
Type *left_canonical = left->type->canonical;
|
||||
Type *right_canonical = right->type->canonical;
|
||||
if (!type_is_any_integer(left_canonical))
|
||||
{
|
||||
SEMA_ERROR(left, "Expected an integer value in the range expression.");
|
||||
return false;
|
||||
}
|
||||
if (!type_is_any_integer(right_canonical))
|
||||
{
|
||||
SEMA_ERROR(right, "Expected an integer value in the range expression.");
|
||||
return false;
|
||||
}
|
||||
if (left_canonical != right_canonical)
|
||||
{
|
||||
Type *type = type_find_max_type(left_canonical, right_canonical);
|
||||
if (!cast_implicit(left, type) || !cast_implicit(right, type)) return expr_poison(expr);
|
||||
}
|
||||
if (left->expr_kind == EXPR_CONST && right->expr_kind == EXPR_CONST)
|
||||
{
|
||||
if (expr_const_compare(&left->const_expr, &right->const_expr, BINARYOP_GT))
|
||||
{
|
||||
SEMA_ERROR(expr, "Left side of the range is smaller than the right.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
expr->type = left->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool expr_check_index_in_range(Type *type, Expr *index)
|
||||
{
|
||||
if (index->expr_kind == EXPR_RANGE)
|
||||
{
|
||||
return expr_check_index_in_range(type, index->range_expr.left) & expr_check_index_in_range(type, index->range_expr.right);
|
||||
}
|
||||
assert(type == type->canonical);
|
||||
if (index->expr_kind == EXPR_CONST)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_POINTER:
|
||||
return true;
|
||||
case TYPE_ARRAY:
|
||||
{
|
||||
BigInt size;
|
||||
bigint_init_unsigned(&size, type->array.len);
|
||||
if (bigint_cmp(&size, &index->const_expr.i) != CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s, exceeding max index of %llu.",
|
||||
bigint_to_error_string(&index->const_expr.i, 10), type->array.len - 1);
|
||||
return false;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
if (bigint_cmp_zero(&index->const_expr.i) == CMP_LT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s.", bigint_to_error_string(&index->const_expr.i, 10));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
TODO
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context *context, Type *parent, Expr *expr)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_SUBSCRIPT);
|
||||
@@ -396,41 +594,25 @@ static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context *
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sema_analyse_expr(context, type_isize, index)) return false;
|
||||
if (index->expr_kind == EXPR_RANGE)
|
||||
{
|
||||
if (!sema_expr_analyse_range(context, type_isize, index)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_expr(context, type_isize, index)) return false;
|
||||
}
|
||||
|
||||
// Unless we already have type_usize, cast to type_isize;
|
||||
if (index->type->canonical->type_kind != type_usize->canonical->type_kind)
|
||||
{
|
||||
if (!cast_implicit(index, type_isize)) return false;
|
||||
}
|
||||
if (!expr_cast_to_index(index)) return false;
|
||||
|
||||
// Check range
|
||||
if (index->expr_kind == EXPR_CONST)
|
||||
if (!expr_check_index_in_range(type, index)) return false;
|
||||
|
||||
if (index->expr_kind == EXPR_RANGE)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_ARRAY:
|
||||
{
|
||||
BigInt size;
|
||||
bigint_init_unsigned(&size, type->array.len);
|
||||
if (bigint_cmp(&size, &index->const_expr.i) != CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s, exceeding max index of %llu.",
|
||||
bigint_to_error_string(&index->const_expr.i, 10), type->array.len - 1);
|
||||
return false;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
if (bigint_cmp_zero(&index->const_expr.i) == CMP_LT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s.", bigint_to_error_string(&index->const_expr.i, 10));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
expr->type = type_get_subarray(inner_type);
|
||||
return true;
|
||||
}
|
||||
expr->type = inner_type;
|
||||
return true;
|
||||
@@ -653,55 +835,7 @@ static DesignatedPath *sema_analyse_init_access(Context *context, DesignatedPath
|
||||
return path;
|
||||
}
|
||||
|
||||
static bool expr_cast_to_index(Expr *index)
|
||||
{
|
||||
if (index->expr_kind == EXPR_RANGE)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true;
|
||||
return cast_implicit(index, type_isize);
|
||||
}
|
||||
|
||||
static bool expr_check_index_in_range(Type *type, Expr *index)
|
||||
{
|
||||
if (index->expr_kind == EXPR_RANGE)
|
||||
{
|
||||
return expr_check_index_in_range(type, index->range_expr.left) & expr_check_index_in_range(type, index->range_expr.right);
|
||||
}
|
||||
assert(type == type->canonical);
|
||||
if (index->expr_kind == EXPR_CONST)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_ARRAY:
|
||||
{
|
||||
BigInt size;
|
||||
bigint_init_unsigned(&size, type->array.len);
|
||||
if (bigint_cmp(&size, &index->const_expr.i) != CMP_GT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s, exceeding max index of %llu.",
|
||||
bigint_to_error_string(&index->const_expr.i, 10), type->array.len - 1);
|
||||
return false;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
if (bigint_cmp_zero(&index->const_expr.i) == CMP_LT)
|
||||
{
|
||||
SEMA_ERROR(index, "Array index out of bounds, was %s.", bigint_to_error_string(&index->const_expr.i, 10));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
TODO
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static DesignatedPath *sema_analyse_init_subscript(Context *context, DesignatedPath *parent, Expr *expr, bool *has_found_match, bool *has_reported_error)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_SUBSCRIPT);
|
||||
@@ -1932,6 +2066,11 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner)
|
||||
*/
|
||||
static bool sema_expr_analyse_addr(Context *context, Expr *expr, Expr *inner)
|
||||
{
|
||||
if (inner->type->type_kind == TYPE_FUNC)
|
||||
{
|
||||
expr->type = type_get_ptr(inner->type);
|
||||
return true;
|
||||
}
|
||||
// 1. Check that it is an lvalue.
|
||||
if (!expr_is_ltype(inner))
|
||||
{
|
||||
@@ -2187,23 +2326,27 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp
|
||||
assert(expr->resolve_status == RESOLVE_RUNNING);
|
||||
Expr *inner = expr->unary_expr.expr;
|
||||
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_DEREF:
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
return sema_expr_analyse_deref(context, expr, inner);
|
||||
case UNARYOP_ADDR:
|
||||
if (!sema_analyse_expr_may_be_function(context, inner)) return false;
|
||||
return sema_expr_analyse_addr(context, expr, inner);
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_NEGMOD:
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
return sema_expr_analyse_neg(context, to, expr, inner);
|
||||
case UNARYOP_BITNEG:
|
||||
if (!sema_analyse_expr(context, to, inner)) return false;
|
||||
return sema_expr_analyse_bit_not(context, to, expr, inner);
|
||||
case UNARYOP_NOT:
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
return sema_expr_analyse_not(context, to, expr, inner);
|
||||
case UNARYOP_DEC:
|
||||
case UNARYOP_INC:
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||
case UNARYOP_ERROR:
|
||||
return false;
|
||||
@@ -2331,6 +2474,8 @@ static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeIn
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
return copy;
|
||||
case TYPE_INFO_INC_ARRAY:
|
||||
case TYPE_INFO_VARARRAY:
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
return copy;
|
||||
@@ -2599,22 +2744,31 @@ static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *macro, Expr *inner)
|
||||
{
|
||||
Expr *func_expr = inner->call_expr.function;
|
||||
if (context->macro_nesting > MAX_MACRO_NESTING)
|
||||
{
|
||||
SEMA_ERROR(context->first_macro_call, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sema_analyse_expr(context, NULL, func_expr)) return false;
|
||||
Expr *func_expr = inner->call_expr.function;
|
||||
bool was_in_macro_call = context->in_macro_call;
|
||||
context->in_macro_call = true;
|
||||
bool ok = sema_analyse_expr(context, NULL, inner->call_expr.function);
|
||||
context->in_macro_call = was_in_macro_call;
|
||||
if (!ok) return ok;
|
||||
|
||||
Decl *decl;
|
||||
switch (func_expr->expr_kind)
|
||||
switch (inner->call_expr.function->expr_kind)
|
||||
{
|
||||
case EXPR_TYPE_ACCESS:
|
||||
TODO
|
||||
case EXPR_IDENTIFIER:
|
||||
decl = func_expr->identifier_expr.decl;
|
||||
break;
|
||||
default:
|
||||
TODO
|
||||
SEMA_ERROR(inner, "Expected a macro identifier here");
|
||||
return false;
|
||||
}
|
||||
if (decl->decl_kind != DECL_MACRO)
|
||||
{
|
||||
@@ -2623,18 +2777,56 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr
|
||||
}
|
||||
Expr **args =func_expr->call_expr.arguments;
|
||||
Decl **func_params = decl->macro_decl.parameters;
|
||||
// TODO handle bare macros.
|
||||
// TODO handle $ args and # args
|
||||
|
||||
unsigned num_args = vec_size(args);
|
||||
// unsigned num_params = vec_size(func_params);
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
{
|
||||
Expr *arg = args[i];
|
||||
Decl *param = func_params[i];
|
||||
if (!sema_analyse_expr(context, param->type, arg)) return false;
|
||||
}
|
||||
|
||||
Ast *body = ast_copy_from_macro(context, inner, decl->macro_decl.body);
|
||||
TODO
|
||||
|
||||
ok = true;
|
||||
macro->type = type_void;
|
||||
|
||||
Ast **saved_returns = context_push_returns(context);
|
||||
context->expected_block_type = to;
|
||||
context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK);
|
||||
VECEACH(body->compound_stmt.stmts, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, body->compound_stmt.stmts[i]))
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vec_size(context->returns))
|
||||
{
|
||||
if (to)
|
||||
{
|
||||
SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to));
|
||||
ok = false;
|
||||
}
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
|
||||
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
|
||||
// Let's unify the return statements.
|
||||
left_canonical = unify_returns(context, left_canonical);
|
||||
if (!left_canonical)
|
||||
{
|
||||
ok = false;
|
||||
goto EXIT;
|
||||
}
|
||||
macro->type = left_canonical;
|
||||
EXIT:
|
||||
context_pop_scope(context);
|
||||
context_pop_returns(context, saved_returns);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Expr *expr, Decl *macro)
|
||||
@@ -2683,30 +2875,7 @@ static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr
|
||||
|
||||
|
||||
|
||||
static inline Ast **context_push_returns(Context *context)
|
||||
{
|
||||
Ast** old_returns = context->returns;
|
||||
if (context->returns_cache)
|
||||
{
|
||||
context->returns = context->returns_cache;
|
||||
context->returns_cache = NULL;
|
||||
vec_resize(context->returns, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->returns = NULL;
|
||||
}
|
||||
return old_returns;
|
||||
}
|
||||
|
||||
static inline void context_pop_returns(Context *context, Ast **restore)
|
||||
{
|
||||
if (!context->returns_cache && context->returns)
|
||||
{
|
||||
context->returns_cache = context->returns;
|
||||
}
|
||||
context->returns = restore;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr)
|
||||
@@ -2739,66 +2908,12 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr
|
||||
|
||||
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
|
||||
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
|
||||
bool all_returns_needs_casts = false;
|
||||
// Let's unify the return statements.
|
||||
VECEACH(context->returns, i)
|
||||
left_canonical = unify_returns(context, left_canonical);
|
||||
if (!left_canonical)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
bool last_expr_was_void = left_canonical == type_void;
|
||||
Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void;
|
||||
bool current_expr_was_void = right_canonical == type_void;
|
||||
if (i > 0 && last_expr_was_void != current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns.");
|
||||
SEMA_PREV(context->returns[i - 1], "Previous return was here.");
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
if (to)
|
||||
{
|
||||
if (current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to));
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
if (!cast_implicit(ret_expr, to))
|
||||
{
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The simple case.
|
||||
if (left_canonical == right_canonical) continue;
|
||||
|
||||
// Try to find a common type:
|
||||
Type *max = type_find_max_type(left_canonical, right_canonical);
|
||||
if (!max)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'",
|
||||
type_to_error_string(left_canonical), type_to_error_string(right_canonical));
|
||||
SEMA_PREV(context->returns[i - 1], "The previous return was here.");
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
left_canonical = max;
|
||||
all_returns_needs_casts = true;
|
||||
}
|
||||
if (all_returns_needs_casts)
|
||||
{
|
||||
VECEACH(context->returns, i)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
if (!cast_implicit(ret_expr, left_canonical))
|
||||
{
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
expr->type = left_canonical;
|
||||
EXIT:
|
||||
@@ -2808,10 +2923,7 @@ EXIT:
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_range(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_compound_literal(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, expr->expr_compound_literal.type_info)) return false;
|
||||
@@ -2853,7 +2965,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
case EXPR_TRY:
|
||||
return sema_expr_analyse_try(context, to, expr);
|
||||
case EXPR_RANGE:
|
||||
return sema_expr_analyse_range(context, to, expr);
|
||||
SEMA_ERROR(expr, "Range expression was not expected here.");
|
||||
return false;
|
||||
case EXPR_CONST:
|
||||
return true;
|
||||
case EXPR_BINARY:
|
||||
@@ -2910,5 +3023,36 @@ bool sema_analyse_expr(Context *context, Type *to, Expr *expr)
|
||||
}
|
||||
if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr);
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
if (expr->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
if (expr->identifier_expr.decl->decl_kind == DECL_FUNC)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected function followed by (...) or prefixed by &.");
|
||||
return false;
|
||||
}
|
||||
if (expr->identifier_expr.decl->decl_kind == DECL_MACRO)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by &.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return to ? cast(expr, to, CAST_TYPE_OPTIONAL_IMPLICIT) : true;
|
||||
}
|
||||
|
||||
bool sema_analyse_expr_may_be_function(Context *context, Expr *expr)
|
||||
{
|
||||
switch (expr->resolve_status)
|
||||
{
|
||||
case RESOLVE_NOT_DONE:
|
||||
expr->resolve_status = RESOLVE_RUNNING;
|
||||
break;
|
||||
case RESOLVE_RUNNING:
|
||||
SEMA_ERROR(expr, "Recursive resolution of expression");
|
||||
return expr_poison(expr);
|
||||
case RESOLVE_DONE:
|
||||
return expr_ok(expr);
|
||||
}
|
||||
if (!sema_analyse_expr_dispatch(context, NULL, expr)) return expr_poison(expr);
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
@@ -1062,6 +1062,9 @@ bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
context->expected_block_type = NULL;
|
||||
context->last_local = &context->locals[0];
|
||||
context->in_volatile_section = 0;
|
||||
context->in_macro = 0;
|
||||
context->macro_counter = 0;
|
||||
context->macro_nesting = 0;
|
||||
func->func.annotations = CALLOCS(*func->func.annotations);
|
||||
context_push_scope(context);
|
||||
Decl **params = signature->params;
|
||||
|
||||
@@ -48,18 +48,28 @@ static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
|
||||
return type_info_poison(type);
|
||||
}
|
||||
uint64_t len = 0;
|
||||
if (type->array.len)
|
||||
switch (type->kind)
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, type_usize, type->array.len)) return type_info_poison(type);
|
||||
if (type->array.len->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(type->array.len, "Expected a constant value as array size.");
|
||||
return type_info_poison(type);
|
||||
}
|
||||
len = bigint_as_unsigned(&type->array.len->const_expr.i);
|
||||
case TYPE_INFO_VARARRAY:
|
||||
type->type = type_get_vararray(type->array.base->type);
|
||||
break;
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
type->type = type_get_subarray(type->array.base->type);
|
||||
break;;
|
||||
case TYPE_INFO_ARRAY:
|
||||
if (!sema_analyse_expr_of_required_type(context, type_usize, type->array.len)) return type_info_poison(type);
|
||||
if (type->array.len->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(type->array.len, "Expected a constant value as array size.");
|
||||
return type_info_poison(type);
|
||||
}
|
||||
len = bigint_as_unsigned(&type->array.len->const_expr.i);
|
||||
type->type = type_get_array(type->array.base->type, len);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST);
|
||||
type->type = type_get_array(type->array.base->type, len);
|
||||
type->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
@@ -101,21 +111,15 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
case DECL_UNION:
|
||||
case DECL_ERROR:
|
||||
case DECL_ENUM:
|
||||
case DECL_TYPEDEF:
|
||||
if (decl->resolve_status == RESOLVE_NOT_DONE)
|
||||
{
|
||||
if (!sema_analyse_decl(context, decl)) return decl_poison(decl);
|
||||
}
|
||||
type_info->type = decl->type;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string);
|
||||
return true;
|
||||
case DECL_TYPEDEF:
|
||||
// TODO func
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info))
|
||||
{
|
||||
decl_poison(decl);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string);
|
||||
type_info->type = decl->typedef_decl.type_info->type;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
case DECL_POISONED:
|
||||
return type_info_poison(type_info);
|
||||
case DECL_FUNC:
|
||||
@@ -166,6 +170,8 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
TODO
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
case TYPE_INFO_VARARRAY:
|
||||
case TYPE_INFO_ARRAY:
|
||||
return sema_resolve_array_type(context, type_info);
|
||||
case TYPE_INFO_POINTER:
|
||||
|
||||
@@ -34,7 +34,9 @@ typedef struct _Entry
|
||||
|
||||
static SymTab symtab;
|
||||
|
||||
const char *main_name;
|
||||
const char *attribute_list[NUMBER_OF_ATTRIBUTES];
|
||||
|
||||
const char *main_kw;
|
||||
|
||||
void symtab_init(uint32_t capacity)
|
||||
{
|
||||
@@ -64,9 +66,21 @@ void symtab_init(uint32_t capacity)
|
||||
assert(type == (TokenType)i);
|
||||
assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned);
|
||||
}
|
||||
|
||||
// Init some constant idents
|
||||
TokenType type = TOKEN_IDENT;
|
||||
main_name = symtab_add("main", 4, fnv1a("main", 4), &type);
|
||||
#define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type)
|
||||
main_kw = KW_DEF("main");
|
||||
attribute_list[ATTRIBUTE_INLINE] = KW_DEF("inline");
|
||||
attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline");
|
||||
attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall");
|
||||
attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("noreturn");
|
||||
attribute_list[ATTRIBUTE_ALIGN] = KW_DEF("align");
|
||||
attribute_list[ATTRIBUTE_PACKED] = KW_DEF("packed");
|
||||
attribute_list[ATTRIBUTE_SECTION] = KW_DEF("section");
|
||||
attribute_list[ATTRIBUTE_CNAME] = KW_DEF("cname");
|
||||
attribute_list[ATTRIBUTE_WEAK] = KW_DEF("weak");
|
||||
attribute_list[ATTRIBUTE_OPAQUE] = KW_DEF("opaque");
|
||||
}
|
||||
|
||||
static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash)
|
||||
|
||||
@@ -124,7 +124,7 @@ const char *token_type_to_string(TokenType type)
|
||||
return "})";
|
||||
|
||||
// Three character tokens
|
||||
case TOKEN_ELIPSIS:
|
||||
case TOKEN_ELLIPSIS:
|
||||
return "...";
|
||||
case TOKEN_MULT_MOD_ASSIGN:
|
||||
return "*%=";
|
||||
|
||||
@@ -48,8 +48,9 @@ unsigned size_error_code;
|
||||
unsigned alignment_error_code;
|
||||
|
||||
#define PTR_OFFSET 0
|
||||
#define VAR_ARRAY_OFFSET 1
|
||||
#define ARRAY_OFFSET 2
|
||||
#define SUB_ARRAY_OFFSET 1
|
||||
#define VAR_ARRAY_OFFSET 2
|
||||
#define ARRAY_OFFSET 3
|
||||
|
||||
Type *type_signed_int_by_bitsize(unsigned bytesize)
|
||||
{
|
||||
@@ -113,10 +114,10 @@ const char *type_to_error_string(Type *type)
|
||||
asprintf(&buffer, "%s[%zu]", type_to_error_string(type->array.base), type->array.len);
|
||||
return buffer;
|
||||
case TYPE_VARARRAY:
|
||||
asprintf(&buffer, "%s[]", type_to_error_string(type->array.base));
|
||||
asprintf(&buffer, "%s[*]", type_to_error_string(type->array.base));
|
||||
return buffer;
|
||||
case TYPE_SUBARRAY:
|
||||
asprintf(&buffer, "%s[:]", type_to_error_string(type->array.base));
|
||||
asprintf(&buffer, "%s[]", type_to_error_string(type->array.base));
|
||||
return buffer;
|
||||
case TYPE_ERROR_UNION:
|
||||
return "error";
|
||||
@@ -294,6 +295,57 @@ static Type *type_generate_ptr(Type *ptr_type, bool canonical)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static Type *type_generate_subarray(Type *arr_type, bool canonical)
|
||||
{
|
||||
if (canonical) arr_type = arr_type->canonical;
|
||||
if (!arr_type->type_cache)
|
||||
{
|
||||
create_type_cache(arr_type);
|
||||
}
|
||||
|
||||
Type *arr = arr_type->type_cache[SUB_ARRAY_OFFSET];
|
||||
if (arr == NULL)
|
||||
{
|
||||
arr = type_new(TYPE_SUBARRAY, strformat("%s[]", arr_type->name));
|
||||
arr->array.base = arr_type;
|
||||
arr_type->type_cache[SUB_ARRAY_OFFSET] = arr;
|
||||
if (arr_type == arr_type->canonical)
|
||||
{
|
||||
arr->canonical = arr;
|
||||
}
|
||||
else
|
||||
{
|
||||
arr->canonical = type_generate_subarray(arr_type->canonical, true);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static Type *type_generate_vararray(Type *arr_type, bool canonical)
|
||||
{
|
||||
if (canonical) arr_type = arr_type->canonical;
|
||||
if (!arr_type->type_cache)
|
||||
{
|
||||
create_type_cache(arr_type);
|
||||
}
|
||||
|
||||
Type *arr = arr_type->type_cache[VAR_ARRAY_OFFSET];
|
||||
if (arr == NULL)
|
||||
{
|
||||
arr = type_new(TYPE_VARARRAY, strformat("%s[*]", arr_type->name));
|
||||
arr->array.base = arr_type;
|
||||
arr_type->type_cache[VAR_ARRAY_OFFSET] = arr;
|
||||
if (arr_type == arr_type->canonical)
|
||||
{
|
||||
arr->canonical = arr;
|
||||
}
|
||||
else
|
||||
{
|
||||
arr->canonical = type_generate_vararray(arr_type->canonical, true);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
Type *type_get_ptr(Type *ptr_type)
|
||||
@@ -301,6 +353,16 @@ Type *type_get_ptr(Type *ptr_type)
|
||||
return type_generate_ptr(ptr_type, false);
|
||||
}
|
||||
|
||||
Type *type_get_subarray(Type *arr_type)
|
||||
{
|
||||
return type_generate_subarray(arr_type, false);
|
||||
}
|
||||
|
||||
Type *type_get_vararray(Type *arr_type)
|
||||
{
|
||||
return type_generate_subarray(arr_type, false);
|
||||
}
|
||||
|
||||
Type *type_get_indexed_type(Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
@@ -330,34 +392,11 @@ Type *type_create_array(Type *arr_type, uint64_t len, bool canonical)
|
||||
{
|
||||
create_type_cache(arr_type);
|
||||
}
|
||||
|
||||
// Dynamic array
|
||||
if (len == 0)
|
||||
{
|
||||
Type *array = arr_type->type_cache[VAR_ARRAY_OFFSET];
|
||||
if (array == NULL)
|
||||
{
|
||||
array = type_new(TYPE_VARARRAY, strformat("%s[]", arr_type->name));
|
||||
array->array.base = arr_type;
|
||||
array->array.len = 0;
|
||||
if (arr_type->canonical == arr_type)
|
||||
{
|
||||
array->canonical = array;
|
||||
}
|
||||
else
|
||||
{
|
||||
array->canonical = type_create_array(arr_type, len, true);
|
||||
}
|
||||
arr_type->type_cache[VAR_ARRAY_OFFSET] = array;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
int entries = (int)vec_size(arr_type->type_cache);
|
||||
for (int i = ARRAY_OFFSET; i < entries; i++)
|
||||
{
|
||||
Type *ptr = arr_type->type_cache[i];
|
||||
if (ptr->array.len == arr_type->array.len)
|
||||
if (ptr->array.len == len)
|
||||
{
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user