From b794c893d61684c5d173c1b59e3655fda81d500e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 25 May 2023 13:46:44 +0200 Subject: [PATCH] Dynamic dispatch. --- lib/std/collections/object.c3 | 6 +- lib/std/io/io_printf.c3 | 70 +----- lib/std/net/inetaddr.c3 | 8 +- releasenotes.md | 3 +- src/compiler/codegen_internal.h | 9 +- src/compiler/compiler_internal.h | 8 +- src/compiler/enums.h | 2 + src/compiler/llvm_codegen.c | 14 ++ src/compiler/llvm_codegen_builtins.c | 4 +- src/compiler/llvm_codegen_expr.c | 228 +++++++++++++++--- src/compiler/llvm_codegen_function.c | 82 ++++++- src/compiler/llvm_codegen_internal.h | 6 + src/compiler/llvm_codegen_module.c | 3 + src/compiler/llvm_codegen_stmt.c | 10 +- src/compiler/llvm_codegen_type.c | 3 +- src/compiler/parse_global.c | 5 +- src/compiler/sema_decls.c | 63 ++++- src/compiler/sema_expr.c | 9 +- src/compiler/sema_stmts.c | 13 + src/compiler/symtab.c | 2 + src/version.h | 2 +- test/test_suite/abi/small_struct_x64.c3t | 4 +- .../test_suite/arrays/complex_array_const.c3t | 2 +- .../attributes/user_defined_attributes.c3t | 2 +- .../compile_time/untyped_conversions.c3t | 21 +- .../compile_time_introspection/qnameof.c3t | 2 +- .../enumerations/enum_associated_value.c3t | 4 +- .../enum_associated_values_other.c3t | 17 +- test/test_suite/errors/error_introspect.c3t | 14 +- .../errors/optional_taddr_and_access.c3t | 5 +- .../test_suite/expressions/pointer_access.c3t | 8 +- .../func_ptr_conversions_and_names.c3t | 13 +- test/test_suite/functions/test_regression.c3t | 12 +- .../functions/test_regression_mingw.c3t | 14 +- .../test_suite/functions/typeless_varargs.c3t | 3 +- .../initializer_lists/general_tests.c3t | 4 +- test/test_suite/initializer_lists/statics.c3t | 4 +- .../initializer_lists/subarrays.c3t | 5 +- test/test_suite/macros/userland_bitcast.c3t | 2 +- .../pointers/subarray_variant_to_ptr.c3t | 2 +- .../statements/custom_foreach_with_ref.c3t | 2 +- .../statements/foreach_custom_macro.c3t | 2 +- test/test_suite/stdlib/map.c3t | 56 ++--- test/test_suite/struct/nested_struct_init.c3t | 12 +- test/test_suite/struct/struct_as_value.c3t | 2 +- test/test_suite/struct/struct_codegen.c3t | 4 +- .../struct/struct_const_construct_simple.c3t | 2 +- test/test_suite/variant/variant_assign.c3t | 6 +- test/test_suite/variant/variant_switch.c3t | 6 +- test/test_suite/variant/variant_test.c3t | 10 +- 50 files changed, 529 insertions(+), 261 deletions(-) diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 83b003059..10592657f 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -26,12 +26,8 @@ struct Object } } -static initialize -{ - io::formatter_register_type(Object); -} -fn void! Object.to_format(Object* o, Formatter* formatter) +fn void! Object.to_format(Object* o, Formatter* formatter) @dynamic { switch (o.type) { diff --git a/lib/std/io/io_printf.c3 b/lib/std/io/io_printf.c3 index 588b890af..fcaa7a256 100644 --- a/lib/std/io/io_printf.c3 +++ b/lib/std/io/io_printf.c3 @@ -22,10 +22,11 @@ fault FormattingFault } def OutputFn = fn void!(char c, void* buffer); -def ToStringFunction = fn String(void* value, Allocator *using); -def ToFormatFunction = fn void!(void* value, Formatter* formatter); def FloatType = double; +fn String any.to_string(void* value, Allocator *using) @interface; +fn void! any.to_format(void* value, Formatter* formatter) @interface; + fn usz! printf(String format, args...) @maydiscard { Formatter formatter; @@ -102,41 +103,6 @@ fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null) *this = { .data = data, .out_fn = out_fn}; } -/** - * @require $checks($Type a, a.to_string()) || $checks($Type a, a.to_format(&&Formatter{})) "Expected a type with 'to_string' or 'to_format' defined" - * @require !$checks($Type a, a.to_string()) || $checks($Type a, a.to_string(&&Allocator{})) "Expected 'to_string' to take an allocator as argument." - * @require !$checks($Type a, a.to_format(&&Formatter{})) || $checks($Type a, Formatter b, a.to_format(&b)) "Expected 'to_format' to take a Formatter as argument." - */ -macro void formatter_register_type($Type) -{ - $if $checks($Type a, a.to_format(&&Formatter {})): - if (!toformat_functions.table.len) - { - toformat_functions.init(64); - } - toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format); - $else - if (!tostring_functions.table.len) - { - tostring_functions.init(64); - } - tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string); - $endif -} - - -static initialize @priority(101) -{ - if (!toformat_functions.table.len) - { - toformat_functions.init(64); - } - if (!tostring_functions.table.len) - { - tostring_functions.init(64); - } -} - fn void! Formatter.out(Formatter* this, char c) @private { this.out_fn(c, this.data)!; @@ -144,7 +110,7 @@ fn void! Formatter.out(Formatter* this, char c) @private macro bool! Formatter.print_with_function(Formatter* this, any arg) { - if (try to_format = toformat_functions.get(arg.type)) + if (&arg.to_format) { PrintFlags old = this.flags; uint old_width = this.width; @@ -155,10 +121,10 @@ macro bool! Formatter.print_with_function(Formatter* this, any arg) this.width = old_width; this.prec = old_prec; } - to_format(arg.ptr, this)!; + arg.to_format(this)!; return true; - } - if (try to_string = tostring_functions.get(arg.type)) + } + if (&arg.to_string) { PrintFlags old = this.flags; uint old_width = this.width; @@ -171,7 +137,7 @@ macro bool! Formatter.print_with_function(Formatter* this, any arg) } @pool() { - this.out_substr(to_string(arg.ptr, mem::temp()))!; + this.out_substr(arg.to_string(mem::temp()))!; return true; }; } @@ -277,21 +243,8 @@ fn void! Formatter.out_str(Formatter* this, any arg) @private } this.out(']')!; case BOOL: - if (*(bool*)arg.ptr) - { - return this.out_substr("true"); - } - else - { - return this.out_substr("false"); - } + return this.out_substr(*(bool*)arg.ptr ? "true" : "false"); default: - switch (arg) - { - case Object: - arg.to_format(this)!; - return; - } if (this.print_with_function(arg)!) return; return this.out_substr("Invalid type"); } @@ -472,8 +425,3 @@ fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys) return this.idx; } -def StringFunctionMap @private = HashMap; -def FormatterFunctionMap @private = HashMap; - -FormatterFunctionMap toformat_functions @private; -StringFunctionMap tostring_functions @private; diff --git a/lib/std/net/inetaddr.c3 b/lib/std/net/inetaddr.c3 index 1bbd12bfb..ded894cd5 100644 --- a/lib/std/net/inetaddr.c3 +++ b/lib/std/net/inetaddr.c3 @@ -37,12 +37,8 @@ struct InetAddress } } -static initialize -{ - io::formatter_register_type(InetAddress); -} -fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) +fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic { if (addr.is_ipv6) { @@ -54,7 +50,7 @@ fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!; } -fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap()) +fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap()) @dynamic { if (addr.is_ipv6) { diff --git a/releasenotes.md b/releasenotes.md index bc11311d2..0d49601de 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,7 @@ ## 0.5.0 Change List ### Changes / improvements +- `@dynamic` and `@interface` for dynamic dispatch. - `$if` now uses `$if :` syntax. - `$assert` now uses `$assert : ` - `$error` is syntax sugar for `$assert false : "Some message"` @@ -22,7 +23,7 @@ - Allow getting the underlying type of anyfault. - De-duplicate string constants. - Change @extname => @extern. -- `define Type = int` is replaced by `typedef Type = int`. +- `define` is replaced by `def`. - LLVM "wrapper" library compilation is exception free. - `private` is replaced by attribute `@private`. - Addition of `@local` for file local visibility. diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index 79d81ea3f..319c364b5 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -3,10 +3,11 @@ enum IntrospectIndex { INTROSPECT_INDEX_KIND = 0, - INTROSPECT_INDEX_SIZEOF = 1, - INTROSPECT_INDEX_INNER = 2, - INTROSPECT_INDEX_LEN = 3, - INTROSPECT_INDEX_ADDITIONAL = 4, + INTROSPECT_INDEX_DTABLE = 1, + INTROSPECT_INDEX_SIZEOF = 2, + INTROSPECT_INDEX_INNER = 3, + INTROSPECT_INDEX_LEN = 4, + INTROSPECT_INDEX_ADDITIONAL = 5, INTROSPECT_INDEX_TOTAL, }; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b5a637171..46fb0ac50 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -558,8 +558,10 @@ typedef struct bool attr_naked : 1; bool attr_test : 1; bool attr_winmain : 1; - bool has_faults : 1; - Decl** generated_lambda; + bool attr_dynamic : 1; + bool attr_interface : 1; + DeclId any_prototype; + Decl **generated_lambda; }; struct { @@ -650,7 +652,6 @@ typedef struct } InitializerDecl; - typedef struct Decl_ { const char *name; @@ -793,6 +794,7 @@ typedef struct bool attr_pure : 1; bool result_unused : 1; bool no_return : 1; + bool is_dynamic_dispatch : 1; AstId body; union { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 8ce09ac97..6803837cf 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -762,10 +762,12 @@ typedef enum ATTRIBUTE_BUILTIN, ATTRIBUTE_CDECL, ATTRIBUTE_DEPRECATED, + ATTRIBUTE_DYNAMIC, ATTRIBUTE_EXPORT, ATTRIBUTE_EXTERN, ATTRIBUTE_EXTNAME, ATTRIBUTE_INLINE, + ATTRIBUTE_INTERFACE, ATTRIBUTE_LITTLEENDIAN, ATTRIBUTE_LOCAL, ATTRIBUTE_MAYDISCARD, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3c2a8ba60..904f489bf 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1044,6 +1044,17 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) llvm_add_global_decl(c, decl); return decl->backend_ref; case DECL_FUNC: + if (decl->func_decl.attr_interface) + { + size_t name_len = strlen(decl->name); + LLVMTypeRef char_array_type = LLVMArrayType(c->byte_type, name_len + 1); + LLVMValueRef selector = llvm_add_global_raw(c, decl_get_extname(decl), char_array_type, 0); + LLVMSetGlobalConstant(selector, 1); + LLVMSetInitializer(selector, llvm_get_zstring(c, decl->name, name_len)); + LLVMSetLinkage(selector, LLVMLinkOnceODRLinkage); + llvm_set_comdat(c, selector); + return decl->backend_ref = selector; + } backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type)); llvm_append_function_attributes(c, decl); if (decl_is_local(decl)) @@ -1301,6 +1312,7 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context llvm_emit_function_decl(gen_context, func); FOREACH_END(); + FOREACH_BEGIN(Decl *func, unit->lambdas) if (only_used && !func->is_live) continue; has_elements = true; @@ -1367,6 +1379,8 @@ static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context FOREACH_END(); + llvm_emit_dynamic_functions(gen_context, gen_context->dynamic_functions); + llvm_emit_constructors_and_destructors(gen_context); // EmitDeferred() diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 65f8fb428..ed655b677 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -116,7 +116,7 @@ INLINE void llvm_emit_compare_exchange(GenContext *c, BEValue *result_value, Exp llvm_value_set(result_value, llvm_emit_extract_value(c, result, 0), type); } -INLINE void llvm_emit_unreachable(GenContext *c, BEValue *result_value, Expr *expr) +INLINE void llvm_emit_unreachable_stmt(GenContext *c, BEValue *result_value, Expr *expr) { llvm_value_set(result_value, LLVMBuildUnreachable(c->builder), type_void); c->current_block = NULL; @@ -622,7 +622,7 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) switch (func) { case BUILTIN_UNREACHABLE: - llvm_emit_unreachable(c, result_value, expr); + llvm_emit_unreachable_stmt(c, result_value, expr); return; case BUILTIN_SWIZZLE: llvm_emit_swizzle(c, result_value, expr, false); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 46762f3ae..1e6f9dcc0 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -13,7 +13,7 @@ static inline LLVMValueRef llvm_emit_expr_to_rvalue(GenContext *c, Expr *expr); static inline LLVMValueRef llvm_emit_exprid_to_rvalue(GenContext *c, ExprId expr_id); static inline LLVMValueRef llvm_update_vector(GenContext *c, LLVMValueRef vector, LLVMValueRef value, MemberIndex index); static inline void llvm_emit_expression_list_expr(GenContext *c, BEValue *be_value, Expr *expr); - +static LLVMValueRef llvm_emit_dynamic_search(GenContext *c, LLVMValueRef type_id_ptr, LLVMValueRef selector); static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEValue parent, Decl *parent_decl, Decl *member); static inline void llvm_emit_builtin_access(GenContext *c, BEValue *be_value, Expr *expr); static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue *ref, Expr *expr); @@ -35,6 +35,7 @@ static inline void llvm_emit_try_unwrap(GenContext *c, BEValue *value, Expr *exp static inline void llvm_emit_any(GenContext *c, BEValue *value, Expr *expr); static inline void llvm_emit_vector_initializer_list(GenContext *c, BEValue *value, Expr *expr); static inline void llvm_extract_bitvalue_from_array(GenContext *c, BEValue *be_value, Decl *member, Decl *parent_decl); +static inline void llvm_emit_type_from_any(GenContext *c, BEValue *be_value); static void llvm_convert_vector_comparison(GenContext *c, BEValue *be_value, LLVMValueRef val, Type *vector_type, bool is_equals); static void llvm_emit_any_pointer(GenContext *c, BEValue *any, BEValue *pointer); @@ -2446,6 +2447,19 @@ static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *e llvm_emit_inc_dec_change(c, &addr, NULL, value, expr, diff); } +static void llvm_emit_dynamic_method_addr(GenContext *c, BEValue *value, Expr *expr) +{ + llvm_emit_expr(c, value, expr->access_expr.parent); + llvm_emit_type_from_any(c, value); + llvm_value_rvalue(c, value); + LLVMValueRef introspect = LLVMBuildIntToPtr(c->builder, value->value, c->ptr_type, ""); + + AlignSize align; + Decl *dyn_fn = expr->access_expr.ref; + LLVMValueRef func = llvm_emit_dynamic_search(c, introspect, llvm_get_ref(c, dyn_fn)); + + llvm_value_set(value, func, type_get_ptr(dyn_fn->type)); +} static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr) { @@ -2553,8 +2567,14 @@ static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr) } value->value = LLVMBuildNeg(c->builder, value->value, "neg"); return; - case UNARYOP_TADDR: case UNARYOP_ADDR: + if (inner->expr_kind == EXPR_ACCESS && inner->access_expr.ref->decl_kind == DECL_FUNC) + { + llvm_emit_dynamic_method_addr(c, value, inner); + return; + } + FALLTHROUGH; + case UNARYOP_TADDR: llvm_emit_expr(c, value, inner); // Create an addr llvm_value_addr(c, value); @@ -4158,9 +4178,7 @@ static inline void llvm_emit_force_unwrap_expr(GenContext *c, BEValue *be_value, llvm_emit_any_from_value(c, &fault_arg, type_anyfault); vec_add(varargs, fault_arg); llvm_emit_panic(c, "Force unwrap failed!", loc, "Unexpected fault '%s' was unwrapped!", varargs); - LLVMBuildUnreachable(c->builder); - c->current_block = NULL; - c->current_block_is_target = false; + llvm_emit_unreachable(c); } llvm_emit_block(c, no_err_block); @@ -5255,6 +5273,107 @@ void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype // 17i. The simple case here is where there is a normal return. // In this case be_value already holds the result } + +static LLVMValueRef llvm_emit_dynamic_search(GenContext *c, LLVMValueRef type_id_ptr, LLVMValueRef selector) +{ + LLVMTypeRef type = c->dyn_find_function_type; + LLVMValueRef func = c->dyn_find_function; + if (!c->dyn_find_function) + { + LLVMTypeRef types[2] = { c->ptr_type, c->ptr_type }; + type = c->dyn_find_function_type = LLVMFunctionType(c->ptr_type, types, 2, false); + func = c->dyn_find_function = LLVMAddFunction(c->module, ".dyn_seach", c->dyn_find_function_type); + + LLVMSetUnnamedAddress(func, LLVMGlobalUnnamedAddr); + LLVMSetFunctionCallConv(func, LLVMFastCallConv); + LLVMSetLinkage(func, LLVMWeakODRLinkage); + + LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context); + + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, func, "entry"); + LLVMPositionBuilderAtEnd(builder, entry); + + AlignSize align; + LLVMValueRef dtable_ptr_in = LLVMGetParam(func, 0); + LLVMValueRef func_ref = LLVMGetParam(func, 1); + + LLVMBasicBlockRef check = llvm_basic_block_new(c, "check"); + LLVMBasicBlockRef missing_function = llvm_basic_block_new(c, "missing_function"); + LLVMBasicBlockRef compare = llvm_basic_block_new(c, "compare"); + LLVMBasicBlockRef match = llvm_basic_block_new(c, "match"); + LLVMBasicBlockRef no_match = llvm_basic_block_new(c, "no_match"); + + LLVMBuildBr(builder, check); + + // check: dtable_ptr = phi + LLVMAppendExistingBasicBlock(func, check); + LLVMPositionBuilderAtEnd(builder, check); + LLVMValueRef dtable_ptr = LLVMBuildPhi(builder, c->ptr_type, ""); + + // dtable_ptr == null + LLVMValueRef cmp = LLVMBuildICmp(builder, LLVMIntEQ, dtable_ptr, LLVMConstNull(c->ptr_type), ""); + + // if (cmp) goto missing_function else compare + LLVMBuildCondBr(builder, cmp, missing_function, compare); + + // missing_function: return null + LLVMAppendExistingBasicBlock(func, missing_function); + LLVMPositionBuilderAtEnd(builder, missing_function); + LLVMBuildRet(builder, LLVMConstNull(c->ptr_type)); + + // function_type = dtable_ptr.function_type + LLVMAppendExistingBasicBlock(func, compare); + LLVMPositionBuilderAtEnd(builder, compare); + + LLVMValueRef function_type = LLVMBuildStructGEP2(builder, c->dtable_type, dtable_ptr, 1, ""); + function_type = LLVMBuildLoad2(builder, c->ptr_type, function_type, ""); + LLVMSetAlignment(function_type, llvm_abi_alignment(c, c->ptr_type)); + + // function_type == func_ref + cmp = LLVMBuildICmp(builder, LLVMIntEQ, function_type, func_ref, ""); + + // if (cmp) goto match else no_match + LLVMBuildCondBr(builder, cmp, match, no_match); + + // match: function_ptr = dtable_ptr.function_ptr + LLVMAppendExistingBasicBlock(func, match); + LLVMPositionBuilderAtEnd(builder, match); + + // Offset = 0 + LLVMValueRef function_ptr = LLVMBuildLoad2(builder, c->ptr_type, dtable_ptr, ""); + LLVMSetAlignment(function_ptr, llvm_abi_alignment(c, c->ptr_type)); + LLVMBuildRet(builder, function_ptr); + + // no match: next = dtable_ptr.next + LLVMAppendExistingBasicBlock(func, no_match); + LLVMPositionBuilderAtEnd(builder, no_match); + LLVMValueRef next = LLVMBuildStructGEP2(builder, c->dtable_type, dtable_ptr, 2, ""); + next = LLVMBuildLoad2(builder, c->ptr_type, next, ""); + LLVMSetAlignment(next, llvm_abi_alignment(c, c->ptr_type)); + + // goto check + LLVMBuildBr(builder, check); + + LLVMBasicBlockRef block_in[2] = { entry, no_match }; + LLVMValueRef value_in[2] = { dtable_ptr_in, next }; + LLVMAddIncoming(dtable_ptr, value_in, block_in, 2); + + LLVMDisposeBuilder(builder); + } + AlignSize align; + LLVMValueRef dtable_ref = llvm_emit_struct_gep_raw(c, + type_id_ptr, + c->introspect_type, + INTROSPECT_INDEX_DTABLE, + llvm_abi_alignment(c, c->introspect_type), + &align); + LLVMValueRef dtable_ptr = llvm_load(c, c->ptr_type, dtable_ref, align, ""); + LLVMValueRef params[2] = { dtable_ptr, selector }; + LLVMValueRef call = LLVMBuildCall2(c->builder, type, func, params, 2, ""); + LLVMSetFunctionCallConv(call, LLVMFastCallConv); + return call; +} + static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr, BEValue *target) { if (expr->call_expr.is_builtin) @@ -5278,9 +5397,46 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr bool always_inline = false; FunctionPrototype *prototype; - // 1. Call through a pointer. - if (!expr->call_expr.is_func_ref) + Expr **args = expr->call_expr.arguments; + + BEValue arg0_pointer = { .value = NULL }; + + // 1. Dynamic dispatch. + if (expr->call_expr.is_dynamic_dispatch) { + assert(vec_size(args)); + Expr *any_val = args[0]; + assert(any_val->expr_kind == EXPR_CAST); + any_val = exprptr(any_val->cast_expr.expr)->unary_expr.expr; + BEValue result; + llvm_emit_expr(c, &result, any_val); + BEValue typeid = result; + llvm_emit_type_from_any(c, &typeid); + llvm_value_rvalue(c, &typeid); + llvm_emit_any_pointer(c, &result, &arg0_pointer); + LLVMValueRef introspect = LLVMBuildIntToPtr(c->builder, typeid.value, c->ptr_type, ""); + + LLVMBasicBlockRef missing_function = llvm_basic_block_new(c, "missing_function"); + LLVMBasicBlockRef match = llvm_basic_block_new(c, "match"); + + AlignSize align; + Decl *dyn_fn = declptr(expr->call_expr.func_ref); + func = llvm_emit_dynamic_search(c, introspect, llvm_get_ref(c, dyn_fn)); + LLVMValueRef cmp = LLVMBuildICmp(c->builder, LLVMIntEQ, func, LLVMConstNull(c->ptr_type), ""); + llvm_emit_cond_br_raw(c, cmp, missing_function, match); + llvm_emit_block(c, missing_function); + scratch_buffer_clear(); + scratch_buffer_printf("No method '%s' could be found on target", dyn_fn->name); + llvm_emit_panic(c, scratch_buffer_to_string(), expr->span, NULL, NULL); + llvm_emit_unreachable(c); + llvm_emit_block(c, match); + + prototype = type_get_resolved_prototype(dyn_fn->type); + func_type = llvm_get_type(c, dyn_fn->type); + } + else if (!expr->call_expr.is_func_ref) + { + // Call through a pointer. Expr *function = exprptr(expr->call_expr.function); // 1a. Find the pointee type for the function pointer: @@ -5312,13 +5468,11 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr assert(func); func_type = llvm_get_type(c, function_decl->type); } - LLVMValueRef arg_values[512]; unsigned arg_count = 0; Type **params = prototype->param_types; ABIArgInfo **abi_args = prototype->abi_args; unsigned param_count = vec_size(params); - Expr **args = expr->call_expr.arguments; Expr **varargs = NULL; Expr *vararg_splat = NULL; if (expr->call_expr.splat_vararg) @@ -5428,7 +5582,14 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr if (arg_expr) { - llvm_emit_expr(c, &temp_value, arg_expr); + if (i == 0 && arg0_pointer.value) + { + temp_value = arg0_pointer; + } + else + { + llvm_emit_expr(c, &temp_value, arg_expr); + } } else { @@ -5931,9 +6092,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } llvm_emit_panic(c, "Attempted to access 'inner' on non composite type", expr->span, NULL, NULL); - c->current_block = NULL; - c->current_block_is_target = false; - LLVMBuildUnreachable(c->builder); + llvm_emit_unreachable(c); llvm_emit_block(c, exit); } { @@ -5962,9 +6121,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } llvm_emit_panic(c, "Attempted to access 'names' on non enum/fault type.", expr->span, NULL, NULL); - c->current_block = NULL; - c->current_block_is_target = false; - LLVMBuildUnreachable(c->builder); + llvm_emit_unreachable(c); llvm_emit_block(c, exit); } { @@ -5997,9 +6154,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex llvm_emit_block(c, next); } llvm_emit_panic(c, "Attempted to access 'len' on non array type", expr->span, NULL, NULL); - c->current_block = NULL; - c->current_block_is_target = false; - LLVMBuildUnreachable(c->builder); + llvm_emit_unreachable(c); llvm_emit_block(c, exit); } { @@ -6101,6 +6256,25 @@ static inline void llvm_emit_any(GenContext *c, BEValue *value, Expr *expr) llvm_value_set(value, var, type_any); } +static inline void llvm_emit_type_from_any(GenContext *c, BEValue *be_value) +{ + if (llvm_value_is_addr(be_value)) + { + AlignSize alignment = 0; + LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, + be_value->value, + llvm_get_type(c, type_any), + 1, + be_value->alignment, + &alignment); + llvm_value_set_address(be_value, pointer_addr, type_typeid, alignment); + } + else + { + llvm_value_set(be_value, llvm_emit_extract_value(c, be_value->value, 1), type_typeid); + } +} + static inline void llvm_emit_builtin_access(GenContext *c, BEValue *be_value, Expr *expr) { Expr *inner = exprptr(expr->builtin_access_expr.inner); @@ -6204,21 +6378,7 @@ static inline void llvm_emit_builtin_access(GenContext *c, BEValue *be_value, Ex return; } case ACCESS_TYPEOFANY: - if (llvm_value_is_addr(be_value)) - { - AlignSize alignment = 0; - LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, - be_value->value, - llvm_get_type(c, type_any), - 1, - be_value->alignment, - &alignment); - llvm_value_set_address(be_value, pointer_addr, type_typeid, alignment); - } - else - { - llvm_value_set(be_value, llvm_emit_extract_value(c, be_value->value, 1), type_typeid); - } + llvm_emit_type_from_any(c, be_value); return; } UNREACHABLE diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index da74a43a3..5123e4b99 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -5,6 +5,7 @@ #include "llvm_codegen_internal.h" +static LLVMValueRef llvm_add_xxlizer(GenContext *c, unsigned priority, bool is_finalizer); static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index); static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef value); static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index, AlignSize alignment); @@ -404,6 +405,7 @@ void llvm_emit_return_implicit(GenContext *c) void llvm_emit_function_body(GenContext *c, Decl *decl) { DEBUG_LOG("Generating function %s.", decl->extname); + if (decl->func_decl.attr_dynamic) vec_add(c->dynamic_functions, decl); assert(decl->backend_ref); llvm_emit_body(c, decl->backend_ref, @@ -568,6 +570,18 @@ void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_nam c->function = prev_function; } +static LLVMValueRef llvm_add_xxlizer(GenContext *c, unsigned priority, bool is_initializer) +{ + LLVMTypeRef initializer_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); + LLVMValueRef **array_ref = is_initializer ? &c->constructors : &c->destructors; + scratch_buffer_clear(); + scratch_buffer_printf(is_initializer ? ".static_initialize.%u" : ".static_finalize.%u", vec_size(*array_ref)); + LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type); + LLVMSetLinkage(function, LLVMInternalLinkage); + LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) }; + vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false)); + return function; +} void llvm_emit_xxlizer(GenContext *c, Decl *decl) { @@ -577,13 +591,8 @@ void llvm_emit_xxlizer(GenContext *c, Decl *decl) // Skip if it doesn't have a body. return; } - LLVMTypeRef initializer_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); - bool is_finalizer = decl->decl_kind == DECL_FINALIZE; - LLVMValueRef **array_ref = is_finalizer ? &c->destructors : &c->constructors; - scratch_buffer_clear(); - scratch_buffer_printf(is_finalizer ? ".static_finalize.%u" : ".static_initialize.%u", vec_size(*array_ref)); - LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type); - LLVMSetLinkage(function, LLVMInternalLinkage); + bool is_initializer = decl->decl_kind == DECL_INITIALIZE; + LLVMValueRef function = llvm_add_xxlizer(c, decl->xxlizer.priority, is_initializer); if (llvm_use_debug(c)) { uint32_t row = decl->span.row; @@ -607,16 +616,67 @@ void llvm_emit_xxlizer(GenContext *c, Decl *decl) llvm_emit_body(c, function, decl->unit->module->name->module, - is_finalizer ? "[static finalizer]" : "[static initializer]", + is_initializer ? "[static initializer]" : "[static finalizer]", decl->span.file_id, NULL, NULL, body); - unsigned priority = decl->xxlizer.priority; - LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) }; - vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false)); } +static void llvm_generate_dyn_proto(GenContext *c, LLVMValueRef proto_ref) +{ + LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context); + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, proto_ref, "never"); + LLVMPositionBuilderAtEnd(builder, entry); + LLVMBuildUnreachable(builder); + LLVMDisposeBuilder(builder); +} + +void llvm_emit_dynamic_functions(GenContext *c, Decl **funcs) +{ + if (!vec_size(funcs)) return; + LLVMValueRef initializer = llvm_add_xxlizer(c, 1, true); + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, initializer, "entry"); + LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context); + LLVMPositionBuilderAtEnd(builder, entry); + LLVMBasicBlockRef last_block = entry; + FOREACH_BEGIN(Decl *decl, funcs) + Type *type = typeinfotype(decl->func_decl.type_parent); + scratch_buffer_clear(); + scratch_buffer_append("$ct.dyn."); + scratch_buffer_append(decl_get_extname(decl)); + LLVMValueRef global = llvm_add_global_raw(c, scratch_buffer_to_string(), c->dtable_type, 0); + Decl *proto = declptr(decl->func_decl.any_prototype); + LLVMValueRef proto_ref = llvm_get_ref(c, proto); + LLVMValueRef vals[3] = { llvm_get_ref(c, decl), proto_ref, LLVMConstNull(c->ptr_type) }; + LLVMSetInitializer(global, LLVMConstNamedStruct(c->dtable_type, vals, 3)); + LLVMValueRef type_id_ptr = LLVMBuildIntToPtr(builder, llvm_get_typeid(c, type), c->ptr_type, ""); + LLVMValueRef dtable_ref = LLVMBuildStructGEP2(builder, c->introspect_type, type_id_ptr, INTROSPECT_INDEX_DTABLE, ""); + LLVMBasicBlockRef check = LLVMAppendBasicBlockInContext(c->context, initializer, "dtable_check"); + LLVMBuildBr(builder, check); + LLVMPositionBuilderAtEnd(builder, check); + LLVMValueRef phi = LLVMBuildPhi(builder, c->ptr_type, "dtable_ref"); + LLVMValueRef load_dtable = LLVMBuildLoad2(builder, c->ptr_type, phi, "dtable_ptr"); + LLVMValueRef is_not_null = LLVMBuildICmp(builder, LLVMIntEQ, load_dtable, LLVMConstNull(c->ptr_type), ""); + LLVMBasicBlockRef after_check = llvm_basic_block_new(c, "dtable_found"); + LLVMBasicBlockRef next = llvm_basic_block_new(c, "dtable_next"); + LLVMBuildCondBr(builder, is_not_null, after_check, next); + LLVMAppendExistingBasicBlock(initializer, next); + LLVMPositionBuilderAtEnd(builder, next); + LLVMValueRef next_ptr = LLVMBuildStructGEP2(builder, c->dtable_type, load_dtable, 2, "next_dtable_ref"); + LLVMValueRef phi_in[2] = { dtable_ref, next_ptr }; + LLVMBasicBlockRef phi_in_block[2] = { last_block, next }; + LLVMAddIncoming(phi, phi_in, phi_in_block, 2); + LLVMBuildBr(builder, check); + LLVMAppendExistingBasicBlock(initializer, after_check); + LLVMPositionBuilderAtEnd(builder, after_check); + LLVMBuildStore(builder, global, phi); + last_block = after_check; + FOREACH_END(); + + LLVMBuildRet(builder, NULL); + LLVMDisposeBuilder(builder); +} void llvm_emit_function_decl(GenContext *c, Decl *decl) { assert(decl->decl_kind == DECL_FUNC); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 61961b5ca..d3132cce4 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -102,6 +102,7 @@ typedef struct GenContext_ LLVMTypeRef fault_type; LLVMTypeRef size_type; LLVMTypeRef typeid_type; + LLVMTypeRef dtable_type; LLVMTypeRef char_ptr_type; LLVMTypeRef ptr_type; LLVMTypeRef chars_type; @@ -128,6 +129,9 @@ typedef struct GenContext_ bool current_block_is_target : 1; LLVMTypeRef type_data_definitions[TYPE_KINDS]; SourceSpan last_emitted_loc; + Decl **dynamic_functions; + LLVMValueRef dyn_find_function; + LLVMTypeRef dyn_find_function_type; } GenContext; // LLVM Intrinsics @@ -462,6 +466,7 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc, const char *fmt, BEValue *value_1, BEValue *value_2); void llvm_emit_panic(GenContext *c, const char *message, SourceSpan loc, const char *fmt, BEValue *args); +void llvm_emit_unreachable(GenContext *c); void llvm_emit_any_from_value(GenContext *c, BEValue *value, Type *type); void llvm_emit_subarray_len(GenContext *context, BEValue *subarray, BEValue *len); @@ -469,6 +474,7 @@ void llvm_emit_subarray_pointer(GenContext *context, BEValue *subarray, BEValue void llvm_emit_compound_stmt(GenContext *c, Ast *ast); LLVMValueRef llvm_emit_const_bitstruct(GenContext *c, ConstInitializer *initializer); void llvm_emit_function_body(GenContext *context, Decl *decl); +void llvm_emit_dynamic_functions(GenContext *context, Decl **funcs); BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef optional); INLINE void llvm_emit_exprid(GenContext *c, BEValue *value, ExprId expr); INLINE void llvm_emit_statement_chain(GenContext *c, AstId current); diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 4f15353bc..cd534a9c4 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -11,6 +11,7 @@ static inline LLVMTypeRef create_introspection_type(GenContext *c) LLVMTypeRef type = LLVMStructCreateNamed(c->context, ".introspect"); LLVMTypeRef introspect_type[INTROSPECT_INDEX_TOTAL] = { [INTROSPECT_INDEX_KIND] = c->byte_type, + [INTROSPECT_INDEX_DTABLE] = c->ptr_type, [INTROSPECT_INDEX_SIZEOF] = c->size_type, [INTROSPECT_INDEX_INNER] = c->typeid_type, [INTROSPECT_INDEX_LEN] = c->size_type, @@ -113,6 +114,8 @@ void gencontext_begin_module(GenContext *c) c->ptr_type = LLVMPointerType(c->byte_type, 0); c->size_type = llvm_get_type(c, type_usz); c->typeid_type = llvm_get_type(c, type_typeid); + LLVMTypeRef dtable_type[3] = { c->ptr_type, c->ptr_type, c->ptr_type }; + c->dtable_type = LLVMStructTypeInContext(c->context, dtable_type, 3, false); c->chars_type = llvm_get_type(c, type_chars); c->introspect_type = create_introspection_type(c); c->fault_type = create_fault_type(c); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index f9dc329fe..4a3a3ba7b 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -470,10 +470,8 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) SourceSpan loc = ast->span; llvm_emit_panic(c, "Infinite loop found", loc, NULL, NULL); - LLVMBuildUnreachable(c->builder); + llvm_emit_unreachable(c); LLVMBasicBlockRef block = llvm_basic_block_new(c, "unreachable_block"); - c->current_block = NULL; - c->current_block_is_target = false; llvm_emit_block(c, block); return; } @@ -1274,6 +1272,12 @@ LLVMValueRef llvm_emit_zstring_named(GenContext *c, const char *str, const char return string; } +void llvm_emit_unreachable(GenContext *c) +{ + LLVMBuildUnreachable(c->builder); + c->current_block = NULL; + c->current_block_is_target = false; +} void llvm_emit_panic(GenContext *c, const char *message, SourceSpan loc, const char *fmt, BEValue *varargs) { diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 55104b2f9..9692de821 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -439,6 +439,7 @@ static inline LLVMValueRef llvm_generate_introspection_global(GenContext *c, LLV } LLVMValueRef values[INTROSPECT_INDEX_TOTAL] = { [INTROSPECT_INDEX_KIND] = LLVMConstInt(c->byte_type, introspect_type, false), + [INTROSPECT_INDEX_DTABLE] = LLVMConstNull(c->ptr_type), [INTROSPECT_INDEX_SIZEOF] = LLVMConstInt(c->size_type, type_size(type), false), [INTROSPECT_INDEX_INNER] = inner ? llvm_get_typeid(c, inner) : llvm_get_zero(c, type_typeid), [INTROSPECT_INDEX_LEN] = LLVMConstInt(c->size_type,len, false), @@ -461,7 +462,7 @@ static inline LLVMValueRef llvm_generate_introspection_global(GenContext *c, LLV LLVMSetInitializer(global_name, strukt); } LLVMSetAlignment(global_name, llvm_abi_alignment(c, c->introspect_type)); - LLVMSetGlobalConstant(global_name, 1); + LLVMSetGlobalConstant(global_name, 0); if (is_external) { LLVMSetLinkage(global_name, LLVMExternalLinkage); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 79f70d154..555c2c91f 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -2306,10 +2306,9 @@ static inline Decl *parse_func_definition(ParseContext *c, AstId contracts, bool return func; } - if (tok_is(c, TOKEN_EOS)) + if (try_consume(c, TOKEN_EOS)) { - SEMA_ERROR_HERE("Expected a function body, if you want to declare an extern function use 'extern' or place it in an .c3i file."); - return poisoned_decl; + return func; } if (tok_is(c, TOKEN_IMPLIES)) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 802525aae..e58399912 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -13,7 +13,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl); static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *current, unsigned current_index, unsigned count); static inline bool sema_analyse_method(SemaContext *context, Decl *decl); -static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type); +static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type, bool is_dynamic, bool is_interface); static inline bool sema_analyse_macro_method(SemaContext *context, Decl *decl); static inline bool unit_add_base_extension_method(CompilationUnit *unit, Type *parent_type, Decl *method_like); static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type, Decl *method_like); @@ -701,6 +701,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) return false; } + // Check parameters for (unsigned i = 0; i < param_count; i++) { @@ -1417,13 +1418,15 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) if (!sema_resolve_type_info(context, parent_type)) return false; Type *type = parent_type->type->canonical; Decl **params = decl->func_decl.signature.params; - if (!vec_size(params)) + bool is_dynamic = decl->func_decl.attr_dynamic; + bool is_interface = decl->func_decl.attr_interface; + if (is_interface && type != type_any) RETURN_SEMA_ERROR(decl, "Only 'any' methods may use '@interface'."); + if (!sema_is_valid_method_param(context, params[0], type, is_dynamic, is_interface)) return false; + if (is_dynamic) { - SEMA_ERROR(decl, "A method must start with a parameter of type %s or %s.", - type_quoted_error_string(parent_type->type), type_quoted_error_string(type_get_ptr(parent_type->type))); - return false; + if (is_interface) RETURN_SEMA_ERROR(decl, "An interface method cannot be '@dynamic'."); } - if (!sema_is_valid_method_param(context, params[0], type)) return false; + return unit_add_method_like(context->unit, type, decl); } @@ -1484,6 +1487,8 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_BUILTIN] = ATTR_MACRO | ATTR_FUNC, [ATTRIBUTE_CDECL] = ATTR_FUNC, [ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | ATTR_FUNC | ATTR_MACRO | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER, + [ATTRIBUTE_DYNAMIC] = ATTR_FUNC, + [ATTRIBUTE_INTERFACE] = ATTR_FUNC, [ATTRIBUTE_EXPORT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES, [ATTRIBUTE_NOSTRIP] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES, [ATTRIBUTE_EXTNAME] = (AttributeDomain)~(ATTR_CALL | ATTR_BITSTRUCT | ATTR_DEFINE | ATTR_MACRO | ATTR_XXLIZER), @@ -1579,6 +1584,12 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, case ATTRIBUTE_TEST: decl->func_decl.attr_test = true; break; + case ATTRIBUTE_INTERFACE: + decl->func_decl.attr_interface = true; + break; + case ATTRIBUTE_DYNAMIC: + decl->func_decl.attr_dynamic = true; + break; case ATTRIBUTE_STDCALL: assert(decl->decl_kind == DECL_FUNC); if (platform_target.arch == ARCH_TYPE_ARM || platform_target.arch == ARCH_TYPE_ARMB) @@ -2377,6 +2388,16 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) } else { + if (decl->func_decl.attr_dynamic) + { + SEMA_ERROR(decl, "Only methods may be annotated '@dynamic'."); + return decl_poison(decl); + } + if (decl->func_decl.attr_interface) + { + SEMA_ERROR(decl, "Only methods to 'any' may be annotated '@interface'."); + return decl_poison(decl); + } if (decl->name == kw_main) { if (is_test) SEMA_ERROR(decl, "Main functions may not be annotated @test."); @@ -2385,6 +2406,15 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) decl_set_external_name(decl); } + bool is_any_interface = decl->func_decl.attr_interface && decl->func_decl.type_parent && typeinfotype(decl->func_decl.type_parent) == type_any; + // Do we have fn void any.foo(void*) { ... }? + if (decl->func_decl.body && is_any_interface) RETURN_SEMA_ERROR(decl, "Interface methods declarations may not have a body."); + if (!decl->func_decl.body && !decl->is_extern && !decl->unit->is_interface_file && !is_any_interface) + { + SEMA_ERROR(decl, "Expected a function body, if you want to declare an extern function use 'extern' or place it in an .c3i file."); + return false; + } + bool pure = false; if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, NULL, &pure)) return decl_poison(decl); decl->func_decl.signature.attrs.is_pure = pure; @@ -2393,15 +2423,32 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) return true; } -static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type) +static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type, bool is_dynamic, bool is_interface) { assert(parent_type->canonical == parent_type && "Expected already the canonical version."); Type *param_type = param->type; if (!param_type) goto ERROR; param_type = param_type->canonical; + + if (is_interface) + { + if (param_type != type_voidptr) RETURN_SEMA_ERROR(param, "The first parameter of an interface must be of type 'void*'."); + return true; + } + + if (is_dynamic) + { + if (param_type->type_kind != TYPE_POINTER || param_type->pointer != parent_type) + { + RETURN_SEMA_ERROR(param, "The fist parameter must be of type %s", type_quoted_error_string(type_get_ptr(parent_type))); + } + return true; + } + // 1. Same type ok! if (param_type == parent_type) return true; + // 2. A pointer is ok! if (param_type->type_kind == TYPE_POINTER && param_type->pointer == parent_type) return true; ERROR: @@ -2433,7 +2480,7 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) SEMA_ERROR(decl, "The first parameter to this method must be of type '%s'.", type_to_error_string(parent_type)); return false; } - if (!sema_is_valid_method_param(context, first_param, parent_type->canonical)) return false; + if (!sema_is_valid_method_param(context, first_param, parent_type->canonical, false, false)) return false; if (first_param->var.kind != VARDECL_PARAM_REF && first_param->var.kind != VARDECL_PARAM) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c0e24a3c9..8f318ad27 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1660,6 +1660,9 @@ static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, } sema_display_deprecated_warning_on_use(context, decl, expr->span); + // Tag dynamic dispatch. + if (struct_var && decl->func_decl.attr_interface) expr->call_expr.is_dynamic_dispatch = true; + return sema_call_analyse_func_invocation(context, decl->type, expr, @@ -5466,11 +5469,15 @@ static const char *sema_addr_check_may_take(Expr *inner) if (inner->unary_expr.operator == UNARYOP_DEREF) return NULL; break; case EXPR_ACCESS: - if (inner->access_expr.ref->decl_kind == DECL_FUNC) + { + Decl *decl = inner->access_expr.ref; + if (decl->decl_kind == DECL_FUNC) { + if (decl->func_decl.attr_interface) return NULL; return "Taking the address of a method should be done through the type e.g. '&Foo.method' not through the value."; } return sema_addr_check_may_take(inner->access_expr.parent); + } case EXPR_GROUP: return sema_addr_check_may_take(inner->inner_expr); case EXPR_SUBSCRIPT: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 378b5457f..5c8781a2a 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2916,6 +2916,19 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, So bool sema_analyse_function_body(SemaContext *context, Decl *func) { if (!decl_ok(func)) return false; + if (func->func_decl.attr_dynamic) + { + Decl *ambiguous = NULL; + Decl *private = NULL; + Decl *any = sema_resolve_type_method(context->unit, type_any, func->name, &ambiguous, &private); + if (!any) + { + SEMA_ERROR(func, "To define a '@dynamic' method, the prototype method 'any.%s(...)' must exist. Did you spell the method name right?", + func->name); + return false; + } + func->func_decl.any_prototype = declid(any); + } Signature *signature = &func->func_decl.signature; FunctionPrototype *prototype = func->type->function.prototype; context->call_env = (CallEnv) { diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index fc687ee11..e52384a66 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -297,10 +297,12 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_BUILTIN] = KW_DEF("@builtin"); attribute_list[ATTRIBUTE_CDECL] = KW_DEF("@cdecl"); attribute_list[ATTRIBUTE_DEPRECATED] = KW_DEF("@deprecated"); + attribute_list[ATTRIBUTE_DYNAMIC] = KW_DEF("@dynamic"); attribute_list[ATTRIBUTE_EXPORT] = KW_DEF("@export"); attribute_list[ATTRIBUTE_EXTERN] = KW_DEF("@extern"); attribute_list[ATTRIBUTE_EXTNAME] = KW_DEF("@extname"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("@inline"); + attribute_list[ATTRIBUTE_INTERFACE] = KW_DEF("@interface"); attribute_list[ATTRIBUTE_LITTLEENDIAN] = KW_DEF("@littleendian"); attribute_list[ATTRIBUTE_LOCAL] = KW_DEF("@local"); attribute_list[ATTRIBUTE_MAYDISCARD] = KW_DEF("@maydiscard"); diff --git a/src/version.h b/src/version.h index 9b1fbe88b..969ced473 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.513" \ No newline at end of file +#define COMPILER_VERSION "0.4.514" \ No newline at end of file diff --git a/test/test_suite/abi/small_struct_x64.c3t b/test/test_suite/abi/small_struct_x64.c3t index aa286b96d..5629a4969 100644 --- a/test/test_suite/abi/small_struct_x64.c3t +++ b/test/test_suite/abi/small_struct_x64.c3t @@ -20,10 +20,10 @@ fn Foo getFoo(Foo f) /* #expect: test.ll -%.introspect = type { i8, i64, i64, i64, [0 x i64] } +%.introspect = type { i8, ptr, i64, i64, i64, [0 x i64] } %Foo = type { i8, i8, i8 } -@"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 3, i64 0, i64 3, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 3, i64 0, i64 3, [0 x i64] zeroinitializer }, align 8 ; Function Attrs: nounwind define i32 @test.testing() #0 { diff --git a/test/test_suite/arrays/complex_array_const.c3t b/test/test_suite/arrays/complex_array_const.c3t index b471af181..58a1fc23e 100644 --- a/test/test_suite/arrays/complex_array_const.c3t +++ b/test/test_suite/arrays/complex_array_const.c3t @@ -15,7 +15,7 @@ Connection[3] link @private /* #expect: test.ll -@"$ct.test.Connection" = linkonce constant %.introspect { i8 10, i64 24, i64 0, i64 3, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Connection" = linkonce global %.introspect { i8 10, ptr null, i64 24, i64 0, i64 3, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [6 x i8] c"link1\00", align 1 @.str.1 = private unnamed_addr constant [6 x i8] c"link2\00", align 1 @.str.2 = private unnamed_addr constant [6 x i8] c"link3\00", align 1 diff --git a/test/test_suite/attributes/user_defined_attributes.c3t b/test/test_suite/attributes/user_defined_attributes.c3t index fc8d08887..2e88b5c2b 100644 --- a/test/test_suite/attributes/user_defined_attributes.c3t +++ b/test/test_suite/attributes/user_defined_attributes.c3t @@ -32,7 +32,7 @@ fn void main() @TestZero /* #expect: test.ll %Foo = type { i32, [1020 x i8], i32, [1020 x i8] } -@"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 2048, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 2048, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @test.f = local_unnamed_addr global %Foo zeroinitializer, align 1024 define weak void @test.testme2() #0 { diff --git a/test/test_suite/compile_time/untyped_conversions.c3t b/test/test_suite/compile_time/untyped_conversions.c3t index 2d9b96a44..da306bb2d 100644 --- a/test/test_suite/compile_time/untyped_conversions.c3t +++ b/test/test_suite/compile_time/untyped_conversions.c3t @@ -24,26 +24,27 @@ fn void main() /* #expect: test.ll -%.introspect = type { i8, i64, i64, i64, [0 x i64] } + +%.introspect = type { i8, ptr, i64, i64, i64, [0 x i64] } %Foo = type { i32, i32 } %"int[]" = type { ptr, i64 } %any = type { ptr, i64 } -@"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [9 x i8] c"%s %s %s\00", align 1 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.a2$int" = linkonce constant %.introspect { i8 15, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.sa$int" = linkonce constant %.introspect { i8 16, i64 16, i64 ptrtoint (ptr @"$ct.int" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.v2$int" = linkonce constant %.introspect { i8 17, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.a2$int" = linkonce global %.introspect { i8 15, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.sa$int" = linkonce global %.introspect { i8 16, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.int" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.v2$int" = linkonce global %.introspect { i8 17, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant [1 x %Foo] [%Foo { i32 1, i32 2 }], align 4 @.__const.1 = private unnamed_addr constant %Foo { i32 1, i32 2 }, align 4 @.__const.2 = private unnamed_addr constant [1 x [2 x i32]] [[2 x i32] [i32 1, i32 2]], align 4 @.__const.3 = private unnamed_addr constant [1 x [2 x double]] [[2 x double] [double 1.000000e+00, double 2.000000e+00]], align 16 @.str.4 = private unnamed_addr constant [15 x i8] c"%s %s {%s, %s}\00", align 1 -@"$ct.a1$a2$int" = linkonce constant %.introspect { i8 15, i64 8, i64 ptrtoint (ptr @"$ct.a2$int" to i64), i64 1, [0 x i64] zeroinitializer }, align 8 -@"$ct.double" = linkonce constant %.introspect { i8 4, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.a2$double" = linkonce constant %.introspect { i8 15, i64 16, i64 ptrtoint (ptr @"$ct.double" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.a1$a2$double" = linkonce constant %.introspect { i8 15, i64 16, i64 ptrtoint (ptr @"$ct.a2$double" to i64), i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.a1$a2$int" = linkonce global %.introspect { i8 15, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.a2$int" to i64), i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.double" = linkonce global %.introspect { i8 4, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.a2$double" = linkonce global %.introspect { i8 15, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.double" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.a1$a2$double" = linkonce global %.introspect { i8 15, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.a2$double" to i64), i64 1, [0 x i64] zeroinitializer }, align 8 ; Function Attrs: nounwind define void @test.test(i64 %0, ptr %1, i64 %2, double %3) #0 { diff --git a/test/test_suite/compile_time_introspection/qnameof.c3t b/test/test_suite/compile_time_introspection/qnameof.c3t index 4d9d6f466..3b3748c4c 100644 --- a/test/test_suite/compile_time_introspection/qnameof.c3t +++ b/test/test_suite/compile_time_introspection/qnameof.c3t @@ -22,7 +22,7 @@ fn void main() /* #expect: qnametest.ll -@"$ct.qnametest.Blob" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.qnametest.Blob" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @qnametest.x = local_unnamed_addr global i32 0, align 4 @.str = private unnamed_addr constant [12 x i8] c"printf: %s\0A\00", align 1 @.str.1 = private unnamed_addr constant [7 x i8] c"printf\00", align 1 diff --git a/test/test_suite/enumerations/enum_associated_value.c3t b/test/test_suite/enumerations/enum_associated_value.c3t index 6ee0bb6fc..cc516d9df 100644 --- a/test/test_suite/enumerations/enum_associated_value.c3t +++ b/test/test_suite/enumerations/enum_associated_value.c3t @@ -18,8 +18,8 @@ fn void main() /* #expect: test.ll -@"$ct.uint" = linkonce constant %.introspect { i8 3, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.test.Foo" = linkonce constant { i8, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 4, i64 ptrtoint (ptr @"$ct.uint" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 1 }, %"char[]" { ptr @.enum.1, i64 1 }] }, align 8 +@"$ct.uint" = linkonce global %.introspect { i8 3, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo" = linkonce global { i8, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.uint" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 1 }, %"char[]" { ptr @.enum.1, i64 1 }] }, align 8 @"test.Foo$val" = linkonce constant [2 x i32] [i32 123, i32 333], align 4 @.str = private unnamed_addr constant [9 x i8] c"Number A\00", align 1 @.str.1 = private unnamed_addr constant [9 x i8] c"Number B\00", align 1 diff --git a/test/test_suite/enumerations/enum_associated_values_other.c3t b/test/test_suite/enumerations/enum_associated_values_other.c3t index cea7a2a0b..346513498 100644 --- a/test/test_suite/enumerations/enum_associated_values_other.c3t +++ b/test/test_suite/enumerations/enum_associated_values_other.c3t @@ -24,11 +24,11 @@ enum Foo : int(String val) /* #expect: abc.ll -@"$ct.abc.Abc" = linkonce constant %.introspect { i8 10, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.abc.Abc" = linkonce global %.introspect { i8 10, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.enum.0 = internal constant [4 x i8] c"ABC\00", align 1 @.enum.1 = internal constant [4 x i8] c"DEF\00", align 1 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.abc.Foo" = linkonce constant { i8, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 3 }, %"char[]" { ptr @.enum.1, i64 3 }] }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.abc.Foo" = linkonce global { i8, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 3 }, %"char[]" { ptr @.enum.1, i64 3 }] }, align 8 @.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1 @.str.1 = private unnamed_addr constant [6 x i8] c"world\00", align 1 @"abc.Foo$val" = linkonce constant [2 x %"char[]"] [%"char[]" { ptr @.str, i64 5 }, %"char[]" { ptr @.str.1, i64 5 }], align 8 @@ -37,18 +37,19 @@ enum Foo : int(String val) // #expect: test.ll -@"$ct.abc.Abc" = linkonce constant %.introspect { i8 10, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.abc.Abc" = linkonce global %.introspect { i8 10, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @abc.dabc = external global i32, align 4 @.enum.0 = internal constant [4 x i8] c"ABC\00", align 1 @.enum.1 = internal constant [4 x i8] c"DEF\00", align 1 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.abc.Foo" = linkonce constant { i8, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 3 }, %"char[]" { ptr @.enum.1, i64 3 }] }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.abc.Foo" = linkonce global { i8, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 3 }, %"char[]" { ptr @.enum.1, i64 3 }] }, align 8 @.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1 @.str.1 = private unnamed_addr constant [6 x i8] c"world\00", align 1 @"abc.Foo$val" = linkonce constant [2 x %"char[]"] [%"char[]" { ptr @.str, i64 5 }, %"char[]" { ptr @.str.1, i64 5 }], align 8 @.str.2 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 -@"$ct.char" = linkonce constant %.introspect { i8 3, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.sa$char" = linkonce constant %.introspect { i8 16, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.char" = linkonce global %.introspect { i8 3, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.sa$char" = linkonce global %.introspect { i8 16, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 + define void @test.main(ptr %0, i64 %1) #0 { entry: diff --git a/test/test_suite/errors/error_introspect.c3t b/test/test_suite/errors/error_introspect.c3t index af4a88c1e..4026930b2 100644 --- a/test/test_suite/errors/error_introspect.c3t +++ b/test/test_suite/errors/error_introspect.c3t @@ -22,18 +22,18 @@ fn void main() @.fault = internal constant [4 x i8] c"BAR\00", align 1 @"foo.Foo$BAZ" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), %"char[]" { ptr @.fault.1, i64 3 }, i64 2 }, align 8 @.fault.1 = internal constant [4 x i8] c"BAZ\00", align 1 -@"$ct.foo.Foo" = linkonce constant %.introspect { i8 9, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.Foo" = linkonce global %.introspect { i8 9, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [4 x i8] c"BAR\00", align 1 @.str.2 = private unnamed_addr constant [4 x i8] c"BAZ\00", align 1 @.str.3 = private unnamed_addr constant [14 x i8] c"Foo.names: %s\00", align 1 -@"$ct.char" = linkonce constant %.introspect { i8 3, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.sa$char" = linkonce constant %.introspect { i8 16, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.String" = linkonce constant %.introspect { i8 18, i64 16, i64 ptrtoint (ptr @"$ct.sa$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.sa$String" = linkonce constant %.introspect { i8 16, i64 16, i64 ptrtoint (ptr @"$ct.String" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.char" = linkonce global %.introspect { i8 3, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.sa$char" = linkonce global %.introspect { i8 16, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.String" = linkonce global %.introspect { i8 18, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.sa$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.sa$String" = linkonce global %.introspect { i8 16, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.String" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 @.str.4 = private unnamed_addr constant [15 x i8] c"Foo.values: %s\00", align 1 -@"$ct.a2$foo.Foo" = linkonce constant %.introspect { i8 15, i64 16, i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.a2$foo.Foo" = linkonce global %.introspect { i8 15, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.foo.Foo" to i64), i64 2, [0 x i64] zeroinitializer }, align 8 @.str.5 = private unnamed_addr constant [17 x i8] c"Foo.elements: %s\00", align 1 -@"$ct.long" = linkonce constant %.introspect { i8 2, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.long" = linkonce global %.introspect { i8 2, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 entry: %x = alloca %"char[][]", align 8 diff --git a/test/test_suite/errors/optional_taddr_and_access.c3t b/test/test_suite/errors/optional_taddr_and_access.c3t index 3aab3f0df..64f4f552d 100644 --- a/test/test_suite/errors/optional_taddr_and_access.c3t +++ b/test/test_suite/errors/optional_taddr_and_access.c3t @@ -27,9 +27,10 @@ fn void main() %Foo = type { i32, i32 } -@"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @"test.MyErr$FOO" = linkonce constant %.fault { i64 ptrtoint (ptr @"$ct.test.MyErr" to i64), %"char[]" { ptr @.fault, i64 3 }, i64 1 }, align 8 -@"$ct.test.MyErr" = linkonce constant %.introspect { i8 9, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@.fault = internal constant [4 x i8] c"FOO\00", align 1 +@"$ct.test.MyErr" = linkonce global %.introspect { i8 9, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @.str.1 = private unnamed_addr constant [17 x i8] c"Not visible: %d\0A\00", align 1 diff --git a/test/test_suite/expressions/pointer_access.c3t b/test/test_suite/expressions/pointer_access.c3t index 8c96ac8b9..c572cfa97 100644 --- a/test/test_suite/expressions/pointer_access.c3t +++ b/test/test_suite/expressions/pointer_access.c3t @@ -42,10 +42,10 @@ fn void testSimple() %.anon = type { i32, i32 } %.anon.0 = type { double } -@"$ct.pointer_access.c" = linkonce constant %.introspect { i8 10, i64 40, i64 0, i64 5, [0 x i64] zeroinitializer }, align 8 -@"$ct.pointer_access.$anon" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.pointer_access.$anon.4" = linkonce constant %.introspect { i8 11, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.pointer_access.ExtraSimple" = linkonce constant %.introspect { i8 10, i64 72, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8 +@"$ct.pointer_access.c" = linkonce global %.introspect { i8 10, ptr null, i64 40, i64 0, i64 5, [0 x i64] zeroinitializer }, align 8 +@"$ct.pointer_access.$anon" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.pointer_access.$anon.4" = linkonce global %.introspect { i8 11, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.pointer_access.ExtraSimple" = linkonce global %.introspect { i8 10, ptr null, i64 72, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [71 x i8] c"a = %d, c.e = %f, c.f = %f, c.j = %f, g = %d, o0 = %f, r = %d, s = %d\0A\00", align 1 define void @pointer_access.testSimple() #0 { diff --git a/test/test_suite/functions/func_ptr_conversions_and_names.c3t b/test/test_suite/functions/func_ptr_conversions_and_names.c3t index ce8c7d9ee..66a6c3099 100644 --- a/test/test_suite/functions/func_ptr_conversions_and_names.c3t +++ b/test/test_suite/functions/func_ptr_conversions_and_names.c3t @@ -32,14 +32,14 @@ fn void main() /* #expect: test.ll @.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 @.str.1 = private unnamed_addr constant [3 x i8] c"%d\00", align 1 @.str.2 = private unnamed_addr constant [3 x i8] c"%d\00", align 1 @.str.3 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 @.str.4 = private unnamed_addr constant [13 x i8] c"fn int(int)*\00", align 1 -@"$ct.char" = linkonce constant %.introspect { i8 3, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.sa$char" = linkonce constant %.introspect { i8 16, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.String" = linkonce constant %.introspect { i8 18, i64 16, i64 ptrtoint (ptr @"$ct.sa$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.char" = linkonce global %.introspect { i8 3, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.sa$char" = linkonce global %.introspect { i8 16, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.String" = linkonce global %.introspect { i8 18, ptr null, i64 16, i64 ptrtoint (ptr @"$ct.sa$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 @.str.5 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 @.str.6 = private unnamed_addr constant [13 x i8] c"fn int(int)*\00", align 1 @.str.7 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 @@ -48,8 +48,9 @@ fn void main() @.str.10 = private unnamed_addr constant [7 x i8] c"test2*\00", align 1 @.str.11 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 @.str.12 = private unnamed_addr constant [14 x i8] c"fn int!(int)*\00", align 1 -@"$ct.fn$int$int$" = linkonce constant %.introspect { i8 13, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.p$fn$int$int$" = linkonce constant %.introspect { i8 19, i64 8, i64 ptrtoint (ptr @"$ct.fn$int$int$" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.fn$int$int$" = linkonce global %.introspect { i8 13, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.p$fn$int$int$" = linkonce global %.introspect { i8 19, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.fn$int$int$" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 + define void @test.main() #0 { entry: diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index d18a90aec..81a53cdc9 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -243,13 +243,13 @@ fn Type getValue(Blob blob) %List = type { i64, i64, ptr, ptr } %Foo = type { i32, i32 } -@"$ct.test.Bobo" = linkonce constant %.introspect { i8 10, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8 -@"$ct.test.Blob" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.test.Foor" = linkonce constant %.introspect { i8 11, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.test.Foo2" = linkonce constant %.introspect { i8 10, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Bobo" = linkonce global %.introspect { i8 10, ptr null, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Blob" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foor" = linkonce global %.introspect { i8 11, ptr null, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo2" = linkonce global %.introspect { i8 10, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.test.MyEnum" = linkonce constant { i8, i64, i64, i64, [3 x %"char[]"] } { i8 8, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 3, [3 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 4 }, %"char[]" { ptr @.enum.1, i64 5 }, %"char[]" { ptr @.enum.2, i64 3 }] }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.MyEnum" = linkonce global { i8, ptr, i64, i64, i64, [3 x %"char[]"] } { i8 8, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 3, [3 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 4 }, %"char[]" { ptr @.enum.1, i64 5 }, %"char[]" { ptr @.enum.2, i64 3 }] }, align 8 @"test_static$x" = internal unnamed_addr global i32 1, align 4 define void @test.Foo2.printme(ptr %0) #0 { diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index 9ca782ac9..848615d14 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -259,13 +259,13 @@ $"$ct.int" = comdat any $"$ct.test.MyEnum" = comdat any -@"$ct.test.Bobo" = linkonce constant %.introspect { i8 10, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.test.Blob" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.test.Foor" = linkonce constant %.introspect { i8 11, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.test.Foo2" = linkonce constant %.introspect { i8 10, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.test.Foo" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, comdat, align 8 -@"$ct.test.MyEnum" = linkonce constant { i8, i64, i64, i64, [3 x %"char[]"] } { i8 8, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 3, [3 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 4 }, %"char[]" { ptr @.enum.1, i64 5 }, %"char[]" { ptr @.enum.2, i64 3 }] }, comdat, align 8 +@"$ct.test.Bobo" = linkonce global %.introspect { i8 10, ptr null, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.test.Blob" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.test.Foor" = linkonce global %.introspect { i8 11, ptr null, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.test.Foo2" = linkonce global %.introspect { i8 10, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.test.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, comdat, align 8 +@"$ct.test.MyEnum" = linkonce global { i8, ptr, i64, i64, i64, [3 x %"char[]"] } { i8 8, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 3, [3 x %"char[]"] [%"char[]" { ptr @.enum.0, i64 4 }, %"char[]" { ptr @.enum.1, i64 5 }, %"char[]" { ptr @.enum.2, i64 3 }] }, comdat, align 8 @.str = private unnamed_addr constant [13 x i8] c"helloWorld!\0A\00", align 1 @"test_static$x" = internal unnamed_addr global i32 1, align 4 @.str.1 = private unnamed_addr constant [16 x i8] c"Test static %d\0A\00", align 1 diff --git a/test/test_suite/functions/typeless_varargs.c3t b/test/test_suite/functions/typeless_varargs.c3t index bf273e5d6..4fcd8a420 100644 --- a/test/test_suite/functions/typeless_varargs.c3t +++ b/test/test_suite/functions/typeless_varargs.c3t @@ -22,7 +22,8 @@ fn int main() /* #expect: test.ll -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 + +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 define void @test.retest(ptr %0, i64 %1) #0 { entry: diff --git a/test/test_suite/initializer_lists/general_tests.c3t b/test/test_suite/initializer_lists/general_tests.c3t index e59434e44..a479fd4b1 100644 --- a/test/test_suite/initializer_lists/general_tests.c3t +++ b/test/test_suite/initializer_lists/general_tests.c3t @@ -37,8 +37,8 @@ fn int test() %"int[]" = type { ptr, i64 } %"Bar[]" = type { ptr, i64 } -@"$ct.general_tests.Baz" = linkonce constant %.introspect { i8 11, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.general_tests.Bar" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.general_tests.Baz" = linkonce global %.introspect { i8 11, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.general_tests.Bar" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant { i32, [4 x i8] } { i32 1, [4 x i8] undef }, align 8 @"test$foo1" = internal unnamed_addr global i32 22, align 4 @.str = private unnamed_addr constant [7 x i8] c"Hello!\00", align 1 diff --git a/test/test_suite/initializer_lists/statics.c3t b/test/test_suite/initializer_lists/statics.c3t index c0710f590..dacb5e0b1 100644 --- a/test/test_suite/initializer_lists/statics.c3t +++ b/test/test_suite/initializer_lists/statics.c3t @@ -35,8 +35,8 @@ fn int main() %Bar = type { i32, i32 } %"Bar[]" = type { ptr, i64 } -@"$ct.statics.Baz" = linkonce constant %.introspect { i8 11, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.statics.Bar" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.statics.Baz" = linkonce global %.introspect { i8 11, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.statics.Bar" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.taddr = private global [1 x %Bar] [%Bar { i32 1, i32 2 }], align 8 @"test$c" = internal unnamed_addr global %"Bar[]" { ptr @.taddr, i64 1 }, align 8 @.str = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1 diff --git a/test/test_suite/initializer_lists/subarrays.c3t b/test/test_suite/initializer_lists/subarrays.c3t index b3bc1869b..9865ccbd6 100644 --- a/test/test_suite/initializer_lists/subarrays.c3t +++ b/test/test_suite/initializer_lists/subarrays.c3t @@ -52,8 +52,8 @@ fn int main() %File = type { ptr } %Baz = type { double } -@"$ct.subarrays.Baz" = linkonce constant %.introspect { i8 11, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.subarrays.Bar" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.subarrays.Baz" = linkonce global %.introspect { i8 11, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.subarrays.Bar" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.taddr = private global [2 x %Bar] [%Bar { i32 3, i32 4 }, %Bar { i32 8, i32 9 }], align 8 @subarrays.arrbar = local_unnamed_addr global %"Bar[]" { ptr @.taddr, i64 2 }, align 8 @.taddr.3 = private global [2 x i32] [i32 1, i32 2], align 4 @@ -68,6 +68,7 @@ fn int main() @.__const = private unnamed_addr constant { i32, [4 x i8] } { i32 1, [4 x i8] undef }, align 8 @.str.9 = private unnamed_addr constant [3 x i8] c"Ok\00", align 1 +; Function Attrs: nounwind define i32 @main() #0 { entry: %w = alloca %Bar, align 4 diff --git a/test/test_suite/macros/userland_bitcast.c3t b/test/test_suite/macros/userland_bitcast.c3t index cbd4075c3..724aaa843 100644 --- a/test/test_suite/macros/userland_bitcast.c3t +++ b/test/test_suite/macros/userland_bitcast.c3t @@ -79,7 +79,7 @@ fn void main() %Foo = type { i16, i8, i8, i16, i16 } -@"$ct.userland_bitcast.Foo" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 5, [0 x i64] zeroinitializer }, align 8 +@"$ct.userland_bitcast.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 5, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [16 x i8] c"%f => %d => %f\0A\00", align 1 @.str.1 = private unnamed_addr constant [18 x i8] c"%e => %llu => %e\0A\00", align 1 diff --git a/test/test_suite/pointers/subarray_variant_to_ptr.c3t b/test/test_suite/pointers/subarray_variant_to_ptr.c3t index 4538809d9..166c4128b 100644 --- a/test/test_suite/pointers/subarray_variant_to_ptr.c3t +++ b/test/test_suite/pointers/subarray_variant_to_ptr.c3t @@ -29,7 +29,7 @@ fn void main() %any = type { ptr, i64 } %"int[]" = type { ptr, i64 } -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 define void @foo.test1(i64 %0, ptr %1) #0 { entry: diff --git a/test/test_suite/statements/custom_foreach_with_ref.c3t b/test/test_suite/statements/custom_foreach_with_ref.c3t index ee211b9a6..db2e53624 100644 --- a/test/test_suite/statements/custom_foreach_with_ref.c3t +++ b/test/test_suite/statements/custom_foreach_with_ref.c3t @@ -85,7 +85,7 @@ fn void main() %Foo = type { [3 x i32] } -@"$ct.foo.Foo" = linkonce constant %.introspect { i8 10, i64 12, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 12, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.str = private unnamed_addr constant [11 x i8] c"getFields\0A\00", align 1 @.str.1 = private unnamed_addr constant [11 x i8] c"Call made\0A\00", align 1 @.__const = private unnamed_addr constant %Foo { [3 x i32] [i32 1, i32 5, i32 7] }, align 4 diff --git a/test/test_suite/statements/foreach_custom_macro.c3t b/test/test_suite/statements/foreach_custom_macro.c3t index fe57d3a2f..7b9bd61a9 100644 --- a/test/test_suite/statements/foreach_custom_macro.c3t +++ b/test/test_suite/statements/foreach_custom_macro.c3t @@ -35,7 +35,7 @@ extern fn int printf(char *fmt, ...); %Foo = type { %"int[]" } %"int[]" = type { ptr, i64 } -@"$ct.foo.Foo" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant [3 x i32] [i32 1, i32 3, i32 10], align 4 @.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index c67f4cd93..2c1b4dd56 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -9,17 +9,14 @@ struct Foo { int x; void* bar; } typedef IntFooMap = HashMap; typedef IntDoubleMap = HashMap; -fn char[] Foo.to_string(Foo* foo, Allocator* allocator = mem::heap()) +fn String Foo.to_string(Foo* foo, Allocator* allocator = mem::heap()) @dynamic { DString s = dstring::new_with_capacity(128, allocator); s.printf("{%s, %p}", foo.x, foo.bar); return s.str(); } -static initialize -{ - io::formatter_register_type(Foo); -} + fn void main() { @@ -55,23 +52,7 @@ fn void main() /* #expect: test.ll -@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @.static_initialize.0, ptr null }] - -define internal void @.static_initialize.0() { -entry: - %0 = load i64, ptr getelementptr inbounds (%"Entry*[]", ptr @std.io.tostring_functions, i32 0, i32 1), align 8 - %not = icmp eq i64 %0, 0 - br i1 %not, label %if.then, label %if.exit - -if.then: ; preds = %entry - %1 = load ptr, ptr @std.core.mem.thread_allocator, align 8 - call void @"std.collections.map$typeid$p$std.io$ToStringFunction$.HashMap.init"(ptr @std.io.tostring_functions, i32 64, float 7.500000e-01, ptr %1) - br label %if.exit - -if.exit: ; preds = %if.then, %entry - %2 = call i8 @"std.collections.map$typeid$p$std.io$ToStringFunction$.HashMap.set"(ptr @std.io.tostring_functions, i64 ptrtoint (ptr @"$ct.test.Foo" to i64), ptr @test.Foo.to_string) - ret void -} +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }] define { ptr, i64 } @test.Foo.to_string(ptr %0, ptr %1) #0 { entry: @@ -102,7 +83,7 @@ entry: ; Function Attrs: nounwind define void @test.main() #0 { entry: - %map = alloca %HashMap.0, align 8 + %map = alloca %HashMap, align 8 %retparam = alloca i64, align 8 %varargslots = alloca [1 x %any], align 16 %literal = alloca %Foo, align 8 @@ -124,7 +105,7 @@ entry: %retparam25 = alloca i64, align 8 %varargslots26 = alloca [1 x %any], align 16 %result = alloca %"Foo[]", align 8 - %map2 = alloca %HashMap.2, align 8 + %map2 = alloca %HashMap.0, align 8 %retparam29 = alloca i64, align 8 %varargslots30 = alloca [1 x %any], align 16 %taddr31 = alloca i8, align 1 @@ -143,14 +124,14 @@ entry: %varargslots52 = alloca [1 x %any], align 16 %indirectarg = alloca %"any[]", align 8 %mark = alloca i64, align 8 - %map3 = alloca %HashMap.2, align 8 + %map3 = alloca %HashMap.0, align 8 %retparam53 = alloca i64, align 8 %varargslots54 = alloca [1 x %any], align 16 %result55 = alloca %"int[]", align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false) %0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, ptr %0) - %1 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 + %1 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %2 = insertvalue %any undef, ptr %1, 0 %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %4 = getelementptr inbounds [1 x %any], ptr %varargslots, i64 0, i64 0 @@ -165,7 +146,7 @@ entry: %9 = getelementptr inbounds { i64, ptr }, ptr %literal, i32 0, i32 1 %hi = load ptr, ptr %9, align 8 %10 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo, ptr %hi) - %11 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 + %11 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %12 = insertvalue %any undef, ptr %11, 0 %13 = insertvalue %any %12, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %14 = getelementptr inbounds [1 x %any], ptr %varargslots2, i64 0, i64 0 @@ -180,7 +161,7 @@ entry: %19 = getelementptr inbounds { i64, ptr }, ptr %literal3, i32 0, i32 1 %hi5 = load ptr, ptr %19, align 8 %20 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo4, ptr %hi5) - %21 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 + %21 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 %22 = insertvalue %any undef, ptr %21, 0 %23 = insertvalue %any %22, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 %24 = getelementptr inbounds [1 x %any], ptr %varargslots7, i64 0, i64 0 @@ -329,3 +310,22 @@ if.exit: ; preds = %noerr_block, %after call void @std.core.mem.allocator.Allocator.reset(ptr %104, i64 %105) ret void } + +define internal void @.static_initialize.0() { +entry: + br label %dtable_check + +dtable_check: ; preds = %dtable_next, %entry + %dtable_ref = phi ptr [ getelementptr inbounds (%.introspect, ptr @"$ct.test.Foo", i32 0, i32 1), %entry ], [ %next_dtable_ref, %dtable_next ] + %dtable_ptr = load ptr, ptr %dtable_ref, align 8 + %0 = icmp eq ptr %dtable_ptr, null + br i1 %0, label %dtable_found, label %dtable_next + +dtable_next: ; preds = %dtable_check + %next_dtable_ref = getelementptr inbounds { ptr, ptr, ptr }, ptr %dtable_ptr, i32 0, i32 2 + br label %dtable_check + +dtable_found: ; preds = %dtable_check + store ptr @"$ct.dyn.test.Foo.to_string", ptr %dtable_ref, align 8 + ret void +} diff --git a/test/test_suite/struct/nested_struct_init.c3t b/test/test_suite/struct/nested_struct_init.c3t index c0cad3dee..fa717fa9d 100644 --- a/test/test_suite/struct/nested_struct_init.c3t +++ b/test/test_suite/struct/nested_struct_init.c3t @@ -38,12 +38,12 @@ fn void main() %Matrix2x2_b = type { %.anon.1 } %.anon.1 = type { [4 x float] } -@"$ct.foo.$anon" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 4, [0 x i64] zeroinitializer }, align 8 -@"$ct.foo.$anon.3" = linkonce constant %.introspect { i8 11, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.foo.Matrix2x2" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 -@"$ct.foo.$anon.6" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 4, [0 x i64] zeroinitializer }, align 8 -@"$ct.foo.$anon.7" = linkonce constant %.introspect { i8 11, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 -@"$ct.foo.Matrix2x2_b" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.$anon" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 4, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.$anon.3" = linkonce global %.introspect { i8 11, ptr null, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.Matrix2x2" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.$anon.6" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 4, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.$anon.7" = linkonce global %.introspect { i8 11, ptr null, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.foo.Matrix2x2_b" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant %Matrix2x2 { %.anon { %.anon.0 { float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00 } } }, align 4 @.__const.8 = private unnamed_addr constant %Matrix2x2_b { %.anon.1 { [4 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00] } }, align 4 @.str = private unnamed_addr constant [13 x i8] c"%f %f %f %f\0A\00", align 1 diff --git a/test/test_suite/struct/struct_as_value.c3t b/test/test_suite/struct/struct_as_value.c3t index f5685bcd7..6aa992353 100644 --- a/test/test_suite/struct/struct_as_value.c3t +++ b/test/test_suite/struct/struct_as_value.c3t @@ -16,7 +16,7 @@ fn Event test(int x) /* #expect: test.ll %Event = type { i32 } -@"$ct.test.Event" = linkonce constant %.introspect { i8 10, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Event" = linkonce global %.introspect { i8 10, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant %Event { i32 1 }, align 4 @.__const.1 = private unnamed_addr constant %Event { i32 2 }, align 4 diff --git a/test/test_suite/struct/struct_codegen.c3t b/test/test_suite/struct/struct_codegen.c3t index c5aa72401..eef4d8bb5 100644 --- a/test/test_suite/struct/struct_codegen.c3t +++ b/test/test_suite/struct/struct_codegen.c3t @@ -16,7 +16,7 @@ fn void test1() %Point = type { i32, i32 } -@"$ct.test.Point" = linkonce constant %.introspect { i8 10, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Point" = linkonce global %.introspect { i8 10, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @.__const = private unnamed_addr constant %Point { i32 5, i32 6 }, align 4 define void @test.test1() #0 { @@ -24,4 +24,4 @@ entry: %p = alloca %Point, align 4 call void @llvm.memcpy.p0.p0.i32(ptr align 4 %p, ptr align 4 @.__const, i32 8, i1 false) ret void -} \ No newline at end of file +} diff --git a/test/test_suite/struct/struct_const_construct_simple.c3t b/test/test_suite/struct/struct_const_construct_simple.c3t index 30ee77aeb..f6e3ea9c0 100644 --- a/test/test_suite/struct/struct_const_construct_simple.c3t +++ b/test/test_suite/struct/struct_const_construct_simple.c3t @@ -20,7 +20,7 @@ Foo foo8 @private = FOO7; /* #expect: structo.ll -@"$ct.structo.Foo" = linkonce constant %.introspect { i8 10, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.structo.Foo" = linkonce global %.introspect { i8 10, ptr null, i64 16, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @structo.x = protected unnamed_addr global i64 16, align 8 @structo.foo1 = protected unnamed_addr global %Foo { i32 1, i64 2 }, align 8 @structo.foo2 = protected unnamed_addr global %Foo { i32 2, i64 0 }, align 8 diff --git a/test/test_suite/variant/variant_assign.c3t b/test/test_suite/variant/variant_assign.c3t index c5a61e84b..1f2203b43 100644 --- a/test/test_suite/variant/variant_assign.c3t +++ b/test/test_suite/variant/variant_assign.c3t @@ -58,9 +58,9 @@ fn int main() /* #expect: foo.ll -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.double" = linkonce constant %.introspect { i8 4, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.bool" = linkonce constant %.introspect { i8 1, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.double" = linkonce global %.introspect { i8 4, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.bool" = linkonce global %.introspect { i8 1, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 define void @foo.test(i64 %0, ptr %1) #0 { entry: diff --git a/test/test_suite/variant/variant_switch.c3t b/test/test_suite/variant/variant_switch.c3t index 9181c3de3..e6b643e57 100644 --- a/test/test_suite/variant/variant_switch.c3t +++ b/test/test_suite/variant/variant_switch.c3t @@ -30,9 +30,9 @@ fn int main() /* #expect: foo.ll -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.double" = linkonce constant %.introspect { i8 4, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.bool" = linkonce constant %.introspect { i8 1, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.double" = linkonce global %.introspect { i8 4, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.bool" = linkonce global %.introspect { i8 1, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 define void @foo.test(i64 %0, ptr %1) #0 { entry: diff --git a/test/test_suite/variant/variant_test.c3t b/test/test_suite/variant/variant_test.c3t index c2561d9ab..7eb7a0ad8 100644 --- a/test/test_suite/variant/variant_test.c3t +++ b/test/test_suite/variant/variant_test.c3t @@ -66,11 +66,11 @@ fn void main() %any = type { ptr, i64 } %"any[]" = type { ptr, i64 } -@"$ct.int" = linkonce constant %.introspect { i8 2, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.double" = linkonce constant %.introspect { i8 4, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.any" = linkonce constant %.introspect { i8 7, i64 16, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.p$int" = linkonce constant %.introspect { i8 19, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 -@"$ct.bool" = linkonce constant %.introspect { i8 1, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.int" = linkonce global %.introspect { i8 2, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.double" = linkonce global %.introspect { i8 4, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.any" = linkonce global %.introspect { i8 7, ptr null, i64 16, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.p$int" = linkonce global %.introspect { i8 19, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.bool" = linkonce global %.introspect { i8 1, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 define void @foo.test(i64 %0, ptr %1) #0 { entry: