Expand inference.

This commit is contained in:
Christoffer Lerno
2026-01-17 14:03:58 +01:00
parent 70c4b24519
commit c2e603ddd8
11 changed files with 248 additions and 175 deletions

View File

@@ -895,7 +895,7 @@ static void print_arch_asm(PlatformTarget *target)
default:
UNREACHABLE_VOID
}
if (scratch_buffer.len) scratch_buffer.len -= 2;
if (scratch_buffer.len) scratch_buffer_delete(2);
printf("%-30s | ", scratch_buffer_to_string());
int len = 0;
for (unsigned j = 0; j < instruction->param_count; j++)

View File

@@ -635,6 +635,7 @@ typedef struct
const char *cname_suffix;
Decl **generated_decls;
} GenericInstanceDecl;
typedef struct
{
bool is_func : 1;

View File

@@ -66,7 +66,7 @@ static bool filename_to_module_in_buffer(const char *path)
}
scratch_buffer_append_char(c);
}
if (last_was_underscore && scratch_buffer.len) scratch_buffer.len--;
if (last_was_underscore && scratch_buffer.len) scratch_buffer_delete(1);
if (!scratch_buffer.len) return false;
return true;
}

View File

@@ -5192,6 +5192,179 @@ static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module
return true;
}
Decl *sema_generate_parameterized_identifier(SemaContext *context, Decl *generic, Decl *alias, Expr **params, Decl **param_decls, const char *suffix, const char *csuffix, SourceSpan invocation_span, SourceSpan span)
{
Module *module = alias->unit->module;
Decl *instance = NULL;
unsigned id = generic->generic_decl.id;
FOREACH(Decl *, g, alias->unit->module->generic_sections)
{
if (g->generic_decl.id != id) continue;
FOREACH (Decl *, candidate, g->generic_decl.instances)
{
if (candidate->name == csuffix)
{
instance = candidate;
goto FOUND;
}
}
}
FOUND:;
if (!instance)
{
DEBUG_LOG("Generate generic instance %s", csuffix);
if (compiler.context.errors_found) return poisoned_decl;
instance = decl_new(DECL_GENERIC_INSTANCE, csuffix, generic->span);
FOREACH_IDX(i, const char *, param_name, generic->generic_decl.parameters)
{
Decl *decl;
Expr *param = params ? params[i] : copy_expr_single(param_decls[i]->var.init_expr);
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 (!suffix)
{
if (!sema_generate_parameter_suffix_to_scratch(context, params, false, false)) return poisoned_decl;
suffix = scratch_buffer_interned();
}
instance->instance_decl.name_suffix = suffix;
instance->instance_decl.cname_suffix = csuffix;
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, VECLAST(params)->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, alias->name);
if (!symbol)
{
sema_error_at(context, span, "The generic '%s' does not exist for this parameterization.", alias->name);
return poisoned_decl;
}
CompilationUnit *unit = symbol->unit;
if (unit->module->stage < ANALYSIS_POST_REGISTER)
{
vec_add(unit->global_decls, symbol);
}
else
{
if (!sema_analyse_decl(context, symbol)) return poisoned_decl;
}
unit_register_external_symbol(context, symbol);
return symbol;
}
Decl *sema_analyse_parameterized_identifier(SemaContext *context, Path *decl_path, const char *name, SourceSpan span,
Expr **params, bool *was_recursive_ref, SourceSpan invocation_span)
{
@@ -5261,176 +5434,9 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *context, Path *decl_pat
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)
{
if (g->generic_decl.id != id) continue;
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)
{
vec_add(unit->global_decls, symbol);
}
else
{
if (!sema_analyse_decl(context, symbol)) return poisoned_decl;
}
unit_register_external_symbol(context, symbol);
return symbol;
return sema_generate_parameterized_identifier(context, generic, alias, params, NULL, NULL, suffix, invocation_span, span);
}
static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext *c, Decl *decl, bool *erase_decl)

View File

@@ -127,6 +127,8 @@ 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_generate_parameterized_identifier(SemaContext *context, Decl *generic, Decl *alias, Expr **params,
Decl **param_decls, const char *suffix, const char *csuffix, SourceSpan invocation_span, 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);

View File

@@ -785,6 +785,22 @@ INLINE Module *sema_find_module_for_path(SemaContext *context, Path *path)
return NULL;
}
static inline bool sema_has_matching_generic_params(Decl *generic_instance, Decl *generic)
{
ASSERT(generic_instance->instance_decl.id != generic->generic_decl.id);
GenericInstanceDecl *gen_instance_decl = &generic_instance->instance_decl;
GenericDecl *gen_decl = &generic->generic_decl;
if (vec_size(gen_instance_decl->params) != vec_size(gen_decl->parameters)) return false;
FOREACH_IDX(i, Decl *, param, gen_instance_decl->params)
{
const char *param_name = gen_decl->parameters[i];
const char *param_name_other = param->name;
bool is_constant_a = str_is_valid_constant(param_name);
bool is_constant_b = str_is_valid_constant(param_name_other);
if (is_constant_a != is_constant_b) return false;
}
return true;
}
INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_resolve)
{
name_resolve->ambiguous_other_decl = NULL;
@@ -853,9 +869,22 @@ INLINE bool sema_resolve_symbol_common(SemaContext *context, NameResolve *name_r
{
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 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);
if (!sema_has_matching_generic_params(context->generic_infer, generic))
{
if (name_resolve->suppress_error) return name_resolve->found = NULL, true;
scratch_buffer_clear();
FOREACH (const char *, name, generic->generic_decl.parameters)
{
scratch_buffer_printf("%s, ", name);
}
scratch_buffer_delete(2);
RETURN_SEMA_ERROR_AT(name_resolve->span, "Found '%s' in the module '%s', but it lacks parameters. Inferring parameter values does not work, since the inferred parameter list '%s' doesn't match parameter list '%s' which %s expects.", found->name, found->unit->module->name->module, context->generic_infer->instance_decl.name_suffix, scratch_buffer_to_string(), found->name);
}
Decl *decl = sema_generate_parameterized_identifier(context, generic, found, NULL, context->generic_infer->instance_decl.params,
context->generic_infer->instance_decl.name_suffix, context->generic_infer->instance_decl.cname_suffix, name_resolve->span, name_resolve->span);
if (!decl_ok(decl)) return false;
ASSERT(decl);
return name_resolve->found = decl, 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;

View File

@@ -611,7 +611,7 @@ static void cpu_features_set_to_features(CpuFeatures enabled_features, CpuFeatur
scratch_buffer_append(list[i]);
scratch_buffer_append_char(',');
}
if (scratch_buffer.len > 0) scratch_buffer.len--;
if (scratch_buffer.len > 0) scratch_buffer_delete(1);
compiler.platform.features = scratch_buffer_copy();
}

View File

@@ -172,6 +172,7 @@ static inline StringSlice slice_from_string(const char *data);
void slice_trim(StringSlice *slice);
void scratch_buffer_clear(void);
void scratch_buffer_delete(size_t len);
void scratch_buffer_append(const char *string);
void scratch_buffer_append_len(const char *string, size_t len);
bool scratch_buffer_may_append(size_t len);

View File

@@ -362,6 +362,12 @@ char *str_copy(const char *start, size_t str_len)
return dst;
}
void scratch_buffer_delete(size_t len)
{
ASSERT(scratch_buffer.len >= len);
scratch_buffer.len -= len;
}
void scratch_buffer_clear(void)
{
scratch_buffer.len = 0;
@@ -553,7 +559,7 @@ void scratch_buffer_append_remove_space(const char *start, int len)
clast = ch;
printed++;
}
if (clast == ' ' && printed > 0) scratch_buffer.len--;
if (clast == ' ' && printed > 0) scratch_buffer_delete(1);
}
void scratch_buffer_append_char(char c)

View File

@@ -0,0 +1,14 @@
import std;
struct Result @generic(WithType2)
{
fault error;
}
macro err(fault error) @generic(OfType, SOME_CONST) @builtin => (Result{OfType}){ .error = error };
faultdef TESTIN;
fn int main()
{
Result{int} x = err(TESTIN); // #error: Found 'err' in the module 'generic_infer_mismatch', but it lacks parameters
return 0;
}

View File

@@ -0,0 +1,14 @@
import std;
struct Result @generic(WithType2)
{
fault error;
}
macro err(fault error) @generic(OfType) @builtin => (Result{OfType}){ .error = error };
faultdef TESTIN;
fn int main()
{
Result{int} x = err(TESTIN);
return 0;
}