From 47c8f99b6ef812dbc22ca56a39ba21bccad47796 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 8 Sep 2026 20:43:39 +0200 Subject: Implement generic pointers --- boot/name_analysis.cc | 123 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) (limited to 'boot/name_analysis.cc') diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index a410ca8..a979a37 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -231,7 +231,7 @@ namespace elna::boot static void collect_field_names(const type& composite_type, ordered_map& names) { - auto record = resolve_underlying_type(composite_type).get(); + auto record = erase_generic(composite_type).get(); if (record == nullptr) { return; @@ -446,6 +446,38 @@ namespace elna::boot } } + std::vector resolving_visitor::enter_parameters(const std::vector& parameters) + { + std::vector result; + + result.reserve(parameters.size()); + for (const identifier& parameter : parameters) + { + const type parameter_symbol = type(std::make_shared(parameter.name())); + auto info = std::make_shared(parameter_symbol); + + info->position.emplace(parameter.position()); + info->file = this->module_file; + + if (this->bag.enter(parameter.name(), info)) + { + result.push_back(parameter_symbol); + } + else + { + auto original = this->bag.lookup(parameter.name()); + + add_error(parameter.position(), parameter.name(), + symbol_declaration_error::redefinition{ + .original = original->position, + .file = this->redefinition_file(original) + }); + result.push_back(parameter_symbol); + } + } + return result; + } + type resolving_visitor::resolve_type(type_expression& expression) { expression.accept(this); @@ -535,15 +567,17 @@ namespace elna::boot std::shared_ptr result_type; if (expression->base.has_value()) { + type base_type; + if (auto unresolved_alias = this->bag.declared(expression->base.value().name())) { - result_type = std::make_shared(type(unresolved_alias)); + base_type = type(unresolved_alias); } else if (auto base_symbol = this->bag.lookup(expression->base.value().name())) { if (auto base_type_info = base_symbol->is_type()) { - result_type = std::make_shared(base_type_info->symbol); + base_type = base_type_info->symbol; } else { @@ -560,6 +594,12 @@ namespace elna::boot this->current_type = type(); return; } + if (!expression->base_arguments.empty()) + { + base_type = type(std::make_shared(base_type, + resolve_arguments(expression->base_arguments))); + } + result_type = std::make_shared(base_type); } else { @@ -736,7 +776,10 @@ namespace elna::boot { if (auto type_info = type_symbol->is_type()) { - expression->type_decoration = type_info->symbol; + expression->type_decoration = expression->arguments.empty() + ? type_info->symbol + : type(std::make_shared(type_info->symbol, + resolve_arguments(expression->arguments))); } } else @@ -884,7 +927,16 @@ namespace elna::boot } else if (auto procedure_symbol = from_symbol_table->is_procedure()) { - expression->type_decoration = type(std::make_shared(procedure_symbol->symbol)); + type procedure = type(std::make_shared(procedure_symbol->symbol)); + + if (!expression->arguments.empty()) + { + expression->argument_types = resolve_arguments(expression->arguments); + procedure = substitute(procedure, procedure_symbol->parameters, + expression->argument_types); + } + expression->type_decoration = procedure; + return; } } else @@ -892,6 +944,32 @@ namespace elna::boot add_error(expression->position(), expression->name, symbol_declaration_error::kind::undeclared_symbol); } + if (!expression->arguments.empty() && !this->current_type.empty()) + { + const type generic = this->current_type; + + expression->argument_types = resolve_arguments(expression->arguments); + this->current_type = type(std::make_shared(generic, + std::vector(expression->argument_types))); + } + // Type check decides whether the name was applied correctly, so what a + // type expression resolved to has to reach it. + if (expression->type_decoration.empty()) + { + expression->type_decoration = this->current_type; + } + } + + std::vector resolving_visitor::resolve_arguments(const std::vector& arguments) + { + std::vector result; + + result.reserve(arguments.size()); + for (type_expression *argument : arguments) + { + result.push_back(resolve_type(*argument)); + } + return result; } void resolving_visitor::visit(array_access_expression *expression) @@ -1096,6 +1174,17 @@ namespace elna::boot { return find_alias_cycle(being_resolved, link->base, alias_path, state); } + else if (auto link = referent.get()) + { + return find_alias_cycle(being_resolved, link->referent, alias_path, state); + } + else if (auto link = referent.get()) + { + // Erasure is argument blind, so the arguments cannot close a cycle + // the generic does not close on its own. This is what lets + // polymorphic recursion terminate. + return find_alias_cycle(being_resolved, link->generic, alias_path, state); + } else if (auto link = referent.get()) { const std::size_t saved_path = alias_path.size(); @@ -1196,8 +1285,22 @@ namespace elna::boot void declaration_visitor::visit(type_declaration *declaration) { + const bool is_generic = !declaration->parameters.empty(); + std::vector parameters; + + if (is_generic) + { + this->bag.enter(); + parameters = enter_parameters(declaration->parameters); + } type underlying = resolve_type(declaration->underlying_type()); + if (is_generic) + { + this->bag.leave(); + underlying = type(std::make_shared(std::move(parameters), underlying)); + } + // Reject the cycle and wire an empty referent: the declaration still // resolves and is entered, so its uses degrade silently. if (auto cycle = find_alias_cycle( @@ -1228,12 +1331,17 @@ namespace elna::boot void declaration_visitor::visit(procedure_declaration *declaration) { + // The scope is opened before the heading is built so that the type + // parameters are visible in it, and it becomes the body's scope, which + // is why the body sees them too. + const std::shared_ptr scope = this->bag.enter(); + const std::vector type_parameters = enter_parameters(declaration->parameters); auto [heading, parameter_names] = build_procedure(declaration->heading()); std::shared_ptr info; if (declaration->body.has_value()) { - info = std::make_shared(heading, std::move(parameter_names), this->bag.enter()); + info = std::make_shared(heading, std::move(parameter_names), scope); auto name_iterator = std::cbegin(info->names); auto type_iterator = std::cbegin(info->symbol.parameters); @@ -1245,12 +1353,13 @@ namespace elna::boot ++name_iterator; ++type_iterator; } - this->bag.leave(); } else { info = std::make_shared(heading, std::move(parameter_names)); } + this->bag.leave(); + info->parameters = type_parameters; info->exported = declaration->identifier.exported(); info->position.emplace(declaration->position()); info->file = this->module_file; -- cgit v1.2.3