From b2bfd87a0695bcc1ea5565cf2b6da678fc3230ca Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 20 May 2020 14:22:22 +0200 Subject: [PATCH] Started work on ranges. Added weak/noreturn/inline/cname/stdcall/section/alignment attributes. Some work on subarrays. Fixes to throws. Function pointers work. --- CMakeLists.txt | 2 +- README.md | 10 +- missing.txt | 7 +- resources/grammar.y | 1 + resources/testfragments/super_simple.c3 | 112 ++++- src/compiler/ast.c | 13 +- src/compiler/compiler_internal.h | 38 +- src/compiler/context.c | 3 +- src/compiler/enums.h | 27 +- src/compiler/lexer.c | 8 +- src/compiler/llvm_codegen.c | 37 +- src/compiler/llvm_codegen_expr.c | 256 +++++------ src/compiler/llvm_codegen_function.c | 86 ++-- src/compiler/llvm_codegen_internal.h | 14 +- src/compiler/llvm_codegen_stmt.c | 10 +- src/compiler/llvm_codegen_type.c | 8 +- src/compiler/parse_expr.c | 13 + src/compiler/parse_stmt.c | 3 +- src/compiler/parser.c | 33 +- src/compiler/{casts.c => sema_casts.c} | 38 +- src/compiler/sema_decls.c | 199 ++++++++- src/compiler/sema_expr.c | 536 +++++++++++++++--------- src/compiler/sema_stmts.c | 3 + src/compiler/sema_types.c | 46 +- src/compiler/symtab.c | 18 +- src/compiler/tokens.c | 2 +- src/compiler/types.c | 95 +++-- 27 files changed, 1105 insertions(+), 513 deletions(-) rename src/compiler/{casts.c => sema_casts.c} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 021627fd0..2e56880d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/README.md b/README.md index 26dcbf6ba..23884a635 100644 --- a/README.md +++ b/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. diff --git a/missing.txt b/missing.txt index aefeb1574..11ffdfdd2 100644 --- a/missing.txt +++ b/missing.txt @@ -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 diff --git a/resources/grammar.y b/resources/grammar.y index 5fd2a730e..e8c011b43 100644 --- a/resources/grammar.y +++ b/resources/grammar.y @@ -372,6 +372,7 @@ label_statement labeled_statement : label_statement | CASE constant_expression ':' + | CASE constant_expression ELLIPSIS constant_expression ':' | DEFAULT ':' ; diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 8972cd4cd..5f56b97e8 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -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)); diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 546b06050..52159f7b1 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -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: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ff8a9b256..42bc37ec6 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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); diff --git a/src/compiler/context.c b/src/compiler/context.c index 2fbbaca87..e04f9902d 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -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; } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index fc8ec89be..681c4ca61 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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 { diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 20ceb2030..b323003ab 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -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, "~"); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 8a3197cc7..0e7a6542c 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -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); } \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a28b06760..41147234e 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -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: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 220ece4e5..c2fc59348 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -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 diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 21f13699f..3e21d4547 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -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) { diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 2744152bc..eb0172b13 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -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) diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 79b711302..53e992723 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -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; } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 39db7ba07..ee249a085 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -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 }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index a08967940..a0dfb0a69 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -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: diff --git a/src/compiler/parser.c b/src/compiler/parser.c index ae0e68122..b3aff9768 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -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; diff --git a/src/compiler/casts.c b/src/compiler/sema_casts.c similarity index 96% rename from src/compiler/casts.c rename to src/compiler/sema_casts.c index 89d26bbb4..4369f6aaf 100644 --- a/src/compiler/casts.c +++ b/src/compiler/sema_casts.c @@ -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); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 37847605f..bc12898a6 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 0f45019e9..0386efc64 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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; } \ No newline at end of file diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 81076b3de..0e438ccfc 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -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; diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index e47a50b47..e3caed94a 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -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: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index ed04fe86a..21dcb8148 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -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) diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 0a94f3773..89039067a 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -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 "*%="; diff --git a/src/compiler/types.c b/src/compiler/types.c index 5d0592db2..608e58013 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -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; }