From 564c93700e0c0f77c6b3ad7fa9fa97563d66fd53 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 3 Jan 2021 00:15:51 +0100 Subject: [PATCH] Fixed bug that would intermittently arise from multiple contexts having the same pointer (should preferably be fixed in a different way later). Free all the arenas before codegen. Change "next" to "nextcase". Allow missing function parameters. Add "inline" structs. --- .clang-tidy | 1 + resources/lib/std/array.c3 | 17 +++ resources/lib/std/io.c3 | 11 +- resources/lib/std/mem.c3 | 24 ++++ resources/rosettacode/antiprime.c3 | 36 +++++ resources/rosettacode/helloworld.c3 | 7 + resources/testfragments/test.c3 | 18 +++ resources/testfragments/toposort.c3 | 89 ++++++++++++ src/compiler/compiler.c | 40 ++++-- src/compiler/compiler_internal.h | 13 +- src/compiler/enums.h | 2 +- src/compiler/llvm_codegen.c | 124 +++++++++-------- src/compiler/llvm_codegen_expr.c | 28 +--- src/compiler/llvm_codegen_function.c | 2 +- src/compiler/llvm_codegen_internal.h | 2 + src/compiler/llvm_codegen_module.c | 4 + src/compiler/llvm_codegen_type.c | 5 +- src/compiler/parse_expr.c | 2 +- src/compiler/parse_global.c | 35 ++++- src/compiler/parse_stmt.c | 6 +- src/compiler/sema_expr.c | 131 +++++++++--------- src/compiler/sema_passes.c | 2 - src/compiler/symtab.c | 8 +- src/compiler/target.c | 1 - src/compiler/tokens.c | 4 +- src/utils/malloc.h | 1 + .../statements/defer_next_switch.c3t | 2 +- test/test_suite/types/enum_parse_errors.c3 | 4 +- 28 files changed, 427 insertions(+), 192 deletions(-) create mode 100644 resources/lib/std/array.c3 create mode 100644 resources/lib/std/mem.c3 create mode 100644 resources/rosettacode/antiprime.c3 create mode 100644 resources/rosettacode/helloworld.c3 create mode 100644 resources/testfragments/test.c3 create mode 100644 resources/testfragments/toposort.c3 diff --git a/.clang-tidy b/.clang-tidy index 8e1aad4eb..974a806d3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -25,6 +25,7 @@ Checks: > WarningsAsErrors: "*" CheckOptions: + - { key: readability-function-cognitive-complexity.Threshold, value: 40 } - { key: readability-identifier-naming.StructCase, value: CamelCase } - { key: readability-identifier-naming.FunctionCase, value: lower_case } - { key: readability-identifier-naming.VariableCase, value: lower_case } diff --git a/resources/lib/std/array.c3 b/resources/lib/std/array.c3 new file mode 100644 index 000000000..5969be682 --- /dev/null +++ b/resources/lib/std/array.c3 @@ -0,0 +1,17 @@ +module std::array; + +import std::mem; + +public macro make($Type, usize elements) +{ + assert(elements > 0); + $Type* ptr = mem::alloc($Type.sizeof, elements); + return ptr[0..(elements - 1)]; +} + +public macro make_zero($Type, usize elements) +{ + assert(elements > 0); + $Type* ptr = mem::calloc($Type.sizeof, elements); + return ptr[0..(elements - 1)]; +} \ No newline at end of file diff --git a/resources/lib/std/io.c3 b/resources/lib/std/io.c3 index f8062840c..407de038c 100644 --- a/resources/lib/std/io.c3 +++ b/resources/lib/std/io.c3 @@ -18,11 +18,16 @@ extern func int fseek(void*, long, int); extern func void* fopen(char *, char *); */ +struct File +{ + void *file; +} + extern func int _puts(char* message) @cname("puts"); extern func int printf(char* message, ...); extern func int _putchar(char c) @cname("putchar"); - +extern File *__stdinp; public func int putchar(char c) @inline { @@ -46,10 +51,6 @@ public func int println(char *message) @inline } /* -struct File -{ - void *file; -} enum Seek { diff --git a/resources/lib/std/mem.c3 b/resources/lib/std/mem.c3 new file mode 100644 index 000000000..3aa7c3036 --- /dev/null +++ b/resources/lib/std/mem.c3 @@ -0,0 +1,24 @@ +module std::mem; + +extern func void* _malloc(usize bytes) @cname("malloc"); +extern func void* _calloc(usize bytes, usize elements) @cname("calloc"); +extern func void _free(void* ptr) @cname("free"); + +public macro malloc($Type) +{ + // TODO: return cast(_malloc($Type.sizeof) as $Type*); + return cast(mem::alloc($Type.sizeof) as $Type*); +} +public func void* alloc(usize size, usize elements = 1) @inline +{ + return _malloc(size * elements); +} + +public func void* calloc(usize size, usize elements = 1) @inline +{ + return _calloc(size, elements); +} +public func void free(void* ptr) @inline +{ + _free(ptr); +} \ No newline at end of file diff --git a/resources/rosettacode/antiprime.c3 b/resources/rosettacode/antiprime.c3 new file mode 100644 index 000000000..92322a3cc --- /dev/null +++ b/resources/rosettacode/antiprime.c3 @@ -0,0 +1,36 @@ +module antiprime; +import std::io; + +extern func int printf(char* message, ...); + +func int countDivisors(int n) +{ + if (n < 2) return 1; + int count = 2; + for (int i = 2; i <= n / 2; i++) + { + if (n % i == 0) count++; + } + return count; +} + +func int main() +{ + int maxDiv; + int count; + io::println("The first 20 anti-primes are:"); + int n = 1; + while (count < 20) + { + int d = countDivisors(n); + if (d > maxDiv) + { + printf("%d ", n); + maxDiv = d; + count++; + } + n++; + } + io::println(""); + return 0; +} \ No newline at end of file diff --git a/resources/rosettacode/helloworld.c3 b/resources/rosettacode/helloworld.c3 new file mode 100644 index 000000000..c5a97c3de --- /dev/null +++ b/resources/rosettacode/helloworld.c3 @@ -0,0 +1,7 @@ +import std::io; + +func int main() +{ + io::println("Hello world"); + return 0; +} \ No newline at end of file diff --git a/resources/testfragments/test.c3 b/resources/testfragments/test.c3 new file mode 100644 index 000000000..5efd5ce4f --- /dev/null +++ b/resources/testfragments/test.c3 @@ -0,0 +1,18 @@ +import std::io; + +extern func int printf(char* message, ...); + +macro void swap(&a, &b) +{ + typeof(a) temp = a; + a = b; + b = temp; +} + +func void main() +{ + int x = 1; + int y = 2; + @swap(x, y); + printf("x: %d y: &d\n", x, y); +} \ No newline at end of file diff --git a/resources/testfragments/toposort.c3 b/resources/testfragments/toposort.c3 new file mode 100644 index 000000000..76bcbc607 --- /dev/null +++ b/resources/testfragments/toposort.c3 @@ -0,0 +1,89 @@ +module topologicalsort; +import std::mem; +import std::array; + +extern func void printf(char* x, ...); + +struct InputPair +{ + int value; + int successor; +} + +struct Entry +{ + int value; + Entry* next; +} + +struct TopoList +{ + int count; + Entry* next; +} + + +public func void sort(InputPair[] pairs, uint elements) +{ + printf(.x = "fe"); + InputPair[] result = @array::make(InputPair, pairs.len()); + TopoList* top = mem::calloc(TopoList.sizeof, elements); + for (int i = 0; i < pairs.len(); i++) + { + InputPair pair = pairs[i]; + assert(pair.value >= 0 && pair.value < elements); + assert(pair.successor >= 0 && pair.successor < elements); + top[pair.successor].count++; + Entry* successor_entry = @mem::malloc(Entry); + *successor_entry = { pair.successor, null }; + Entry** next_ref = &top[pair.value].next; + while (*next_ref) + { + next_ref = &(*next_ref).next; + } + *next_ref = successor_entry; + } + int[] intout = @array::make(int, elements); + int count = 0; + while LOOP: (1) + { + for (int i = 1; i < elements; i++) + { + if (top[i].count == 0) + { + intout[count++] = i; + Entry *next = top[i].next; + while (next) + { + top[next.value].count--; + next = next.next; + } + top[i].count = -1; + continue LOOP; + } + } + break; + } + printf("Got %d elements.\n", count); + for (int i = 0; i < count; i++) + { + printf("%d\n", intout[i]); + } +} + +public func void main() +{ + InputPair[10] pairs = { + { 9, 2 }, + { 3, 7 }, + { 7, 5 }, + { 5, 8 }, + { 8, 6 }, + { 4, 6 }, + { 1, 3 }, + { 7, 4 }, + { 9, 5 }, + { 2, 8 }, + }; + sort(&pairs, 10); +} \ No newline at end of file diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index f3bb973b3..40852ac79 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -80,6 +80,8 @@ void compiler_compile(BuildTarget *target) { vec_add(target->sources, strformat("%s/std/builtin.c3", compiler.lib_dir)); vec_add(target->sources, strformat("%s/std/io.c3", compiler.lib_dir)); + vec_add(target->sources, strformat("%s/std/mem.c3", compiler.lib_dir)); + vec_add(target->sources, strformat("%s/std/array.c3", compiler.lib_dir)); } VECEACH(target->sources, i) { @@ -90,38 +92,39 @@ void compiler_compile(BuildTarget *target) vec_add(contexts, context); parse_file(context); } + unsigned source_count = vec_size(contexts); assert(contexts); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_process_imports(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_register_globals(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_conditional_compilation(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_decls(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_ct_assert(contexts[i]); } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { sema_analysis_pass_functions(contexts[i]); } @@ -129,7 +132,7 @@ void compiler_compile(BuildTarget *target) if (build_options.command == COMMAND_GENERATE_HEADERS) { - VECEACH(contexts, i) + for (unsigned i = 0; i < source_count; i++) { Context *context = contexts[i]; header_gen(context); @@ -137,11 +140,14 @@ void compiler_compile(BuildTarget *target) return; } + llvm_codegen_setup(); - VECEACH(contexts, i) + + void **gen_contexts = malloc(source_count * sizeof(void*)); + for (unsigned i = 0; i < source_count; i++) { Context *context = contexts[i]; - llvm_codegen(context); + gen_contexts[i] = llvm_gen(context); } printf("-- AST/EXPR INFO -- \n"); @@ -152,7 +158,23 @@ void compiler_compile(BuildTarget *target) printf(" * Token memory use: %llukb\n", (unsigned long long)(toktype_arena.allocated) / 1024); printf(" * Sourceloc memory use: %llukb\n", (unsigned long long)(sourceloc_arena.allocated) / 1024); printf(" * Token data memory use: %llukb\n", (unsigned long long)(tokdata_arena.allocated) / 1024); + + ast_arena_free(); + decl_arena_free(); + expr_arena_free(); + type_info_arena_free(); + sourceloc_arena_free(); + tokdata_arena_free(); + print_arena_status(); + + free_arena(); + + for (unsigned i = 0; i < source_count; i++) + { + llvm_codegen(gen_contexts[i]); + } + exit(EXIT_SUCCESS); } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 8bf63318f..118471c0b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -437,6 +437,7 @@ typedef struct _Decl bool is_packed : 1; bool is_opaque : 1; bool needs_additional_pad : 1; + bool is_substruct : 1; void *backend_ref; const char *cname; AlignSize alignment; @@ -1323,9 +1324,8 @@ extern const char *kw_kindof; extern const char *kw_nameof; extern const char *kw_qnameof; extern const char *kw_len; +extern const char *kw_inline; extern const char *kw_ordinal; -extern const char *kw___alloc; -extern const char *kw___free; extern const char *kw___round; extern const char *kw___ceil; extern const char *kw___trunc; @@ -1392,7 +1392,8 @@ CastKind cast_to_bool_kind(Type *type); bool cast_implicitly_to_runtime(Context *context, Expr *expr); -void llvm_codegen(Context *context); +void llvm_codegen(void *module); +void *llvm_gen(Context *context); void llvm_codegen_setup(); void header_gen(Context *context); @@ -1699,6 +1700,12 @@ static inline uint64_t aligned_offset(uint64_t offset, uint64_t alignment) return ((offset + alignment - 1) / alignment) * alignment; } +static inline bool type_is_substruct(Type *type) +{ + assert(type == type->canonical); + return type->type_kind == TYPE_STRUCT && type->decl->is_substruct; +} + static inline bool type_is_float(Type *type) { assert(type == type->canonical); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 7f6378bd1..557062d0e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -417,7 +417,7 @@ typedef enum TOKEN_LOCAL, TOKEN_MACRO, TOKEN_MODULE, - TOKEN_NEXT, + TOKEN_NEXTCASE, TOKEN_NULL, TOKEN_PUBLIC, TOKEN_RETURN, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index d86d85f52..b9eaefe7b 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -47,7 +47,10 @@ static void gencontext_init(GenContext *context, Context *ast_context) static void gencontext_destroy(GenContext *context) { + free(context->ir_filename); + free(context->object_filename); LLVMContextDispose(context->context); + free(context); } LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, unsigned int align, bool bitcast) @@ -245,8 +248,7 @@ void gencontext_emit_object_file(GenContext *context) LLVMDisposeMessage(layout); // Generate .o or .obj file - char *filename = strformat("%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); - if (LLVMTargetMachineEmitToFile(target_machine(), context->module, filename, LLVMObjectFile, &err)) + if (LLVMTargetMachineEmitToFile(target_machine(), context->module, context->object_filename, LLVMObjectFile, &err)) { error_exit("Could not emit object file: %s", err); } @@ -255,8 +257,7 @@ void gencontext_emit_object_file(GenContext *context) void gencontext_print_llvm_ir(GenContext *context) { char *err = NULL; - char *filename = strformat("%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); - if (LLVMPrintModuleToFile(context->module, filename, &err)) + if (LLVMPrintModuleToFile(context->module, context->ir_filename, &err)) { error_exit("Could not emit ir to file: %s", err); } @@ -671,53 +672,10 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl) } } -void llvm_codegen(Context *context) +void llvm_codegen(void *context) { - assert(intrinsics_setup); - GenContext gen_context; - gencontext_init(&gen_context, context); - gencontext_begin_module(&gen_context); - // EmitDeferred() - VECEACH(context->external_symbol_list, i) - { - llvm_emit_extern_decl(&gen_context, context->external_symbol_list[i]); - } - VECEACH(context->methods, i) - { - llvm_emit_function_decl(&gen_context, context->methods[i]); - } - VECEACH(context->functions, i) - { - llvm_emit_function_decl(&gen_context, context->functions[i]); - } - VECEACH(context->types, i) - { - gencontext_emit_decl(&gen_context, context->types[i]); - } - VECEACH(context->vars, i) - { - gencontext_emit_global_variable_definition(&gen_context, context->vars[i]); - } - VECEACH(context->functions, i) - { - Decl *decl = context->functions[i]; - if (decl->func.body) llvm_emit_function_body(&gen_context, decl); - } - VECEACH(context->methods, i) - { - Decl *decl = context->methods[i]; - if (decl->func.body) llvm_emit_function_body(&gen_context, decl); - } - - if (llvm_use_debug(&gen_context)) LLVMDIBuilderFinalize(gen_context.debug.builder); - - // If it's in test, then we want to serialize the IR before it is optimized. - if (build_options.test_mode) - { - gencontext_print_llvm_ir(&gen_context); - gencontext_verify_ir(&gen_context); - } - + GenContext *gen_context = context; + LLVMModuleRef module = gen_context->module; // Starting from here we could potentially thread this: LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate(); LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level); @@ -725,7 +683,7 @@ void llvm_codegen(Context *context) LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, build_options.optimization_level == OPTIMIZATION_NONE); LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, get_inlining_threshold()); LLVMPassManagerRef pass_manager = LLVMCreatePassManager(); - LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(gen_context.module); + LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(module); LLVMAddAnalysisPasses(target_machine(), function_pass_manager); LLVMAddAnalysisPasses(target_machine(), pass_manager); LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager); @@ -739,7 +697,7 @@ void llvm_codegen(Context *context) // Run function passes LLVMInitializeFunctionPassManager(function_pass_manager); - LLVMValueRef current_function = LLVMGetFirstFunction(gen_context.module); + LLVMValueRef current_function = LLVMGetFirstFunction(module); while (current_function) { LLVMRunFunctionPassManager(function_pass_manager, current_function); @@ -749,20 +707,70 @@ void llvm_codegen(Context *context) LLVMDisposePassManager(function_pass_manager); // Run module pass - LLVMRunPassManager(pass_manager, gen_context.module); + LLVMRunPassManager(pass_manager, module); LLVMDisposePassManager(pass_manager); // Serialize the LLVM IR, if requested, also verify the IR in this case if (build_options.emit_llvm) { - gencontext_print_llvm_ir(&gen_context); - gencontext_verify_ir(&gen_context); + gencontext_print_llvm_ir(gen_context); + gencontext_verify_ir(gen_context); } - if (build_options.emit_bitcode) gencontext_emit_object_file(&gen_context); + if (build_options.emit_bitcode) gencontext_emit_object_file(gen_context); - gencontext_end_module(&gen_context); - gencontext_destroy(&gen_context); + gencontext_end_module(gen_context); + gencontext_destroy(gen_context); + + +} +void *llvm_gen(Context *context) +{ + assert(intrinsics_setup); + GenContext *gen_context = calloc(sizeof(GenContext), 1); + gencontext_init(gen_context, context); + gencontext_begin_module(gen_context); + // EmitDeferred() + VECEACH(context->external_symbol_list, i) + { + llvm_emit_extern_decl(gen_context, context->external_symbol_list[i]); + } + VECEACH(context->methods, i) + { + llvm_emit_function_decl(gen_context, context->methods[i]); + } + VECEACH(context->functions, i) + { + llvm_emit_function_decl(gen_context, context->functions[i]); + } + VECEACH(context->types, i) + { + gencontext_emit_decl(gen_context, context->types[i]); + } + VECEACH(context->vars, i) + { + gencontext_emit_global_variable_definition(gen_context, context->vars[i]); + } + VECEACH(context->functions, i) + { + Decl *decl = context->functions[i]; + if (decl->func.body) llvm_emit_function_body(gen_context, decl); + } + VECEACH(context->methods, i) + { + Decl *decl = context->methods[i]; + if (decl->func.body) llvm_emit_function_body(gen_context, decl); + } + + if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(gen_context->debug.builder); + + // If it's in test, then we want to serialize the IR before it is optimized. + if (build_options.test_mode) + { + gencontext_print_llvm_ir(gen_context); + gencontext_verify_ir(gen_context); + } + return gen_context; } void llvm_attribute_add_int(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, uint64_t val, int index) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 27868dc97..c3e9e20e8 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1437,7 +1437,7 @@ static void gencontext_emit_slice_assign(GenContext *c, BEValue *be_value, Expr // We will be replacing the slice assign with code that roughly looks like this: // size_t end = slice_end; // size_t slice_current = slice_start; - // while (slice_current < end) pointer[slice_current++] = value; + // while (slice_current <= end) pointer[slice_current++] = value; // First, find the value assigned. Expr *assigned_value = expr->slice_assign_expr.right; @@ -1470,7 +1470,7 @@ static void gencontext_emit_slice_assign(GenContext *c, BEValue *be_value, Expr LLVMValueRef offset = LLVMBuildPhi(c->builder, llvm_get_type(c, start_type), ""); // Check if we're not at the end. - LLVMValueRef not_at_end = llvm_emit_int_comparison(c, start_type, end_type, offset, end_index, BINARYOP_LT); + LLVMValueRef not_at_end = llvm_emit_int_comparison(c, start_type, end_type, offset, end_index, BINARYOP_LE); // If jump to the assign block if we're not at the end index. BEValue value; @@ -2405,26 +2405,6 @@ void gencontext_emit_call_intrinsic_expr(GenContext *c, BEValue *be_value, Expr llvm_emit_fp_intrinsic_expr(c, intrinsic_id_ceil, be_value, expr); return; } - if (function_decl->name == kw___alloc) - { - unsigned arguments = vec_size(expr->call_expr.arguments); - BEValue size_value; - llvm_emit_expr(c, &size_value, expr->call_expr.arguments[0]); - llvm_value_rvalue(c, &size_value); - LLVMValueRef result = LLVMBuildArrayMalloc(c->builder, llvm_get_type(c, type_byte), llvm_value_rvalue_store(c, &size_value), ""); - result = LLVMBuildBitCast(c->builder, result, llvm_get_type(c, expr->type), ""); - llvm_value_set(be_value, result, expr->type); - return; - } - if (function_decl->name == kw___free) - { - unsigned arguments = vec_size(expr->call_expr.arguments); - BEValue size_value; - llvm_emit_expr(c, &size_value, expr->call_expr.arguments[0]); - llvm_value_rvalue(c, &size_value); - LLVMBuildFree(c->builder, llvm_value_rvalue_store(c, &size_value)); - return; - } UNREACHABLE } @@ -2539,7 +2519,7 @@ void llvm_emit_parameter(GenContext *context, LLVMValueRef **args, ABIArgInfo *i } } -void gencontext_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) +void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) { printf("Optimize call return\n"); FunctionSignature *signature; @@ -3041,7 +3021,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) gencontext_emit_access_addr(c, value, expr); return; case EXPR_CALL: - gencontext_emit_call_expr(c, value, expr); + llvm_emit_call_expr(c, value, expr); return; case EXPR_GROUP: expr = expr->group_expr; diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index f9a2984d9..7c4452347 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -636,6 +636,6 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case DECL_ENUM: TODO case NON_TYPE_DECLS: - UNREACHABLE + return; } } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index af9b4ae4a..65d7f5af1 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -72,6 +72,8 @@ typedef struct LLVMBuilderRef builder; LLVMBasicBlockRef current_block; LLVMBasicBlockRef catch_block; + char *ir_filename; + char *object_filename; // The recipient of the error value in a catch(err = ...) expression. LLVMValueRef error_var; LLVMTypeRef bool_type; diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index edc509853..793695395 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -8,6 +8,10 @@ void gencontext_begin_module(GenContext *context) { assert(!context->module && "Expected no module"); + + asprintf(&context->ir_filename, "%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); + asprintf(&context->object_filename, "%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); + const char *full_path = context->ast_context->file->full_path; char *mangled_module_name = strformat("%s-%s", context->ast_context->module->name->module, context->ast_context->file->name); context->module = LLVMModuleCreateWithNameInContext(mangled_module_name, context->context); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index cfa514a46..dfa64ac56 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -125,7 +125,10 @@ static inline LLVMTypeRef llvm_type_from_ptr(GenContext *context, Type *type) { return type->backend_type = llvm_get_type(context, type->canonical); } - + if (type == type_voidptr) + { + return type->backend_type = llvm_get_ptr_type(context, type_byte); + } return type->backend_type = LLVMPointerType(llvm_get_type(context, type->pointer), /** TODO **/0); } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 6eff7bb0b..154695aa4 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -580,7 +580,7 @@ static Expr *parse_else_expr(Context *context, Expr *left) case TOKEN_RETURN: case TOKEN_BREAK: case TOKEN_CONTINUE: - case TOKEN_NEXT: + case TOKEN_NEXTCASE: { Ast *ast = TRY_AST_OR(parse_jump_stmt_no_eos(context), poisoned_expr); else_expr->else_expr.is_jump = true; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 909e613e7..b2accdf30 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1014,7 +1014,7 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) * | type_expression IDENT '=' initializer * ; */ -static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool type_only) +static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool require_name) { TypeInfo *type = TRY_TYPE_OR(parse_type(context), false); Decl *param = decl_new_var(context->tok.id, type, VARDECL_PARAM, parent_visibility); @@ -1026,7 +1026,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili const char *name = param->name; - if (!name && !type_only) + if (!name && require_name) { if (!TOKEN_IS(TOKEN_COMMA) && !TOKEN_IS(TOKEN_RPAREN)) { @@ -1038,7 +1038,7 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili sema_error_at_prev_end(context->tok, "Unexpected end of the parameter list, did you forget an ')'?"); return false; } - SEMA_ERROR(type, "The function parameter must be named."); + SEMA_ERROR(type, "The parameter must be named."); return false; } if (name && try_consume(context, TOKEN_EQ)) @@ -1086,7 +1086,7 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa } else { - if (!parse_param_decl(context, parent_visibility, &(signature->params), is_interface)) return false; + if (!parse_param_decl(context, parent_visibility, &(signature->params), false)) return false; } if (!try_consume(context, TOKEN_COMMA)) { @@ -1121,7 +1121,6 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa */ bool parse_struct_body(Context *context, Decl *parent) { - CONSUME_OR(TOKEN_LBRACE, false); assert(decl_is_struct_type(parent)); @@ -1161,6 +1160,23 @@ bool parse_struct_body(Context *context, Decl *parent) } continue; } + bool was_inline = false; + if (token_type == TOKEN_IDENT && TOKSTR(context->tok) == kw_inline) + { + if (parent->decl_kind != DECL_STRUCT) + { + SEMA_TOKEN_ERROR(context->tok, "Only structs may have 'inline' elements, did you make a mistake?"); + return false; + } + if (index > 0) + { + SEMA_TOKID_ERROR(context->prev_tok, "Only the first element may be 'inline', did you order your fields wrong?"); + return false; + } + parent->is_substruct = true; + was_inline = true; + advance(context); + } TypeInfo *type = TRY_TYPE_OR(parse_type(context), false); while (1) { @@ -1175,6 +1191,11 @@ bool parse_struct_body(Context *context, Decl *parent) } advance(context); if (!try_consume(context, TOKEN_COMMA)) break; + if (was_inline) + { + SEMA_ERROR(member, "'Inline' can only be applied to a single member, so please define it on its own line."); + return false; + } } CONSUME_OR(TOKEN_EOS, false); } @@ -1575,7 +1596,7 @@ static inline bool parse_enum_spec(Context *context, TypeInfo **type_ref, Decl** if (!try_consume(context, TOKEN_LPAREN)) return true; while (!try_consume(context, TOKEN_RPAREN)) { - if (!parse_param_decl(context, parent_visibility, parameters_ref, false)) return false; + if (!parse_param_decl(context, parent_visibility, parameters_ref, true)) return false; if (!try_consume(context, TOKEN_COMMA)) { EXPECT_OR(TOKEN_RPAREN, false); @@ -1698,7 +1719,7 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit func->func.function_signature.rtype = return_type; func->func.function_signature.failable = try_consume(context, TOKEN_BANG); SourceSpan start = source_span_from_token_id(context->tok.id); - bool had_error; + bool had_error = false; Path *path = parse_path_prefix(context, &had_error); if (had_error) return poisoned_decl; if (path || TOKEN_IS(TOKEN_TYPE_IDENT)) diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index d6dd9976d..01148e1ed 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -499,7 +499,7 @@ static inline Ast* parse_continue(Context *context) static inline Ast* parse_next(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); - advance_and_verify(context, TOKEN_NEXT); + advance_and_verify(context, TOKEN_NEXTCASE); if (!TOKEN_IS(TOKEN_EOS)) { if (TOKEN_IS(TOKEN_CONST_IDENT) && context->next_tok.type == TOKEN_COLON) @@ -964,7 +964,7 @@ Ast *parse_stmt(Context *context) Ast *ast = TRY_AST(parse_break(context)); RETURN_AFTER_EOS(ast); } - case TOKEN_NEXT: + case TOKEN_NEXTCASE: { Ast *ast = TRY_AST(parse_next(context)); RETURN_AFTER_EOS(ast); @@ -1121,7 +1121,7 @@ Ast *parse_jump_stmt_no_eos(Context *context) { switch (context->tok.type) { - case TOKEN_NEXT: + case TOKEN_NEXTCASE: return parse_next(context); case TOKEN_RETURN: return parse_return(context); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ca7817598..529dbd4b7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -26,6 +26,16 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr); #define MACRO_COPY_AST_LIST(x) x = ast_copy_list_from_macro(context, x) #define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, x) +static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) +{ + Expr *embedded_struct = expr_new(EXPR_ACCESS, parent->span); + embedded_struct->resolve_status = RESOLVE_DONE; + embedded_struct->access_expr.parent = parent; + embedded_struct->access_expr.ref = parent_decl->strukt.members[0]; + embedded_struct->type = embedded_struct->access_expr.ref->type; + return embedded_struct; +} + static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr); static inline bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr); static inline bool expr_const_int_valid(Expr *expr, Type *type) @@ -696,7 +706,7 @@ static inline int find_index_of_named_parameter(Decl **func_params, Expr *expr) { if (func_params[i]->name == name) return (int)i; } - SEMA_ERROR(expr, "There's no parameter with the name '%s', if you want an assignment expression, enclose it in ().", name); + SEMA_ERROR(expr, "There's no parameter with the name '%s'.", name); return -1; } @@ -737,41 +747,6 @@ static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr { return sema_expr_analyse_intrinsic_fp_invocation(context, expr, decl, to); } - if (decl->name == kw___alloc) - { - if (to && type_is_pointer(to)) - { - expr->type = to; - } - else - { - expr->type = type_voidptr; - } - if (arguments != 1) - { - SEMA_ERROR(expr, "Expected 1 argument to intrinsic __alloc."); - return false; - } - if (!sema_analyse_expr_of_required_type(context, type_usize, expr->call_expr.arguments[0], false)) return false; - return true; - } - if (decl->name == kw___free) - { - expr->type = type_void; - if (arguments != 1) - { - SEMA_ERROR(expr, "Expected 1 argument to intrinsic __free."); - return false; - } - Expr *pointer_expr = expr->call_expr.arguments[0]; - if (!sema_analyse_expr(context, NULL, pointer_expr)) return false; - if (pointer_expr->type->type_kind != TYPE_POINTER) - { - // TODO vararrays - handle them. - SEMA_ERROR(expr, "Expected expected to free a pointer type."); - } - return true; - } UNREACHABLE } @@ -1351,19 +1326,38 @@ static bool expr_check_index_in_range(Context *context, Type *type, Expr *index_ return true; } -static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context *context, Type *parent, Expr *expr) +static Type *sema_expr_find_indexable_type_recursively(Type **type, Expr **parent) { + while (1) + { + Type *inner_type = type_get_indexed_type(*type); + if (!inner_type && type_is_substruct(*type)) + { + Expr *embedded_struct = expr_access_inline_member(*parent, (*type)->decl); + *type = embedded_struct->type->canonical; + *parent = embedded_struct; + continue; + } + return inner_type; + } +} +static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) +{ + if (!sema_analyse_expr(context, NULL, expr->subscript_expr.expr)) return false; + expr->failable = expr->subscript_expr.expr->failable; assert(expr->expr_kind == EXPR_SUBSCRIPT); Expr *subscripted = expr->subscript_expr.expr; - Type *type = parent ? parent->canonical : subscripted->type->canonical; + Type *type = subscripted->type->canonical; Expr *index = expr->subscript_expr.index; - Type *inner_type = type_get_indexed_type(type); + Type *current_type = type; + Expr *current_expr = subscripted; + + Type *inner_type = sema_expr_find_indexable_type_recursively(¤t_type, ¤t_expr); if (!inner_type) { - SEMA_ERROR((parent ? expr : subscripted), "Cannot index '%s'.", type_to_error_string(type)); + SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(type)); return false; } - if (!sema_analyse_expr(context, type_isize, index)) return false; expr->constant = index->constant & subscripted->constant; @@ -1373,35 +1367,35 @@ static inline bool sema_expr_analyse_subscript_after_parent_resolution(Context * if (!expr_cast_to_index(context, index)) return false; // Check range - if (!expr_check_index_in_range(context, type, index, false, expr->subscript_expr.from_back)) return false; + if (!expr_check_index_in_range(context, current_type, index, false, expr->subscript_expr.from_back)) return false; + expr->subscript_expr.expr = current_expr; expr->failable |= index->failable; expr->type = inner_type; return true; } -static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) -{ - if (!sema_analyse_expr(context, NULL, expr->subscript_expr.expr)) return false; - expr->failable = expr->subscript_expr.expr->failable; - return sema_expr_analyse_subscript_after_parent_resolution(context, NULL, expr); -} - -static inline bool sema_expr_analyse_slice_after_parent_resolution(Context *context, Type *parent, Expr *expr) +static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) { + if (!sema_analyse_expr(context, NULL, expr->slice_expr.expr)) return false; + expr->failable = expr->slice_expr.expr->failable; assert(expr->expr_kind == EXPR_SLICE); Expr *subscripted = expr->slice_expr.expr; expr->pure = subscripted->pure; expr->constant = subscripted->constant; - Type *type = parent ? parent->canonical : subscripted->type->canonical; + Type *type = subscripted->type->canonical; Expr *start = expr->slice_expr.start; Expr *end = expr->slice_expr.end; - Type *inner_type = type_get_indexed_type(type); + + Expr *current_expr = subscripted; + + Type *inner_type = sema_expr_find_indexable_type_recursively(&type, ¤t_expr); if (!inner_type) { - SEMA_ERROR((parent ? expr : subscripted), "Cannot slice '%s'.", type_to_error_string(type)); + SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type)); return false; } + expr->slice_expr.expr = current_expr; if (!sema_analyse_expr(context, type_isize, start)) return false; expr->pure &= start->pure; @@ -1479,13 +1473,6 @@ static inline bool sema_expr_analyse_slice_after_parent_resolution(Context *cont return true; } -static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) -{ - if (!sema_analyse_expr(context, NULL, expr->slice_expr.expr)) return false; - expr->failable = expr->slice_expr.expr->failable; - return sema_expr_analyse_slice_after_parent_resolution(context, NULL, expr); -} - static inline void insert_access_deref(Expr *expr) { Expr *deref = expr_new(EXPR_UNARY, expr->span); @@ -1855,6 +1842,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) return false; } + static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { Expr *parent = expr->access_expr.parent; @@ -1881,8 +1869,13 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) if (is_pointer) { type = type->pointer; + if (!sema_cast_rvalue(context, NULL, parent)) return false; + insert_access_deref(expr); + parent = expr->access_expr.parent; } const char *kw = TOKSTR(expr->access_expr.sub_element); + Expr *current_parent = parent; +CHECK_DEEPER: switch (type->type_kind) { case TYPE_SUBARRAY: @@ -1922,22 +1915,28 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) SEMA_ERROR(expr, "Cannot access '%s' on '%s'", TOKSTR(expr->access_expr.sub_element), type_to_error_string(parent_type)); return false; } + Decl *decl = type->decl; context_push_scope(context); add_members_to_context(context, decl); Decl *member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); - context_pop_scope(context); + + if (!member) { + // We have a potential embedded struct check: + if (type_is_substruct(type)) + { + Expr *embedded_struct = expr_access_inline_member(parent, type->decl); + current_parent = embedded_struct; + type = embedded_struct->type->canonical; + goto CHECK_DEEPER; + } SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw); return false; } - if (is_pointer) - { - if (!sema_cast_ident_rvalue(context, NULL, expr->access_expr.parent)) return false; - insert_access_deref(expr); - } + expr->access_expr.parent = current_parent; expr->constant = expr->access_expr.parent->constant; expr->pure = expr->access_expr.parent->pure; diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 0cae8250a..242372f94 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -48,8 +48,6 @@ void sema_analysis_pass_process_imports(Context *context) context_add_intrinsic(context, kw___round); context_add_intrinsic(context, kw___trunc); context_add_intrinsic(context, kw___ceil); - context_add_intrinsic(context, kw___alloc); - context_add_intrinsic(context, kw___free); context_add_intrinsic(context, kw___sqrt); DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors); } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 6c272dce5..31254cc23 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -45,9 +45,8 @@ const char *kw_nameof; const char *kw_qnameof; const char *kw_kindof; const char *kw_len; +const char *kw_inline; const char *kw_ordinal; -const char *kw___alloc; -const char *kw___free; const char *kw___round; const char *kw___ceil; const char *kw___trunc; @@ -85,6 +84,7 @@ void symtab_init(uint32_t capacity) // Init some constant idents TokenType type = TOKEN_IDENT; #define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type) + kw_inline = KW_DEF("inline"); kw_main = KW_DEF("main"); kw_sizeof = KW_DEF("sizeof"); kw_alignof = KW_DEF("alignof"); @@ -95,14 +95,12 @@ void symtab_init(uint32_t capacity) kw_len = KW_DEF("len"); kw_align = KW_DEF("align"); kw_ordinal = KW_DEF("ordinal"); - kw___alloc = KW_DEF("__alloc"); - kw___free = KW_DEF("__free"); kw___round = KW_DEF("__round"); kw___sqrt = KW_DEF("__sqrt"); kw___trunc = KW_DEF("__trunc"); kw___ceil = KW_DEF("__ceil"); - attribute_list[ATTRIBUTE_INLINE] = KW_DEF("inline"); + attribute_list[ATTRIBUTE_INLINE] = kw_inline; attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline"); attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall"); attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("noreturn"); diff --git a/src/compiler/target.c b/src/compiler/target.c index 92102708a..3909e3310 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -480,7 +480,6 @@ void target_setup(void) break; default: FATAL_ERROR("Unsupported architecture."); - break; } build_target.int_128 = os_target_supports_int128(build_target.os, build_target.arch); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 0bcefb250..7094f8134 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -240,8 +240,8 @@ const char *token_type_to_string(TokenType type) return "macro"; case TOKEN_MODULE: return "module"; - case TOKEN_NEXT: - return "next"; + case TOKEN_NEXTCASE: + return "nextcase"; case TOKEN_NULL: return "null"; case TOKEN_PUBLIC: diff --git a/src/utils/malloc.h b/src/utils/malloc.h index 02f636e7b..fb95d5a51 100644 --- a/src/utils/malloc.h +++ b/src/utils/malloc.h @@ -10,6 +10,7 @@ extern Vmem name##_arena; \ typedef unsigned type##Id; \ static inline type *name##_alloc(void) { return (type *)vmem_alloc(&name##_arena, sizeof(type)); } \ +static inline void name##_arena_free(void) { vmem_free(&name##_arena); } \ static inline type *name##_calloc(void) { \ type *ptr = name##_alloc(); \ memset(ptr, 0, sizeof(type)); \ diff --git a/test/test_suite/statements/defer_next_switch.c3t b/test/test_suite/statements/defer_next_switch.c3t index ecee9023f..0b85418b4 100644 --- a/test/test_suite/statements/defer_next_switch.c3t +++ b/test/test_suite/statements/defer_next_switch.c3t @@ -12,7 +12,7 @@ func void test(int i) { case 1: defer test2(); - if (b) next; + if (b) nextcase; test1(); case 2: test1(); diff --git a/test/test_suite/types/enum_parse_errors.c3 b/test/test_suite/types/enum_parse_errors.c3 index 3a5a45fed..a46e18efa 100644 --- a/test/test_suite/types/enum_parse_errors.c3 +++ b/test/test_suite/types/enum_parse_errors.c3 @@ -1,5 +1,5 @@ -enum EnumWithErrorWithMissingName : int (int) // #error: function parameter must be named +enum EnumWithErrorWithMissingName : int (int) // #error: The parameter must be named { TEST } @@ -9,7 +9,7 @@ enum EnumWithErrorData : int (int // #error: end of the parameter list TEST } -enum EnumWithErrorData2 : int (int, int bar) // #error: function parameter must be named +enum EnumWithErrorData2 : int (int, int bar) // #error: The parameter must be named { TEST }