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/symbol.cc | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 7 deletions(-) (limited to 'boot/symbol.cc') diff --git a/boot/symbol.cc b/boot/symbol.cc index 0d847e1..9092866 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -119,6 +119,22 @@ namespace elna::boot return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type && left_procedure->parameters == right_procedure->parameters; } + if (auto left_parameter = resolved_this.get()) + { + return left_parameter == resolved_that.get(); + } + if (auto left_generic = resolved_this.get()) + { + return left_generic == resolved_that.get(); + } + if (auto left_instance = resolved_this.get()) + { + auto right_instance = resolved_that.get(); + + return right_instance != nullptr + && left_instance->generic == right_instance->generic + && left_instance->arguments == right_instance->arguments; + } return resolved_this.empty() && resolved_that.empty(); } @@ -192,6 +208,18 @@ namespace elna::boot { return "extern"; } + else if constexpr (std::is_same_v>) + { + return payload->name; + } + else if constexpr (std::is_same_v>) + { + return "generic"; + } + else if constexpr (std::is_same_v>) + { + return payload->generic.to_string() + "#[" + join(payload->arguments) + "]"; + } }, payload); } @@ -240,6 +268,21 @@ namespace elna::boot { } + parameter_type::parameter_type(const std::string& name) + : name(name) + { + } + + generic_type::generic_type(std::vector&& parameters, type referent) + : parameters(std::move(parameters)), referent(std::move(referent)) + { + } + + instantiated_type::instantiated_type(type generic, std::vector&& arguments) + : generic(std::move(generic)), arguments(std::move(arguments)) + { + } + info::~info() = default; std::shared_ptr info::is_type() @@ -471,12 +514,171 @@ namespace elna::boot } } + type unwrap_aliases(const type& checked) + { + if (auto alias = checked.get()) + { + return unwrap_aliases(alias->referent); + } + return checked; + } + + std::shared_ptr unwrap_generic(const type& checked) + { + return unwrap_aliases(checked).get(); + } + + type substitute(const type& subject, const std::vector& parameters, + const std::vector& arguments) + { + if (auto parameter = subject.get()) + { + for (std::size_t i = 0; i < parameters.size() && i < arguments.size(); ++i) + { + if (parameters[i].get() == parameter) + { + return arguments[i]; + } + } + return subject; + } + if (auto pointer = subject.get()) + { + return type(std::make_shared( + substitute(pointer->base, parameters, arguments))); + } + if (auto slice = subject.get()) + { + return type(std::make_shared( + substitute(slice->base, parameters, arguments))); + } + if (auto array = subject.get()) + { + return type(std::make_shared( + substitute(array->base, parameters, arguments), array->size)); + } + if (auto qualified = subject.get()) + { + return type(std::make_shared( + substitute(qualified->unqualified, parameters, arguments))); + } + if (auto procedure = subject.get()) + { + procedure_type::return_t result_return = procedure->return_type; + + if (!result_return.no_return && !result_return.proper_type.empty()) + { + result_return = procedure_type::return_t( + substitute(result_return.proper_type, parameters, arguments)); + } + auto result = std::make_shared(std::move(result_return)); + + result->parameters.reserve(procedure->parameters.size()); + for (const type& parameter_type : procedure->parameters) + { + result->parameters.push_back(substitute(parameter_type, parameters, arguments)); + } + return type(result); + } + if (auto instance = subject.get()) + { + std::vector substituted; + + substituted.reserve(instance->arguments.size()); + for (const type& argument : instance->arguments) + { + substituted.push_back(substitute(argument, parameters, arguments)); + } + return type(std::make_shared(instance->generic, std::move(substituted))); + } + return subject; + } + + type instantiate(const std::shared_ptr& instance) + { + std::shared_ptr const generic = unwrap_generic(instance->generic); + + return generic == nullptr + ? type() + : substitute(generic->referent, generic->parameters, instance->arguments); + } + + type erase_generic(const type& checked) + { + type resolved = resolve_underlying_type(checked); + + if (auto instance = resolved.get()) + { + std::shared_ptr const generic = unwrap_generic(instance->generic); + + return generic == nullptr ? type() : resolve_underlying_type(generic->referent); + } + if (auto generic = resolved.get()) + { + return resolve_underlying_type(generic->referent); + } + return resolved; + } + + /* + * Rewrites a type taken from an instantiation's erased referent so that it + * reads as the instantiation sees it. The identity for anything else. + */ + static type substitute_from(const type& subject, const type& taken) + { + auto instance = subject.get(); + + if (instance == nullptr) + { + return taken; + } + std::shared_ptr const generic = unwrap_generic(instance->generic); + + return generic == nullptr + ? type() + : substitute(taken, generic->parameters, instance->arguments); + } + + type base_of(const type& derived) + { + const type resolved = resolve_underlying_type(derived); + auto record = erase_generic(resolved).get(); + + return record == nullptr || record->base.empty() + ? type() + : substitute_from(resolved, record->base); + } + + bool is_base_of(const type& base, const type& derived) + { + for (type current = base_of(derived); !current.empty(); current = base_of(current)) + { + if (current == base) + { + return true; + } + } + return false; + } + type resolve_aliases(const type& checked) { if (auto alias = checked.get()) { return resolve_aliases(alias->referent); } + if (auto instance = checked.get()) + { + std::shared_ptr const generic = unwrap_generic(instance->generic); + + // A record referent makes the instantiation nominal, so resolution + // stops here and Stack#[^File] stays distinct from Stack#[^Socket]. + if (generic == nullptr || unwrap_aliases(generic->referent).get() != nullptr) + { + return checked; + } + return resolve_aliases(instantiate(instance)); + } return checked; } @@ -535,6 +737,7 @@ namespace elna::boot { return checked.get() != nullptr || checked.get() != nullptr + || checked.get() != nullptr || is_primitive_type(checked, "Pointer"); } @@ -575,20 +778,18 @@ namespace elna::boot { const type resolved_type = resolve_underlying_type(composite_type); - if (auto record = resolved_type.get()) + if (auto record = erase_generic(resolved_type).get()) { for (const auto& [name, field_type] : record->fields) { if (name == field_name) { - return field_type; + return substitute_from(resolved_type, field_type); } } - if (!record->base.empty()) - { - return lookup_field(record->base, field_name); - } } - return type(); + const type base = base_of(resolved_type); + + return base.empty() ? type() : lookup_field(base, field_name); } } -- cgit v1.2.3