Improve ordering of method registration to support adding methods to generic modules with method constraints #1746

This commit is contained in:
Christoffer Lerno
2024-12-31 18:15:38 +01:00
parent 5e32c8a828
commit c0dcae4f1d
9 changed files with 80 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
module test;
def Bob = Test(<any>);
fn void Bob.crash(&self) {}
fn int main()
{
Test(<any>) foo;
return 0;
}
<*
@require $defined(String.hash)
*>
module test::generic(<Some_Type>);
enum Test
{
ABC
}