From d96624c57859829def65c8ada386928d03704bde Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 29 Dec 2025 17:01:03 +0100 Subject: [PATCH] Decoupled generics (#2695) --- lib/std/collections/list.c3 | 4 +- releasenotes.md | 1 + src/compiler/ast.c | 44 +- src/compiler/compiler.c | 17 +- src/compiler/compiler_internal.h | 80 +++- src/compiler/context.c | 54 +-- src/compiler/copying.c | 38 +- src/compiler/enums.h | 5 +- src/compiler/json_output.c | 43 +- src/compiler/llvm_codegen.c | 10 +- src/compiler/llvm_codegen_internal.h | 1 - src/compiler/module.c | 24 +- src/compiler/parse_expr.c | 2 +- src/compiler/parse_global.c | 268 ++++++++--- src/compiler/parser.c | 5 +- src/compiler/parser_internal.h | 4 +- src/compiler/sema_decls.c | 453 +++++++++--------- src/compiler/sema_expr.c | 37 +- src/compiler/sema_internal.h | 5 +- src/compiler/sema_liveness.c | 2 + src/compiler/sema_name_resolution.c | 109 ++--- src/compiler/sema_passes.c | 63 ++- src/compiler/sema_stmts.c | 1 + src/compiler/sema_types.c | 3 + src/compiler/semantic_analyser.c | 26 +- src/compiler/symtab.c | 1 + src/compiler/types.c | 17 +- src/utils/lib.h | 1 - test/src/test_suite_runner.c3 | 4 + .../enumerations/enum_map_overload.c3t | 8 +- test/test_suite/errors/else_with_void.c3t | 8 +- .../expressions/assign_eval_order.c3t | 6 +- test/test_suite/expressions/maybe_deref.c3t | 4 +- .../expressions/overload_through_overload.c3t | 8 +- test/test_suite/expressions/take_address.c3t | 2 +- test/test_suite/functions/test_regression.c3t | 118 +++-- .../functions/test_regression_mingw.c3t | 140 +++--- .../generic/different_generic_def.c3 | 4 +- test/test_suite/generic/enum_set_test.c3t | 18 +- test/test_suite/generic/generic_distinct.c3t | 4 +- test/test_suite/generic/generic_error_out.c3 | 2 +- test/test_suite/generic/generic_idents.c3t | 46 +- test/test_suite/generic/generic_interface.c3t | 2 +- .../generic/generic_lambda_complex.c3t | 6 +- test/test_suite/generic/generic_lookup.c3 | 6 +- .../generic/generic_merge_constraints.c3 | 20 + test/test_suite/generic/generic_no_arg.c3 | 4 +- test/test_suite/generic/generic_no_params.c3 | 4 +- test/test_suite/generic/generic_num.c3t | 2 +- test/test_suite/generic/generic_over_fn.c3t | 7 +- test/test_suite/generic/generic_recursion.c3t | 4 +- test/test_suite/generic/generic_self_ref.c3 | 2 +- test/test_suite/generic/generic_testcases.c3t | 171 +++++++ test/test_suite/generic/generic_with_enum.c3t | 4 +- .../generic/generic_without_param.c3 | 2 +- test/test_suite/generic/recursive_generic2.c3 | 2 +- .../test_suite/generic/used_without_param2.c3 | 2 +- .../test_suite/generic/used_without_param3.c3 | 2 +- .../initializer_lists/init_any_interface.c3 | 2 +- .../methods/operator_assign_mutate.c3t | 10 +- .../module/module_generic_mixing.c3 | 10 +- .../overloading/set_not_set_overload.c3t | 6 +- test/test_suite/overloading/set_overload.c3t | 6 +- test/test_suite/stdlib/map_linux.c3t | 44 +- test/test_suite/stdlib/map_macos.c3t | 44 +- test/test_suite/stdlib/priorityqueue.c3t | 2 +- test/test_suite/struct/struct_pack_error.c3t | 5 +- .../switch/switch_in_defer_macro.c3t | 12 +- 68 files changed, 1263 insertions(+), 808 deletions(-) create mode 100644 test/test_suite/generic/generic_merge_constraints.c3 create mode 100644 test/test_suite/generic/generic_testcases.c3t diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index 6b5869f3e..47b4061a2 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -461,7 +461,7 @@ macro void List.post_alloc(&self) @private // Functions for equatable types -fn usz? List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) +fn usz? List.index_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE) { foreach (i, v : self) { @@ -470,7 +470,7 @@ fn usz? List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) return NOT_FOUND?; } -fn usz? List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) +fn usz? List.rindex_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE) { foreach_r (i, v : self) { diff --git a/releasenotes.md b/releasenotes.md index 6c8302bd1..12b5b7e9e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -11,6 +11,7 @@ - Testing for the presence of methods at the top level is prohibited previous to method registration. - `$$MASK_TO_INT` and `$$INT_TO_MASK` to create bool masks from integers and back. - Better error messages when slicing a pointer to a slice or vector. #2681 +- Generics using `@generic` rather than module based. ### Fixes - Regression with npot vector in struct triggering an assert #2219. diff --git a/src/compiler/ast.c b/src/compiler/ast.c index ec4debb5d..591e99907 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -129,6 +129,8 @@ const char *decl_to_a_name(Decl *decl) case DECL_FNTYPE: return "a function type"; case DECL_FUNC: return "a function"; case DECL_GROUP: return "group"; + case DECL_GENERIC: return "a generic declaration"; + case DECL_GENERIC_INSTANCE: return "a generic instance"; case DECL_IMPORT: return "an import"; case DECL_LABEL: return "a label"; case DECL_MACRO: return "a macro"; @@ -502,6 +504,29 @@ bool ast_supports_continue(Ast *stmt) return stmt->for_stmt.cond || !stmt->flow.skip_first; } +static void scratch_buffer_append_but_mangle_underscore_dot(const char *name, const char *end, const char *suffix) +{ + char c; + while (name != end && (c = *(name++)) != 0) + { + switch (c) + { + case ':': + ASSERT(name[0] == ':'); + scratch_buffer_append_char('_'); + name++; + break; + case '.': + scratch_buffer_append("__"); + break; + default: + scratch_buffer_append_char(c); + break; + } + } + if (suffix) scratch_buffer_append(suffix); +} + void scratch_buffer_set_extern_decl_name(Decl *decl, bool clear) { if (clear) scratch_buffer_clear(); @@ -536,14 +561,29 @@ void scratch_buffer_set_extern_decl_name(Decl *decl, bool clear) if (module) scratch_buffer_append_module(module, decl->is_export); scratch_buffer_append(decl->is_export ? "__" : "."); const char *name = decl_is_user_defined_type(decl) ? decl->type->name : decl->name; + const char *template_end = NULL; + const char *suffix = NULL; + if (decl->is_templated) + { + template_end = strchr(name, '{'); + suffix = declptr(decl->instance_id)->instance_decl.cname_suffix; + } if (!name) name = "$anon"; if (decl->is_export) { - scratch_buffer_append_but_mangle_underscore_dot(name); + scratch_buffer_append_but_mangle_underscore_dot(name, template_end, suffix); } else { - scratch_buffer_append(name); + if (template_end) + { + scratch_buffer_append_len(name, template_end - name); + } + else + { + scratch_buffer_append(name); + } + if (suffix) scratch_buffer_append(suffix); } if (decl->visibility == VISIBLE_LOCAL) { diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 3b4ad3c88..4610f38e5 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -77,7 +77,6 @@ void compiler_init(BuildOptions *build_options) htable_init(&compiler.context.compiler_defines, 16 * 1024); methodtable_init(&compiler.context.method_extensions, 16 * 1024); compiler.context.module_list = NULL; - compiler.context.generic_module_list = NULL; compiler.context.method_extension_list = NULL; vmem_init(&ast_arena, START_VMEM_SIZE); @@ -1459,7 +1458,7 @@ void compile() core_path->module = kw_std__core; core_path->span = INVALID_SPAN; core_path->len = strlen(kw_std__core); - compiler.context.core_module = compiler_find_or_create_module(core_path, NULL); + compiler.context.core_module = compiler_find_or_create_module(core_path); CompilationUnit *unit = CALLOCS(CompilationUnit); unit->file = source_file_generate("core_internal.c3"); unit->module = compiler.context.core_module; @@ -1613,7 +1612,7 @@ Module *global_context_find_module(const char *name) return htable_get(&compiler.context.modules, (void *)name); } -Module *compiler_find_or_create_module(Path *module_name, const char **parameters) +Module *compiler_find_or_create_module(Path *module_name) { Module *module = global_context_find_module(module_name->module); if (module) return module; @@ -1644,19 +1643,9 @@ Module *compiler_find_or_create_module(Path *module_name, const char **parameter module->short_path = symtab_add(name, len, fnv1a(name, len), &type); } module->stage = ANALYSIS_NOT_BEGUN; - module->parameters = parameters; - module->is_generic = vec_size(parameters) > 0; htable_init(&module->symbols, 0x1000); htable_set(&compiler.context.modules, (void *)module_name->module, module); - if (parameters) - { - vec_add(compiler.context.generic_module_list, module); - } - else - { - vec_add(compiler.context.module_list, module); - } - + vec_add(compiler.context.module_list, module); return module; } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 27a069145..5a3042816 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -615,6 +615,26 @@ typedef struct Attr **attrs; } AttrDecl; +typedef struct +{ + const char **parameters; + AstId contracts; + unsigned id; + Decl **instances; + Decl *owner; + Decl **decls; + Decl **conditional_decls; +} GenericDecl; + +typedef struct +{ + unsigned id; + Decl **templates; + Decl **params; + const char *name_suffix; + const char *cname_suffix; + Decl **generated_decls; +} GenericInstanceDecl; typedef struct { bool is_func : 1; @@ -696,6 +716,8 @@ typedef struct Decl_ bool resolved_attributes : 1; bool allow_deprecated : 1; bool attr_structlike : 1; + bool is_template : 1; + bool is_templated : 1; union { void *backend_ref; @@ -703,11 +725,19 @@ typedef struct Decl_ int tb_register; void *backend_value; void *tb_symbol; - bool in_init; + struct + { + bool in_init; + }; }; AlignSize offset; AlignSize padding; AlignSize alignment; + union + { + DeclId generic_id; + DeclId instance_id; + }; struct CompilationUnit_ *unit; union { @@ -754,6 +784,8 @@ typedef struct Decl_ LabelDecl label; TypeAliasDecl type_alias_decl; VarDecl var; + GenericDecl generic_decl; + GenericInstanceDecl instance_decl; }; } Decl; @@ -1622,26 +1654,21 @@ typedef struct Module_ const char *short_path; // Extname in case a module is renamed externally const char *extname; - - const char **parameters; + Decl **generic_sections; bool is_external : 1; bool is_c_library : 1; bool is_exported : 1; - bool is_generic : 1; bool no_extprefix : 1; AnalysisStage stage : 6; - AstId contracts; HTable symbols; - struct CompilationUnit_ **units; - Module *generic_module; + CompilationUnit **units; Module *parent_module; Module *top_module; Module **sub_modules; Decl **benchmarks; Decl **tests; Decl **lambdas_to_evaluate; - const char *generic_suffix; InliningSpan inlined_at; } Module; @@ -1713,6 +1740,7 @@ typedef struct LexMode mode; } Lexer; + struct CompilationUnit_ { Module *module; @@ -1725,11 +1753,12 @@ struct CompilationUnit_ Decl **lambdas; Decl **enums; Decl **attributes; - Decl **faulttypes; Decl **faults; const char **links; Visibility default_visibility; Attr *if_attr; + Decl *default_generic_section; + Decl **generic_decls; bool export_by_default; bool is_interface_file; bool benchmark_by_default; @@ -1793,8 +1822,6 @@ typedef struct JumpTarget_ AstId defer; } JumpTarget; - - struct SemaContext_ { // Evaluated in this. @@ -1832,10 +1859,8 @@ struct SemaContext_ DynamicScope active_scope; Expr *return_expr; bool is_temp; - struct - { - Module *infer; - } generic; + Decl *generic_infer; + Decl *generic_instance; }; typedef enum @@ -1959,6 +1984,7 @@ typedef struct CopyStruct_ bool single_static; bool copy_in_use; bool is_template; + DeclId instance_id; } CopyStruct; typedef struct @@ -1976,7 +2002,6 @@ typedef struct Module *core_module; CompilationUnit *core_unit; Module **module_list; - Module **generic_module_list; Type **type; const char *lib_dir; const char **sources; @@ -2271,7 +2296,7 @@ void copy_begin(void); void copy_end(void); Expr *copy_expr_single(Expr *source_expr); Decl **copy_decl_list_single(Decl **decl_list); -Decl **copy_decl_list_single_for_unit(Decl **decl_list); +Decl **copy_decl_list_single_for_generic(Decl **decl_list, Decl *generic_instance); Attr **copy_attributes_single(Attr** attr_list); Decl *copy_lambda_deep(Decl *decl); Ast *copy_ast_single(Ast *source_ast); @@ -2327,7 +2352,7 @@ void global_context_add_decl(Decl *type_decl); void linking_add_link(Linking *linker, const char *link); -Module *compiler_find_or_create_module(Path *module_name, const char **parameters); +Module *compiler_find_or_create_module(Path *module_name); Module *global_context_find_module(const char *name); const char *get_object_extension(void); const char *get_exe_extension(void); @@ -2339,7 +2364,7 @@ void unit_register_external_symbol(SemaContext *context, Decl *decl); bool unit_add_import(CompilationUnit *unit, Path *path, bool private_import, bool is_non_recursive); bool unit_add_alias(CompilationUnit *unit, Decl *decl); bool context_set_module_from_filename(ParseContext *context); -bool context_set_module(ParseContext *context, Path *path, const char **generic_parameters); +bool context_set_module(ParseContext *context, Path *path); bool context_is_macro(SemaContext *context); // --- Decl functions @@ -2460,6 +2485,7 @@ bool lexer_next_token(Lexer *lexer); void scratch_buffer_append_module(Module *module, bool is_export); Decl *module_find_symbol(Module *module, const char *symbol); const char *module_create_object_file_name(Module *module); +Decl *module_find_symbol_in_unit(Module *module, CompilationUnit *unit, const char *symbol); bool parse_file(File *file); Decl **parse_include_file(File *file, CompilationUnit *unit); @@ -2481,6 +2507,7 @@ Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, c Decl *sema_decl_stack_resolve_symbol(const char *symbol); void sema_decl_stack_restore(Decl **state); void sema_decl_stack_push(Decl *decl); +Decl *sema_find_generic_instance(SemaContext *context, Module *module, Decl *generic, Decl *instance, const char *name); bool sema_error_failed_cast(SemaContext *context, Expr *expr, Type *from, Type *to); bool sema_add_local(SemaContext *context, Decl *decl); @@ -2499,6 +2526,7 @@ bool sema_cast_const(Expr *expr); bool sema_expr_check_discard(SemaContext *context, Expr *expr); bool sema_analyse_inferred_expr(SemaContext *context, Type *to, Expr *expr, bool *no_match_ref); bool sema_analyse_decl(SemaContext *context, Decl *decl); +void sema_analyse_inner_func_ptr(SemaContext *c, Decl *decl); bool sema_analyse_method_register(SemaContext *context, Decl *method); bool sema_resolve_type_structure(SemaContext *context, Type *type); @@ -3333,19 +3361,19 @@ INLINE bool type_is_user_defined(Type *type) } -static inline Module *type_find_generic(Type *type) +static inline DeclId type_find_generic(Type *type) { Type *canonical = type->canonical; if (canonical != type && type_is_user_defined(canonical)) { - Module *module = canonical->decl->unit->module; - if (module->generic_module) return module; + Decl *decl = canonical->decl; + if (decl->is_templated) return decl->generic_id; } - if (!type_is_user_defined(type)) return NULL; - Module *module = type->decl->unit->module; - if (module->generic_module) return module; - return module->generic_module ? module : NULL; + if (!type_is_user_defined(type)) return 0; + Decl *decl = type->decl; + return decl->is_templated ? decl->generic_id : 0; } + static inline Type *type_flatten_to_int(Type *type) { while (1) diff --git a/src/compiler/context.c b/src/compiler/context.c index 1a063dce0..fba352781 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -14,38 +14,10 @@ CompilationUnit *unit_create(File *file) } -static inline bool create_module_or_check_name(CompilationUnit *unit, Path *module_name, const char **parameters) +static inline bool create_module_or_check_name(CompilationUnit *unit, Path *module_name) { Module *module = unit->module; - if (!module) - { - module = unit->module = compiler_find_or_create_module(module_name, parameters); - if (module->is_generic != (parameters != NULL)) - { - print_error_at(module_name->span, "'%s' is both used as regular and generic module, it can't be both.", - module_name->module); - SEMA_NOTE(module->name, "The definition here is different."); - return false; - } - if (!module->is_generic) goto DONE; - if (vec_size(parameters) != vec_size(module->parameters)) - { - PRINT_ERROR_AT(module_name, "The parameter declarations of the generic module '%s' don't match.", module_name->module); - SEMA_NOTE(module->name, "A different definition can be found here."); - return false; - } - FOREACH_IDX(idx, const char *, name, parameters) - { - bool is_type = str_is_type(name); - if (is_type != str_is_type(module->parameters[idx])) - { - PRINT_ERROR_AT(module_name, "The parameter declarations of the generic module '%s' don't match.", module_name->module); - SEMA_NOTE(module->name, "The other definition is here."); - return false; - } - } - goto DONE; - } + if (!module) module = unit->module = compiler_find_or_create_module(module_name); if (unit->module->name->module != module_name->module) { RETURN_PRINT_ERROR_AT(false, @@ -54,8 +26,6 @@ static inline bool create_module_or_check_name(CompilationUnit *unit, Path *modu module_name->module, module->name->module); } - -DONE:; vec_add(module->units, unit); return true; } @@ -124,14 +94,14 @@ bool context_set_module_from_filename(ParseContext *context) path->span = context->span; path->module = module_name; path->len = scratch_buffer.len; - return create_module_or_check_name(context->unit, path, NULL); + return create_module_or_check_name(context->unit, path); } -bool context_set_module(ParseContext *context, Path *path, const char **generic_parameters) +bool context_set_module(ParseContext *context, Path *path) { if (!check_module_name(path)) return false; - return create_module_or_check_name(context->unit, path, generic_parameters); + return create_module_or_check_name(context->unit, path); } bool context_is_macro(SemaContext *context) @@ -152,6 +122,12 @@ void unit_register_external_symbol(SemaContext *context, Decl *decl) void decl_register(CompilationUnit *unit, Decl *decl) { + if (decl->is_templated) + { + DEBUG_LOG("Registering generic symbol '%s' in %s.", decl->name, unit->module->name->module); + vec_add(declptr(decl->instance_id)->instance_decl.generated_decls, decl); + return; + } DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, unit->module->name->module); if (decl->visibility < VISIBLE_LOCAL) @@ -168,6 +144,8 @@ void decl_register(CompilationUnit *unit, Decl *decl) case DECL_DECLARRAY: case DECL_ENUM_CONSTANT: case DECL_GROUP: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: case DECL_IMPORT: case DECL_LABEL: case DECL_POISONED: @@ -218,9 +196,11 @@ void decl_register(CompilationUnit *unit, Decl *decl) } + void unit_register_global_decl(CompilationUnit *unit, Decl *decl) { - ASSERT(!decl->unit || decl->unit->module->is_generic); + ASSERT_SPAN(decl, !decl->is_template); + ASSERT_SPAN(decl, !decl->unit || decl->is_templated); decl->unit = unit; switch (decl->decl_kind) { @@ -299,6 +279,8 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) case DECL_ENUM_CONSTANT: case DECL_FNTYPE: case DECL_GROUP: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: case DECL_IMPORT: case DECL_LABEL: UNREACHABLE_VOID diff --git a/src/compiler/copying.c b/src/compiler/copying.c index f34d5acf9..9fbc62606 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -913,25 +913,48 @@ Attr **copy_attributes_single(Attr** attr_list) return attrs; } -Decl **copy_decl_list_single_for_unit(Decl **decl_list) +Decl **copy_decl_list_single_for_generic(Decl **decl_list, Decl *generic_instance) { + const char *name_suffix = generic_instance->instance_decl.name_suffix; bool old_is_template = copy_struct.is_template; + bool old_instance = copy_struct.instance_id; copy_struct.is_template = true; + copy_struct.instance_id = declid(generic_instance); copy_begin(); Decl **result = copy_decl_list_macro(decl_list); + FOREACH(Decl *, decl, result) + { + // Only change the name if it's not a macro or a function. + if ((decl->decl_kind != DECL_FUNC && decl->decl_kind != DECL_MACRO) || !decl->func_decl.type_parent) + { + scratch_buffer_clear(); + scratch_buffer_append(decl->name); + scratch_buffer_append(name_suffix); + decl->name = scratch_buffer_interned(); + if (decl_is_user_defined_type(decl)) + { + decl->type->name = decl->name; + } + } + decl->is_templated = true; + decl->instance_id = declid(generic_instance); + } copy_end(); copy_struct.is_template = old_is_template; + copy_struct.instance_id = old_instance; return result; } Decl *copy_lambda_deep(Decl *decl) { bool old_is_template = copy_struct.is_template; + DeclId old_instance = copy_struct.instance_id; copy_struct.is_template = true; copy_begin(); Decl *result = copy_decl(©_struct, decl); copy_end(); copy_struct.is_template = old_is_template; + copy_struct.instance_id = old_instance; return result; } @@ -1028,6 +1051,16 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) if (!decl) return NULL; if (c->single_static && decl_is_resolved_static_var(decl)) return decl; Decl *copy = decl_copy(decl); + ASSERT(!copy->is_template || c->instance_id); + + if (c->instance_id) + { + copy->is_template = false; + copy->is_templated = true; + copy->instance_id = c->instance_id; + + } + copy_reg_ref(c, decl, copy); if (decl->resolved_attributes) { @@ -1043,6 +1076,9 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) break; case DECL_ERASED: break; + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: + UNREACHABLE; case DECL_INTERFACE: copy_decl_type(copy); MACRO_COPY_TYPE_LIST(copy->interfaces); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 02b14e83a..d41f4bff8 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -279,6 +279,7 @@ typedef enum ATTRIBUTE_EXTERN, ATTRIBUTE_FINALIZER, ATTRIBUTE_FORMAT, + ATTRIBUTE_GENERIC, ATTRIBUTE_IF, ATTRIBUTE_INLINE, ATTRIBUTE_INIT, @@ -688,6 +689,8 @@ typedef enum DECL_FNTYPE, DECL_FUNC, DECL_GROUP, + DECL_GENERIC, + DECL_GENERIC_INSTANCE, DECL_IMPORT, DECL_LABEL, DECL_MACRO, @@ -1776,7 +1779,7 @@ typedef enum case DECL_ALIAS: case DECL_CT_ASSERT: case DECL_CT_EXEC: case DECL_FAULT: \ case DECL_CT_ECHO: case DECL_CT_INCLUDE: case DECL_GROUP: \ case DECL_BODYPARAM: case DECL_VAR: case DECL_ENUM_CONSTANT: \ - case DECL_POISONED: case DECL_ALIAS_PATH + case DECL_POISONED: case DECL_ALIAS_PATH: case DECL_GENERIC: case DECL_GENERIC_INSTANCE // -- Expr helper macros #define NON_RUNTIME_EXPR EXPR_POISONED: \ diff --git a/src/compiler/json_output.c b/src/compiler/json_output.c index 229c5aa5e..8bbe0ac72 100644 --- a/src/compiler/json_output.c +++ b/src/compiler/json_output.c @@ -68,13 +68,6 @@ static inline void emit_modules(FILE *file) PRINTF("\t\t\"%s\"", module->name->module); } PRINT("\n\t],\n"); - PRINT("\t\"generic_modules\": [\n"); - FOREACH_IDX(j, Module *, module, compiler.context.generic_module_list) - { - if (j != 0) fputs(",\n", file); - PRINTF("\t\t\"%s\"", module->name->module); - } - fputs("\n\t],\n", file); } static inline const char *decl_type_to_string(Decl *type) @@ -109,6 +102,8 @@ static inline const char *decl_type_to_string(Decl *type) case DECL_LABEL: case DECL_POISONED: case DECL_VAR: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE } UNREACHABLE @@ -410,17 +405,6 @@ static inline void emit_types(FILE *file) } fputs("\n\t],\n", file); - fputs("\t\"generic_types\": [\n", file); - { - bool first = true; - FOREACH_DECL(Decl *type, compiler.context.generic_module_list) - if (!decl_is_user_defined_type(type) && type->decl_kind != DECL_TYPE_ALIAS) continue; - if (decl_is_hidden(type)) continue; - INSERT_COMMA; - emit_type_data(file, module, type); - FOREACH_DECL_END; - } - fputs("\n\t],\n", file); } static inline void emit_globals(FILE *file) @@ -505,29 +489,6 @@ static inline void emit_functions(FILE *file) } fputs("\n\t],\n", file); - fputs("\t\"generic_functions\": [\n", file); - { - bool first = true; - FOREACH_DECL(Decl *func, compiler.context.generic_module_list) - if (func->decl_kind != DECL_FUNC) continue; - if (decl_is_hidden(func)) continue; - INSERT_COMMA; - emit_func_data(file, module, func); - FOREACH_DECL_END; - } - fputs("\n\t],\n", file); - - fputs("\t\"generic_macros\": [\n", file); - { - bool first = true; - FOREACH_DECL(Decl *func, compiler.context.generic_module_list) - if (func->decl_kind != DECL_MACRO) continue; - if (decl_is_hidden(func)) continue; - INSERT_COMMA; - emit_macro_data(file, module, func); - FOREACH_DECL_END; - } - fputs("\n\t],\n", file); } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 43ad99d15..fcc067fa7 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -50,7 +50,6 @@ static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) bool module_should_weaken(Module *module) { - if (module->generic_module) return true; Module *top = module->top_module; return top && (top->name->module == kw_std || top->name->module == kw_libc); } @@ -59,7 +58,6 @@ static void gencontext_init(GenContext *context, Module *module, LLVMContextRef { ASSERT(LLVMIsMultithreaded()); memset(context, 0, sizeof(GenContext)); - context->weaken = module_should_weaken(module); if (shared_context) { @@ -499,7 +497,7 @@ void llvm_set_decl_linkage(GenContext *c, Decl *decl) { bool is_var = decl->decl_kind == DECL_VAR; bool is_weak = decl->is_weak; - bool should_weaken = is_weak || (!decl->is_extern && module_should_weaken(decl->unit->module)); + bool should_weaken = is_weak || (!decl->is_extern && (decl->is_templated || module_should_weaken(decl->unit->module))); LLVMValueRef ref = decl->backend_ref; LLVMValueRef opt_ref = is_var ? decl->var.optional_ref : NULL; bool is_static = is_var && decl->var.is_static; @@ -1407,6 +1405,8 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) case DECL_CT_INCLUDE: case DECL_GROUP: case DECL_INTERFACE: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE; } UNREACHABLE @@ -1416,7 +1416,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMContextRef shared_context) { Path *test_path = path_create_from_string("_$test", 5, INVALID_SPAN); - Module *test_module = compiler_find_or_create_module(test_path, NULL); + Module *test_module = compiler_find_or_create_module(test_path); DebugInfo actual_debug_info = compiler.build.debug_info; compiler.build.debug_info = DEBUG_INFO_NONE; @@ -1485,7 +1485,7 @@ INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMC INLINE GenContext *llvm_gen_benchmarks(Module** modules, unsigned module_count, LLVMContextRef shared_context) { Path *benchmark_path = path_create_from_string("$benchmark", 10, INVALID_SPAN); - Module *benchmark_module = compiler_find_or_create_module(benchmark_path, NULL); + Module *benchmark_module = compiler_find_or_create_module(benchmark_path); DebugInfo actual_debug_info = compiler.build.debug_info; compiler.build.debug_info = DEBUG_INFO_NONE; diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 11b0f4ee9..cdd2466d5 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -83,7 +83,6 @@ typedef struct GenContext_ { bool shared_context; bool in_init_ref; - bool weaken; bool emitting_load_store_check; LLVMModuleRef module; LLVMBuilderRef global_builder; diff --git a/src/compiler/module.c b/src/compiler/module.c index 9df4b6a31..e11758654 100644 --- a/src/compiler/module.c +++ b/src/compiler/module.c @@ -10,27 +10,13 @@ Decl *module_find_symbol(Module *module, const char *symbol) return decl && decl->visibility != VISIBLE_LOCAL ? decl : NULL; } -void scratch_buffer_append_but_mangle_underscore_dot(const char *name) +Decl *module_find_symbol_in_unit(Module *module, CompilationUnit *unit, const char *symbol) { - char c; - while ((c = *(name++)) != 0) - { - switch (c) - { - case ':': - ASSERT(name[0] == ':'); - scratch_buffer_append_char('_'); - name++; - break; - case '.': - scratch_buffer_append("__"); - break; - default: - scratch_buffer_append_char(c); - break; - } - } + Decl *decl = htable_get(&unit->local_symbols, (void*)symbol); + if (decl) return decl; + return module_find_symbol(module, symbol); } + void scratch_buffer_append_module(Module *module, bool is_export) { if (module->extname) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index a4deebc26..5b57e1925 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -414,7 +414,7 @@ static Expr *parse_lambda(ParseContext *c, Expr *left, SourceSpan lhs_span UNUSE sig->params = decls; sig->rtype = return_type ? type_infoid(return_type) : 0; sig->variadic = variadic; - if (!parse_attributes(c, &func->attributes, NULL, NULL, NULL)) return poisoned_expr; + if (!parse_attributes(c, &func->attributes, NULL, NULL, NULL, NULL)) return poisoned_expr; RANGE_EXTEND_PREV(func); if (tok_is(c, TOKEN_IMPLIES)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 896bd8223..9b6635178 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -169,14 +169,14 @@ static inline Path *parse_module_path(ParseContext *c) * * module_params ::= '{' module_param (',' module_param)* '}' */ -static inline bool parse_optional_module_params(ParseContext *c, const char ***tokens_ref) +static inline bool parse_optional_module_params(ParseContext *c, Decl **decl_ref) { - - *tokens_ref = NULL; - + SourceSpan start = c->span; if (!try_consume(c, TOKEN_LBRACE)) return true; if (try_consume(c, TOKEN_RBRACE)) RETURN_PRINT_ERROR_HERE("The generic parameter list cannot be empty, it needs at least one element."); + const char **tokens = NULL; + // No params while (1) { @@ -195,15 +195,94 @@ static inline bool parse_optional_module_params(ParseContext *c, const char ***t default: RETURN_PRINT_ERROR_HERE("Only generic parameters are allowed here as parameters to the module."); } - vec_add(*tokens_ref, symstr(c)); + vec_add(tokens, symstr(c)); advance(c); if (!try_consume(c, TOKEN_COMMA)) { if (!consume(c, TOKEN_RBRACE, "Expected '}'.")) return false; + Decl *decl = decl_new(DECL_GENERIC, "", extend_span_with_token(start, c->prev_span)); + decl->generic_decl.parameters = tokens; + *decl_ref = decl; return true; } } } + +static inline Ast *sema_contract_find_first_non_comment(AstId contracts) +{ + while (contracts) + { + Ast *current = astptr(contracts); + contracts = current->next; + ASSERT(current->ast_kind == AST_CONTRACT); + switch (current->contract_stmt.kind) + { + case CONTRACT_UNKNOWN: + case CONTRACT_PURE: + case CONTRACT_PARAM: + case CONTRACT_OPTIONALS: + case CONTRACT_ENSURE: + case CONTRACT_REQUIRE: + return current; + case CONTRACT_COMMENT: + continue; + } + } + return NULL; +} + +static inline Ast *sema_contract_first_non_comment_require(AstId contracts) +{ + while (contracts) + { + Ast *current = astptr(contracts); + contracts = current->next; + ASSERT(current->ast_kind == AST_CONTRACT); + switch (current->contract_stmt.kind) + { + case CONTRACT_UNKNOWN: + case CONTRACT_PURE: + case CONTRACT_PARAM: + case CONTRACT_OPTIONALS: + case CONTRACT_ENSURE: + return current; + case CONTRACT_REQUIRE: + case CONTRACT_COMMENT: + continue; + } + } + return NULL; +} + +static int generic_id = 1; +static inline void unify_generic_decl(CompilationUnit *unit, Decl *decl) +{ + unsigned params = vec_size(decl->generic_decl.parameters); + FOREACH(Decl *, d, unit->module->generic_sections) + { + unsigned candidate_params = vec_size(d->generic_decl.parameters); + if (candidate_params != params) continue; + for (unsigned i = 0; i < params; i++) + { + if (d->generic_decl.parameters[i] != decl->generic_decl.parameters[i]) + { + goto NEXT; + } + } + decl->generic_decl.id = d->generic_decl.id; + return; +NEXT:; + } + decl->generic_decl.id = generic_id++; +} + +void parse_attach_generics(ParseContext *c, Decl *generic_decl) +{ + vec_add(c->unit->generic_decls, generic_decl); + unify_generic_decl(c->unit, generic_decl); + vec_add(c->unit->module->generic_sections, generic_decl); +} + /** * module ::= MODULE module_path ('{' module_params '}')? (@public|@private|@local|@test|@export|@cname) EOS */ @@ -236,57 +315,64 @@ bool parse_module(ParseContext *c, AstId contracts) path->len = (unsigned)strlen("#invalid"); path->module = "#invalid"; path->span = INVALID_SPAN; - context_set_module(c, path, NULL); + context_set_module(c, path); recover_top_level(c); return false; } // Is this a generic module? - const char **generic_parameters = NULL; - if (!parse_optional_module_params(c, &generic_parameters)) + Decl *generic_decl_old = NULL; + if (!parse_optional_module_params(c, &generic_decl_old)) { - if (!context_set_module(c, path, NULL)) return false; + if (!context_set_module(c, path)) return false; recover_top_level(c); - if (contracts) RETURN_PRINT_ERROR_AT(false, astptr(contracts), "Contracts cannot be use with non-generic modules."); + if (contracts) + { + Ast *first_contract = sema_contract_find_first_non_comment(contracts); + if (first_contract) RETURN_PRINT_ERROR_AT(false, first_contract, "Contracts cannot be use with non-generic modules."); + } return true; } - if (!context_set_module(c, path, generic_parameters)) return false; - if (contracts) + if (!context_set_module(c, path)) return false; + Visibility visibility = VISIBLE_PUBLIC; + Attr** attrs = NULL; + bool is_cond = false; + Decl *generic_decl = NULL; + if (!parse_attributes(c, &attrs, &visibility, NULL, &is_cond, &generic_decl)) return false; + if (generic_decl_old) { - AstId old_contracts = c->unit->module->contracts; - if (old_contracts) + //SEMA_DEPRECATED(generic_decl, "Module-based generics is deprecated, use `@generic` instead."); + if (generic_decl) { - Ast *last = ast_last(astptr(old_contracts)); - last->next = contracts; + SEMA_NOTE(generic_decl_old, "Old generics combined with new will ignore the former."); } else { - c->unit->module->contracts = contracts; - } - while (contracts) - { - Ast *current = astptr(contracts); - contracts = current->next; - ASSERT(current->ast_kind == AST_CONTRACT); - switch (current->contract_stmt.kind) - { - case CONTRACT_UNKNOWN: - case CONTRACT_PURE: - case CONTRACT_PARAM: - case CONTRACT_OPTIONALS: - case CONTRACT_ENSURE: - break; - case CONTRACT_REQUIRE: - case CONTRACT_COMMENT: - continue; - } - RETURN_PRINT_ERROR_AT(false, current, "Invalid constraint - only '@require' is valid for modules."); + generic_decl = generic_decl_old; } } - Visibility visibility = VISIBLE_PUBLIC; - Attr** attrs = NULL; - bool is_cond; - if (!parse_attributes(c, &attrs, &visibility, NULL, &is_cond)) return false; + if (contracts) + { + if (!generic_decl) + { + Ast *first_contract = sema_contract_find_first_non_comment(contracts); + if (first_contract) RETURN_PRINT_ERROR_AT(false, first_contract, "Contracts can only be used with '@generic' modules."); + } + else + { + Ast *first_invalid_contract = sema_contract_first_non_comment_require(contracts); + if (first_invalid_contract) + { + RETURN_PRINT_ERROR_AT(false, first_invalid_contract, "Invalid constraint - only '@require' is valid for '@generic'."); + } + } + } + if (generic_decl) + { + if (contracts) generic_decl->generic_decl.contracts = contracts; + parse_attach_generics(c, generic_decl); + c->unit->default_generic_section = generic_decl; + } FOREACH(Attr *, attr, attrs) { if (attr->is_custom) RETURN_PRINT_ERROR_AT(false, attr, "Custom attributes cannot be used with 'module'."); @@ -884,7 +970,7 @@ Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type) advance(c); bool is_cond; - if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, NULL)) return poisoned_decl; decl->is_cond = is_cond; if (tok_is(c, TOKEN_EQ)) { @@ -965,7 +1051,7 @@ Decl *parse_const_declaration(ParseContext *c, bool is_global, bool is_extern) else { bool is_cond; - if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, NULL)) return poisoned_decl; decl->is_cond = is_cond; } @@ -997,7 +1083,7 @@ Decl *parse_var_decl(ParseContext *c) case TOKEN_IDENT: decl = decl_new_var_current(c, NULL, VARDECL_LOCAL); advance(c); - if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, NULL)) return poisoned_decl; decl->is_cond = is_cond; if (!tok_is(c, TOKEN_EQ)) { @@ -1012,7 +1098,7 @@ Decl *parse_var_decl(ParseContext *c) decl = decl_new_var_current(c, NULL, c->tok == TOKEN_CT_IDENT ? VARDECL_LOCAL_CT : VARDECL_LOCAL_CT_TYPE); advance(c); span = c->span; - if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl; + if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, NULL)) return poisoned_decl; if (is_cond || decl->attributes) { print_error_at(span, "Attributes are not allowed on compile time variables."); @@ -1172,6 +1258,22 @@ bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos) advance(c); Expr **list = NULL; + // Special handling of generic attributes + if (!attr->is_custom && attr->attr_kind == ATTRIBUTE_GENERIC) + { + CONSUME_OR_RET(TOKEN_LPAREN, false); + while (1) + { + Expr *expr = parse_decl_or_expr(c); + vec_add(list, expr); + if (try_consume(c, TOKEN_RPAREN)) break; + CONSUME_OR_RET(TOKEN_COMMA, false); + } + attr->exprs = list; + *attribute_ref = attr; + return true; + } + // Consume the optional (expr | attr_param, ...) if (try_consume(c, TOKEN_LPAREN)) { @@ -1262,14 +1364,29 @@ static bool parse_attributes_for_global(ParseContext *c, Decl *decl) decl->is_export = c->unit->export_by_default; bool is_builtin = false; bool is_cond; - if (!parse_attributes(c, &decl->attributes, &visibility, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond)) return false; + Decl *generics = NULL; + if (!parse_attributes(c, &decl->attributes, &visibility, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, &generics)) return false; + if (generics) + { + parse_attach_generics(c, generics); + } + if (!generics && c->unit->default_generic_section) + { + generics = c->unit->default_generic_section; + } + if (generics) + { + decl->generic_id = declid(generics); + decl->is_template = true; + } decl->is_cond = is_cond; decl->is_autoimport = is_builtin; decl->visibility = visibility; return true; } -static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, bool use_comma) +static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, Decl ** + generic_ref, bool use_comma) { Visibility visibility = -1; // NOLINT if (cond_ref) *cond_ref = false; @@ -1287,6 +1404,39 @@ static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, bool parsed_builtin = false; switch (attr->attr_kind) { + case ATTRIBUTE_GENERIC: + { + if (!generic_ref) RETURN_PRINT_ERROR_AT(false, attr, "'%s' cannot be used here.", attr->name); + if (*generic_ref) RETURN_PRINT_ERROR_AT(false, attr, "Only a single '%s' attribute may be added.", attr->name); + if (vec_size(attr->exprs) < 1) RETURN_PRINT_ERROR_AT(false, attr, "'%s' must have at least one parameter.", attr->name); + *generic_ref = decl_new(DECL_GENERIC, "", attr->span); + const char **names = NULL; + FOREACH(Expr *, expr, attr->exprs) + { + switch (expr->expr_kind) + { + case EXPR_TYPEINFO: + { + TypeInfo *type_info = expr->type_expr; + if (type_info->kind != TYPE_INFO_IDENTIFIER || type_info->subtype != TYPE_COMPRESSED_NONE) break; + if (type_info->unresolved.path) break; + vec_add(names, type_info->unresolved.name); + continue; + } + case EXPR_UNRESOLVED_IDENTIFIER: + { + if (!expr->unresolved_ident_expr.is_const || expr->unresolved_ident_expr.path) break; + vec_add(names, expr->unresolved_ident_expr.ident); + continue; + } + default: + break; + } + RETURN_PRINT_ERROR_AT(false, expr, "Generic parameters must be type or constant identifiers."); + } + (*generic_ref)->generic_decl.parameters = names; + break; + } case ATTRIBUTE_PUBLIC: parsed_visibility = VISIBLE_PUBLIC; break; @@ -1327,7 +1477,7 @@ static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, if (other_attr->name == name) RETURN_PRINT_ERROR_AT(false, attr, "Repeat of attribute '%s' here.", name); } ADD: - vec_add(*attributes_ref, attr); + if (attr->attr_kind != ATTRIBUTE_GENERIC) vec_add(*attributes_ref, attr); if (use_comma && !try_consume(c, TOKEN_COMMA)) break; } return true; @@ -1340,9 +1490,10 @@ ADD: * * @return true if parsing succeeded, false if recovery is needed */ -bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref) +bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, Decl ** + generic_ref) { - return parse_attribute_list(c, attributes_ref, visibility_ref, builtin_ref, cond_ref, false); + return parse_attribute_list(c, attributes_ref, visibility_ref, builtin_ref, cond_ref, generic_ref, false); } /** @@ -1460,7 +1611,7 @@ static inline bool parse_enum_param_decl(ParseContext *c, Decl*** parameters) if (token_is_some_ident(c->tok)) RETURN_PRINT_ERROR_HERE("Expected a name starting with a lower-case letter."); RETURN_PRINT_ERROR_HERE("Expected a member name here."); } - if (!parse_attributes(c, ¶m->attributes, NULL, NULL, NULL)) return false; + if (!parse_attributes(c, ¶m->attributes, NULL, NULL, NULL, NULL)) return false; vec_add(*parameters, param); RANGE_EXTEND_PREV(param); return true; @@ -1707,7 +1858,7 @@ CHECK_ELLIPSIS: Decl *param = decl_new_var(name, span, type, param_kind); param->var.type_info = type ? type_infoid(type) : 0; param->var.self_addr = ref; - if (!parse_attributes(c, ¶m->attributes, NULL, NULL, NULL)) return false; + if (!parse_attributes(c, ¶m->attributes, NULL, NULL, NULL, NULL)) return false; if (!no_name) { if (try_consume(c, TOKEN_EQ)) @@ -1847,7 +1998,7 @@ static bool parse_struct_body(ParseContext *c, Decl *parent) else { bool is_cond; - if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond)) return false; + if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond, NULL)) return false; member->is_cond = true; if (!parse_struct_body(c, member)) return decl_poison(parent); } @@ -1890,7 +2041,7 @@ static bool parse_struct_body(ParseContext *c, Decl *parent) } advance(c); bool is_cond; - if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond)) return false; + if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond, NULL)) return false; member->is_cond = true; if (!try_consume(c, TOKEN_COMMA)) break; if (was_inline) @@ -2040,7 +2191,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) is_consecutive = true; } bool is_cond = false; - if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false; + if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond, NULL)) return false; member_decl->is_cond = is_cond; CONSUME_OR_RET(TOKEN_EOS, false); unsigned index = vec_size(decl->strukt.members); @@ -2061,7 +2212,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) member_decl->var.end = NULL; } bool is_cond = false; - if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false; + if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond, NULL)) return false; member_decl->is_cond = is_cond; CONSUME_EOS_OR_RET(false); if (is_consecutive) @@ -2110,6 +2261,9 @@ static inline Decl *parse_interface_declaration(ParseContext *c) } while (try_consume(c, TOKEN_COMMA)); } decl->interfaces = parents; + + if (!parse_attributes_for_global(c, decl)) return poisoned_decl; + if (!parse_interface_body(c, decl)) return poisoned_decl; return decl; } @@ -2244,7 +2398,7 @@ static inline Decl *parse_alias_type(ParseContext *c, AstId contracts, bool has_ { return poisoned_decl; } - if (!parse_attributes(c, &decl_type->attributes, NULL, NULL, NULL)) return poisoned_decl; + if (!parse_attributes(c, &decl_type->attributes, NULL, NULL, NULL, NULL)) return poisoned_decl; RANGE_EXTEND_PREV(decl_type); RANGE_EXTEND_PREV(decl); CONSUME_EOS_OR_RET(poisoned_decl); @@ -2425,7 +2579,7 @@ static inline Decl *parse_attrdef(ParseContext *c) bool is_cond; bool is_builtin = false; - if (!parse_attribute_list(c, &attributes, NULL, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, true)) return poisoned_decl; + if (!parse_attribute_list(c, &attributes, NULL, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, NULL,true)) return poisoned_decl; decl->attr_decl.attrs = attributes; CONSUME_EOS_OR_RET(poisoned_decl); return decl; diff --git a/src/compiler/parser.c b/src/compiler/parser.c index e6dc14581..3d249e3dd 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -72,13 +72,14 @@ static inline void parse_translation_unit(ParseContext *c) if (!decl) continue; if (decl_ok(decl)) { + Decl *gs = decl->is_template ? declptr(decl->generic_id) : NULL; if (decl->is_cond) { - add_decl_to_list(&c->unit->global_cond_decls, decl); + add_decl_to_list(gs ? &gs->generic_decl.conditional_decls : &c->unit->global_cond_decls, decl); } else { - add_decl_to_list(&c->unit->global_decls, decl); + add_decl_to_list(gs ? &gs->generic_decl.decls : &c->unit->global_decls, decl); } } else diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 26b42ab60..d765a6e3c 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -46,7 +46,8 @@ Ast *parse_short_body(ParseContext *c, TypeInfoId return_type, bool is_regular_f bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos); -bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref); +bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, Decl ** + generic_ref); bool parse_switch_body(ParseContext *c, Ast ***cases, TokenType case_type, TokenType default_type); Expr *parse_ct_expression_list(ParseContext *c, bool allow_decl); @@ -61,6 +62,7 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool vasplat); Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info); +void parse_attach_generics(ParseContext *c, Decl *generic_decl); INLINE void add_decl_to_list(Decl ***list, Decl *decl) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 856866fb8..a3a992856 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -46,7 +46,7 @@ static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceS static inline bool sema_analyse_alias(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl); -static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit); +static CompilationUnit *unit_copy_for_generic(Module *module, CompilationUnit *unit); static inline bool sema_resolve_align_expr(SemaContext *context, Expr *expr, AlignSize *result) { @@ -71,8 +71,6 @@ static inline bool sema_resolve_align_expr(SemaContext *context, Expr *expr, Ali *result = (AlignSize)align; return true; } -static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, SourceSpan from_span); - static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param); static inline bool sema_analyse_enum(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_raw_enum(SemaContext *context, Decl *decl, bool *erase_decl); @@ -192,7 +190,7 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent if (decl->resolve_status == RESOLVE_RUNNING) RETURN_SEMA_ERROR(decl, "Circular dependency resolving member."); // Mark the unit, it should not have been assigned at this point. - ASSERT_SPAN(decl, !decl->unit || decl->unit->module->is_generic || decl->unit == parent->unit); + ASSERT_SPAN(decl, !decl->unit || decl->is_templated || decl->unit == parent->unit); decl->unit = parent->unit; // Pick the domain for attribute analysis. @@ -2613,7 +2611,6 @@ NONE: */ static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl *method) { - CompilationUnit *unit = context->unit; ASSERT(parent_type->canonical == parent_type); const char *name = method->name; @@ -2634,18 +2631,12 @@ static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl if (!decl_ok(other)) return false; if (other) { - if (unit->module->generic_module && other->unit->module->generic_module == unit->module->generic_module && other->unit->module != unit->module) - { - const char *module_name = unit->module->generic_module->name->module; - RETURN_SEMA_ERROR(method, "The same method is generated by multiple instances of '%s': '%s%s' and '%s%s'. " - "You need to use `@if` to restrict it to one of them, or move it out of the generic module entirely.", - module_name, module_name, unit->module->generic_suffix, module_name, other->unit->module->generic_suffix); - } - + // We might sometimes end up adding the same method twice. + if (other == method) return false; SEMA_ERROR(method, "This %s is already defined for '%s'.", method_name_by_decl(method), parent_type->name); SEMA_NOTE(other, "The previous definition was here."); - return false; + return decl_poison(method); } DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method->name); @@ -3104,6 +3095,7 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ [ATTRIBUTE_EXTERN] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, [ATTRIBUTE_FINALIZER] = ATTR_FUNC, [ATTRIBUTE_FORMAT] = ATTR_FUNC | ATTR_MACRO | ATTR_FNTYPE, + [ATTRIBUTE_GENERIC] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | ATTR_UNION | ATTR_STRUCT | ATTR_INTERFACE | ATTR_ALIAS, [ATTRIBUTE_IF] = (AttributeDomain)~(ATTR_CALL | ATTR_PARAM), [ATTRIBUTE_INIT] = ATTR_FUNC, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, @@ -3298,9 +3290,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ case ATTRIBUTE_WASM: if (args > 2) RETURN_SEMA_ERROR(attr->exprs[2], "Too many arguments to '@wasm', expected 0, 1 or 2 arguments"); decl->is_export = true; - if (context->unit->module->is_generic) + if (decl->is_templated) { - RETURN_SEMA_ERROR(attr, "'@wasm' is not allowed in generic modules."); + RETURN_SEMA_ERROR(attr, "'@wasm' is not allowed on generic declarations."); } if (args == 0) return true; if (decl->has_extname) @@ -3332,9 +3324,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ decl->has_extname = true; return true; case ATTRIBUTE_EXPORT: - if (context->unit->module->is_generic) + if (decl->is_templated) { - RETURN_SEMA_ERROR(attr, "'@export' is not allowed in generic modules."); + RETURN_SEMA_ERROR(attr, "'@export' is not allowed on generic declarations."); } if (expr) { @@ -3358,6 +3350,8 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ case ATTRIBUTE_NOALIAS: decl->var.no_alias = true; return true; + case ATTRIBUTE_GENERIC: + UNREACHABLE; case ATTRIBUTE_IF: if (!expr) RETURN_SEMA_ERROR(attr, "'@if' requires a boolean argument."); if (!sema_analyse_expr_rvalue(context, expr)) return false; @@ -3446,9 +3440,9 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ case ATTRIBUTE_SECTION: case ATTRIBUTE_CNAME: case ATTRIBUTE_EXTERN: - if (context->unit->module->is_generic) + if (decl->is_templated) { - RETURN_SEMA_ERROR(attr, "'%s' attributes are not allowed in generic modules.", attr->name); + RETURN_SEMA_ERROR(attr, "'%s' attributes are not allowed for generic declarations.", attr->name); } if (!expr) { @@ -4928,15 +4922,15 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *c if (is_static) context->call_env.kind = CALL_ENV_FUNCTION_STATIC; decl->in_init = true; - Module *generic = type_find_generic(decl->type); + Decl *generic = declptrzero(type_find_generic(decl->type)); if (generic) { - Module *temp = context->generic.infer; - context->generic.infer = generic; + Decl *temp = context->generic_infer; + context->generic_infer = generic; generic = temp; } success = sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, false, true, check_defined); - context->generic.infer = generic; + context->generic_infer = generic; if (!success && check_defined) return false; decl->in_init = false; context->call_env.kind = env_kind; @@ -4991,83 +4985,6 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local, bool *c return success; } - -static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) -{ - CompilationUnit *copy = unit_create(unit->file); - copy->imports = copy_decl_list_single(unit->imports); - copy->module_aliases = copy_decl_list_single(unit->module_aliases); - copy->public_imports = NULL; - if (unit->public_imports) - { - FOREACH(Decl *, import, copy->imports) - { - if (import->import.import_private_as_public) vec_add(copy->public_imports, import); - } - } - copy->global_decls = copy_decl_list_single_for_unit(unit->global_decls); - copy->global_cond_decls = copy_decl_list_single_for_unit(unit->global_cond_decls); - copy->module = module; - ASSERT(!unit->functions && !unit->macro_methods && !unit->methods && !unit->enums && !unit->ct_includes && !unit->types); - return copy; -} - -static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, SourceSpan from_span) -{ - unsigned decls = 0; - Decl* params_decls[MAX_PARAMS]; - unsigned count = vec_size(module->parameters); - for (unsigned i = 0; i < count; i++) - { - const char *param_name = module->parameters[i]; - bool is_value = str_is_valid_constant(param_name); - Expr *param = params[i]; - if (param->expr_kind != EXPR_TYPEINFO) - { - if (!is_value) RETURN_NULL_SEMA_ERROR(param, "Expected a type, not a value."); - Decl *decl = decl_new_var(param_name, param->span, NULL, VARDECL_CONST); - decl->var.init_expr = param; - decl->type = param->type; - decl->resolve_status = RESOLVE_NOT_DONE; - params_decls[decls++] = decl; - continue; - } - if (is_value) RETURN_NULL_SEMA_ERROR(param, "Expected a value, not a type."); - TypeInfo *type_info = param->type_expr; - if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return NULL; - Decl *decl = decl_new_with_type(param_name, params[i]->span, DECL_TYPE_ALIAS); - decl->resolve_status = RESOLVE_DONE; - ASSERT(type_info->resolve_status == RESOLVE_DONE); - decl->type_alias_decl.type_info = type_info; - decl->type->name = decl->name; - decl->type->canonical = type_info->type->canonical; - params_decls[decls++] = decl; - } - - Module *new_module = compiler_find_or_create_module(path, NULL); - new_module->is_generic = false; - new_module->generic_module = module; - FOREACH(CompilationUnit *, unit, module->units) - { - vec_add(new_module->units, unit_copy(new_module, unit)); - } - CompilationUnit *first_context = new_module->units[0]; - for (unsigned i = 0; i < decls; i++) - { - vec_add(first_context->global_decls, params_decls[i]); - } - - if (module->contracts) - { - copy_begin(); - new_module->contracts = astid(copy_ast_macro(astptr(module->contracts))); - copy_end(); - } - new_module->inlined_at = (InliningSpan) { .span = from_span, .prev = copy_inlining_span(context->inlined_at) }; - - return new_module; -} - static inline void mangle_type_param(Type *type, bool mangled) { type = type->canonical; @@ -5078,81 +4995,23 @@ static inline void mangle_type_param(Type *type, bool mangled) else { scratch_buffer_append(type->name); - if (type_is_user_defined(type) && type->decl->unit->module->generic_suffix) - { - scratch_buffer_append(type->decl->unit->module->generic_suffix); - } } } -static bool sema_generate_parameterized_name_to_scratch(SemaContext *context, Module *module, Expr **params, - bool mangled, bool *was_recursive_ref) + +static bool sema_generate_parameter_suffix_to_scratch(SemaContext *context, Expr **params, bool mangled, bool *was_recursive_ref) { - // First resolve - FOREACH_IDX(i, Expr *, param, params) - { - if (param->expr_kind == EXPR_TYPEINFO) - { - TypeInfo *type_info = param->type_expr; - if (was_recursive_ref && type_info->kind == TYPE_INFO_GENERIC) *was_recursive_ref = true; - if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; - Type *type = type_info->type->canonical; - if (type->type_kind == TYPE_OPTIONAL) RETURN_SEMA_ERROR(type_info, "Expected a non-optional type."); - switch (sema_resolve_storage_type(context, type)) - { - case STORAGE_ERROR: - return false; - case STORAGE_NORMAL: - case STORAGE_VOID: - break; - case STORAGE_WILDCARD: - RETURN_SEMA_ERROR(type_info, "The type is undefined and cannot be used as a parameter type."); - case STORAGE_COMPILE_TIME: - RETURN_SEMA_ERROR(type_info, "Expected a runtime type but it was %s.", type_invalid_storage_type_name(type)); - case STORAGE_UNKNOWN: - RETURN_SEMA_ERROR(type_info, - "%s doesn't have a well defined size and cannot be used as a parameter type.", - type_quoted_error_string(type)); - } - if (type_is_func_ptr(type)) - { - if (!sema_resolve_type_decl(context, type->pointer)) return false; - } - } - else - { - if (!sema_analyse_ct_expr(context, param)) return false; - Type *type = param->type->canonical; - if (type->type_kind == TYPE_TYPEDEF) type = type_flatten(type); - - bool is_enum_or_fault = type_kind_is_enum_or_fault(type->type_kind); - if (!type_is_integer_or_bool_kind(type) && !is_enum_or_fault) - { - RETURN_SEMA_ERROR(param, "Only integer, bool, fault and enum values may be generic arguments."); - } - ASSERT(expr_is_const(param)); - } - } - scratch_buffer_clear(); - if (mangled) - { - scratch_buffer_append_module(module, true); - scratch_buffer_append("$"); - } - else - { - scratch_buffer_append("{"); - } + scratch_buffer_append(mangled ? "$" : "{"); FOREACH_IDX(j, Expr *, param, params) { if (j != 0) { scratch_buffer_append(mangled ? "$" : ", "); } - if (param->expr_kind == EXPR_TYPEINFO) + if (expr_is_const_typeid(param)) { - mangle_type_param(param->type_expr->type, mangled); + mangle_type_param(param->const_expr.typeid, mangled); } else { @@ -5226,14 +5085,13 @@ static bool sema_generate_parameterized_name_to_scratch(SemaContext *context, Mo return true; } -static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module, SourceSpan param_span, SourceSpan invocation_span) +static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module, Decl *instance, AstId contracts, SourceSpan param_span, SourceSpan invocation_span) { - ASSERT(module->contracts); - AstId contract = module->contracts; - while (contract) + ASSERT(contracts); + while (contracts) { - Ast *ast = astptr(contract); - contract = ast->next; + Ast *ast = astptr(contracts); + contracts = ast->next; ASSERT_SPAN(ast, ast->ast_kind == AST_CONTRACT); SemaContext temp_context; if (ast->contract_stmt.kind == CONTRACT_COMMENT) continue; @@ -5241,7 +5099,9 @@ static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module InliningSpan *old_span = c->inlined_at; InliningSpan new_span = { .prev = old_span, .span = invocation_span }; SemaContext *new_context = context_transform_for_eval(c, &temp_context, module->units[0]); + Decl *old_generic_instance = new_context->generic_instance; InliningSpan *old_inlined_at = new_context->inlined_at; + new_context->generic_instance = instance; new_context->inlined_at = &new_span; FOREACH(Expr *, expr, ast->contract_stmt.contract.decl_exprs->expression_list) @@ -5266,83 +5126,239 @@ static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module return false; } new_context->inlined_at = old_inlined_at; + new_context->generic_instance = old_generic_instance; sema_context_destroy(&temp_context); } return true; } -Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, +Decl *sema_analyse_parameterized_identifier(SemaContext *context, Path *decl_path, const char *name, SourceSpan span, Expr **params, bool *was_recursive_ref, SourceSpan invocation_span) { - Decl *alias = sema_resolve_parameterized_symbol(c, name, decl_path, span); + Decl *alias = sema_resolve_parameterized_symbol(context, name, decl_path, span); if (!alias) return poisoned_decl; - Module *module = alias->unit->module; - - unsigned parameter_count = vec_size(module->parameters); + ASSERT_AT(invocation_span, alias->is_template && alias->generic_id); + Decl *generic = declptr(alias->generic_id); + unsigned parameter_count = vec_size(generic->generic_decl.parameters); ASSERT(parameter_count > 0); unsigned count = vec_size(params); if (parameter_count != count) { if (!count) { - sema_error_at(c, invocation_span, - "'%s' must be instantiatied with generic module arguments inside the '{}', did you forget them?", name, (int)parameter_count); + sema_error_at(context, invocation_span, + "'%s' requires generic arguments inside the '{}', did you forget them?", name, (int)parameter_count); } else { - sema_error_at(c, extend_span_with_token(params[0]->span, vectail(params)->span), - "The generic module expected %d argument(s), but you supplied %d, did you make a mistake?", + // 'Foo' expects 2 generic arguments, but you supplied 1, did you make a mistake? + sema_error_at(context, extend_span_with_token(params[0]->span, vectail(params)->span), + "'%s' expects %d %s, but you supplied %d, did you make a mistake?", + name, parameter_count, + parameter_count == 1 ? "argument" : "arguments", vec_size(params)); } return poisoned_decl; } - if (!sema_generate_parameterized_name_to_scratch(c, module, params, true, was_recursive_ref)) return poisoned_decl; - const char *path_string = scratch_buffer_interned(); - Module *instantiated_module = global_context_find_module(path_string); - - AnalysisStage stage = c->unit->module->generic_module - ? c->unit->module->stage - : c->unit->module->stage - 1; - bool instantiation = false; - if (!instantiated_module) + // First resolve + FOREACH_IDX(i, Expr *, param, params) { - instantiation = true; - Path *path = CALLOCS(Path); - path->module = path_string; - path->span = module->name->span; - path->len = scratch_buffer.len; - instantiated_module = module_instantiate_generic(c, module, path, params, invocation_span); - if (!instantiated_module) return poisoned_decl; - if (!sema_generate_parameterized_name_to_scratch(c, module, params, false, NULL)) return poisoned_decl; - instantiated_module->generic_suffix = scratch_buffer_copy(); - sema_analyze_stage(instantiated_module, stage > ANALYSIS_POST_REGISTER ? ANALYSIS_POST_REGISTER : stage); - } - if (compiler.context.errors_found) return poisoned_decl; - Decl *symbol = module_find_symbol(instantiated_module, name); - if (!symbol) - { - sema_error_at(c, span, "The generic module '%s' does not have '%s' for this parameterization.", module->name->module, name); - return poisoned_decl; - } - if (instantiation) - { - if (instantiated_module->contracts) + if (!sema_analyse_expr_rvalue(context, param)) return poisoned_decl; + const char *param_name = generic->generic_decl.parameters[i]; + bool is_type = str_is_type(param_name); + if (expr_is_const_typeid(param)) { - SourceSpan param_span = extend_span_with_token(params[0]->span, params[parameter_count - 1]->span); - if (!sema_analyse_generic_module_contracts(c, instantiated_module, param_span, invocation_span)) + if (!is_type) { + SEMA_ERROR(param, "Expected a value, not a type, for parameter '%s'.", param_name); return poisoned_decl; } + Type *type = param->const_expr.typeid; + if (type_is_func_ptr(type)) + { + if (!sema_resolve_type_decl(context, type->pointer)) return poisoned_decl; + } } - if (stage > ANALYSIS_POST_REGISTER) + else { - sema_analyze_stage(instantiated_module, stage); - if (compiler.context.errors_found) return poisoned_decl; + if (is_type) + { + SEMA_ERROR(param, "Expected a type, not a value, for parameter '%s'.", param_name); + return poisoned_decl; + } + if (!sema_analyse_ct_expr(context, param)) return poisoned_decl; + Type *type = param->type->canonical; + if (type->type_kind == TYPE_TYPEDEF) type = type_flatten(type); + + bool is_enum_or_fault = type_kind_is_enum_or_fault(type->type_kind); + if (!type_is_integer_or_bool_kind(type) && !is_enum_or_fault) + { + SEMA_ERROR(param, "Only integer, bool, fault and enum values may be generic arguments."); + return poisoned_decl; + } + ASSERT(expr_is_const(param)); } } + Module *module = alias->unit->module; + if (!sema_generate_parameter_suffix_to_scratch(context, params, true, was_recursive_ref)) return poisoned_decl; + const char *suffix = scratch_buffer_interned(); + Decl *instance = NULL; + unsigned id = generic->generic_decl.id; + FOREACH(Decl *, g, alias->unit->module->generic_sections) + { + FOREACH (Decl *, candidate, g->generic_decl.instances) + { + if (candidate->name == suffix) + { + instance = candidate; + goto FOUND; + } + } + } +FOUND:; + bool instantiation = instance == NULL; + if (!instance) + { + DEBUG_LOG("Generate generic instance %s", suffix); + if (compiler.context.errors_found) return poisoned_decl; + instance = decl_new(DECL_GENERIC_INSTANCE, suffix, generic->span); + FOREACH_IDX(i, const char *, param_name, generic->generic_decl.parameters) + { + Decl *decl; + Expr *param = params[i]; + ASSERT_SPAN(param, param->expr_kind == EXPR_CONST); + if (expr_is_const_typeid(param)) + { + decl = decl_new_var(param_name, param->span, NULL, VARDECL_PARAM_CT_TYPE); + } + else + { + ASSERT(param->expr_kind == EXPR_CONST); + decl = decl_new_var(param_name, param->span, NULL, VARDECL_CONST); + } + decl->var.init_expr = param; + decl->unit = alias->unit; + decl->resolve_status = RESOLVE_DONE; + decl->type = param->type; + vec_add(instance->instance_decl.params, decl); + } + instance->unit = alias->unit; + Decl **copied = NULL; + Decl **copied_cond = NULL; + if (!sema_generate_parameter_suffix_to_scratch(context, params, false, was_recursive_ref)) return poisoned_decl; + instance->instance_decl.name_suffix = scratch_buffer_copy(); + if (!sema_generate_parameter_suffix_to_scratch(context, params, true, was_recursive_ref)) return poisoned_decl; + instance->instance_decl.cname_suffix = scratch_buffer_copy(); + instance->instance_decl.id = id; + FOREACH(Decl *, g, module->generic_sections) + { + if (g->generic_decl.id == generic->generic_decl.id) + { + vec_add(instance->instance_decl.templates, g); + Decl **decls = g->generic_decl.decls; + Decl **cond_decls = g->generic_decl.conditional_decls; + decls = decls ? copy_decl_list_single_for_generic(decls, instance) : NULL; + cond_decls = cond_decls ? copy_decl_list_single_for_generic(cond_decls, instance) : NULL; + FOREACH(Decl *, d, decls) vec_add(copied, d); + FOREACH(Decl *, d, cond_decls) vec_add(copied_cond, d); + } + } + vec_add(generic->generic_decl.instances, instance); + AnalysisStage stage = module->stage; + ASSERT(stage > ANALYSIS_IMPORTS); + // Add all the normal top level declarations + FOREACH(Decl *, decl, copied) unit_register_global_decl(decl->unit, decl); + // Add all the conditional declarations + FOREACH(Decl *, decl, copied_cond) + { + unit_register_optional_global_decl(decl->unit, decl); + if (decl->decl_kind != DECL_ERASED) vec_add(copied, decl); + } + if (compiler.context.errors_found) return poisoned_decl; + + // Check contracts + FOREACH(Decl *, decl, module->generic_sections) + { + if (decl->generic_decl.id == generic->generic_decl.id) + { + AstId contracts = decl->generic_decl.contracts; + if (!contracts) continue; + copy_begin(); + contracts = astid(copy_ast_macro(astptr(contracts))); + copy_end(); + SourceSpan param_span = extend_span_with_token(params[0]->span, params[parameter_count - 1]->span); + if (!sema_analyse_generic_module_contracts(context, module, instance, contracts, param_span, invocation_span)) + { + return poisoned_decl; + } + } + } + + if (stage < ANALYSIS_METHODS_REGISTER) goto EXIT; + FOREACH(Decl *, decl, copied) + { + if (decl->decl_kind != DECL_FUNC && decl->decl_kind != DECL_MACRO) continue; + if (!decl->func_decl.type_parent) continue; + SemaContext gen_context; + sema_context_init(&gen_context, decl->unit); + gen_context.generic_instance = instance; + if (sema_analyse_method_register(&gen_context, decl)) + { + if (decl->decl_kind == DECL_MACRO) + { + vec_add(decl->unit->macro_methods, decl); + } + else + { + vec_add(decl->unit->methods, decl); + } + } + } + if (stage < ANALYSIS_DECLS) goto EXIT; + FOREACH(Decl *, decl, copied) + { + SemaContext context_gen; + sema_context_init(&context_gen, decl->unit); + context_gen.active_scope = (DynamicScope) { .depth = 0 }; + sema_analyse_decl(&context_gen, decl); + context_gen.generic_instance = instance; + sema_analyse_inner_func_ptr(&context_gen, decl); + FOREACH(TypeInfo *, info, decl->unit->check_type_variable_array) + { + sema_check_type_variable_array(&context_gen, info); + } + sema_context_destroy(&context_gen); + } + if (stage < ANALYSIS_FUNCTIONS) goto EXIT; + if (compiler.context.errors_found) return poisoned_decl; + FOREACH(Decl *, decl, copied) + { + SemaContext context_gen; + switch (decl->decl_kind) + { + case DECL_FUNC: + sema_context_init(&context_gen, decl->unit); + analyse_func_body(&context_gen, decl); + sema_context_destroy(&context_gen); + break; + default: + break; + } + } + ASSERT(stage < ANALYSIS_INTERFACE); +EXIT:; + if (compiler.context.errors_found) return poisoned_decl; + } + Decl *symbol = sema_find_generic_instance(context, module, generic, instance, name); + if (!symbol) + { + sema_error_at(context, span, "The generic '%s' does not exist for this parameterization.", name); + return poisoned_decl; + } + CompilationUnit *unit = symbol->unit; if (unit->module->stage < ANALYSIS_POST_REGISTER) { @@ -5350,9 +5366,9 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con } else { - if (!sema_analyse_decl(c, symbol)) return poisoned_decl; + if (!sema_analyse_decl(context, symbol)) return poisoned_decl; } - unit_register_external_symbol(c, symbol); + unit_register_external_symbol(context, symbol); return symbol; } @@ -5514,6 +5530,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) SemaContext temp_context; context = context_transform_for_eval(context, &temp_context, decl->unit); + context->generic_instance = decl->is_templated ? declptr(decl->instance_id) : NULL; if (decl->resolve_status == RESOLVE_RUNNING) { SEMA_ERROR(decl, decl->name @@ -5522,7 +5539,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) goto FAILED; } decl->resolve_status = RESOLVE_RUNNING; - ASSERT(decl->unit); + ASSERT_SPAN(decl, decl->unit); bool erase_decl = false; switch (decl->decl_kind) { @@ -5583,6 +5600,8 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_IMPORT: case DECL_LABEL: case DECL_POISONED: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE } if (erase_decl) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index b4d7d8ce7..58c0f063e 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -211,12 +211,13 @@ typedef struct { bool in_no_eval; InliningSpan *old_inlining; + Decl *generic_instance; } ContextSwitchState; static inline ContextSwitchState context_switch_state_push(SemaContext *context, SemaContext *new_context) { - ContextSwitchState state = { .in_no_eval = new_context->call_env.in_no_eval, .old_inlining = new_context->inlined_at }; + ContextSwitchState state = { .in_no_eval = new_context->call_env.in_no_eval, .old_inlining = new_context->inlined_at, .generic_instance = new_context->generic_instance }; new_context->call_env.in_no_eval = context->call_env.in_no_eval; new_context->inlined_at = context->inlined_at; return state; @@ -226,6 +227,7 @@ static inline void context_switch_stat_pop(SemaContext *swapped, ContextSwitchSt { swapped->call_env.in_no_eval = state.in_no_eval; swapped->inlined_at = state.old_inlining; + swapped->generic_instance = state.generic_instance; } Expr *sema_enter_inline_member(Expr *parent, CanonicalType *type) @@ -963,6 +965,8 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_GROUP: case DECL_IMPORT: case DECL_TYPE_ALIAS: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE case DECL_POISONED: return expr_poison(expr); @@ -1690,6 +1694,7 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee, SemaContext default_context; SemaContext *new_context = context_transform_for_eval(context, &default_context, param->unit); ContextSwitchState switch_state = context_switch_state_push(context, new_context); + new_context->generic_instance = declptrzero(param->generic_id); InliningSpan inlined_at; if (!new_context->inlined_at) { @@ -2824,6 +2829,7 @@ static inline bool sema_expr_setup_call_analysis(SemaContext *context, CalledDec Decl *decl = callee->definition; sema_context_init(macro_context, decl->unit); + macro_context->generic_instance = declptrzero(decl->instance_id); macro_context->compilation_unit = context->unit; macro_context->macro_call_depth = context->macro_call_depth + 1; macro_context->call_env = context->call_env; @@ -7110,7 +7116,6 @@ static bool sema_expr_fold_hash(SemaContext *context, Expr *expr) } expr_replace(expr, copy_expr_single(decl->var.init_expr)); if (is_ref) expr_set_to_ref(expr); - REMINDER("Handle inlining at"); } return expr_ok(expr); } @@ -7141,20 +7146,20 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef bool is_unwrapped_var = expr_is_unwrapped_ident(left); - Module *generic = type_find_generic(left->type); + Decl *generic = declptrzero(type_find_generic(left->type)); if (generic) { - Module *temp = context->generic.infer; - context->generic.infer = generic; + Decl *temp = context->generic_infer; + context->generic_infer = generic; generic = temp; } // 3. Evaluate right side to required type. if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, is_unwrapped_var, false, failed_ref)) { - context->generic.infer = generic; + context->generic_infer = generic; return false; } - context->generic.infer = generic; + context->generic_infer = generic; if (is_unwrapped_var && IS_OPTIONAL(right)) { sema_rewrap_var(context, left->ident_expr); @@ -10401,6 +10406,8 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr, case DECL_LABEL: case DECL_MACRO: case DECL_POISONED: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: RETURN_SEMA_ERROR(main_var, "'%s' does not have an external name.", decl->name); case DECL_FAULT: goto RETURN_CT; @@ -11856,10 +11863,10 @@ bool sema_analyse_cond_expr(SemaContext *context, Expr *expr, CondResult *result static inline bool sema_analyse_expr_rhs_param(SemaContext *context, Type *to, Expr *expr, bool *no_match_ref) { - Module *generic_module = context->generic.infer; - context->generic.infer = to ? type_find_generic(to) : NULL; + Decl *generic = context->generic_infer; + context->generic_infer = declptrzero(to ? type_find_generic(to) : 0); bool success = sema_analyse_expr_rhs(context, to, expr, true, no_match_ref, false); - context->generic.infer = generic_module; + context->generic_infer = generic; return success; } @@ -11872,13 +11879,13 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo } else { - Module *generic; - if (to && (generic = type_find_generic(to)) != NULL) + Decl *generic; + if (to && (generic = declptrzero(type_find_generic(to))) != NULL) { - Module *generic_module = context->generic.infer; - context->generic.infer = generic; + Decl *generic_prev = context->generic_infer; + context->generic_infer = generic; bool success = sema_analyse_inferred_expr(context, to, expr, no_match_ref); - context->generic.infer = generic_module; + context->generic_infer = generic_prev; if (!success) return false; } else diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 816b7c294..f363112fa 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -127,7 +127,7 @@ void cast_promote_vararg(Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); void cast_to_int_to_max_bit_size(Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); bool sema_decl_if_cond(SemaContext *context, Decl *decl); -Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, +Decl *sema_analyse_parameterized_identifier(SemaContext *context, Path *decl_path, const char *name, SourceSpan span, Expr **params, bool *was_recursive_ref, SourceSpan invocation_span); bool sema_parameterized_type_is_found(SemaContext *context, Path *decl_path, const char *name, SourceSpan span); Type *sema_resolve_type_get_func(Signature *signature, CallABI abi); @@ -138,6 +138,9 @@ bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr * bool sema_analyse_const_enum_constant_val(SemaContext *context, Decl *decl); bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, bool *erase_decl); +void unit_register_optional_global_decl(CompilationUnit *unit, Decl *decl); +bool analyse_func_body(SemaContext *context, Decl *decl); + INLINE bool sema_analyse_stmt_chain(SemaContext *context, Ast *statement) { if (!ast_ok(statement)) return false; diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index bb48b5a43..6a7a09eeb 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -634,6 +634,8 @@ RETRY: case DECL_IMPORT: case DECL_LABEL: case DECL_MACRO: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE_VOID case DECL_FNTYPE: sema_trace_func_liveness(&decl->fntype_decl.signature); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 22c13905c..1ef5c2415 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -9,6 +9,26 @@ typedef long long int ssize_t; #endif +Decl *sema_find_generic_instance(SemaContext *context, Module *module, Decl *generic, Decl *instance, const char *name) +{ + scratch_buffer_clear(); + scratch_buffer_append(name); + scratch_buffer_append(instance->instance_decl.name_suffix); + const char *full_name = scratch_buffer_interned(); + FOREACH(Decl *, g, module->generic_sections) + { + if (g->generic_decl.id == generic->generic_decl.id) + { + FOREACH(Decl *, decl, instance->instance_decl.generated_decls) + { + if (decl->name != full_name) continue; + if (decl->visibility == VISIBLE_LOCAL && decl->unit != context->unit) continue; + return decl; + } + } + } + return NULL; +} INLINE bool sema_resolve_ambiguity(SemaContext *context, Decl **current, Decl *candidate, Decl **ambiguous); static inline bool matches_subpath(Path *path_to_check, Path *path_to_find) { @@ -209,8 +229,6 @@ Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *in INLINE bool module_inclusion_match(Module *a, Module *b) { Module *temp; - while ((temp = a->generic_module)) a = temp; - while ((temp = b->generic_module)) b = temp; // Quick check if (a->top_module != b->top_module) return false; @@ -230,9 +248,6 @@ static bool decl_is_visible(CompilationUnit *unit, Decl *decl) // 1. Same module as unit -> ok if (module == unit->module) return true; - // This never matches a generic module. - if (module->generic_module) return false; - // 2. Skip to imports for private decls bool is_public = decl->visibility == VISIBLE_PUBLIC; if (!is_public) goto IMPORT_CHECK; @@ -290,7 +305,7 @@ static BoolErr sema_first_is_preferred(SemaContext *context, Decl *decl, Decl *d { // (1) and (2) if ((decl->is_autoimport && !decl2->is_autoimport) - || (decl2->unit->module->generic_module && !decl->unit->module->generic_module)) return BOOL_TRUE; + || (decl2->is_template && !decl->is_template)) return BOOL_TRUE; // Now analyse common parents, we only check if this is a redef. if (decl2->decl_kind != DECL_TYPE_ALIAS || !decl2->is_weak) return BOOL_FALSE; @@ -441,14 +456,6 @@ static bool sema_resolve_path_symbol(SemaContext *context, NameResolve *name_res } const char *symbol = name_resolve->symbol; - // 0. std module special handling. - if (path->module == compiler.context.std_module_path.module) - { - name_resolve->path_found = &compiler.context.std_module; - name_resolve->found = module_find_symbol(&compiler.context.std_module, symbol); - return true; - } - CompilationUnit *unit = context->unit; // 1. Do we match our own path? @@ -527,7 +534,7 @@ static bool sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_ CompilationUnit *unit = context->unit; // Search in file scope. - if ((decl = htable_get(&unit->local_symbols, (void *) symbol))) + if ((decl = htable_get(&unit->local_symbols, (void *)symbol))) { name_resolve->found = decl; return true; @@ -661,12 +668,10 @@ static void sema_report_error_on_decl(SemaContext *context, NameResolve *name_re Decl *decl = name_resolve->maybe_decl; Module *module = decl->unit->module; const char *maybe_name = decl_to_name(decl); - Module *generic_module = module->generic_module; - if (!generic_module && module->is_generic) generic_module = module; - const char *module_name = generic_module ? generic_module->name->module : module->name->module; - if (decl_is_visible(context->unit, decl) && generic_module && !name_resolve->is_parameterized) + const char *module_name = module->name->module; + if (decl_is_visible(context->unit, decl) && decl->is_template && !name_resolve->is_parameterized) { - sema_error_at(context, span, "Did you mean the %s '%s' in the generic module %s? If so, use '%s{...}' instead.", + sema_error_at(context, span, "Did you mean the %s generic '%s' in module %s? If so, use '%s{...}' instead.", maybe_name, symbol, module_name, symbol); return; } @@ -770,29 +775,13 @@ INLINE Module *sema_module_matches_path(SemaContext *context, Module *module, Pa return NULL; } -INLINE Module *sema_find_module_for_path(SemaContext *context, Path *path, bool prefer_generic) +INLINE Module *sema_find_module_for_path(SemaContext *context, Path *path) { - if (prefer_generic) - { - FOREACH(Module *, module, compiler.context.generic_module_list) - { - Module *module_match = sema_module_matches_path(context, module, path); - if (module_match) return module_match; - } - } FOREACH(Module *, module, compiler.context.module_list) { Module *module_match = sema_module_matches_path(context, module, path); if (module_match) return module_match; } - if (!prefer_generic) - { - FOREACH(Module *, module, compiler.context.generic_module_list) - { - Module *module_match = sema_module_matches_path(context, module, path); - if (module_match) return module_match; - } - } return NULL; } @@ -807,7 +796,7 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r if (!name_resolve->found && !name_resolve->maybe_decl && !name_resolve->private_decl && !name_resolve->path_found) { if (name_resolve->suppress_error) return true; - Module *module_with_path = sema_find_module_for_path(context, name_resolve->path, name_resolve->is_parameterized); + Module *module_with_path = sema_find_module_for_path(context, name_resolve->path); if (module_with_path) { RETURN_SEMA_ERROR(name_resolve, "'%s' could not be found in %s.", name_resolve->symbol, module_with_path->name->module); @@ -817,6 +806,14 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r } else { + // In a generic context, match parameters first of all. + if (context->generic_instance) + { + FOREACH(Decl *, param, context->generic_instance->instance_decl.params) + { + if (param->name == name_resolve->symbol) return name_resolve->found = param, true; + } + } if (!sema_resolve_no_path_symbol(context, name_resolve)) return false; } @@ -834,22 +831,31 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r return false; } if (found->decl_kind != DECL_ALIAS) unit_register_external_symbol(context, found); - if (found->unit->module->is_generic) + if (found->is_template) { if (name_resolve->is_parameterized) return true; - if (context->generic.infer) + Decl *generic = declptr(found->generic_id); + if (context->generic_instance) { - if (context->generic.infer->generic_module != found->unit->module) + if (generic->generic_decl.id == context->generic_instance->instance_decl.id) + { + Decl *candidate = sema_find_generic_instance(context, found->unit->module, generic, context->generic_instance, found->name); + if (candidate) return name_resolve->found = candidate, true; + } + } + else if (context->generic_infer) + { + if (context->generic_infer->instance_decl.id != generic->generic_decl.id) { if (name_resolve->suppress_error) return name_resolve->found = NULL, true; - RETURN_SEMA_ERROR_AT(name_resolve->span, "Found '%s' in the generic module '%s', but it doesn't match the inferred module '%s'.", found->name, found->unit->module->name->module, - context->generic.infer->generic_module->name->module); + RETURN_SEMA_ERROR_AT(name_resolve->span, "Found '%s' in the module '%s', but it doesn't match the inferred generic '%s'.", found->name, found->unit->module->name->module, + context->generic_infer->unit->module->name->module); } - Decl *symbol = module_find_symbol(context->generic.infer, found->name); - if (symbol) return name_resolve->found = symbol, true; + Decl *candidate = sema_find_generic_instance(context, found->unit->module, generic, context->generic_infer, found->name); + if (candidate) return name_resolve->found = candidate, true; } if (name_resolve->suppress_error) return name_resolve->found = NULL, true; - RETURN_SEMA_ERROR_AT(name_resolve->span, "'%s' is defined in the generic module '%s', did you forget the parameters '{ ... }'?", found->name, found->unit->module->name->module); + RETURN_SEMA_ERROR_AT(name_resolve->span, "'%s' is a generic %s, did you forget the parameters '{ ... }'?", found->name, decl_to_name(found)); } else { @@ -857,17 +863,6 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r if (name_resolve->suppress_error) return name_resolve->found = NULL, true; bool is_type = decl_is_user_defined_type(name_resolve->found); const char *str = is_type ? "type" : "symbol"; - if (found->unit->module->generic_suffix) - { - if (found->unit->module->generic_module == context->unit->module->generic_module) - { - Decl *decl = module_find_symbol(context->unit->module->generic_module, found->name); - ASSERT(decl); - name_resolve->found = decl; - return true; - } - RETURN_SEMA_ERROR_AT(name_resolve->span, "This %s was matched as '%s%s' in module '%s%s', so parameterizing it further doesn't work.", str, found->name, found->unit->module->generic_suffix, found->unit->module->generic_module->name->module, found->unit->module->generic_suffix); - } if (is_type) { RETURN_SEMA_ERROR_AT(name_resolve->span, "'%s' is not a generic type. Did you want an initializer " diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index e431b8c28..a295d6209 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -53,7 +53,7 @@ void sema_analyse_pass_module_hierarchy(Module *module) // No match, so we create a synthetic module. Path *path = path_create_from_string(slice.ptr, slice.len, module->name->span); DEBUG_LOG("Creating parent module for %s: %s", module->name->module, path->module); - Module *parent_module = compiler_find_or_create_module(path, NULL); + Module *parent_module = compiler_find_or_create_module(path); module->parent_module = parent_module; vec_add(parent_module->sub_modules, module); sema_analyze_stage(parent_module, ANALYSIS_MODULE_HIERARCHY); @@ -101,7 +101,7 @@ void sema_analysis_pass_process_imports(Module *module) if (unit->if_attr) { unit->error_import = import; - import_module = compiler_find_or_create_module(path, NULL); + import_module = compiler_find_or_create_module(path); goto FOUND_MODULE; } PRINT_ERROR_AT(import, "No module named '%s' could be found, did you type the name right?", path->module); @@ -134,7 +134,7 @@ NEXT:; if (unit->if_attr) { unit->error_import = alias_module; - import_module = compiler_find_or_create_module(path, NULL); + import_module = compiler_find_or_create_module(path); goto FOUND_ALIAS; } PRINT_ERROR_AT(path, "No module named '%s' could be found, did you type the name right?", path->module); @@ -163,6 +163,22 @@ FOUND_ALIAS: DEBUG_LOG("Pass finished processing %d import(s) with %d error(s).", total_import_count, compiler.context.errors_found); } +void unit_register_optional_global_decl(CompilationUnit *unit, Decl *decl) +{ + SemaContext context; + sema_context_init(&context, unit); + if (decl->is_templated) context.generic_instance = declptr(decl->instance_id); + if (sema_decl_if_cond(&context, decl)) + { + unit_register_global_decl(unit, decl); + } + else + { + decl->decl_kind = DECL_ERASED; + } + sema_context_destroy(&context); + +} INLINE void register_global_decls(CompilationUnit *unit, Decl **decls) { FOREACH(Decl *, decl, decls) @@ -443,14 +459,17 @@ void sema_analysis_pass_process_methods(Module *module, bool process_generic) vec_add(unit->generic_methods_to_register, method); continue; } - sema_analyse_method_register(&context, method); - if (method->decl_kind == DECL_MACRO) + if (sema_analyse_method_register(&context, method)) { - vec_add(unit->macro_methods, method); - } - else - { - vec_add(unit->methods, method); + + if (method->decl_kind == DECL_MACRO) + { + vec_add(unit->macro_methods, method); + } + else + { + vec_add(unit->methods, method); + } } } sema_context_destroy(&context); @@ -467,6 +486,7 @@ void sema_analysis_pass_process_methods(Module *module, bool process_generic) DEBUG_LOG("Pass finished with %d error(s).", compiler.context.errors_found); } + void sema_analysis_pass_register_conditional_units(Module *module) { DEBUG_LOG("Pass: Register conditional units for %s", module->name->module); @@ -497,6 +517,15 @@ void sema_analysis_pass_register_conditional_units(Module *module) { vec_resize(unit->global_decls, 0); vec_resize(unit->global_cond_decls, 0); + FOREACH(Decl *, decl, module->generic_sections) + { + if (decl->unit == unit) + { + vec_resize(decl->generic_decl.conditional_decls, 0); + vec_resize(decl->generic_decl.decls, 0); + } + } + vec_resize(unit->ct_includes, 0); continue; } CHECK_LINK: @@ -565,13 +594,7 @@ RETRY:; Decl **decls = unit->global_cond_decls; FOREACH(Decl *, decl, decls) { - SemaContext context; - sema_context_init(&context, unit); - if (sema_decl_if_cond(&context, decl)) - { - unit_register_global_decl(unit, decl); - } - sema_context_destroy(&context); + unit_register_optional_global_decl(unit, decl); } vec_resize(decls, 0); RETRY_INCLUDES: @@ -630,7 +653,7 @@ void sema_analysis_pass_ct_echo(Module *module) DEBUG_LOG("Pass finished with %d error(s).", compiler.context.errors_found); } -static inline bool analyse_func_body(SemaContext *context, Decl *decl) +bool analyse_func_body(SemaContext *context, Decl *decl) { if (!decl->func_decl.body) return true; if (decl->is_extern) @@ -648,7 +671,7 @@ static inline bool analyse_func_body(SemaContext *context, Decl *decl) return true; } -INLINE void sema_analyse_inner_func_ptr(SemaContext *c, Decl *decl) +void sema_analyse_inner_func_ptr(SemaContext *c, Decl *decl) { Type *inner; switch (decl->decl_kind) @@ -776,7 +799,7 @@ static bool sema_check_interface(SemaContext *context, Decl *decl, TypeInfo *int } return true; } -static inline bool sema_check_interfaces(SemaContext *context, Decl *decl) +bool sema_check_interfaces(SemaContext *context, Decl *decl) { Decl **store = sema_decl_stack_store(); sema_add_methods_to_decl_stack(context, decl); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 6e7b2636d..61651d8ed 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -3410,6 +3410,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) // Stop if it's already poisoned. if (!decl_ok(func)) return false; + context->generic_instance = func->is_templated ? declptr(func->instance_id) : NULL; // Check the signature here we test for variadic raw, since we don't support it. Signature *signature = &func->func_decl.signature; if (signature->variadic == VARIADIC_RAW) diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index f03343139..42d746a9e 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -286,6 +286,8 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_CT_INCLUDE: case DECL_DECLARRAY: case DECL_GROUP: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: UNREACHABLE } UNREACHABLE @@ -460,6 +462,7 @@ INLINE bool sema_resolve_generic_type(SemaContext *context, TypeInfo *type_info) compiler.generic_depth--; if (!decl_ok(type)) return false; if (!sema_analyse_decl(context, type)) return false; + ASSERT_SPAN(type_info, type != NULL); type_info->type = type->type; if (!was_recursive) return true; if (!context->current_macro && (context->call_env.kind == CALL_ENV_FUNCTION || context->call_env.kind == CALL_ENV_FUNCTION_STATIC) diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index af50a7469..6b62ee01e 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -238,6 +238,7 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) FOREACH(Decl *, decl, decls) { decl->unit = unit; + ASSERT(decl->is_template && decl->generic_id); switch (decl->decl_kind) { case DECL_ALIAS_PATH: @@ -258,6 +259,8 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) case DECL_ENUM_CONSTANT: case DECL_ERASED: case DECL_GROUP: + case DECL_GENERIC: + case DECL_GENERIC_INSTANCE: case DECL_LABEL: UNREACHABLE_VOID case DECL_ALIAS: @@ -278,6 +281,8 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) break; } htable_set(&unit->module->symbols, (void *)decl->name, decl); + htable_set(&unit->local_symbols, (void *)decl->name, decl); + if (decl->visibility == VISIBLE_PUBLIC) { global_context_add_decl(decl); @@ -285,25 +290,20 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) } } -static void analyze_generic_module(Module *module) +static void analyze_generics(Module *module) { - ASSERT(module->parameters && module->is_generic); FOREACH(CompilationUnit *, unit, module->units) { - register_generic_decls(unit, unit->global_decls); - register_generic_decls(unit, unit->global_cond_decls); + FOREACH(Decl *, section, unit->generic_decls) + { + register_generic_decls(unit, section->generic_decl.decls); + register_generic_decls(unit, section->generic_decl.conditional_decls); + } } } static void sema_analyze_to_stage(AnalysisStage stage) { - if (stage <= ANALYSIS_MODULE_TOP) - { - FOREACH(Module *, module, compiler.context.generic_module_list) - { - sema_analyze_stage(module, stage); - } - } FOREACH(Module *, module, compiler.context.module_list) { sema_analyze_stage(module, stage); @@ -485,9 +485,9 @@ void sema_analysis_run(void) // We parse the generic modules, just by storing the decls. - FOREACH(Module *, module, compiler.context.generic_module_list) + FOREACH(Module *, module, compiler.context.module_list) { - analyze_generic_module(module); + analyze_generics(module); } for (AnalysisStage stage = ANALYSIS_NOT_BEGUN + 1; stage <= ANALYSIS_LAST; stage++) diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index bcc4bd296..dd873f9f6 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -353,6 +353,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_EXTERN] = KW_DEF("@extern"); attribute_list[ATTRIBUTE_FINALIZER] = KW_DEF("@finalizer"); attribute_list[ATTRIBUTE_FORMAT] = KW_DEF("@format"); + attribute_list[ATTRIBUTE_GENERIC] = KW_DEF("@generic"); attribute_list[ATTRIBUTE_IF] = KW_DEF("@if"); attribute_list[ATTRIBUTE_INIT] = KW_DEF("@init"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("@inline"); diff --git a/src/compiler/types.c b/src/compiler/types.c index 50b30aa7e..ec36de9bf 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -285,12 +285,10 @@ const char *type_to_error_string(Type *type) case TYPE_INTERFACE: { Decl *decl = type->decl; - const char *suffix = decl->unit->module->generic_suffix; - if (!suffix && !type_is_inner_type(type)) return type->name; + if (!type_is_inner_type(type)) return type->name; scratch_buffer_clear(); type_add_parent_to_scratch(decl); scratch_buffer_append(decl->name ? decl->name : "(anon)"); - if (suffix) scratch_buffer_append(suffix); return scratch_buffer_copy(); } case TYPE_FUNC_PTR: @@ -354,23 +352,14 @@ static const char *type_to_error_string_with_path(Type *type) case TYPE_INTERFACE: { Decl *decl = type->decl; - const char *suffix = decl->unit->module->generic_suffix; scratch_buffer_clear(); - if (decl->unit->module->generic_module) - { - scratch_buffer_append(decl->unit->module->generic_module->name->module); - } - else - { - scratch_buffer_append(decl->unit->module->name->module); - } + scratch_buffer_append(decl->unit->module->name->module); scratch_buffer_append("::"); - if (suffix || type_is_inner_type(type)) + if (type_is_inner_type(type)) { type_add_parent_to_scratch(decl); } scratch_buffer_append(decl->name); - if (suffix) scratch_buffer_append(suffix); return scratch_buffer_copy(); } case TYPE_FUNC_PTR: diff --git a/src/utils/lib.h b/src/utils/lib.h index 80add0754..d49203041 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -182,7 +182,6 @@ void scratch_buffer_append_remove_space(const char *start, int len); void scratch_buffer_append_signed_int(int64_t i); void scratch_buffer_append_double(double d); void scratch_buffer_append_shell_escaped(const char *string); -void scratch_buffer_append_but_mangle_underscore_dot(const char *name); void scratch_buffer_append_cmd_argument(const char *string); UNUSED void scratch_buffer_append_unsigned_int(uint64_t i); void scratch_buffer_printf(const char *format, ...); diff --git a/test/src/test_suite_runner.c3 b/test/src/test_suite_runner.c3 index 0fa99c768..a30ddbf5e 100644 --- a/test/src/test_suite_runner.c3 +++ b/test/src/test_suite_runner.c3 @@ -452,7 +452,11 @@ fn bool test_file(Path file_path, TestOutput* output, usz index) update_status(index, file_path, output); bool single; int thread_id = context.thread_id_counter.add(1); + $if env::WIN32: Path test_dir = context.start_cwd.tappend(string::tformat("_c3test_%s", thread_id))!!; + $else + Path test_dir = path::temp_directory(tmem).tappend(string::tformat("_c3test_%s", thread_id))!!; + $endif (void)path::rmtree(test_dir); defer (void)path::rmtree(test_dir); if (@catch(path::mkdir(test_dir))) diff --git a/test/test_suite/enumerations/enum_map_overload.c3t b/test/test_suite/enumerations/enum_map_overload.c3t index 287bd15e3..bf10fe708 100644 --- a/test/test_suite/enumerations/enum_map_overload.c3t +++ b/test/test_suite/enumerations/enum_map_overload.c3t @@ -23,9 +23,9 @@ entry: %.anon = alloca i32, align 4 %i = alloca i32, align 4 %v = alloca i32, align 4 - call void @"std_collections_enummap$test.Foo$int$.EnumMap.set"(ptr @test.map, i32 0, i32 123) #1 - call void @"std_collections_enummap$test.Foo$int$.EnumMap.set"(ptr @test.map, i32 1, i32 -1) #1 - %0 = call i64 @"std_collections_enummap$test.Foo$int$.EnumMap.len"(ptr @test.map) #1 + call void @"std.collections.enummap.EnumMap$test.Foo$int$.set"(ptr @test.map, i32 0, i32 123) #1 + call void @"std.collections.enummap.EnumMap$test.Foo$int$.set"(ptr @test.map, i32 1, i32 -1) #1 + %0 = call i64 @"std.collections.enummap.EnumMap$test.Foo$int$.len"(ptr @test.map) #1 %trunc = trunc i64 %0 to i32 store i32 0, ptr %.anon, align 4 br label %loop.cond @@ -39,7 +39,7 @@ loop.body: ; preds = %loop.cond %2 = load i32, ptr %.anon, align 4 store i32 %2, ptr %i, align 4 %3 = load i32, ptr %.anon, align 4 - %4 = call i32 @"std_collections_enummap$test.Foo$int$.EnumMap.get"(ptr @test.map, i32 %3) #1 + %4 = call i32 @"std.collections.enummap.EnumMap$test.Foo$int$.get"(ptr @test.map, i32 %3) #1 store i32 %4, ptr %v, align 4 %5 = load i32, ptr %.anon, align 4 %addnsw = add nsw i32 %5, 1 diff --git a/test/test_suite/errors/else_with_void.c3t b/test/test_suite/errors/else_with_void.c3t index 881830ed8..1a6f440a4 100644 --- a/test/test_suite/errors/else_with_void.c3t +++ b/test/test_suite/errors/else_with_void.c3t @@ -25,17 +25,17 @@ fn void main() define void @func_point_test.main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %"HashMap{String, FuncPoint}", align 8 %test_index = alloca %"char[]", align 8 %retparam = alloca ptr, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false) - %0 = call ptr @"std_collections_map$String$fn$void$String$$.HashMap.tinit"(ptr %map, i32 16, float 7.500000e-01) - %1 = call i8 @"std_collections_map$String$fn$void$String$$.HashMap.set"(ptr %map, ptr @.str, i64 4, ptr @func_point_test.test) + %0 = call ptr @"std.collections.map.HashMap$String$fn$void$String$$.tinit"(ptr %map, i32 16, float 7.500000e-01) + %1 = call i8 @"std.collections.map.HashMap$String$fn$void$String$$.set"(ptr %map, ptr @.str, i64 4, ptr @func_point_test.test) store %"char[]" { ptr @.str.1, i64 5 }, ptr %test_index, align 8 %lo = load ptr, ptr %test_index, align 8 %ptradd = getelementptr inbounds i8, ptr %test_index, i64 8 %hi = load i64, ptr %ptradd, align 8 - %2 = call i64 @"std_collections_map$String$fn$void$String$$.HashMap.get"(ptr %retparam, ptr %map, ptr %lo, i64 %hi) + %2 = call i64 @"std.collections.map.HashMap$String$fn$void$String$$.get"(ptr %retparam, ptr %map, ptr %lo, i64 %hi) %not_err = icmp eq i64 %2, 0 %3 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %3, label %after_check, label %else_block diff --git a/test/test_suite/expressions/assign_eval_order.c3t b/test/test_suite/expressions/assign_eval_order.c3t index 39328a75d..3d7e0ae1c 100644 --- a/test/test_suite/expressions/assign_eval_order.c3t +++ b/test/test_suite/expressions/assign_eval_order.c3t @@ -37,16 +37,16 @@ fn int main(String[] args) define i32 @test.main(ptr %0, i64 %1) #0 { entry: %args = alloca %"char[][]", align 8 - %sretparam = alloca %List, align 8 + %sretparam = alloca %"List{int}", align 8 %result = alloca [2 x i32], align 4 store ptr %0, ptr %args, align 8 %ptradd = getelementptr inbounds i8, ptr %args, i64 8 store i64 %1, ptr %ptradd, align 8 %2 = call i32 @test.abc() - call void @test.l(ptr sret(%List) align 8 %sretparam) + call void @test.l(ptr sret(%"List{int}") align 8 %sretparam) %3 = call i32 @test.idx() %sext = sext i32 %3 to i64 - call void @"std_collections_list$int$.List.set"(ptr %sretparam, i64 %sext, i32 %2) + call void @"std.collections.list.List$int$.set"(ptr %sretparam, i64 %sext, i32 %2) %4 = call i32 @test.abc() %5 = call i64 @test.a() store i64 %5, ptr %result, align 4 diff --git a/test/test_suite/expressions/maybe_deref.c3t b/test/test_suite/expressions/maybe_deref.c3t index df31d4c24..fc51c94ea 100644 --- a/test/test_suite/expressions/maybe_deref.c3t +++ b/test/test_suite/expressions/maybe_deref.c3t @@ -33,7 +33,7 @@ entry: %x2 = alloca i8, align 1 %x = alloca ptr, align 8 %x3 = alloca i8, align 1 - %l = alloca %List, align 8 + %l = alloca %"List{int}", align 8 %lptr = alloca ptr, align 8 %x4 = alloca i32, align 4 %self = alloca ptr, align 8 @@ -56,7 +56,7 @@ entry: %7 = load i8, ptr %6, align 1 store i8 %7, ptr %x3, align 1 call void @llvm.memset.p0.i64(ptr align 8 %l, i8 0, i64 40, i1 false) - call void @"std_collections_list$int$.List.push"(ptr %l, i32 42) #4 + call void @"std.collections.list.List$int$.push"(ptr %l, i32 42) #4 store ptr %l, ptr %lptr, align 8 %8 = load ptr, ptr %lptr, align 8 store ptr %8, ptr %self, align 8 diff --git a/test/test_suite/expressions/overload_through_overload.c3t b/test/test_suite/expressions/overload_through_overload.c3t index 124029108..87d38d8ee 100644 --- a/test/test_suite/expressions/overload_through_overload.c3t +++ b/test/test_suite/expressions/overload_through_overload.c3t @@ -58,7 +58,7 @@ entry: %y = alloca [2 x %Abc], align 4 %result = alloca %Abc, align 4 %result1 = alloca %Abc, align 4 - %l = alloca %List, align 8 + %l = alloca %"List{Abc}", align 8 %literal = alloca %Abc, align 4 %result3 = alloca %Abc, align 4 %result4 = alloca %Abc, align 4 @@ -97,14 +97,14 @@ entry: call void @llvm.memset.p0.i64(ptr align 8 %l, i8 0, i64 40, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %literal, ptr align 4 @.__const.2, i32 4, i1 false) %10 = load i32, ptr %literal, align 4 - call void @"std_collections_list$test.Abc$.List.push"(ptr %l, i32 %10) #4 - %11 = call ptr @"std_collections_list$test.Abc$.List.get_ref"(ptr %l, i64 0) #4 + call void @"std.collections.list.List$test.Abc$.push"(ptr %l, i32 %10) #4 + %11 = call ptr @"std.collections.list.List$test.Abc$.get_ref"(ptr %l, i64 0) #4 %12 = load i32, ptr %11, align 4 %13 = load i32, ptr %b, align 4 %14 = call i32 @test.Abc.add(i32 %12, i32 %13) store i32 %14, ptr %result3, align 4 call void @llvm.memcpy.p0.p0.i32(ptr align 4 %11, ptr align 4 %result3, i32 4, i1 false) - %15 = call ptr @"std_collections_list$test.Abc$.List.get_ref"(ptr %l, i64 0) #4 + %15 = call ptr @"std.collections.list.List$test.Abc$.get_ref"(ptr %l, i64 0) #4 %16 = load i32, ptr %b, align 4 %17 = call i32 @test.Abc.sub_self(ptr %15, i32 %16) store i32 %17, ptr %result4, align 4 diff --git a/test/test_suite/expressions/take_address.c3t b/test/test_suite/expressions/take_address.c3t index 710736dbc..f6a94baec 100644 --- a/test/test_suite/expressions/take_address.c3t +++ b/test/test_suite/expressions/take_address.c3t @@ -25,6 +25,6 @@ fn void test(Foo x) {} define void @testing.main() #0 { entry: - call void @blurb.test(ptr @"foo$int$.hello") + call void @blurb.test(ptr @"foo.hello$int$") ret void } diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 6ee9bb1e8..e01aff0ef 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -228,13 +228,13 @@ fn Type getValue(Blob blob) /* #expect: test.ll -%Blob = type { i32 } -%Blob.0 = type { double } +%"Blob{int}" = type { i32 } +%"Blob{double}" = type { double } %Bobo = type { i16, float, i16, i16, float, i16 } %"int[]" = type { ptr, i64 } -%LinkedList = type { %any, i64, ptr, ptr } +%"LinkedList{int}" = type { %any, i64, ptr, ptr } %any = type { ptr, i64 } -%List = type { i64, i64, %any, ptr } +%"List{int}" = type { i64, i64, %any, ptr } %Foo = type { i32, i32 } @"$ct.test.Bobo" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 20, i64 0, i64 6, [0 x i64] zeroinitializer }, align 8 @@ -263,17 +263,14 @@ entry: ret i32 %add } -; Function Attrs: declare i32 @printf(ptr, ...) #0 -; Function Attrs: define void @test.helloWorld() #0 { entry: %0 = call i32 (ptr, ...) @printf(ptr @.str) ret void } -; Function Attrs: define i32 @test.test_static() #0 { entry: %0 = load i32, ptr @test_static.x, align 4 @@ -285,7 +282,6 @@ entry: ret i32 %3 } -; Function Attrs: define i32 @test.helo(double %0, ptr byval(%Bobo) align 8 %1) #0 { entry: %de = alloca [3 x i32], align 4 @@ -298,7 +294,6 @@ entry: ret i32 1 } -; Function Attrs: define i32 @test.test1(i32 %0, i32 %1) #0 { entry: %a = alloca i32, align 4 @@ -318,7 +313,6 @@ if.exit: ; preds = %entry ret i32 %4 } -; Function Attrs: define i32 @test.sum_us(ptr %0, i64 %1) #0 { entry: %x = alloca %"int[]", align 8 @@ -356,7 +350,6 @@ if.exit: ; preds = %entry ret i32 %13 } -; Function Attrs: define i32 @test.sumd(ptr %0, i64 %1) #0 { entry: %x = alloca %"int[]", align 8 @@ -398,16 +391,15 @@ loop.exit: ; preds = %loop.cond ret i32 %9 } -; Function Attrs: define void @test.main() #0 { entry: - %list = alloca %LinkedList, align 8 + %list = alloca %"LinkedList{int}", align 8 %i = alloca i32, align 4 %elements = alloca i32, align 4 - %array = alloca %List, align 8 + %array = alloca %"List{int}", align 8 %i1 = alloca i32, align 4 - %a = alloca %Blob, align 4 - %b = alloca %Blob.0, align 8 + %a = alloca %"Blob{int}", align 4 + %b = alloca %"Blob{double}", align 8 %ddx = alloca %Foo, align 4 %fro = alloca i32, align 4 %x = alloca [4 x i32], align 16 @@ -422,15 +414,15 @@ entry: %2 = call i32 @test.test_static() call void @hello_world.hello() call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 40, i1 false) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 10) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 15) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 30) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 10) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 15) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 30) store i32 0, ptr %i, align 4 br label %loop.cond loop.cond: ; preds = %loop.body, %entry %3 = load i32, ptr %i, align 4 - %4 = call i64 @"std_collections_linkedlist$int$.LinkedList.len"(ptr %list) #3 + %4 = call i64 @"std.collections.linkedlist.LinkedList$int$.len"(ptr %list) #3 %trunc = trunc i64 %4 to i32 %lt = icmp slt i32 %3, %trunc br i1 %lt, label %loop.body, label %loop.exit @@ -438,7 +430,7 @@ loop.cond: ; preds = %loop.body, %entry loop.body: ; preds = %loop.cond %5 = load i32, ptr %i, align 4 %sext = sext i32 %5 to i64 - %6 = call i32 @"std_collections_linkedlist$int$.LinkedList.get"(ptr %list, i64 %sext) + %6 = call i32 @"std.collections.linkedlist.LinkedList$int$.get"(ptr %list, i64 %sext) %7 = load i32, ptr %i, align 4 %8 = call i32 (ptr, ...) @printf(ptr @.str.2, i32 %7, i32 %6) %9 = load i32, ptr %i, align 4 @@ -447,22 +439,22 @@ loop.body: ; preds = %loop.cond br label %loop.cond loop.exit: ; preds = %loop.cond - call void @"std_collections_linkedlist$int$.LinkedList.free"(ptr %list) + call void @"std.collections.linkedlist.LinkedList$int$.free"(ptr %list) %10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3) store i32 3, ptr %elements, align 4 %11 = call i32 (ptr, ...) @printf(ptr @.str.4) call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 40, i1 false) - call void @"std_collections_list$int$.List.push"(ptr %array, i32 100) #3 - call void @"std_collections_list$int$.List.push"(ptr %array, i32 200) #3 - call void @"std_collections_list$int$.List.push"(ptr %array, i32 400) #3 - call void @"std_collections_list$int$.List.push"(ptr %array, i32 600) #3 - call void @"std_collections_list$int$.List.insert_at"(ptr %array, i64 2, i32 300) + call void @"std.collections.list.List$int$.push"(ptr %array, i32 100) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 200) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 400) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 600) #3 + call void @"std.collections.list.List$int$.insert_at"(ptr %array, i64 2, i32 300) store i32 0, ptr %i1, align 4 br label %loop.cond2 loop.cond2: ; preds = %loop.body5, %loop.exit %12 = load i32, ptr %i1, align 4 - %13 = call i64 @"std_collections_list$int$.List.len"(ptr %array) #3 + %13 = call i64 @"std.collections.list.List$int$.len"(ptr %array) #3 %trunc3 = trunc i64 %13 to i32 %lt4 = icmp slt i32 %12, %trunc3 br i1 %lt4, label %loop.body5, label %loop.exit8 @@ -470,7 +462,7 @@ loop.cond2: ; preds = %loop.body5, %loop.e loop.body5: ; preds = %loop.cond2 %14 = load i32, ptr %i1, align 4 %sext6 = sext i32 %14 to i64 - %15 = call i32 @"std_collections_list$int$.List.get"(ptr %array, i64 %sext6) #3 + %15 = call i32 @"std.collections.list.List$int$.get"(ptr %array, i64 %sext6) #3 %16 = load i32, ptr %i1, align 4 %17 = call i32 (ptr, ...) @printf(ptr @.str.5, i32 %16, i32 %15) %18 = load i32, ptr %i1, align 4 @@ -479,18 +471,18 @@ loop.body5: ; preds = %loop.cond2 br label %loop.cond2 loop.exit8: ; preds = %loop.cond2 - call void @"std_collections_list$int$.List.free"(ptr %array) + call void @"std.collections.list.List$int$.free"(ptr %array) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %a, ptr align 4 @.__const.6, i32 4, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 8 %b, ptr align 8 @.__const.7, i32 8, i1 false) %19 = load i32, ptr %a, align 4 - %20 = call i32 @"test2$int$.getValue"(i32 %19) + %20 = call i32 @"test2.getValue$int$"(i32 %19) %21 = call i32 (ptr, ...) @printf(ptr @.str.8, i32 %20) %22 = load double, ptr %b, align 8 - %23 = call double @"test2$double$.getValue"(double %22) + %23 = call double @"test2.getValue$double$"(double %22) %24 = call i32 (ptr, ...) @printf(ptr @.str.9, double %23) - %25 = call i32 @"test2$int$.getMult"(i32 25) + %25 = call i32 @"test2.getMult$int$"(i32 25) %26 = call i32 (ptr, ...) @printf(ptr @.str.10, i32 %25) - %27 = call double @"test2$double$.getMult"(double 3.300000e+00) + %27 = call double @"test2.getMult$double$"(double 3.300000e+00) %28 = call i32 (ptr, ...) @printf(ptr @.str.11, double %27) call void @test.helloWorld() store i32 0, ptr %ddx, align 4 @@ -543,34 +535,68 @@ loop.exit8: ; preds = %loop.cond2 define void @hello_world.hello() entry: %0 = call i32 (ptr, ...) @printf(ptr @.str) - %1 = call double @"foo$double$.check"(double 1.110000e+01) + %1 = call double @"foo.check$double$"(double 1.110000e+01) %2 = call i32 (ptr, ...) @printf(ptr @.str.1, double %1) ret void -// #expect: foo.double.ll +// #expect: foo.ll -define weak double @"foo$double$.check"(double %0) +define weak double @"foo.check$double$"(double %0) #0 { entry: %fmul = fmul double %0, %0 ret double %fmul -// #expect: test2.double.ll -%Blob = type { double } -@"test2$double$.argh" = weak local_unnamed_addr global double 2.340000e+02, align 8 +// #expect: test2.ll -define weak double @"test2$double$.getMult"(double %0) +%"Blob{int}" = type { i32 } +%"Blob{double}" = type { double } + +@"$ct.test2.Blob$int$" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 4, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$ct.test2.Blob$double$" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@.enum.FOO = internal constant [4 x i8] c"FOO\00", align 1 +@.enum.BAR = internal constant [4 x i8] c"BAR\00", align 1 +@"$ct.int" = linkonce global %.introspect { i8 2, i64 0, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8 +@"$ct.test2.Hello$int$" = linkonce global { i8, i64, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 0, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.FOO, i64 3 }, %"char[]" { ptr @.enum.BAR, i64 3 }] }, align 8 +@"$ct.test2.Hello$double$" = linkonce global { i8, i64, ptr, i64, i64, i64, [2 x %"char[]"] } { i8 8, i64 0, ptr null, i64 4, i64 ptrtoint (ptr @"$ct.int" to i64), i64 2, [2 x %"char[]"] [%"char[]" { ptr @.enum.FOO, i64 3 }, %"char[]" { ptr @.enum.BAR, i64 3 }] }, align 8 +@"test2.argh$int$" = weak local_unnamed_addr global i32 234, align 4 +@"test2.argh$double$" = weak local_unnamed_addr global double 2.340000e+02, align 8 + +; Function Attrs: nounwind uwtable +define weak i32 @"test2.getMult$int$"(i32 %0) #0 { entry: - %fmul = fmul double %0, %0 - ret double %fmul + %mul = mul i32 %0, %0 + ret i32 %mul +} -define weak i32 @"test2$double$.hello"() +; Function Attrs: nounwind uwtable +define weak i32 @"test2.hello$int$"() #0 { entry: ret i32 1 +} -define weak double @"test2$double$.getValue"(double %0) +define weak i32 @"test2.getValue$int$"(i32 %0) #0 { entry: - %blob = alloca %Blob, align 8 + %blob = alloca %"Blob{int}", align 4 + store i32 %0, ptr %blob, align 4 + %1 = load i32, ptr %blob, align 4 + ret i32 %1 +} + +define weak double @"test2.getMult$double$"(double %0) #0 { +entry: + %fmul = fmul double %0, %0 + ret double %fmul +} + +define weak i32 @"test2.hello$double$"() #0 { +entry: + ret i32 1 +} + +define weak double @"test2.getValue$double$"(double %0) #0 { +entry: + %blob = alloca %"Blob{double}", align 8 store double %0, ptr %blob, align 8 %1 = load double, ptr %blob, align 8 ret double %1 diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index bf9cdd005..4a9ee96a3 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -230,13 +230,13 @@ fn Type getValue(Blob blob) /* #expect: test.ll -%Blob = type { i32 } -%Blob.0 = type { double } +%"Blob{int}" = type { i32 } +%"Blob{double}" = type { double } %Bobo = type { i16, float, i16, i16, float, i16 } %"int[]" = type { ptr, i64 } -%LinkedList = type { %any, i64, ptr, ptr } +%"LinkedList{int}" = type { %any, i64, ptr, ptr } %any = type { ptr, i64 } -%List = type { i64, i64, %any, ptr } +%"List{int}" = type { i64, i64, %any, ptr } %Foo = type { i32, i32 } $"$ct.test.Bobo" = comdat any @@ -271,8 +271,8 @@ $"$ct.test.MyEnum" = comdat any @.str.3 = private unnamed_addr constant [14 x i8] c"Elements: %d\0A\00", align 1 @.str.4 = private unnamed_addr constant [7 x i8] c"Hello\0A\00", align 1 @.str.5 = private unnamed_addr constant [17 x i8] c"Element[%d]: %d\0A\00", align 1 -@.__const.6 = private unnamed_addr constant %Blob { i32 42 }, align 4 -@.__const.7 = private unnamed_addr constant %Blob.0 { double 3.330000e+01 }, align 8 +@.__const.6 = private unnamed_addr constant %"Blob{int}" { i32 42 }, align 4 +@.__const.7 = private unnamed_addr constant %"Blob{double}" { double 3.330000e+01 }, align 8 @.str.8 = private unnamed_addr constant [10 x i8] c"a was %d\0A\00", align 1 @.str.9 = private unnamed_addr constant [10 x i8] c"b was %f\0A\00", align 1 @.str.10 = private unnamed_addr constant [17 x i8] c"Mult int was %d\0A\00", align 1 @@ -307,17 +307,14 @@ entry: ret i32 %add } -; Function Attrs: declare i32 @printf(ptr, ...) #0 -; Function Attrs: define void @test.helloWorld() #0 { entry: %0 = call i32 (ptr, ...) @printf(ptr @.str) ret void } -; Function Attrs: define i32 @test.test_static() #0 { entry: %0 = load i32, ptr @test_static.x, align 4 @@ -329,7 +326,6 @@ entry: ret i32 %3 } -; Function Attrs: define i32 @test.helo(double %0, ptr align 4 %1) #0 { entry: %de = alloca [3 x i32], align 4 @@ -342,7 +338,6 @@ entry: ret i32 1 } -; Function Attrs: define i32 @test.test1(i32 %0, i32 %1) #0 { entry: %a = alloca i32, align 4 @@ -362,7 +357,6 @@ if.exit: ; preds = %entry ret i32 %4 } -; Function Attrs: define i32 @test.sum_us(ptr align 8 %0) #0 { entry: %sum = alloca i32, align 4 @@ -398,7 +392,6 @@ if.exit: ; preds = %entry ret i32 %12 } -; Function Attrs: define i32 @test.sumd(ptr align 8 %0) #0 { entry: %sum = alloca i32, align 4 @@ -436,16 +429,15 @@ loop.exit: ; preds = %loop.cond ret i32 %8 } -; Function Attrs: define void @test.main() #0 { entry: - %list = alloca %LinkedList, align 8 + %list = alloca %"LinkedList{int}", align 8 %i = alloca i32, align 4 %elements = alloca i32, align 4 - %array = alloca %List, align 8 + %array = alloca %"List{int}", align 8 %i1 = alloca i32, align 4 - %a = alloca %Blob, align 4 - %b = alloca %Blob.0, align 8 + %a = alloca %"Blob{int}", align 4 + %b = alloca %"Blob{double}", align 8 %ddx = alloca %Foo, align 4 %fro = alloca i32, align 4 %x = alloca [4 x i32], align 16 @@ -466,15 +458,15 @@ entry: %2 = call i32 @test.test_static() call void @hello_world.hello() call void @llvm.memset.p0.i64(ptr align 8 %list, i8 0, i64 40, i1 false) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 10) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 15) - call void @"std_collections_linkedlist$int$.LinkedList.push"(ptr %list, i32 30) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 10) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 15) + call void @"std.collections.linkedlist.LinkedList$int$.push"(ptr %list, i32 30) store i32 0, ptr %i, align 4 br label %loop.cond loop.cond: ; preds = %loop.body, %entry %3 = load i32, ptr %i, align 4 - %4 = call i64 @"std_collections_linkedlist$int$.LinkedList.len"(ptr %list) #3 + %4 = call i64 @"std.collections.linkedlist.LinkedList$int$.len"(ptr %list) #3 %trunc = trunc i64 %4 to i32 %lt = icmp slt i32 %3, %trunc br i1 %lt, label %loop.body, label %loop.exit @@ -482,7 +474,7 @@ loop.cond: ; preds = %loop.body, %entry loop.body: ; preds = %loop.cond %5 = load i32, ptr %i, align 4 %sext = sext i32 %5 to i64 - %6 = call i32 @"std_collections_linkedlist$int$.LinkedList.get"(ptr %list, i64 %sext) + %6 = call i32 @"std.collections.linkedlist.LinkedList$int$.get"(ptr %list, i64 %sext) %7 = load i32, ptr %i, align 4 %8 = call i32 (ptr, ...) @printf(ptr @.str.2, i32 %7, i32 %6) %9 = load i32, ptr %i, align 4 @@ -491,22 +483,22 @@ loop.body: ; preds = %loop.cond br label %loop.cond loop.exit: ; preds = %loop.cond - call void @"std_collections_linkedlist$int$.LinkedList.free"(ptr %list) + call void @"std.collections.linkedlist.LinkedList$int$.free"(ptr %list) %10 = call i32 (ptr, ...) @printf(ptr @.str.3, i32 3) store i32 3, ptr %elements, align 4 %11 = call i32 (ptr, ...) @printf(ptr @.str.4) call void @llvm.memset.p0.i64(ptr align 8 %array, i8 0, i64 40, i1 false) - call void @"std_collections_list$int$.List.push"(ptr %array, i32 100) - call void @"std_collections_list$int$.List.push"(ptr %array, i32 200) - call void @"std_collections_list$int$.List.push"(ptr %array, i32 400) - call void @"std_collections_list$int$.List.push"(ptr %array, i32 600) #3 - call void @"std_collections_list$int$.List.insert_at"(ptr %array, i64 2, i32 300) + call void @"std.collections.list.List$int$.push"(ptr %array, i32 100) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 200) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 400) #3 + call void @"std.collections.list.List$int$.push"(ptr %array, i32 600) #3 + call void @"std.collections.list.List$int$.insert_at"(ptr %array, i64 2, i32 300) store i32 0, ptr %i1, align 4 br label %loop.cond2 loop.cond2: ; preds = %loop.body5, %loop.exit %12 = load i32, ptr %i1, align 4 - %13 = call i64 @"std_collections_list$int$.List.len"(ptr %array) #3 + %13 = call i64 @"std.collections.list.List$int$.len"(ptr %array) #3 %trunc3 = trunc i64 %13 to i32 %lt4 = icmp slt i32 %12, %trunc3 br i1 %lt4, label %loop.body5, label %loop.exit8 @@ -514,7 +506,7 @@ loop.cond2: ; preds = %loop.body5, %loop.e loop.body5: ; preds = %loop.cond2 %14 = load i32, ptr %i1, align 4 %sext6 = sext i32 %14 to i64 - %15 = call i32 @"std_collections_list$int$.List.get"(ptr %array, i64 %sext6) #3 + %15 = call i32 @"std.collections.list.List$int$.get"(ptr %array, i64 %sext6) #3 %16 = load i32, ptr %i1, align 4 %17 = call i32 (ptr, ...) @printf(ptr @.str.5, i32 %16, i32 %15) %18 = load i32, ptr %i1, align 4 @@ -523,18 +515,18 @@ loop.body5: ; preds = %loop.cond2 br label %loop.cond2 loop.exit8: ; preds = %loop.cond2 - call void @"std_collections_list$int$.List.free"(ptr %array) + call void @"std.collections.list.List$int$.free"(ptr %array) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %a, ptr align 4 @.__const.6, i32 4, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 8 %b, ptr align 8 @.__const.7, i32 8, i1 false) %19 = load i32, ptr %a, align 4 - %20 = call i32 @"test2$int$.getValue"(i32 %19) + %20 = call i32 @"test2.getValue$int$"(i32 %19) %21 = call i32 (ptr, ...) @printf(ptr @.str.8, i32 %20) %22 = load i64, ptr %b, align 8 - %23 = call double @"test2$double$.getValue"(i64 %22) + %23 = call double @"test2.getValue$double$"(i64 %22) %24 = call i32 (ptr, ...) @printf(ptr @.str.9, double %23) - %25 = call i32 @"test2$int$.getMult"(i32 25) + %25 = call i32 @"test2.getMult$int$"(i32 25) %26 = call i32 (ptr, ...) @printf(ptr @.str.10, i32 %25) - %27 = call double @"test2$double$.getMult"(double 3.300000e+00) + %27 = call double @"test2.getMult$double$"(double 3.300000e+00) %28 = call i32 (ptr, ...) @printf(ptr @.str.11, double %27) call void @test.helloWorld() store i32 0, ptr %ddx, align 4 @@ -589,50 +581,62 @@ loop.exit8: ; preds = %loop.cond2 store ptr null, ptr %b2, align 8 ret void } + declare void @llvm.memcpy.p0.p0.i32(ptr noalias declare void @hello_world.hello() #0 declare void @llvm.memset.p0.i64(ptr -declare void @"std_collections_linkedlist$int$.LinkedList.push"(ptr, i32) #0 -declare i64 @"std_collections_linkedlist$int$.LinkedList.len"(ptr) #0 -declare i32 @"std_collections_linkedlist$int$.LinkedList.get"(ptr, i64) #0 -declare void @"std_collections_linkedlist$int$.LinkedList.free"(ptr) #0 -declare void @"std_collections_list$int$.List.push"(ptr, i32) #0 -declare void @"std_collections_list$int$.List.insert_at"(ptr, i64, i32) #0 -declare i64 @"std_collections_list$int$.List.len"(ptr) #0 -declare i32 @"std_collections_list$int$.List.get"(ptr, i64) #0 -declare void @"std_collections_list$int$.List.free"(ptr) #0 -declare i32 @"test2$int$.getValue"(i32) #0 -declare double @"test2$double$.getValue"(i64) #0 -declare i32 @"test2$int$.getMult"(i32) #0 -declare double @"test2$double$.getMult"(double) #0 +declare void @"std.collections.linkedlist.LinkedList$int$.push"(ptr, i32) #0 +declare i64 @"std.collections.linkedlist.LinkedList$int$.len"(ptr) #0 +declare i32 @"std.collections.linkedlist.LinkedList$int$.get"(ptr, i64) #0 +declare void @"std.collections.linkedlist.LinkedList$int$.free"(ptr) #0 +declare void @"std.collections.list.List$int$.push"(ptr, i32) #0 +declare void @"std.collections.list.List$int$.insert_at"(ptr, i64, i32) #0 +declare i64 @"std.collections.list.List$int$.len"(ptr) #0 +declare i32 @"std.collections.list.List$int$.get"(ptr, i64) #0 +declare void @"std.collections.list.List$int$.free"(ptr) #0 +declare i32 @"test2.getValue$int$"(i32) #0 +declare double @"test2.getValue$double$"(i64) #0 +declare i32 @"test2.getMult$int$"(i32) #0 +declare double @"test2.getMult$double$"(double) #0 -// #expect: hello_world.ll +// #expect: foo.ll -define void @hello_world.hello() #0 { -entry: - %0 = call i32 (ptr, ...) @printf(ptr @.str) - %1 = call double @"foo$double$.check"(double 1.110000e+01) - %2 = call i32 (ptr, ...) @printf(ptr @.str.1, double %1) - ret void -} -// #expect: foo.double.ll - -define weak_odr double @"foo$double$.check"(double %0) +define weak_odr double @"foo.check$double$"(double %0) #0 comdat { entry: %fmul = fmul double %0, %0 ret double %fmul +} -// #expect: test2.int.ll +// #expect: test2.ll -%Blob = type { i32 } -@"test2$int$.argh" = weak_odr local_unnamed_addr global i32 234, comdat, align 4 - -define weak_odr i32 @"test2$int$.getMult"(i32 %0) +define weak_odr i32 @"test2.getValue$int$"(i32 %0) #0 comdat { entry: - %mul = mul i32 %0, %0 - ret i32 %mul + %blob = alloca %"Blob{int}", align 4 + store i32 %0, ptr %blob, align 4 + %1 = load i32, ptr %blob, align 4 + ret i32 %1 +} + +define weak_odr double @"test2.getMult$double$"(double %0) +entry: + %fmul = fmul double %0, %0 + ret double %fmul +} + +define weak_odr i32 @"test2.hello$double$"() #0 +entry: + ret i32 1 +} + +define weak_odr double @"test2.getValue$double$"(i64 %0) #0 comdat { +entry: + %blob = alloca %"Blob{double}", align 8 + store i64 %0, ptr %blob, align 8 + %1 = load double, ptr %blob, align 8 + ret double %1 +} !llvm.module.flags = !{!0, !1, !2, !3, !4, !5} @@ -641,4 +645,4 @@ entry: !2 = !{i32 2, !"wchar_size", i32 4} !3 = !{i32 4, !"PIC Level", i32 2} !4 = !{i32 1, !"uwtable", i32 2} -!5 = !{i32 2, !"frame-pointer", i32 2} \ No newline at end of file +!5 = !{i32 2, !"frame-pointer", i32 2} diff --git a/test/test_suite/generic/different_generic_def.c3 b/test/test_suite/generic/different_generic_def.c3 index 8d2589aae..00dbcef0f 100644 --- a/test/test_suite/generic/different_generic_def.c3 +++ b/test/test_suite/generic/different_generic_def.c3 @@ -2,7 +2,7 @@ module test; import test2; fn int main() { - Foo1{int} a; + Foo1{int} a; // #error: 'Foo1' expects 2 arguments, but you supplied 1, did you make a mistake? return 0; } @@ -13,7 +13,7 @@ struct Foo Type f; } -module test2{Type, FOO}; // #error: declarations of the generic +module test2{Type, FOO}; struct Foo1 { diff --git a/test/test_suite/generic/enum_set_test.c3t b/test/test_suite/generic/enum_set_test.c3t index afbe2ba56..97b8611d7 100644 --- a/test/test_suite/generic/enum_set_test.c3t +++ b/test/test_suite/generic/enum_set_test.c3t @@ -47,21 +47,21 @@ entry: %taddr11 = alloca i8, align 1 %retparam12 = alloca i64, align 8 store i32 0, ptr %set, align 4 - %0 = call i8 @"std_collections_enumset$test.Abc$.EnumSet.has"(ptr %set, i32 1) + %0 = call i8 @"std.collections.enumset.EnumSet$test.Abc$.has"(ptr %set, i32 1) store i8 %0, ptr %taddr, align 1 %1 = insertvalue %any undef, ptr %taddr, 0 %2 = insertvalue %any %1, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %2, ptr %varargslots, align 16 %3 = call i64 @std.io.printf(ptr %retparam, ptr @.str, i64 14, ptr %varargslots, i64 1) - call void @"std_collections_enumset$test.Abc$.EnumSet.add"(ptr %set, i32 0) - %4 = call i8 @"std_collections_enumset$test.Abc$.EnumSet.has"(ptr %set, i32 1) + call void @"std.collections.enumset.EnumSet$test.Abc$.add"(ptr %set, i32 0) + %4 = call i8 @"std.collections.enumset.EnumSet$test.Abc$.has"(ptr %set, i32 1) store i8 %4, ptr %taddr2, align 1 %5 = insertvalue %any undef, ptr %taddr2, 0 %6 = insertvalue %any %5, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %6, ptr %varargslots1, align 16 %7 = call i64 @std.io.printf(ptr %retparam3, ptr @.str.1, i64 14, ptr %varargslots1, i64 1) - call void @"std_collections_enumset$test.Abc$.EnumSet.add"(ptr %set, i32 1) - %8 = call i8 @"std_collections_enumset$test.Abc$.EnumSet.has"(ptr %set, i32 1) + call void @"std.collections.enumset.EnumSet$test.Abc$.add"(ptr %set, i32 1) + %8 = call i8 @"std.collections.enumset.EnumSet$test.Abc$.has"(ptr %set, i32 1) store i8 %8, ptr %taddr5, align 1 %9 = insertvalue %any undef, ptr %taddr5, 0 %10 = insertvalue %any %9, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 @@ -69,16 +69,16 @@ entry: %11 = call i64 @std.io.printf(ptr %retparam6, ptr @.str.2, i64 14, ptr %varargslots4, i64 1) store i32 0, ptr %set2, align 4 %12 = load i32, ptr %set, align 4 - call void @"std_collections_enumset$test.Abc$.EnumSet.add_all"(ptr %set2, i32 %12) - %13 = call i8 @"std_collections_enumset$test.Abc$.EnumSet.has"(ptr %set2, i32 1) + call void @"std.collections.enumset.EnumSet$test.Abc$.add_all"(ptr %set2, i32 %12) + %13 = call i8 @"std.collections.enumset.EnumSet$test.Abc$.has"(ptr %set2, i32 1) store i8 %13, ptr %taddr8, align 1 %14 = insertvalue %any undef, ptr %taddr8, 0 %15 = insertvalue %any %14, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %15, ptr %varargslots7, align 16 %16 = call i64 @std.io.printf(ptr %retparam9, ptr @.str.3, i64 14, ptr %varargslots7, i64 1) %17 = load i32, ptr %set2, align 4 - call void @"std_collections_enumset$test.Abc$.EnumSet.remove_all"(ptr %set, i32 %17) - %18 = call i8 @"std_collections_enumset$test.Abc$.EnumSet.has"(ptr %set, i32 1) + call void @"std.collections.enumset.EnumSet$test.Abc$.remove_all"(ptr %set, i32 %17) + %18 = call i8 @"std.collections.enumset.EnumSet$test.Abc$.has"(ptr %set, i32 1) store i8 %18, ptr %taddr11, align 1 %19 = insertvalue %any undef, ptr %taddr11, 0 %20 = insertvalue %any %19, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 diff --git a/test/test_suite/generic/generic_distinct.c3t b/test/test_suite/generic/generic_distinct.c3t index 8e0747013..ce4e821d7 100644 --- a/test/test_suite/generic/generic_distinct.c3t +++ b/test/test_suite/generic/generic_distinct.c3t @@ -16,8 +16,8 @@ fn void foo() {} define void @test.main() #0 { entry: - call void @"test1$test.Foo$1$.foo"() + call void @"test1.foo$test.Foo$1$"() ret void } -declare extern_weak void @"test1$test.Foo$1$.foo"() #0 +declare extern_weak void @"test1.foo$test.Foo$1$"() #0 diff --git a/test/test_suite/generic/generic_error_out.c3 b/test/test_suite/generic/generic_error_out.c3 index 68f9fbb0f..2cb714268 100644 --- a/test/test_suite/generic/generic_error_out.c3 +++ b/test/test_suite/generic/generic_error_out.c3 @@ -6,5 +6,5 @@ fn void foo(HashMap{char[], String}* f) { } fn void main() { HashMap{char[], char[]} x; - foo(&x); // #error: Implicitly casting 'std::collections::map::HashMap{char[], char[]}*' to 'std::collections::map::HashMap{char[], String}*' is not permitted, but you may do an explicit cast by placing '(std::collections::map::HashMap{char[], String}*)' before the expression + foo(&x); // #error: Implicitly casting 'HashMap{char[], char[]}*' to 'HashMap{char[], String}*' is not permitted, but you may do an explicit cast by placing '(HashMap{char[], String}*)' before the expression } diff --git a/test/test_suite/generic/generic_idents.c3t b/test/test_suite/generic/generic_idents.c3t index b217a9104..d4f8bb775 100644 --- a/test/test_suite/generic/generic_idents.c3t +++ b/test/test_suite/generic/generic_idents.c3t @@ -27,44 +27,46 @@ fn double getIt2(double i) return doubleAddMult(i, 2, 3); } -/* #expect: gen.int.ll - -define weak i32 @"gen$int$.mult"(i32 %0) #0 { -entry: - %mul = mul i32 %0, %0 - ret i32 %mul -} - -define weak i32 @"gen$int$.addMult"(i32 %0, i32 %1, i32 %2) #0 { -entry: - %mul = mul i32 %0, %1 - %add = add i32 %mul, %2 - ret i32 %add -} - -// #expect: test.ll +/* #expect: test.ll define i32 @test.getIt(i32 %0) #0 { entry: - %1 = call i32 @"gen$int$.mult"(i32 %0) + %1 = call i32 @"gen.mult$int$"(i32 %0) %add = add i32 %1, 1 ret i32 %add } define double @test.getIt2(double %0) #0 { entry: - %1 = call double @"gen$double$.addMult"(double %0, double 2.000000e+00, double 3.000000e+00) + %1 = call double @"gen.addMult$double$"(double %0, double 2.000000e+00, double 3.000000e+00) ret double %1 } -declare extern_weak i32 @"gen$int$.mult"(i32) +declare extern_weak i32 @"gen.mult$int$"(i32) #0 +declare extern_weak double @"gen.addMult$double$"(double, double, double) #0 -declare extern_weak double @"gen$double$.addMult"(double, double, double) +// #expect: gen.ll -// #expect: gen.double.ll +define weak i32 @"gen.mult$int$"(i32 %0) #0 { +entry: + %mul = mul i32 %0, %0 + ret i32 %mul +} +define weak i32 @"gen.addMult$int$"(i32 %0, i32 %1, i32 %2) #0 { +entry: + %mul = mul i32 %0, %1 + %add = add i32 %mul, %2 + ret i32 %add +} -define weak double @"gen$double$.addMult"(double %0, double %1, double %2) +define weak double @"gen.mult$double$"(double %0) #0 { +entry: + %fmul = fmul double %0, %0 + ret double %fmul +} + +define weak double @"gen.addMult$double$"(double %0, double %1, double %2) #0 { entry: %fmul = fmul double %0, %1 %fadd = fadd double %fmul, %2 diff --git a/test/test_suite/generic/generic_interface.c3t b/test/test_suite/generic/generic_interface.c3t index 22d003489..56df954f5 100644 --- a/test/test_suite/generic/generic_interface.c3t +++ b/test/test_suite/generic/generic_interface.c3t @@ -25,7 +25,7 @@ fn int main() define i32 @main() #0 { entry: - %ts = alloca %TestStruct, align 8 + %ts = alloca %"TestStruct{int}", align 8 call void @llvm.memset.p0.i64(ptr align 8 %ts, i8 0, i64 16, i1 false) ret i32 0 } diff --git a/test/test_suite/generic/generic_lambda_complex.c3t b/test/test_suite/generic/generic_lambda_complex.c3t index d633bbbb0..702ad086d 100644 --- a/test/test_suite/generic/generic_lambda_complex.c3t +++ b/test/test_suite/generic/generic_lambda_complex.c3t @@ -170,7 +170,7 @@ fn void main() define void @text_test.main() #0 { entry: %foo_tmpl = alloca %"char[]", align 8 - %ft = alloca %TextTemplate, align 8 + %ft = alloca %"TextTemplate{Foo}", align 8 %error_var = alloca i64, align 8 %indirectarg = alloca %"char[]", align 8 %varargslots = alloca [1 x %any], align 16 @@ -185,7 +185,7 @@ entry: %ptradd = getelementptr inbounds i8, ptr %foo_tmpl, i64 8 %hi = load i64, ptr %ptradd, align 8 store %"char[]" { ptr @.str.2, i64 2 }, ptr %indirectarg, align 8 - %1 = call i64 @"abc$text_test.Foo$.TextTemplate.init"(ptr %ft, ptr %lo, i64 %hi, ptr @.str.1, i64 2, ptr byval(%"char[]") align 8 %indirectarg, ptr byval(%any) align 8 %0) + %1 = call i64 @"abc.TextTemplate$text_test.Foo$.init"(ptr %ft, ptr %lo, i64 %hi, ptr @.str.1, i64 2, ptr byval(%"char[]") align 8 %indirectarg, ptr byval(%any) align 8 %0) %not_err = icmp eq i64 %1, 0 %2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %2, label %after_check, label %assign_optional @@ -208,7 +208,7 @@ panic_block: ; preds = %assign_optional unreachable noerr_block: ; preds = %after_check - %6 = call i64 @"abc$text_test.Foo$.TextTemplate.free"(ptr %ft) + %6 = call i64 @"abc.TextTemplate$text_test.Foo$.free"(ptr %ft) %not_err3 = icmp eq i64 %6, 0 %7 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) br i1 %7, label %after_check5, label %assign_optional4 diff --git a/test/test_suite/generic/generic_lookup.c3 b/test/test_suite/generic/generic_lookup.c3 index 800495454..a7720fb54 100644 --- a/test/test_suite/generic/generic_lookup.c3 +++ b/test/test_suite/generic/generic_lookup.c3 @@ -22,7 +22,7 @@ fn void d() fn void d2() { - elastic_array::type_is_overaligned(); // #error: 'type_is_overaligned' is defined in the generic module 'std::collections::elastic_array', did you forget the parameters '{ ... }'? + elastic_array::type_is_overaligned(); // #error: 'type_is_overaligned' is a generic macro, did you forget the parameters '{ ... }' } fn void e() @@ -32,12 +32,12 @@ fn void e() fn void f() { - ElasticArray x; // #error: 'ElasticArray' is defined in the generic module 'std::collections::elastic_array', did you forget the parameters '{ ... }'? + ElasticArray x; // #error: 'ElasticArray' is a generic struct, did you forget the parameters '{ ... }'? } fn void g() { - (ElasticArray){}; // #error: 'ElasticArray' is defined in the generic module 'std::collections::elastic_array', did you forget the parameters '{ ... }' + (ElasticArray){}; // #error: 'ElasticArray' is a generic struct, did you forget the parameters '{ ... }'? } fn int main(String[] args) diff --git a/test/test_suite/generic/generic_merge_constraints.c3 b/test/test_suite/generic/generic_merge_constraints.c3 new file mode 100644 index 000000000..4039d1bcd --- /dev/null +++ b/test/test_suite/generic/generic_merge_constraints.c3 @@ -0,0 +1,20 @@ +<* @require THE_CONST < 5 *> +module the_generic @generic(THE_CONST, Type); +struct Abc +{ int a; } + +module the_generic @generic(THE_CONST, Type); + +struct TheStruct +{ + Type[THE_CONST] el; +} + +module test; +import the_generic; + +fn int main() +{ + TheStruct{5, double} s; // #error: Parameter(s) failed validation: @require "THE_CONST < 5" violated + return x; +} \ No newline at end of file diff --git a/test/test_suite/generic/generic_no_arg.c3 b/test/test_suite/generic/generic_no_arg.c3 index 5ca842501..591f47e6c 100644 --- a/test/test_suite/generic/generic_no_arg.c3 +++ b/test/test_suite/generic/generic_no_arg.c3 @@ -8,7 +8,7 @@ fn void foo(Foo f) {} fn void a() { - List{} a; // #error: must be instantiatied with generic module arguments + List{} a; // #error: 'List' requires generic arguments inside the '{}', did you forget them } fn void b() { @@ -16,7 +16,7 @@ fn void b() } fn void c() { - bar::test{}(); // #error: must be instantiatied with generic module arguments + bar::test{}(); // #error: 'test' requires generic arguments inside the '{}', did you forget them } fn int main() diff --git a/test/test_suite/generic/generic_no_params.c3 b/test/test_suite/generic/generic_no_params.c3 index 2741e332f..243a64c80 100644 --- a/test/test_suite/generic/generic_no_params.c3 +++ b/test/test_suite/generic/generic_no_params.c3 @@ -17,12 +17,12 @@ import std::collections::list; fn void test1() { - int x = values::test{}(); // #error: must be instantiatied with generic + int x = values::test{}(); // #error: requires generic arguments inside } fn void test2() { - List{Values{ }} v1s; // #error: must be instantiatied with generic + List{Values{ }} v1s; // #error: 'Values' requires generic arguments inside the '{}', did you forget them } fn void main() diff --git a/test/test_suite/generic/generic_num.c3t b/test/test_suite/generic/generic_num.c3t index 30fd00a2b..7b89fb531 100644 --- a/test/test_suite/generic/generic_num.c3t +++ b/test/test_suite/generic/generic_num.c3t @@ -24,7 +24,7 @@ entry: %varargslots = alloca [1 x %any], align 16 %taddr = alloca i32, align 4 %retparam = alloca i64, align 8 - %0 = call i32 @"hello$int$_123$.x"(i32 4) + %0 = call i32 @"hello.x$int$_123$"(i32 4) store i32 %0, ptr %taddr, align 4 %1 = insertvalue %any undef, ptr %taddr, 0 %2 = insertvalue %any %1, i64 ptrtoint (ptr @"$ct.int" to i64), 1 diff --git a/test/test_suite/generic/generic_over_fn.c3t b/test/test_suite/generic/generic_over_fn.c3t index aacd43101..c71ad2fb6 100644 --- a/test/test_suite/generic/generic_over_fn.c3t +++ b/test/test_suite/generic/generic_over_fn.c3t @@ -118,7 +118,7 @@ loop.body: ; preds = %loop.cond %lo = load ptr, ptr %list, align 8 %ptradd10 = getelementptr inbounds i8, ptr %list, i64 8 %hi = load i64, ptr %ptradd10, align 8 - call void @"test_generic$sa$int$fn$int$int$$int$$.sort"(ptr %lo, i64 %hi, i64 0, i64 %sub, ptr @sort_test.cmp_int_value) + call void @"test_generic.sort$sa$int$fn$int$int$$int$$"(ptr %lo, i64 %hi, i64 0, i64 %sub, ptr @sort_test.cmp_int_value) %16 = load i64, ptr %.anon, align 8 %addnuw = add nuw i64 %16, 1 store i64 %addnuw, ptr %.anon, align 8 @@ -188,7 +188,7 @@ loop.body: ; preds = %loop.cond %lo = load ptr, ptr %list, align 8 %ptradd10 = getelementptr inbounds i8, ptr %list, i64 8 %hi = load i64, ptr %ptradd10, align 8 - call void @"test_generic$sa$int$fn$int$int$$int$$.sort"(ptr %lo, i64 %hi, i64 0, i64 %sub, ptr @sort_test.cmp_int_value2) + call void @"test_generic.sort$sa$int$fn$int$int$$int$$"(ptr %lo, i64 %hi, i64 0, i64 %sub, ptr @sort_test.cmp_int_value2) %16 = load i64, ptr %.anon, align 8 %addnuw = add nuw i64 %16, 1 store i64 %addnuw, ptr %.anon, align 8 @@ -198,5 +198,4 @@ loop.exit: ; preds = %loop.cond ret void } -declare extern_weak void @"test_generic$sa$int$fn$int$int$$int$$.sort"(ptr, i64, i64, i64, ptr) #0 - +declare extern_weak void @"test_generic.sort$sa$int$fn$int$int$$int$$"(ptr, i64, i64, i64, ptr) #0 \ No newline at end of file diff --git a/test/test_suite/generic/generic_recursion.c3t b/test/test_suite/generic/generic_recursion.c3t index 43b538cf3..36f1351d8 100644 --- a/test/test_suite/generic/generic_recursion.c3t +++ b/test/test_suite/generic/generic_recursion.c3t @@ -16,6 +16,6 @@ TreeNode abc; /* #expect: test.ll -%TreeNode = type { ptr, ptr, %List } -%List = type { i64, i64, %any, ptr } +%TreeNode = type { ptr, ptr, %"List{TreeNode}" } +%"List{TreeNode}" = type { i64, i64, %any, ptr } @test.abc = local_unnamed_addr global %TreeNode zeroinitializer, align 8 \ No newline at end of file diff --git a/test/test_suite/generic/generic_self_ref.c3 b/test/test_suite/generic/generic_self_ref.c3 index a518375a1..46dabc097 100644 --- a/test/test_suite/generic/generic_self_ref.c3 +++ b/test/test_suite/generic/generic_self_ref.c3 @@ -10,7 +10,7 @@ struct Widget List {any} children; } -fn void Widget{Label}.draw(Widget* self) // #error: The same method is generated by multiple instances +fn void Widget{Label}.draw(Widget* self) // #error: This method is already defined for 'Widget{Label}' { io::printfn("Hello Label"); } diff --git a/test/test_suite/generic/generic_testcases.c3t b/test/test_suite/generic/generic_testcases.c3t new file mode 100644 index 000000000..0a5a8879d --- /dev/null +++ b/test/test_suite/generic/generic_testcases.c3t @@ -0,0 +1,171 @@ +// #target: macos-aarch64 +module the_generic @generic(THE_CONST, Type); +struct Abc +{ int a; } + +module the_generic @generic(THE_CONST, Type); + +struct TheStruct +{ + Type[THE_CONST] el; +} + +fn Type TheStruct.foo2(&self) +{ + return self.el[0]; +} + +struct TheStruct2 +{ + union + { + struct + { + Type a, b; + } + } +} +Type foo = THE_CONST; + +Type bar @local; + +Type* baz = &bar; + +fn Type square(Type t) => t * THE_CONST; + +module test; +import the_generic; + +<* @require Type.sizeof < 5 *> +fn Type square(Type t) @generic(Type) => t * 3; +fn Type bob(Type2 t) @generic(Type2) => feok * 3; + +fn int main() +{ + TheStruct{5, double} s; + TheStruct{5, int} s2; + s.el[4] = 1.2; + double y = s.foo2(); + double y22 = s2.foo2(); + int x = the_generic::foo{4, int}; + int* b = the_generic::baz{4, int}; + x = the_generic::square{3, int}(x); + x = square{int}(x); + return x; +} + +/* #expect: the_generic.ll + +@"the_generic.foo$5$double$" = weak local_unnamed_addr global double 5.000000e+00, align 8 +@"the_generic.bar$5$double$.31595" = internal global double 0.000000e+00, align 8 +@"the_generic.baz$5$double$" = weak local_unnamed_addr global ptr @"the_generic.bar$5$double$.31595", align 8 +@"the_generic.foo$5$int$" = weak local_unnamed_addr global i32 5, align 4 +@"the_generic.bar$5$int$.31615" = internal global i32 0, align 4 +@"the_generic.baz$5$int$" = weak local_unnamed_addr global ptr @"the_generic.bar$5$int$.31615", align 8 +@"the_generic.foo$4$int$" = weak local_unnamed_addr global i32 4, align 4 +@"the_generic.bar$4$int$.31635" = internal global i32 0, align 4 +@"the_generic.baz$4$int$" = weak local_unnamed_addr global ptr @"the_generic.bar$4$int$.31635", align 8 +@"the_generic.foo$3$int$" = weak local_unnamed_addr global i32 3, align 4 +@"the_generic.bar$3$int$.31655" = internal global i32 0, align 4 +@"the_generic.baz$3$int$" = weak local_unnamed_addr global ptr @"the_generic.bar$3$int$.31655", align 8 + +define weak double @"the_generic.TheStruct$5$double$.foo2"(ptr %0) #0 { +entry: + %1 = load double, ptr %0, align 8 + ret double %1 +} + +define weak i32 @"the_generic.TheStruct$5$int$.foo2"(ptr %0) #0 { +entry: + %1 = load i32, ptr %0, align 4 + ret i32 %1 +} + +define weak i32 @"the_generic.TheStruct$4$int$.foo2"(ptr %0) #0 { +entry: + %1 = load i32, ptr %0, align 4 + ret i32 %1 +} + +define weak i32 @"the_generic.TheStruct$3$int$.foo2"(ptr %0) #0 { +entry: + %1 = load i32, ptr %0, align 4 + ret i32 %1 +} + +define weak double @"the_generic.square$5$double$"(double %0) #0 { +entry: + %fmul = fmul double %0, 5.000000e+00 + ret double %fmul +} + +define weak i32 @"the_generic.square$5$int$"(i32 %0) #0 { +entry: + %mul = mul i32 %0, 5 + ret i32 %mul +} + +define weak i32 @"the_generic.square$4$int$"(i32 %0) #0 { +entry: + %mul = mul i32 %0, 4 + ret i32 %mul +} + +define weak i32 @"the_generic.square$3$int$"(i32 %0) #0 { +entry: + %mul = mul i32 %0, 3 + ret i32 %mul +} + +// #expect: test.ll + +%"TheStruct{5, double}" = type { [5 x double] } +%"TheStruct{5, int}" = type { [5 x i32] } + +@"the_generic.foo$4$int$" = extern_weak global i32, align 4 +@"the_generic.baz$4$int$" = extern_weak global ptr, align 8 + +define i32 @main() #0 { +entry: + %s = alloca %"TheStruct{5, double}", align 8 + %s2 = alloca %"TheStruct{5, int}", align 4 + %y = alloca double, align 8 + %y22 = alloca double, align 8 + %x = alloca i32, align 4 + %b = alloca ptr, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %s, i8 0, i64 40, i1 false) + call void @llvm.memset.p0.i64(ptr align 4 %s2, i8 0, i64 20, i1 false) + %ptradd = getelementptr inbounds i8, ptr %s, i64 32 + store double 1.200000e+00, ptr %ptradd, align 8 + %0 = call double @"the_generic.TheStruct$5$double$.foo2"(ptr %s) + store double %0, ptr %y, align 8 + %1 = call i32 @"the_generic.TheStruct$5$int$.foo2"(ptr %s2) + %sifp = sitofp i32 %1 to double + store double %sifp, ptr %y22, align 8 + %2 = load i32, ptr @"the_generic.foo$4$int$", align 4 + store i32 %2, ptr %x, align 4 + %3 = load ptr, ptr @"the_generic.baz$4$int$", align 8 + store ptr %3, ptr %b, align 8 + %4 = load i32, ptr %x, align 4 + %5 = call i32 @"the_generic.square$3$int$"(i32 %4) + store i32 %5, ptr %x, align 4 + %6 = load i32, ptr %x, align 4 + %7 = call i32 @"test.square$int$"(i32 %6) + store i32 %7, ptr %x, align 4 + %8 = load i32, ptr %x, align 4 + ret i32 %8 +} + +define weak i32 @"test.square$int$"(i32 %0) #0 { +entry: + %mul = mul i32 %0, 3 + ret i32 %mul +} + +declare void @llvm.memset.p0.i64(ptr + +declare extern_weak double @"the_generic.TheStruct$5$double$.foo2"(ptr) #0 + +declare extern_weak i32 @"the_generic.TheStruct$5$int$.foo2"(ptr) #0 + +declare extern_weak i32 @"the_generic.square$3$int$"(i32) #0 diff --git a/test/test_suite/generic/generic_with_enum.c3t b/test/test_suite/generic/generic_with_enum.c3t index af3675b24..40b449de1 100644 --- a/test/test_suite/generic/generic_with_enum.c3t +++ b/test/test_suite/generic/generic_with_enum.c3t @@ -24,11 +24,11 @@ fn void main() /* #expect: tester.ll -%Foo = type { i32 } +%"Foo{tester.Abc:ONE}" = type { i32 } define void @tester.main() #0 { entry: - %x = alloca %Foo, align 4 + %x = alloca %"Foo{tester.Abc:ONE}", align 4 %e = alloca i32, align 4 store i32 0, ptr %x, align 4 store i32 0, ptr %e, align 4 diff --git a/test/test_suite/generic/generic_without_param.c3 b/test/test_suite/generic/generic_without_param.c3 index c8d869627..9f00eeefc 100644 --- a/test/test_suite/generic/generic_without_param.c3 +++ b/test/test_suite/generic/generic_without_param.c3 @@ -3,7 +3,7 @@ import std; import abc; fn void main() { - abc::test(); // #error: 'test' is defined in the generic module 'abc', did you + abc::test(); // #error: 'test' is a generic function, did you forget the parameters '{ ... }'? } module abc{Type}; fn void test() {} \ No newline at end of file diff --git a/test/test_suite/generic/recursive_generic2.c3 b/test/test_suite/generic/recursive_generic2.c3 index 772f3db34..9fe06073d 100644 --- a/test/test_suite/generic/recursive_generic2.c3 +++ b/test/test_suite/generic/recursive_generic2.c3 @@ -2,7 +2,7 @@ module foo { Type }; import std::collections::list; -struct Foo { // #error: Recursive definition of 'Foo' +struct Foo { // #error: Recursive definition of 'Foo{int}' Type foo; Allocator allocator; List { Foo } children; diff --git a/test/test_suite/generic/used_without_param2.c3 b/test/test_suite/generic/used_without_param2.c3 index 92be3d148..414d8dff2 100644 --- a/test/test_suite/generic/used_without_param2.c3 +++ b/test/test_suite/generic/used_without_param2.c3 @@ -5,6 +5,6 @@ import std::collections::maybe; fn void test() { maybe::Maybe{float} f = (Maybe) {.value=6.6, .has_value=true}; - Maybe x; // #error: 'Maybe' is defined in the generic module 'std::collections::maybe', did you forget the parameters '{ ... }' + Maybe x; // #error: 'Maybe' is a generic struct, did you forget the parameters '{ ... }'? } diff --git a/test/test_suite/generic/used_without_param3.c3 b/test/test_suite/generic/used_without_param3.c3 index a56cd8c41..5e27e32e8 100644 --- a/test/test_suite/generic/used_without_param3.c3 +++ b/test/test_suite/generic/used_without_param3.c3 @@ -4,5 +4,5 @@ import std::collections::maybe; fn void test() { - (Maybe) {.value=6.6, .has_value=true}; // #error: is defined in the generic module + (Maybe) {.value=6.6, .has_value=true}; // #error: 'Maybe' is a generic struct, did you forget the parameters '{ ... }' } \ No newline at end of file diff --git a/test/test_suite/initializer_lists/init_any_interface.c3 b/test/test_suite/initializer_lists/init_any_interface.c3 index c7c2263da..98e7f5261 100644 --- a/test/test_suite/initializer_lists/init_any_interface.c3 +++ b/test/test_suite/initializer_lists/init_any_interface.c3 @@ -10,7 +10,7 @@ struct Bar (Foo) fn int main() { Test{Foo} a = {{}}; Test{any} b = {{}}; - Test{Foo} a2 = {(Bar){}}; // #error: It is not possible to cast + Test{Foo} a2 = {(Bar){}}; // #error: You can only convert pointers to an interface return 0; } diff --git a/test/test_suite/methods/operator_assign_mutate.c3t b/test/test_suite/methods/operator_assign_mutate.c3t index c8629a014..5a7a640e5 100644 --- a/test/test_suite/methods/operator_assign_mutate.c3t +++ b/test/test_suite/methods/operator_assign_mutate.c3t @@ -32,17 +32,17 @@ entry: %coerce = alloca %Foo, align 8 %.anon5 = alloca i32, align 4 %coerce6 = alloca %Foo, align 8 - call void @"std_collections_list$long$.List.push"(ptr @test.stones, i64 1) #2 - call void @"std_collections_list$long$.List.push"(ptr @test.stones, i64 100) #2 - %0 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 0) #2 + call void @"std.collections.list.List$long$.push"(ptr @test.stones, i64 1) #2 + call void @"std.collections.list.List$long$.push"(ptr @test.stones, i64 100) #2 + %0 = call ptr @"std.collections.list.List$long$.get_ref"(ptr @test.stones, i64 0) #2 %1 = load i64, ptr %0, align 8 %mul = mul i64 %1, 3 store i64 %mul, ptr %0, align 8 - %2 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 1) #2 + %2 = call ptr @"std.collections.list.List$long$.get_ref"(ptr @test.stones, i64 1) #2 %3 = load i64, ptr %2, align 8 %mul1 = mul i64 %3, 2 store i64 %mul1, ptr %2, align 8 - %4 = call ptr @"std_collections_list$long$.List.get_ref"(ptr @test.stones, i64 0) #2 + %4 = call ptr @"std.collections.list.List$long$.get_ref"(ptr @test.stones, i64 0) #2 %5 = load i64, ptr %4, align 8 %add = add i64 %5, 31 store i64 %add, ptr %4, align 8 diff --git a/test/test_suite/module/module_generic_mixing.c3 b/test/test_suite/module/module_generic_mixing.c3 index 473589005..683ccb566 100644 --- a/test/test_suite/module/module_generic_mixing.c3 +++ b/test/test_suite/module/module_generic_mixing.c3 @@ -1,7 +1,11 @@ module test; -fn void main() {} +fn void main() +{ + test{int}(); +} -module test{Type}; // #error: generic -fn void test() { +module test{Type}; +fn void test() +{ Type a; } \ No newline at end of file diff --git a/test/test_suite/overloading/set_not_set_overload.c3t b/test/test_suite/overloading/set_not_set_overload.c3t index eb993c868..a7e47a32c 100644 --- a/test/test_suite/overloading/set_not_set_overload.c3t +++ b/test/test_suite/overloading/set_not_set_overload.c3t @@ -33,14 +33,14 @@ entry: %z = alloca [20 x [20 x i8]], align 16 %foo = alloca %Map, align 8 %result = alloca %"char[]", align 8 - %0 = call ptr @"std_collections_list$a3$int$.List.tinit"(ptr @test.x, i64 16) + %0 = call ptr @"std.collections.list.List$a3$int$.tinit"(ptr @test.x, i64 16) call void @llvm.memcpy.p0.p0.i32(ptr align 4 %y, ptr align 4 @.__const, i32 12, i1 false) call void @llvm.memcpy.p0.p0.i32(ptr align 8 %coerce, ptr align 4 %y, i32 12, i1 false) %lo = load i64, ptr %coerce, align 8 %ptradd = getelementptr inbounds i8, ptr %coerce, i64 8 %hi = load i32, ptr %ptradd, align 8 - call void @"std_collections_list$a3$int$.List.push"(ptr @test.x, i64 %lo, i32 %hi) #3 - %1 = call ptr @"std_collections_list$a3$int$.List.get_ref"(ptr @test.x, i64 0) #3 + call void @"std.collections.list.List$a3$int$.push"(ptr @test.x, i64 %lo, i32 %hi) #3 + %1 = call ptr @"std.collections.list.List$a3$int$.get_ref"(ptr @test.x, i64 0) #3 store i32 4, ptr %1, align 4 call void @llvm.memset.p0.i64(ptr align 16 %z, i8 0, i64 400, i1 false) %2 = insertvalue %"char[20][]" undef, ptr %z, 0 diff --git a/test/test_suite/overloading/set_overload.c3t b/test/test_suite/overloading/set_overload.c3t index 6ec9e2237..267c61c7e 100644 --- a/test/test_suite/overloading/set_overload.c3t +++ b/test/test_suite/overloading/set_overload.c3t @@ -16,9 +16,9 @@ fn void main() define void @test.main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %"HashMap{char[], int}", align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false) - %0 = call i8 @"std_collections_map$sa$char$int$.HashMap.set"(ptr %map, ptr @.str, i64 5, i32 4) - %1 = call i8 @"std_collections_map$sa$char$int$.HashMap.set"(ptr %map, ptr @.str.1, i64 3, i32 5) + %0 = call i8 @"std.collections.map.HashMap$sa$char$int$.set"(ptr %map, ptr @.str, i64 5, i32 4) + %1 = call i8 @"std.collections.map.HashMap$sa$char$int$.set"(ptr %map, ptr @.str.1, i64 3, i32 5) ret void } \ No newline at end of file diff --git a/test/test_suite/stdlib/map_linux.c3t b/test/test_suite/stdlib/map_linux.c3t index 5f9a6db71..02dd071dd 100644 --- a/test/test_suite/stdlib/map_linux.c3t +++ b/test/test_suite/stdlib/map_linux.c3t @@ -88,7 +88,7 @@ entry: define void @test.main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %"HashMap{int, Foo}", align 8 %varargslots = alloca [1 x %any], align 16 %retparam = alloca i64, align 8 %literal = alloca %Foo, align 8 @@ -110,7 +110,7 @@ entry: %varargslots33 = alloca [1 x %any], align 16 %result = alloca %"Foo[]", align 8 %retparam37 = alloca i64, align 8 - %map2 = alloca %HashMap.0, align 8 + %map2 = alloca %"HashMap{int, double}", align 8 %varargslots43 = alloca [1 x %any], align 16 %taddr44 = alloca i8, align 1 %retparam45 = alloca i64, align 8 @@ -124,7 +124,7 @@ entry: %result65 = alloca %"double[]", align 8 %retparam66 = alloca i64, align 8 %state = alloca ptr, align 8 - %map3 = alloca %HashMap.0, align 8 + %map3 = alloca %"HashMap{int, double}", align 8 %varargslots72 = alloca [1 x %any], align 16 %result76 = alloca %"int[]", align 8 %retparam77 = alloca i64, align 8 @@ -133,7 +133,7 @@ entry: %lo = load i64, ptr %0, align 8 %ptradd = getelementptr inbounds i8, ptr %0, i64 8 %hi = load ptr, ptr %ptradd, align 8 - %1 = call ptr @"std_collections_map$int$test.Foo$.HashMap.init"(ptr %map, i64 %lo, ptr %hi, i32 16, float 7.500000e-01) + %1 = call ptr @"std.collections.map.HashMap$int$test.Foo$.init"(ptr %map, i64 %lo, ptr %hi, i32 16, float 7.500000e-01) %ptradd1 = getelementptr inbounds i8, ptr %map, i64 32 %2 = insertvalue %any undef, ptr %ptradd1, 0 %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 @@ -143,7 +143,7 @@ entry: %lo2 = load i32, ptr %literal, align 8 %ptradd3 = getelementptr inbounds i8, ptr %literal, i64 8 %hi4 = load ptr, ptr %ptradd3, align 8 - %5 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i32 %lo2, ptr %hi4) + %5 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 1, i32 %lo2, ptr %hi4) %ptradd6 = getelementptr inbounds i8, ptr %map, i64 32 %6 = insertvalue %any undef, ptr %ptradd6, 0 %7 = insertvalue %any %6, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 @@ -153,13 +153,13 @@ entry: %lo9 = load i32, ptr %literal8, align 8 %ptradd10 = getelementptr inbounds i8, ptr %literal8, i64 8 %hi11 = load ptr, ptr %ptradd10, align 8 - %9 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i32 %lo9, ptr %hi11) + %9 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 1, i32 %lo9, ptr %hi11) %ptradd13 = getelementptr inbounds i8, ptr %map, i64 32 %10 = insertvalue %any undef, ptr %ptradd13, 0 %11 = insertvalue %any %10, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 store %any %11, ptr %varargslots12, align 16 %12 = call i64 @std.io.printfn(ptr %retparam14, ptr @.str.3, i64 12, ptr %varargslots12, i64 1) - %13 = call i64 @"std_collections_map$int$test.Foo$.HashMap.get"(ptr %retparam16, ptr %map, i32 1) + %13 = call i64 @"std.collections.map.HashMap$int$test.Foo$.get"(ptr %retparam16, ptr %map, i32 1) %not_err = icmp eq i64 %13, 0 %14 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %14, label %after_check, label %after_check19 @@ -174,13 +174,13 @@ after_check: ; preds = %entry br i1 %18, label %after_check19, label %after_check19 after_check19: ; preds = %entry, %after_check, %after_check - %19 = call i8 @"std_collections_map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) + %19 = call i8 @"std.collections.map.HashMap$int$test.Foo$.has_key"(ptr %map, i32 1) store i8 %19, ptr %taddr, align 1 %20 = insertvalue %any undef, ptr %taddr, 0 %21 = insertvalue %any %20, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %21, ptr %varargslots20, align 16 %22 = call i64 @std.io.printfn(ptr %retparam21, ptr @.str.5, i64 9, ptr %varargslots20, i64 1) - %23 = call i8 @"std_collections_map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2) + %23 = call i8 @"std.collections.map.HashMap$int$test.Foo$.has_key"(ptr %map, i32 2) store i8 %23, ptr %taddr25, align 1 %24 = insertvalue %any undef, ptr %taddr25, 0 %25 = insertvalue %any %24, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 @@ -190,12 +190,12 @@ after_check19: ; preds = %entry, %after_check %lo30 = load i32, ptr %literal29, align 8 %ptradd31 = getelementptr inbounds i8, ptr %literal29, i64 8 %hi32 = load ptr, ptr %ptradd31, align 8 - %27 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i32 %lo30, ptr %hi32) + %27 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 7, i32 %lo30, ptr %hi32) %28 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo34 = load i64, ptr %28, align 8 %ptradd35 = getelementptr inbounds i8, ptr %28, i64 8 %hi36 = load ptr, ptr %ptradd35, align 8 - %29 = call { ptr, i64 } @"std_collections_map$int$test.Foo$.HashMap.values"(ptr %map, i64 %lo34, ptr %hi36) + %29 = call { ptr, i64 } @"std.collections.map.HashMap$int$test.Foo$.values"(ptr %map, i64 %lo34, ptr %hi36) store { ptr, i64 } %29, ptr %result, align 8 %30 = insertvalue %any undef, ptr %result, 0 %31 = insertvalue %any %30, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1 @@ -206,26 +206,26 @@ after_check19: ; preds = %entry, %after_check %lo40 = load i64, ptr %33, align 8 %ptradd41 = getelementptr inbounds i8, ptr %33, i64 8 %hi42 = load ptr, ptr %ptradd41, align 8 - %34 = call ptr @"std_collections_map$int$double$.HashMap.init"(ptr %map2, i64 %lo40, ptr %hi42, i32 16, float 7.500000e-01) - %35 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00) - %36 = call i8 @"std_collections_map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00) + %34 = call ptr @"std.collections.map.HashMap$int$double$.init"(ptr %map2, i64 %lo40, ptr %hi42, i32 16, float 7.500000e-01) + %35 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map2, i32 4, double 1.300000e+00) + %36 = call i8 @"std.collections.map.HashMap$int$double$.has_value"(ptr %map2, double 1.300000e+00) store i8 %36, ptr %taddr44, align 1 %37 = insertvalue %any undef, ptr %taddr44, 0 %38 = insertvalue %any %37, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %38, ptr %varargslots43, align 16 %39 = call i64 @std.io.printfn(ptr %retparam45, ptr @.str.9, i64 12, ptr %varargslots43, i64 1) - %40 = call i8 @"std_collections_map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00) + %40 = call i8 @"std.collections.map.HashMap$int$double$.has_value"(ptr %map2, double 1.200000e+00) store i8 %40, ptr %taddr49, align 1 %41 = insertvalue %any undef, ptr %taddr49, 0 %42 = insertvalue %any %41, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %42, ptr %varargslots48, align 16 %43 = call i64 @std.io.printfn(ptr %retparam50, ptr @.str.10, i64 12, ptr %varargslots48, i64 1) - %44 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00) + %44 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map2, i32 100, double 3.400000e+00) %45 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo54 = load i64, ptr %45, align 8 %ptradd55 = getelementptr inbounds i8, ptr %45, i64 8 %hi56 = load ptr, ptr %ptradd55, align 8 - %46 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.keys"(ptr %map2, i64 %lo54, ptr %hi56) + %46 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.keys"(ptr %map2, i64 %lo54, ptr %hi56) store { ptr, i64 } %46, ptr %result57, align 8 %47 = insertvalue %any undef, ptr %result57, 0 %48 = insertvalue %any %47, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 @@ -235,7 +235,7 @@ after_check19: ; preds = %entry, %after_check %lo62 = load i64, ptr %50, align 8 %ptradd63 = getelementptr inbounds i8, ptr %50, i64 8 %hi64 = load ptr, ptr %ptradd63, align 8 - %51 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.values"(ptr %map2, i64 %lo62, ptr %hi64) + %51 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.values"(ptr %map2, i64 %lo62, ptr %hi64) store { ptr, i64 } %51, ptr %result65, align 8 %52 = insertvalue %any undef, ptr %result65, 0 %53 = insertvalue %any %52, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 @@ -248,14 +248,14 @@ after_check19: ; preds = %entry, %after_check %lo69 = load i64, ptr %56, align 8 %ptradd70 = getelementptr inbounds i8, ptr %56, i64 8 %hi71 = load ptr, ptr %ptradd70, align 8 - %57 = call ptr @"std_collections_map$int$double$.HashMap.init"(ptr %map3, i64 %lo69, ptr %hi71, i32 16, float 7.500000e-01) - %58 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) - %59 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) + %57 = call ptr @"std.collections.map.HashMap$int$double$.init"(ptr %map3, i64 %lo69, ptr %hi71, i32 16, float 7.500000e-01) + %58 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map3, i32 5, double 3.200000e+00) + %59 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map3, i32 7, double 5.200000e+00) %60 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo73 = load i64, ptr %60, align 8 %ptradd74 = getelementptr inbounds i8, ptr %60, i64 8 %hi75 = load ptr, ptr %ptradd74, align 8 - %61 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.keys"(ptr %map3, i64 %lo73, ptr %hi75) + %61 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.keys"(ptr %map3, i64 %lo73, ptr %hi75) store { ptr, i64 } %61, ptr %result76, align 8 %62 = insertvalue %any undef, ptr %result76, 0 %63 = insertvalue %any %62, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 diff --git a/test/test_suite/stdlib/map_macos.c3t b/test/test_suite/stdlib/map_macos.c3t index f4bc05e48..d5ce8117e 100644 --- a/test/test_suite/stdlib/map_macos.c3t +++ b/test/test_suite/stdlib/map_macos.c3t @@ -88,7 +88,7 @@ entry: define void @test.main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %"HashMap{int, Foo}", align 8 %varargslots = alloca [1 x %any], align 16 %retparam = alloca i64, align 8 %literal = alloca %Foo, align 8 @@ -110,7 +110,7 @@ entry: %varargslots33 = alloca [1 x %any], align 16 %result = alloca %"Foo[]", align 8 %retparam37 = alloca i64, align 8 - %map2 = alloca %HashMap.0, align 8 + %map2 = alloca %"HashMap{int, double}", align 8 %varargslots43 = alloca [1 x %any], align 16 %taddr44 = alloca i8, align 1 %retparam45 = alloca i64, align 8 @@ -124,7 +124,7 @@ entry: %result65 = alloca %"double[]", align 8 %retparam66 = alloca i64, align 8 %state = alloca ptr, align 8 - %map3 = alloca %HashMap.0, align 8 + %map3 = alloca %"HashMap{int, double}", align 8 %varargslots72 = alloca [1 x %any], align 16 %result76 = alloca %"int[]", align 8 %retparam77 = alloca i64, align 8 @@ -133,7 +133,7 @@ entry: %lo = load i64, ptr %0, align 8 %ptradd = getelementptr inbounds i8, ptr %0, i64 8 %hi = load ptr, ptr %ptradd, align 8 - %1 = call ptr @"std_collections_map$int$test.Foo$.HashMap.init"(ptr %map, i64 %lo, ptr %hi, i32 16, float 7.500000e-01) + %1 = call ptr @"std.collections.map.HashMap$int$test.Foo$.init"(ptr %map, i64 %lo, ptr %hi, i32 16, float 7.500000e-01) %ptradd1 = getelementptr inbounds i8, ptr %map, i64 32 %2 = insertvalue %any undef, ptr %ptradd1, 0 %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 @@ -143,7 +143,7 @@ entry: %lo2 = load i32, ptr %literal, align 8 %ptradd3 = getelementptr inbounds i8, ptr %literal, i64 8 %hi4 = load ptr, ptr %ptradd3, align 8 - %5 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i32 %lo2, ptr %hi4) + %5 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 1, i32 %lo2, ptr %hi4) %ptradd6 = getelementptr inbounds i8, ptr %map, i64 32 %6 = insertvalue %any undef, ptr %ptradd6, 0 %7 = insertvalue %any %6, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 @@ -153,13 +153,13 @@ entry: %lo9 = load i32, ptr %literal8, align 8 %ptradd10 = getelementptr inbounds i8, ptr %literal8, i64 8 %hi11 = load ptr, ptr %ptradd10, align 8 - %9 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i32 %lo9, ptr %hi11) + %9 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 1, i32 %lo9, ptr %hi11) %ptradd13 = getelementptr inbounds i8, ptr %map, i64 32 %10 = insertvalue %any undef, ptr %ptradd13, 0 %11 = insertvalue %any %10, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 store %any %11, ptr %varargslots12, align 16 %12 = call i64 @std.io.printfn(ptr %retparam14, ptr @.str.3, i64 12, ptr %varargslots12, i64 1) - %13 = call i64 @"std_collections_map$int$test.Foo$.HashMap.get"(ptr %retparam16, ptr %map, i32 1) + %13 = call i64 @"std.collections.map.HashMap$int$test.Foo$.get"(ptr %retparam16, ptr %map, i32 1) %not_err = icmp eq i64 %13, 0 %14 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %14, label %after_check, label %after_check19 @@ -174,13 +174,13 @@ after_check: ; preds = %entry br i1 %18, label %after_check19, label %after_check19 after_check19: ; preds = %entry, %after_check, %after_check - %19 = call i8 @"std_collections_map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) + %19 = call i8 @"std.collections.map.HashMap$int$test.Foo$.has_key"(ptr %map, i32 1) store i8 %19, ptr %taddr, align 1 %20 = insertvalue %any undef, ptr %taddr, 0 %21 = insertvalue %any %20, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %21, ptr %varargslots20, align 16 %22 = call i64 @std.io.printfn(ptr %retparam21, ptr @.str.5, i64 9, ptr %varargslots20, i64 1) - %23 = call i8 @"std_collections_map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2) + %23 = call i8 @"std.collections.map.HashMap$int$test.Foo$.has_key"(ptr %map, i32 2) store i8 %23, ptr %taddr25, align 1 %24 = insertvalue %any undef, ptr %taddr25, 0 %25 = insertvalue %any %24, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 @@ -190,12 +190,12 @@ after_check19: ; preds = %entry, %after_check %lo30 = load i32, ptr %literal29, align 8 %ptradd31 = getelementptr inbounds i8, ptr %literal29, i64 8 %hi32 = load ptr, ptr %ptradd31, align 8 - %27 = call i8 @"std_collections_map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i32 %lo30, ptr %hi32) + %27 = call i8 @"std.collections.map.HashMap$int$test.Foo$.set"(ptr %map, i32 7, i32 %lo30, ptr %hi32) %28 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo34 = load i64, ptr %28, align 8 %ptradd35 = getelementptr inbounds i8, ptr %28, i64 8 %hi36 = load ptr, ptr %ptradd35, align 8 - %29 = call { ptr, i64 } @"std_collections_map$int$test.Foo$.HashMap.values"(ptr %map, i64 %lo34, ptr %hi36) + %29 = call { ptr, i64 } @"std.collections.map.HashMap$int$test.Foo$.values"(ptr %map, i64 %lo34, ptr %hi36) store { ptr, i64 } %29, ptr %result, align 8 %30 = insertvalue %any undef, ptr %result, 0 %31 = insertvalue %any %30, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1 @@ -206,26 +206,26 @@ after_check19: ; preds = %entry, %after_check %lo40 = load i64, ptr %33, align 8 %ptradd41 = getelementptr inbounds i8, ptr %33, i64 8 %hi42 = load ptr, ptr %ptradd41, align 8 - %34 = call ptr @"std_collections_map$int$double$.HashMap.init"(ptr %map2, i64 %lo40, ptr %hi42, i32 16, float 7.500000e-01) - %35 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00) - %36 = call i8 @"std_collections_map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00) + %34 = call ptr @"std.collections.map.HashMap$int$double$.init"(ptr %map2, i64 %lo40, ptr %hi42, i32 16, float 7.500000e-01) + %35 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map2, i32 4, double 1.300000e+00) + %36 = call i8 @"std.collections.map.HashMap$int$double$.has_value"(ptr %map2, double 1.300000e+00) store i8 %36, ptr %taddr44, align 1 %37 = insertvalue %any undef, ptr %taddr44, 0 %38 = insertvalue %any %37, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %38, ptr %varargslots43, align 16 %39 = call i64 @std.io.printfn(ptr %retparam45, ptr @.str.9, i64 12, ptr %varargslots43, i64 1) - %40 = call i8 @"std_collections_map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00) + %40 = call i8 @"std.collections.map.HashMap$int$double$.has_value"(ptr %map2, double 1.200000e+00) store i8 %40, ptr %taddr49, align 1 %41 = insertvalue %any undef, ptr %taddr49, 0 %42 = insertvalue %any %41, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 store %any %42, ptr %varargslots48, align 16 %43 = call i64 @std.io.printfn(ptr %retparam50, ptr @.str.10, i64 12, ptr %varargslots48, i64 1) - %44 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00) + %44 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map2, i32 100, double 3.400000e+00) %45 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo54 = load i64, ptr %45, align 8 %ptradd55 = getelementptr inbounds i8, ptr %45, i64 8 %hi56 = load ptr, ptr %ptradd55, align 8 - %46 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.keys"(ptr %map2, i64 %lo54, ptr %hi56) + %46 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.keys"(ptr %map2, i64 %lo54, ptr %hi56) store { ptr, i64 } %46, ptr %result57, align 8 %47 = insertvalue %any undef, ptr %result57, 0 %48 = insertvalue %any %47, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 @@ -235,7 +235,7 @@ after_check19: ; preds = %entry, %after_check %lo62 = load i64, ptr %50, align 8 %ptradd63 = getelementptr inbounds i8, ptr %50, i64 8 %hi64 = load ptr, ptr %ptradd63, align 8 - %51 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.values"(ptr %map2, i64 %lo62, ptr %hi64) + %51 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.values"(ptr %map2, i64 %lo62, ptr %hi64) store { ptr, i64 } %51, ptr %result65, align 8 %52 = insertvalue %any undef, ptr %result65, 0 %53 = insertvalue %any %52, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 @@ -248,14 +248,14 @@ after_check19: ; preds = %entry, %after_check %lo69 = load i64, ptr %56, align 8 %ptradd70 = getelementptr inbounds i8, ptr %56, i64 8 %hi71 = load ptr, ptr %ptradd70, align 8 - %57 = call ptr @"std_collections_map$int$double$.HashMap.init"(ptr %map3, i64 %lo69, ptr %hi71, i32 16, float 7.500000e-01) - %58 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) - %59 = call i8 @"std_collections_map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) + %57 = call ptr @"std.collections.map.HashMap$int$double$.init"(ptr %map3, i64 %lo69, ptr %hi71, i32 16, float 7.500000e-01) + %58 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map3, i32 5, double 3.200000e+00) + %59 = call i8 @"std.collections.map.HashMap$int$double$.set"(ptr %map3, i32 7, double 5.200000e+00) %60 = call ptr @llvm.threadlocal.address.p0(ptr @std.core.mem.allocator.thread_allocator) %lo73 = load i64, ptr %60, align 8 %ptradd74 = getelementptr inbounds i8, ptr %60, i64 8 %hi75 = load ptr, ptr %ptradd74, align 8 - %61 = call { ptr, i64 } @"std_collections_map$int$double$.HashMap.keys"(ptr %map3, i64 %lo73, ptr %hi75) + %61 = call { ptr, i64 } @"std.collections.map.HashMap$int$double$.keys"(ptr %map3, i64 %lo73, ptr %hi75) store { ptr, i64 } %61, ptr %result76, align 8 %62 = insertvalue %any undef, ptr %result76, 0 %63 = insertvalue %any %62, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 diff --git a/test/test_suite/stdlib/priorityqueue.c3t b/test/test_suite/stdlib/priorityqueue.c3t index ba18f1f3d..f02441e3f 100644 --- a/test/test_suite/stdlib/priorityqueue.c3t +++ b/test/test_suite/stdlib/priorityqueue.c3t @@ -28,4 +28,4 @@ fn bool Foo.less(Foo* x, Foo y) @inline /* #expect: test.ll -%PrivatePriorityQueue = type { %List } +%"PrivatePriorityQueue{Foo, true}" = type { %"List{Foo}" } diff --git a/test/test_suite/struct/struct_pack_error.c3t b/test/test_suite/struct/struct_pack_error.c3t index 9d2a8bfe3..233b624fa 100644 --- a/test/test_suite/struct/struct_pack_error.c3t +++ b/test/test_suite/struct/struct_pack_error.c3t @@ -35,9 +35,10 @@ fn void main() /* #expect: test.ll -%Client = type <{ i32, %MsgHeader, i16, [2 x i8], %ElasticArray }> +%Client = type <{ i32, %MsgHeader, i16, [2 x i8], %"ElasticArray{ulong, 1}" }> %MsgHeader = type { i64 } -%ElasticArray = type { i64, [1 x i64] } +%"ElasticArray{ulong, 1}" = type { i64, [1 x i64] } + define void @test.main() #0 { entry: %a = alloca ptr, align 8 diff --git a/test/test_suite/switch/switch_in_defer_macro.c3t b/test/test_suite/switch/switch_in_defer_macro.c3t index a8017db4e..6cc6f7a61 100644 --- a/test/test_suite/switch/switch_in_defer_macro.c3t +++ b/test/test_suite/switch/switch_in_defer_macro.c3t @@ -674,9 +674,9 @@ fn void test() %"UintTest[]" = type { ptr, i64 } %UintTest = type { %"char[]", i64 } %ByteReader = type { %"char[]", i64 } -%Lexer = type { %any, %any, %"char[]", %Trie, ptr, i8, i8, i32, i32, i32, %.anon } -%Trie = type { %List } -%List = type { i64, i64, %any, ptr } +%"Lexer{Token, Comment}" = type { %any, %any, %"char[]", %"Trie{Token, ushort}", ptr, i8, i8, i32, i32, i32, %.anon } +%"Trie{Token, ushort}" = type { %"List{TrieNode{Token, ushort}}" } +%"List{TrieNode{Token, ushort}}" = type { i64, i64, %any, ptr } %.anon = type { %"char[]" } @"$ct.lexer_test.UintTest" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 24, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @@ -750,7 +750,7 @@ entry: %.anon = alloca i64, align 8 %tc = alloca %UintTest, align 8 %br = alloca %ByteReader, align 8 - %lex = alloca %Lexer, align 8 + %lex = alloca %"Lexer{Token, Comment}", align 8 %error_var = alloca i64, align 8 %taddr = alloca %any, align 8 %kind = alloca i8, align 1 @@ -788,7 +788,7 @@ loop.body: ; preds = %loop.cond %lo5 = load i64, ptr %7, align 8 %ptradd6 = getelementptr inbounds i8, ptr %7, i64 8 %hi7 = load ptr, ptr %ptradd6, align 8 - %8 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.init"(ptr %lex, i64 %lo2, ptr %hi4, ptr @lexer_test.is_ident_char, i64 %lo5, ptr %hi7) + %8 = call i64 @"lexer.Lexer$lexer_test.Token$lexer_test.Comment$.init"(ptr %lex, i64 %lo2, ptr %hi4, ptr @lexer_test.is_ident_char, i64 %lo5, ptr %hi7) %not_err = icmp eq i64 %8, 0 %9 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %9, label %after_check, label %assign_optional @@ -805,7 +805,7 @@ guard_block: ; preds = %assign_optional ret i64 %10 noerr_block: ; preds = %after_check - %11 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex) + %11 = call i64 @"lexer.Lexer$lexer_test.Token$lexer_test.Comment$.next"(ptr %retparam, ptr %lex) %not_err9 = icmp eq i64 %11, 0 %12 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true) br i1 %12, label %after_check11, label %assign_optional10