From 49eacb8824d411c79e8159a938736d7196be7a0d Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 14 Nov 2022 11:14:45 +0100 Subject: [PATCH] More support for test. Panic function update. --- lib/std/libc.c3 | 9 +++ lib/std/runtime.c3 | 65 ++++++++++++--- src/build/build_options.c | 13 +++ src/build/build_options.h | 3 + src/build/builder.c | 9 ++- src/compiler/compiler.c | 48 ++++++++++- src/compiler/compiler_internal.h | 2 + src/compiler/context.c | 2 +- src/compiler/enums.h | 2 +- src/compiler/expr.c | 1 + src/compiler/llvm_codegen.c | 114 +++++++++++++++++---------- src/compiler/llvm_codegen_expr.c | 12 +-- src/compiler/llvm_codegen_function.c | 11 ++- src/compiler/llvm_codegen_internal.h | 1 + src/compiler/llvm_codegen_module.c | 5 +- src/compiler/sema_casts.c | 22 ++++++ src/compiler/sema_decls.c | 11 +++ src/compiler/sema_expr.c | 9 +-- src/compiler/symtab.c | 5 +- src/compiler/types.c | 1 - src/main.c | 2 + 21 files changed, 269 insertions(+), 78 deletions(-) diff --git a/lib/std/libc.c3 b/lib/std/libc.c3 index 213539133..f8e2775d3 100644 --- a/lib/std/libc.c3 +++ b/lib/std/libc.c3 @@ -62,6 +62,11 @@ extern fn LongDivResult ldiv(long number, long denom); extern fn int rand(); extern fn void srand(uint seed); +define JmpBuf = CInt[$$JMP_BUF_SIZE]; + +extern fn CInt setjmp(JmpBuf* buffer); +extern fn void longjmp(JmpBuf* buffer, CInt value); + // MB functions omitted // string @@ -131,6 +136,7 @@ $default: macro CFile stderr() { return (CFile*)(uptr)2; } $endswitch; + const HAS_MALLOC_SIZE = env::OS_TYPE == OsType.LINUX || env::OS_TYPE == OsType.WIN32 @@ -233,6 +239,7 @@ define SignalFunction = fn void(int); extern fn SignalFunction signal(int sig, SignalFunction function); // Incomplete + module libc::errno; const Errno EPERM = 1; /* Operation not permitted */ @@ -368,3 +375,5 @@ const Errno EKEYREJECTED = 129; /* Key was rejected by service */ const Errno EOWNERDEAD = 130; /* Owner died */ const Errno ENOTRECOVERABLE = 131; /* State not recoverable */ + + diff --git a/lib/std/runtime.c3 b/lib/std/runtime.c3 index e6422e550..6283aee49 100644 --- a/lib/std/runtime.c3 +++ b/lib/std/runtime.c3 @@ -28,33 +28,72 @@ struct VarArrayHeader void *allocator; } +define TestFn = fn void!(); + struct TestRunner { - char** test_names; - void** test_fns; - uint count; + char[][] test_names; + TestFn[] test_fns; + JmpBuf buf; } fn TestRunner test_runner_create() { return TestRunner { - .test_names = $$TEST_NAMES, .test_fns = $$TEST_FNS, - .count = $$TEST_COUNT, + .test_names = $$TEST_NAMES, }; } -extern fn void puts(char* c); +import libc; -fn void TestRunner.run(TestRunner* runner) +private TestRunner* current_runner; +fn void test_panic(char[] message, char[] file, char[] function, uint line) { - for (uint i = 0; i < runner.count; i++) - { - puts(runner.test_names[i]); - } + io::println("[error]"); + io::printfln("\n Error: %s", message); + io::printfln(" - in %s %s:%s.\n", function, file, line); + libc::longjmp(¤t_runner.buf, 1); } -fn void run_test_runner() +fn bool TestRunner.run(TestRunner* runner) { - test_runner_create().run(); + current_runner = runner; + PanicFn old_panic = builtin::panic; + defer builtin::panic = old_panic; + builtin::panic = &test_panic; + int tests_passed = 0; + int tests = runner.test_names.len; + io::println("----- TESTS -----"); + foreach(i, char[] name : runner.test_names) + { + io::printf("Testing %s ... ", name); + if (libc::setjmp(&runner.buf) == 0) + { + if (catch err = runner.test_fns[i]()) + { + io::println("[failed]"); + continue; + } + io::println("[ok]"); + tests_passed++; + } + } + io::printfln("\n%d test(s) run.\n", tests); + io::print("Test Result: "); + if (tests_passed < tests) + { + io::print("FAILED"); + } + else + { + io::print("ok"); + } + io::printfln(". %d passed, %d failed.", tests_passed, tests - tests_passed); + return tests == tests_passed; +} + +fn bool __run_default_test_runner() +{ + return test_runner_create().run(); } \ No newline at end of file diff --git a/src/build/build_options.c b/src/build/build_options.c index d30b77179..c3e3399d1 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -63,6 +63,7 @@ static void usage(void) OUTPUT(" compile [ ...] Compile files without a project into an executable."); OUTPUT(" init Initialize a new project structure."); OUTPUT(" build [] Build the target in the current project."); + OUTPUT(" test Run the unit tests in the current project."); OUTPUT(" clean Clean all build files."); OUTPUT(" run [] Run (and build if needed) the target in the current project."); OUTPUT(" dist [] Clean and build a target for distribution."); @@ -244,6 +245,12 @@ static void parse_command(BuildOptions *options) options->command = COMMAND_UNIT_TEST; return; } + if (arg_match("compile-test")) + { + options->command = COMMAND_COMPILE_TEST; + options->testing = true; + return; + } if (arg_match("compile")) { options->command = COMMAND_COMPILE; @@ -275,6 +282,12 @@ static void parse_command(BuildOptions *options) parse_optional_target(options); return; } + if (arg_match("test")) + { + options->command = COMMAND_TEST; + options->testing = true; + return; + } if (arg_match("run")) { options->command = COMMAND_RUN; diff --git a/src/build/build_options.h b/src/build/build_options.h index df2ff314d..c58d4b59c 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -23,6 +23,7 @@ typedef enum COMMAND_MISSING = 0, COMMAND_COMPILE, COMMAND_COMPILE_ONLY, + COMMAND_COMPILE_TEST, COMMAND_GENERATE_HEADERS, COMMAND_INIT, COMMAND_BUILD, @@ -35,6 +36,7 @@ typedef enum COMMAND_DIST, COMMAND_DOCS, COMMAND_BENCH, + COMMAND_TEST, COMMAND_UNIT_TEST, COMMAND_PRINT_SYNTAX, } CompilerCommand; @@ -335,6 +337,7 @@ typedef struct const char *llvm_file_dir; const char *asm_file_dir; bool run_after_compile; + bool generate_test_runner; bool test_output; bool output_headers; bool output_ast; diff --git a/src/build/builder.c b/src/build/builder.c index cfd25ff5c..46b096126 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -69,6 +69,8 @@ bool command_is_projectless(CompilerCommand command) case COMMAND_COMPILE_RUN: case COMMAND_DYNAMIC_LIB: case COMMAND_STATIC_LIB: + case COMMAND_COMPILE_TEST: + case COMMAND_UNIT_TEST: return true; case COMMAND_MISSING: case COMMAND_GENERATE_HEADERS: @@ -80,8 +82,8 @@ bool command_is_projectless(CompilerCommand command) case COMMAND_DIST: case COMMAND_DOCS: case COMMAND_BENCH: - case COMMAND_UNIT_TEST: case COMMAND_PRINT_SYNTAX: + case COMMAND_TEST: return false; } UNREACHABLE @@ -90,6 +92,11 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * { switch (options->command) { + case COMMAND_COMPILE_TEST: + case COMMAND_TEST: + target->run_after_compile = true; + target->type = TARGET_TYPE_TEST; + break; case COMMAND_RUN: case COMMAND_COMPILE_RUN: case COMMAND_CLEAN_RUN: diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 5a03167c7..d3b77dd1b 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -334,8 +334,11 @@ void compiler_compile(void) { switch (active_target.type) { - case TARGET_TYPE_EXECUTABLE: case TARGET_TYPE_TEST: + active_target.name = "testrun"; + output_exe = exe_name(); + break; + case TARGET_TYPE_EXECUTABLE: if (!global_context.main) { puts("No main function was found, compilation only object files are generated."); @@ -648,6 +651,48 @@ void print_syntax(BuildOptions *options) void resolve_libraries(void); +static int jump_buffer_size() +{ + switch (active_target.arch_os_target) + { + case ARCH_OS_TARGET_DEFAULT: + return 512; + case ELF_RISCV32: + case ELF_RISCV64: + case LINUX_RISCV32: + case LINUX_RISCV64: + REMINDER("RISCV jmpbuf size is unknown"); + return 512; + case ELF_X64: + case FREEBSD_X64: + case LINUX_X64: + case MACOS_X64: + case WINDOWS_X64: + case MINGW_X64: + case NETBSD_X64: + case OPENBSD_X64: + // Based on MacOS headers + return ((9 * 2) + 3 + 16); + case LINUX_AARCH64: + case ELF_AARCH64: + case MACOS_AARCH64: + // Based on MacOS headers + return ((14 + 8 + 2) * 2); + case LINUX_X86: + case MCU_X86: + case NETBSD_X86: + case OPENBSD_X86: + case WINDOWS_X86: + case ELF_X86: + case FREEBSD_X86: + return 18; + case WASM32: + case WASM64: + REMINDER("WASM setjmp size is unknown"); + return 512; + } + UNREACHABLE +} void compile() { symtab_init(active_target.symtab_size); @@ -679,6 +724,7 @@ void compile() setup_bool_define("COMPILER_SAFE_MODE", active_target.feature.safe_mode); setup_int_define("LLVM_VERSION", llvm_version_major, type_int); setup_bool_define("BENCHMARKING", active_target.benchmarking); + setup_int_define("JMP_BUF_SIZE", jump_buffer_size(), type_int); setup_bool_define("TESTING", active_target.testing); type_init_cint(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d2bef6f02..9e44f17d9 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1660,6 +1660,7 @@ typedef struct Path std_module_path; Decl *panic_var; Decl *main; + Decl *test_func; Decl *decl_stack[MAX_GLOBAL_DECL_STACK]; Decl** decl_stack_bottom; Decl** decl_stack_top; @@ -1826,6 +1827,7 @@ extern const char *builtin_defines[NUMBER_OF_BUILTIN_DEFINES]; extern const char *type_property_list[NUMBER_OF_TYPE_PROPERTIES]; extern const char *kw_std__core; extern const char *kw_std__core__types; +extern const char *kw___run_default_test_runner; extern const char *kw_typekind; extern const char *kw_argc; diff --git a/src/compiler/context.c b/src/compiler/context.c index 284bdb3e4..40ef1a296 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -5,7 +5,7 @@ #include "compiler_internal.h" -CompilationUnit * unit_create(File *file) +CompilationUnit *unit_create(File *file) { CompilationUnit *unit = CALLOCS(CompilationUnit); unit->file = file; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 22b5fe264..9f805f988 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -111,6 +111,7 @@ typedef enum CAST_ENUMLOW, CAST_APTSA, CAST_SAPTR, + CAST_SASA, CAST_SABOOL, CAST_STST, CAST_PTRANY, @@ -900,7 +901,6 @@ typedef enum BUILTIN_DEF_LINE, BUILTIN_DEF_LINE_RAW, BUILTIN_DEF_MODULE, - BUILTIN_DEF_TEST_COUNT, BUILTIN_DEF_TEST_NAMES, BUILTIN_DEF_TEST_FNS, BUILTIN_DEF_TIME, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 22de22261..9b1a8b214 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -375,6 +375,7 @@ static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_ case CAST_PTRPTR: case CAST_APTSA: case CAST_SAPTR: + case CAST_SASA: case CAST_ENUMLOW: return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind); case CAST_PTRANY: diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 27281bc30..caad6ba0c 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -7,7 +7,7 @@ #include #include #include - +#include typedef struct LLVMOpaquePassBuilderOptions *LLVMPassBuilderOptionsRef; LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes, LLVMTargetMachineRef TM, @@ -1004,76 +1004,104 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) UNREACHABLE } -INLINE void llvm_gen_tests(GenContext *test_owner, Module** modules, unsigned module_count) +static void llvm_gen_test_main(GenContext *c) { + Decl *test_runner = global_context.test_func; + if (!test_runner) + { + error_exit("No test runner found."); + } + assert(!global_context.main && "Main should not be set if a test main is generated."); + global_context.main = test_runner; + LLVMTypeRef cint = llvm_get_type(c, type_cint); + LLVMTypeRef main_type = LLVMFunctionType(cint, NULL, 0, true); + LLVMTypeRef runner_type = LLVMFunctionType(c->byte_type, NULL, 0, true); + LLVMValueRef func = LLVMAddFunction(c->module, kw_main, main_type); + LLVMValueRef other_func = LLVMAddFunction(c->module, test_runner->extname, runner_type); + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, func, "entry"); + LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context); + LLVMPositionBuilderAtEnd(builder, entry); + LLVMValueRef val = LLVMBuildCall2(builder, runner_type, other_func, NULL, 0, ""); + val = LLVMBuildSelect(builder, LLVMBuildTrunc(builder, val, c->bool_type, ""), + LLVMConstNull(cint), LLVMConstInt(cint, 1, false), ""); + LLVMBuildRet(builder, val); + LLVMDisposeBuilder(builder); + gencontext_print_llvm_ir(c); +} +INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMContextRef shared_context) +{ + Path *test_path = path_create_from_string("$test", 5, INVALID_SPAN); + Module *test_module = compiler_find_or_create_module(test_path, NULL, false); + + GenContext *c = cmalloc(sizeof(GenContext)); + active_target.debug_info = DEBUG_INFO_NONE; + gencontext_init(c, test_module, shared_context); + gencontext_begin_module(c); + LLVMValueRef *names = NULL; LLVMValueRef *decls = NULL; - LLVMTypeRef void_test = LLVMFunctionType(LLVMVoidTypeInContext(test_owner->context), NULL, 0, false); - LLVMTypeRef opt_test = LLVMFunctionType(test_owner->fault_type, NULL, 0, false); + LLVMTypeRef opt_test = LLVMFunctionType(llvm_get_type(c, type_anyerr), NULL, 0, false); for (unsigned i = 0; i < module_count; i++) { Module *module = modules[i]; - bool gen_proto = test_owner->code_module != module; FOREACH_BEGIN(Decl *test, module->tests) LLVMValueRef ref; - if (gen_proto) - { - LLVMTypeRef type = test->type->function.prototype->rtype == type_void ? void_test : opt_test; - ref = LLVMAddFunction(test_owner->module, test->extname, type); - } - else - { - ref = test->backend_ref; - } + LLVMTypeRef type = opt_test; + ref = LLVMAddFunction(c->module, test->extname, type); scratch_buffer_clear(); scratch_buffer_printf("%s::%s", module->name->module, test->name); - LLVMTypeRef char_array_type = LLVMArrayType(test_owner->byte_type, scratch_buffer.len + 1); - LLVMValueRef global_string = llvm_add_global_raw(test_owner, ".test", char_array_type, 0); - llvm_set_internal_linkage(global_string); - LLVMSetGlobalConstant(global_string, 1); - LLVMSetInitializer(global_string, llvm_get_zstring(test_owner, scratch_buffer_to_string(), scratch_buffer.len)); - LLVMValueRef str = LLVMBuildBitCast(test_owner->builder, global_string, test_owner->char_ptr_type, ""); - vec_add(names, str); - ref = LLVMBuildBitCast(test_owner->builder, ref, test_owner->char_ptr_type, ""); + LLVMValueRef name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".test.name"); + vec_add(names, name); vec_add(decls, ref); FOREACH_END(); } unsigned test_count = vec_size(decls); - LLVMTypeRef ptr_of_chars_ptrs = LLVMPointerType(test_owner->char_ptr_type, 0); LLVMValueRef name_ref; LLVMValueRef decl_ref; + LLVMTypeRef chars_type = llvm_get_type(c, type_chars); if (test_count) { - LLVMValueRef array_of_names = LLVMConstArray(test_owner->char_ptr_type, names, test_count); - LLVMValueRef array_of_decls = LLVMConstArray(test_owner->char_ptr_type, decls, test_count); + LLVMValueRef array_of_names = LLVMConstArray(chars_type, names, test_count); + LLVMValueRef array_of_decls = LLVMConstArray(LLVMPointerType(opt_test, 0), decls, test_count); LLVMTypeRef arr_type = LLVMTypeOf(array_of_names); - name_ref = llvm_add_global_raw(test_owner, ".test_names", arr_type, llvm_alloc_size(test_owner, arr_type)); - decl_ref = llvm_add_global_raw(test_owner, ".test_decls", arr_type, llvm_alloc_size(test_owner, arr_type)); + name_ref = llvm_add_global_raw(c, ".test_names", arr_type, llvm_alloc_size(c, arr_type)); + decl_ref = llvm_add_global_raw(c, ".test_decls", LLVMTypeOf(array_of_decls), llvm_alloc_size(c, arr_type)); llvm_set_internal_linkage(name_ref); llvm_set_internal_linkage(decl_ref); LLVMSetGlobalConstant(name_ref, 1); LLVMSetGlobalConstant(decl_ref, 1); LLVMSetInitializer(name_ref, array_of_names); LLVMSetInitializer(decl_ref, array_of_decls); - name_ref = LLVMBuildBitCast(test_owner->builder, name_ref, ptr_of_chars_ptrs, ""); - decl_ref = LLVMBuildBitCast(test_owner->builder, decl_ref, ptr_of_chars_ptrs, ""); + name_ref = LLVMBuildBitCast(c->builder, name_ref, llvm_get_ptr_type(c, type_chars), ""); + decl_ref = LLVMBuildBitCast(c->builder, decl_ref, llvm_get_ptr_type(c, type_voidptr), ""); } else { - name_ref = LLVMConstNull(ptr_of_chars_ptrs); - decl_ref = LLVMConstNull(ptr_of_chars_ptrs); + name_ref = LLVMConstNull(llvm_get_ptr_type(c, type_chars)); + decl_ref = LLVMConstNull(llvm_get_ptr_type(c, type_voidptr)); } - LLVMValueRef name_list = llvm_add_global_raw(test_owner, "C3_TEST_NAME_LIST", ptr_of_chars_ptrs, llvm_alloc_size(test_owner, ptr_of_chars_ptrs)); + LLVMValueRef count = llvm_const_int(c, type_usize, test_count); + Type *chars_array = type_get_subarray(type_chars); + LLVMValueRef name_list = llvm_add_global(c, test_names_var_name, chars_array, type_alloca_alignment(chars_array)); LLVMSetGlobalConstant(name_list, 1); - LLVMSetInitializer(name_list, name_ref); - LLVMValueRef decl_list = llvm_add_global_raw(test_owner, "C3_TEST_DECL_LIST", ptr_of_chars_ptrs, llvm_alloc_size(test_owner, ptr_of_chars_ptrs)); + LLVMSetInitializer(name_list, llvm_emit_aggregate_two(c, chars_array, name_ref, count)); + Type *decls_array_type = type_get_subarray(type_voidptr); + LLVMValueRef decl_list = llvm_add_global(c, test_fns_var_name, decls_array_type, type_alloca_alignment(decls_array_type)); LLVMSetGlobalConstant(decl_list, 1); - LLVMSetInitializer(decl_list, decl_ref); - LLVMTypeRef int_len_type = LLVMInt32TypeInContext(test_owner->context); - LLVMValueRef name_count = llvm_add_global_raw(test_owner, "C3_TEST_COUNT", int_len_type, llvm_alloc_size(test_owner, int_len_type)); - LLVMSetGlobalConstant(name_count, 1); - LLVMSetInitializer(name_count, LLVMConstInt(int_len_type, test_count, false)); + LLVMSetInitializer(decl_list, llvm_emit_aggregate_two(c, decls_array_type, decl_ref, count)); + + if (active_target.type == TARGET_TYPE_TEST) + { + llvm_gen_test_main(c); + } + + if (llvm_use_debug(c)) + { + LLVMDIBuilderFinalize(c->debug.builder); + LLVMDisposeDIBuilder(c->debug.builder); + } + return c; } void **llvm_gen(Module** modules, unsigned module_count) { @@ -1093,7 +1121,7 @@ void **llvm_gen(Module** modules, unsigned module_count) } if (!gen_contexts) return NULL; GenContext *first = gen_contexts[0]; - llvm_gen_tests(first, modules, module_count); + vec_add(gen_contexts, llvm_gen_tests(modules, module_count, context)); unsigned count = vec_size(gen_contexts); for (unsigned i = 1; i < count; i++) { @@ -1110,7 +1138,7 @@ void **llvm_gen(Module** modules, unsigned module_count) if (!result) continue; vec_add(gen_contexts, result); } - llvm_gen_tests(gen_contexts[0], modules, module_count); + vec_add(gen_contexts, llvm_gen_tests(modules, module_count, NULL)); return (void**)gen_contexts; } @@ -1153,7 +1181,7 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context llvm_emit_function_decl(gen_context, func); FOREACH_END(); - if (!active_target.testing && unit->main_function && unit->main_function->is_synthetic) + if (active_target.type != TARGET_TYPE_TEST && unit->main_function && unit->main_function->is_synthetic) { llvm_emit_function_decl(gen_context, unit->main_function); } @@ -1178,7 +1206,7 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); FOREACH_END(); - if (!active_target.testing && unit->main_function && unit->main_function->is_synthetic) + if (active_target.type != TARGET_TYPE_TEST && unit->main_function && unit->main_function->is_synthetic) { llvm_emit_function_body(gen_context, unit->main_function); } diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 64a5a07c7..ce78ba34f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1237,6 +1237,11 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *valu case CAST_APTSA: llvm_emit_arr_to_subarray_cast(c, value, to_type); break; + case CAST_SASA: + assert(type_is_pointer(value->type->array.base)); + llvm_value_addr(c, value); + llvm_value_bitcast(c, value, to_type); + break; case CAST_SAPTR: llvm_emit_subarray_pointer(c, value, value); if (value->type != to_type) @@ -6023,13 +6028,10 @@ static LLVMValueRef llvm_get_test_hook_global(GenContext *c, Expr *expr) switch (expr->test_hook_expr) { case BUILTIN_DEF_TEST_FNS: - name = "C3_TEST_DECL_LIST"; - break; - case BUILTIN_DEF_TEST_COUNT: - name = "C3_TEST_COUNT"; + name = test_fns_var_name; break; case BUILTIN_DEF_TEST_NAMES: - name = "C3_TEST_NAME_LIST"; + name = test_names_var_name; break; default: UNREACHABLE diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index aa69759b3..9ed9dadf5 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -497,8 +497,17 @@ void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_nam LLVMValueRef file_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 2, alignment, &align_to_use); llvm_store_to_ptr_raw_aligned(c, file_name, c->debug.file_name, align_to_use); c->debug.stack_slot_row = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 3, alignment, &align_to_use); + LLVMValueRef last_ptr = NULL; + if (function_name != kw_main && function_name != kw_mainstub) + { + last_ptr = c->debug.last_ptr; + } + else + { + last_ptr = prev_ptr; + } llvm_store_to_ptr_raw_aligned(c, - c->debug.last_ptr, + last_ptr, c->debug.stack_slot, type_alloca_alignment(type_voidptr)); } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 42701fa78..1df6ca883 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -86,6 +86,7 @@ typedef struct GenContext_ LLVMTypeRef fault_type; LLVMTypeRef size_type; LLVMTypeRef char_ptr_type; + LLVMTypeRef chars_type; Decl *panic_var; struct { diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index d1fd4ea9d..3a4519646 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -127,6 +127,7 @@ void gencontext_begin_module(GenContext *c) c->fault_type = create_fault_type(c); c->size_type = llvm_get_type(c, type_usize); c->char_ptr_type = LLVMPointerType(c->byte_type, 0); + c->chars_type = llvm_get_type(c, type_chars); if (c->panic_var) c->panic_var->backend_ref = NULL; if (active_target.debug_info != DEBUG_INFO_NONE) @@ -142,8 +143,8 @@ void gencontext_begin_module(GenContext *c) { c->debug.stack_type = LLVMStructCreateNamed(c->context, ".$callstack"); LLVMTypeRef types[4] = { LLVMPointerType(c->debug.stack_type, 0), - LLVMPointerType(c->byte_type, 0), - LLVMPointerType(c->byte_type, 0), + c->chars_type, + c->chars_type, llvm_get_type(c, type_uint) }; LLVMStructSetBody(c->debug.stack_type, types, 4, false); c->debug.last_ptr = NULL; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 0214c3b43..0b7a13376 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -549,6 +549,8 @@ bool cast_may_explicit(Type *from_type, Type *to_type, bool ignore_failability, case TYPE_UNION: return type_is_structurally_equivalent(from_type, to_type); case TYPE_SUBARRAY: + if (to_kind == TYPE_SUBARRAY && type_is_pointer(to_type->array.base) + && type_is_pointer(from_type->array.base)) return true; return to_kind == TYPE_POINTER; case TYPE_VECTOR: return type_is_structurally_equivalent(type_get_array(from_type->array.base, (uint32_t)from_type->array.len), to_type); @@ -821,6 +823,17 @@ bool cast_may_implicit(Type *from_type, Type *to_type, CastOption option) { return from->pointer->type_kind == TYPE_ARRAY && from->pointer->array.base == base; } + + // Allow casting void*[] to int*[] + if (from->type_kind == TYPE_SUBARRAY && from->array.base == type_voidptr && type_is_pointer(to->array.base)) + { + return true; + } + // Allow casting int*[] -> void*[] + if (from->type_kind == TYPE_SUBARRAY && to->array.base == type_voidptr && type_is_pointer(from->array.base)) + { + return true; + } return false; } @@ -1450,6 +1463,14 @@ static bool err_to_bool(Expr *expr, Type *to_type) return insert_cast(expr, CAST_ERBOOL, to_type); } +static inline bool subarray_to_subarray(Expr *expr, Type *to_type) +{ + if (expr_is_const(expr)) + { + expr->type = to_type; + } + return insert_cast(expr, CAST_SASA, to_type); +} static inline bool subarray_to_bool(Expr *expr) { if (expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_INITIALIZER) @@ -1562,6 +1583,7 @@ static bool cast_inner(Expr *expr, Type *from_type, Type *to, Type *to_type) case TYPE_SUBARRAY: if (to->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_SAPTR, to); if (to->type_kind == TYPE_BOOL) return subarray_to_bool(expr); + if (to->type_kind == TYPE_SUBARRAY) return subarray_to_subarray(expr, to); break; case TYPE_VECTOR: if (to->type_kind == TYPE_ARRAY) return vec_to_arr(expr, to_type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 921cd7a37..c0cd23eae 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1930,6 +1930,8 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) SEMA_ERROR(params[0], "Expected zero, 1 or 2 parameters for main."); return false; } + if (active_target.type == TARGET_TYPE_TEST) return true; + Decl *function; if (!subarray_param && is_int_return) { @@ -2068,6 +2070,15 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) if (!sema_analyse_func_macro(context, decl, true)) return false; + if (decl->name == kw___run_default_test_runner) + { + if (global_context.test_func) + { + SEMA_ERROR(decl, "Multiple test runners defined."); + return false; + } + global_context.test_func = decl; + } Signature *sig = &decl->func_decl.signature; Type *func_type = sema_analyse_function_signature(context, decl, sig->abi, sig, true); decl->type = func_type; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f02d49fe6..7b68318d1 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -5947,18 +5947,13 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr * expr_replace(expr, value); return true; } - case BUILTIN_DEF_TEST_COUNT: - expr->type = type_uint; - expr->test_hook_expr = BUILTIN_DEF_TEST_COUNT; - expr->expr_kind = EXPR_TEST_HOOK; - return true; case BUILTIN_DEF_TEST_NAMES: - expr->type = type_get_ptr(type_get_ptr(type_char)); + expr->type = type_get_subarray(type_chars); expr->test_hook_expr = BUILTIN_DEF_TEST_NAMES; expr->expr_kind = EXPR_TEST_HOOK; return true; case BUILTIN_DEF_TEST_FNS: - expr->type = type_get_ptr(type_voidptr); + expr->type = type_get_subarray(type_voidptr); expr->test_hook_expr = BUILTIN_DEF_TEST_FNS; expr->expr_kind = EXPR_TEST_HOOK; return true; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index e856d2a5b..a25192bf5 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -79,6 +79,7 @@ const char *kw_return; const char *kw_std; const char *kw_std__core; const char *kw_std__core__types; +const char *kw___run_default_test_runner; const char *kw_type; const char *kw_typekind; @@ -131,7 +132,6 @@ void symtab_init(uint32_t capacity) builtin_defines[BUILTIN_DEF_TIME] = KW_DEF("TIME"); builtin_defines[BUILTIN_DEF_TEST_NAMES] = KW_DEF("TEST_NAMES"); builtin_defines[BUILTIN_DEF_TEST_FNS] = KW_DEF("TEST_FNS"); - builtin_defines[BUILTIN_DEF_TEST_COUNT] = KW_DEF("TEST_COUNT"); type = TOKEN_TYPE_IDENT; kw_typekind = KW_DEF("TypeKind"); @@ -148,7 +148,7 @@ void symtab_init(uint32_t capacity) kw_incr = KW_DEF("incr"); kw_inline = KW_DEF("inline"); kw_inout = KW_DEF("inout"); - kw_mainstub = KW_DEF("_$mainstub"); + kw_mainstub = KW_DEF("_$start"); kw_main = KW_DEF("main"); kw_nameof = KW_DEF("nameof"); kw_noinline = KW_DEF("noinline"); @@ -160,6 +160,7 @@ void symtab_init(uint32_t capacity) kw_std = KW_DEF("std"); kw_std__core = KW_DEF("std::core"); kw_std__core__types = KW_DEF("std::core::types"); + kw___run_default_test_runner = KW_DEF("__run_default_test_runner"); kw_type = KW_DEF("type"); type_property_list[TYPE_PROPERTY_MAX] = builtin_list[BUILTIN_MAX] = KW_DEF("max"); diff --git a/src/compiler/types.c b/src/compiler/types.c index 0cfec7ad2..bdcbee843 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1472,7 +1472,6 @@ void type_setup(PlatformTarget *target) size_subarray = (unsigned)(alignment_subarray * 2); type_init("anyerr", &t.anyerr, TYPE_ANYERR, target->width_pointer, target->align_pointer); type_chars = type_get_subarray(type_char); - } int type_kind_bitsize(TypeKind kind) diff --git a/src/main.c b/src/main.c index 990ee6f7f..bbd37d0bb 100644 --- a/src/main.c +++ b/src/main.c @@ -63,6 +63,7 @@ int main_real(int argc, const char *argv[]) case COMMAND_COMPILE_RUN: case COMMAND_DYNAMIC_LIB: case COMMAND_STATIC_LIB: + case COMMAND_COMPILE_TEST: compile_target(&build_options); break; case COMMAND_CLEAN: @@ -74,6 +75,7 @@ int main_real(int argc, const char *argv[]) case COMMAND_DIST: case COMMAND_DOCS: case COMMAND_BENCH: + case COMMAND_TEST: compile_file_list(&build_options); break; case COMMAND_MISSING: