diff --git a/releasenotes.md b/releasenotes.md index 493229809..78dbbc124 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -12,6 +12,7 @@ - Add `"name"` project property to override the name of the resulting binary. #1719 - Improved `add-project` to take arguments. - Improve error reporting when using type names as the function argument #1750. +- Improve ordering of method registration to support adding methods to generic modules with method constraints #1746 ### Fixes - Fix case trying to initialize a `char[*]*` from a String. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 6100eacbc..2664f52d1 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1636,6 +1636,7 @@ struct CompilationUnit_ Decl **vars; Decl **macros; Decl **methods_to_register; + Decl **generic_methods_to_register; Decl **methods; Decl **macro_methods; Decl **global_decls; @@ -2312,6 +2313,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 sema_analyse_decl(SemaContext *context, Decl *decl); + bool sema_analyse_method_register(SemaContext *context, Decl *method); bool sema_resolve_type_structure(SemaContext *context, Type *type, SourceSpan span); bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl); @@ -2347,6 +2349,7 @@ BoolErr sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol); bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, ArraySize *len_ref); bool sema_resolve_type_info(SemaContext *context, TypeInfo *type_info, ResolveTypeKind kind); +bool sema_unresolved_type_is_generic(SemaContext *context, TypeInfo *type_info); void print_error_at(SourceSpan loc, const char *message, ...); void print_error_after(SourceSpan loc, const char *message, ...); diff --git a/src/compiler/context.c b/src/compiler/context.c index 93d7a65a9..5b4dfa445 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -187,6 +187,11 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) ASSERT0(decl->name); if (decl->func_decl.type_parent) { + if (type_infoptr(decl->func_decl.type_parent)->kind == TYPE_INFO_GENERIC) + { + vec_add(unit->generic_methods_to_register, decl); + return; + } vec_add(unit->methods_to_register, decl); return; } @@ -200,6 +205,11 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) ASSERT0(decl->name); if (decl->func_decl.type_parent) { + if (type_infoptr(decl->func_decl.type_parent)->kind == TYPE_INFO_GENERIC) + { + vec_add(unit->generic_methods_to_register, decl); + return; + } vec_add(unit->methods_to_register, decl); return; } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index dadec433b..956e891f3 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -47,11 +47,14 @@ typedef enum ANALYSIS_IMPORTS, ANALYSIS_REGISTER_GLOBAL_DECLARATIONS, ANALYSIS_METHODS_REGISTER, + ANALYSIS_METHODS_REGISTER_GENERIC, ANALYSIS_INCLUDES, ANALYSIS_METHODS_INCLUDES, + ANALYSIS_METHODS_INCLUDES_GENERIC, ANALYSIS_REGISTER_CONDITIONAL_UNITS, ANALYSIS_REGISTER_CONDITIONAL_DECLARATIONS, ANALYSIS_METHODS_CONDITIONAL, + ANALYSIS_METHODS_CONDITIONAL_GENERIC, ANALYSIS_POST_REGISTER, ANALYSIS_DECLS, ANALYSIS_CT_ECHO, diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index d34e1f657..21e8aed68 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -77,7 +77,7 @@ void sema_analysis_pass_register_global_declarations(Module *module); void sema_analysis_pass_process_includes(Module *module); void sema_analysis_pass_register_conditional_units(Module *module); void sema_analysis_pass_register_conditional_declarations(Module *module); -void sema_analysis_pass_process_methods(Module *module); +void sema_analysis_pass_process_methods(Module *module, bool process_generic); void sema_analysis_pass_decls(Module *module); void sema_analysis_pass_ct_assert(Module *module); void sema_analysis_pass_ct_echo(Module *module); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 139b7429c..9656a173a 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -386,15 +386,21 @@ void sema_analysis_pass_process_includes(Module *module) } -void sema_analysis_pass_process_methods(Module *module) +void sema_analysis_pass_process_methods(Module *module, bool process_generic) { DEBUG_LOG("Pass: Process methods register for module '%s'.", module->name->module); FOREACH(CompilationUnit *, unit, module->units) { SemaContext context; sema_context_init(&context, unit); - FOREACH(Decl *, method, unit->methods_to_register) + FOREACH(Decl *, method, process_generic ? unit->generic_methods_to_register : unit->methods_to_register) { + TypeInfo *parent_type_info = type_infoptr(method->func_decl.type_parent); + if (!process_generic && sema_unresolved_type_is_generic(&context, parent_type_info)) + { + vec_add(unit->generic_methods_to_register, method); + continue; + } sema_analyse_method_register(&context, method); if (method->decl_kind == DECL_MACRO) { @@ -406,7 +412,14 @@ void sema_analysis_pass_process_methods(Module *module) } } sema_context_destroy(&context); - vec_resize(unit->methods_to_register, 0); + if (process_generic) + { + vec_resize(unit->generic_methods_to_register, 0); + } + else + { + vec_resize(unit->methods_to_register, 0); + } } DEBUG_LOG("Pass finished with %d error(s).", compiler.context.errors_found); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 652115ec4..f1feb362d 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -363,6 +363,21 @@ INLINE bool sema_resolve_vatype(SemaContext *context, TypeInfo *type_info) return true; } +bool sema_unresolved_type_is_generic(SemaContext *context, TypeInfo *type_info) +{ + RETRY: + if (type_info->kind == TYPE_INFO_GENERIC) return true; + if (type_info->resolve_status == RESOLVE_DONE) return false; + if (type_info->kind != TYPE_INFO_IDENTIFIER) return false; + if (type_info->subtype != TYPE_COMPRESSED_NONE) return false; + Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name, type_info->unresolved.path, type_info->span); + if (decl->decl_kind != DECL_TYPEDEF) return false; + if (decl->resolve_status == RESOLVE_DONE) return false; + if (decl->typedef_decl.is_func) return false; + type_info = decl->typedef_decl.type_info; + goto RETRY; +} + // Foo(<...>) INLINE bool sema_resolve_generic_type(SemaContext *context, TypeInfo *type_info) { diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index d1ea7a0ef..5df72f52e 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -159,13 +159,19 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) sema_analysis_pass_register_global_declarations(module); break; case ANALYSIS_METHODS_REGISTER: - sema_analysis_pass_process_methods(module); + sema_analysis_pass_process_methods(module, false); + break; + case ANALYSIS_METHODS_REGISTER_GENERIC: + sema_analysis_pass_process_methods(module, true); break; case ANALYSIS_INCLUDES: sema_analysis_pass_process_includes(module); break; case ANALYSIS_METHODS_INCLUDES: - sema_analysis_pass_process_methods(module); + sema_analysis_pass_process_methods(module, false); + break; + case ANALYSIS_METHODS_INCLUDES_GENERIC: + sema_analysis_pass_process_methods(module, true); break; case ANALYSIS_REGISTER_CONDITIONAL_UNITS: sema_analysis_pass_register_conditional_units(module); @@ -174,7 +180,10 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) sema_analysis_pass_register_conditional_declarations(module); break; case ANALYSIS_METHODS_CONDITIONAL: - sema_analysis_pass_process_methods(module); + sema_analysis_pass_process_methods(module, false); + break; + case ANALYSIS_METHODS_CONDITIONAL_GENERIC: + sema_analysis_pass_process_methods(module, true); break; case ANALYSIS_POST_REGISTER: break; @@ -265,7 +274,6 @@ 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); diff --git a/test/test_suite/methods/extension_method_check.c3 b/test/test_suite/methods/extension_method_check.c3 new file mode 100644 index 000000000..ae5ad5226 --- /dev/null +++ b/test/test_suite/methods/extension_method_check.c3 @@ -0,0 +1,19 @@ +module test; + +def Bob = Test(); +fn void Bob.crash(&self) {} + +fn int main() +{ + Test() foo; + return 0; +} +<* + @require $defined(String.hash) +*> +module test::generic(); + +enum Test +{ + ABC +} \ No newline at end of file