diff --git a/src/compiler/asm_target.c b/src/compiler/asm_target.c index 32ff2f6c9..bbd8d5beb 100644 --- a/src/compiler/asm_target.c +++ b/src/compiler/asm_target.c @@ -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++) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ad3190a1f..682b1e444 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -635,6 +635,7 @@ typedef struct const char *cname_suffix; Decl **generated_decls; } GenericInstanceDecl; + typedef struct { bool is_func : 1; diff --git a/src/compiler/context.c b/src/compiler/context.c index fba352781..da51cb4a7 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -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; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6977b6843..720a2f537 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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) diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index f363112fa..9c16df82a 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -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); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 74073972d..588f3585b 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -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; diff --git a/src/compiler/target.c b/src/compiler/target.c index 2d05e0f17..a98b6b43e 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -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(); } diff --git a/src/utils/lib.h b/src/utils/lib.h index c16df6afb..cc32b1d90 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -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); diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index 09dbada47..9bca4db3e 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -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) diff --git a/test/test_suite/generic/generic_infer_mismatch.c3 b/test/test_suite/generic/generic_infer_mismatch.c3 new file mode 100644 index 000000000..583427953 --- /dev/null +++ b/test/test_suite/generic/generic_infer_mismatch.c3 @@ -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; +} \ No newline at end of file diff --git a/test/test_suite/generic/generic_infer_ok.c3 b/test/test_suite/generic/generic_infer_ok.c3 new file mode 100644 index 000000000..655bf393b --- /dev/null +++ b/test/test_suite/generic/generic_infer_ok.c3 @@ -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; +} \ No newline at end of file