More support for test. Panic function update.

This commit is contained in:
Christoffer Lerno
2022-11-14 11:14:45 +01:00
committed by Christoffer Lerno
parent 450113d161
commit 49eacb8824
21 changed files with 269 additions and 78 deletions

View File

@@ -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 */

View File

@@ -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(&current_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();
}

View File

@@ -63,6 +63,7 @@ static void usage(void)
OUTPUT(" compile <file1> [<file2> ...] Compile files without a project into an executable.");
OUTPUT(" init <project name> Initialize a new project structure.");
OUTPUT(" build [<target>] 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 [<target>] Run (and build if needed) the target in the current project.");
OUTPUT(" dist [<target>] 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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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:

View File

@@ -7,7 +7,7 @@
#include <llvm-c/Error.h>
#include <llvm-c/Comdat.h>
#include <llvm-c/Linker.h>
#include <setjmp.h>
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);
}

View File

@@ -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

View File

@@ -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));
}

View File

@@ -86,6 +86,7 @@ typedef struct GenContext_
LLVMTypeRef fault_type;
LLVMTypeRef size_type;
LLVMTypeRef char_ptr_type;
LLVMTypeRef chars_type;
Decl *panic_var;
struct
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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");

View File

@@ -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)

View File

@@ -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: