diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-08 20:43:39 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-08 20:43:39 +0200 |
| commit | 47c8f99b6ef812dbc22ca56a39ba21bccad47796 (patch) | |
| tree | 7c057a6d408f9b30801767ed528f7f4c207bfbce /boot/symbol.cc | |
| parent | 8f9ba0c67479d8926edead8aa828458c5b3bc1be (diff) | |
| download | elna-47c8f99b6ef812dbc22ca56a39ba21bccad47796.tar.gz | |
Implement generic pointers
Diffstat (limited to 'boot/symbol.cc')
| -rw-r--r-- | boot/symbol.cc | 215 |
1 files changed, 208 insertions, 7 deletions
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<parameter_type>()) + { + return left_parameter == resolved_that.get<parameter_type>(); + } + if (auto left_generic = resolved_this.get<generic_type>()) + { + return left_generic == resolved_that.get<generic_type>(); + } + if (auto left_instance = resolved_this.get<instantiated_type>()) + { + auto right_instance = resolved_that.get<instantiated_type>(); + + 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<T, std::shared_ptr<parameter_type>>) + { + return payload->name; + } + else if constexpr (std::is_same_v<T, std::shared_ptr<generic_type>>) + { + return "generic"; + } + else if constexpr (std::is_same_v<T, std::shared_ptr<instantiated_type>>) + { + 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<type>&& parameters, type referent) + : parameters(std::move(parameters)), referent(std::move(referent)) + { + } + + instantiated_type::instantiated_type(type generic, std::vector<type>&& arguments) + : generic(std::move(generic)), arguments(std::move(arguments)) + { + } + info::~info() = default; std::shared_ptr<type_info> info::is_type() @@ -471,12 +514,171 @@ namespace elna::boot } } + type unwrap_aliases(const type& checked) + { + if (auto alias = checked.get<alias_type>()) + { + return unwrap_aliases(alias->referent); + } + return checked; + } + + std::shared_ptr<generic_type> unwrap_generic(const type& checked) + { + return unwrap_aliases(checked).get<generic_type>(); + } + + type substitute(const type& subject, const std::vector<type>& parameters, + const std::vector<type>& arguments) + { + if (auto parameter = subject.get<parameter_type>()) + { + for (std::size_t i = 0; i < parameters.size() && i < arguments.size(); ++i) + { + if (parameters[i].get<parameter_type>() == parameter) + { + return arguments[i]; + } + } + return subject; + } + if (auto pointer = subject.get<pointer_type>()) + { + return type(std::make_shared<pointer_type>( + substitute(pointer->base, parameters, arguments))); + } + if (auto slice = subject.get<slice_type>()) + { + return type(std::make_shared<slice_type>( + substitute(slice->base, parameters, arguments))); + } + if (auto array = subject.get<array_type>()) + { + return type(std::make_shared<array_type>( + substitute(array->base, parameters, arguments), array->size)); + } + if (auto qualified = subject.get<constant_type>()) + { + return type(std::make_shared<constant_type>( + substitute(qualified->unqualified, parameters, arguments))); + } + if (auto procedure = subject.get<procedure_type>()) + { + 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<procedure_type>(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<instantiated_type>()) + { + std::vector<type> substituted; + + substituted.reserve(instance->arguments.size()); + for (const type& argument : instance->arguments) + { + substituted.push_back(substitute(argument, parameters, arguments)); + } + return type(std::make_shared<instantiated_type>(instance->generic, std::move(substituted))); + } + return subject; + } + + type instantiate(const std::shared_ptr<instantiated_type>& instance) + { + std::shared_ptr<generic_type> 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<instantiated_type>()) + { + std::shared_ptr<generic_type> const generic = unwrap_generic(instance->generic); + + return generic == nullptr ? type() : resolve_underlying_type(generic->referent); + } + if (auto generic = resolved.get<generic_type>()) + { + 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<instantiated_type>(); + + if (instance == nullptr) + { + return taken; + } + std::shared_ptr<generic_type> 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<record_type>(); + + 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<alias_type>()) { return resolve_aliases(alias->referent); } + if (auto instance = checked.get<instantiated_type>()) + { + std::shared_ptr<generic_type> 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<record_type>() != nullptr) + { + return checked; + } + return resolve_aliases(instantiate(instance)); + } return checked; } @@ -535,6 +737,7 @@ namespace elna::boot { return checked.get<pointer_type>() != nullptr || checked.get<procedure_type>() != nullptr + || checked.get<parameter_type>() != 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<record_type>()) + if (auto record = erase_generic(resolved_type).get<record_type>()) { 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); } } |
