diff options
Diffstat (limited to 'boot/symbol.cc')
| -rw-r--r-- | boot/symbol.cc | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/boot/symbol.cc b/boot/symbol.cc index 310ae8a..dcbd460 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -700,6 +700,103 @@ namespace elna::boot return subject; } + bool bind_arguments(const std::vector<type>& parameters, std::vector<type>& bindings, + const type& declared, const type& actual) + { + const type left = resolve_aliases(declared); + const type right = resolve_aliases(actual); + + if (const auto parameter = left.get<parameter_type>()) + { + for (std::size_t i = 0; i < parameters.size(); ++i) + { + if (parameters[i].get<parameter_type>() == parameter && bindings[i].empty()) + { + // The actual type is recorded as it stands, which inside a + // generic body may itself be an abstract parameter. + bindings[i] = actual; + } + } + return true; + } + else if (const auto left_pointer = left.get<pointer_type>()) + { + const std::shared_ptr<pointer_type> right_pointer = right.get<pointer_type>(); + + return right_pointer != nullptr + && bind_arguments(parameters, bindings, left_pointer->base, right_pointer->base); + } + else if (const auto left_slice = left.get<slice_type>()) + { + const std::shared_ptr<slice_type> right_slice = right.get<slice_type>(); + + return right_slice != nullptr + && bind_arguments(parameters, bindings, left_slice->base, right_slice->base); + } + else if (const auto left_array = left.get<array_type>()) + { + const std::shared_ptr<array_type> right_array = right.get<array_type>(); + + return right_array != nullptr + && bind_arguments(parameters, bindings, left_array->base, right_array->base); + } + else if (const auto left_constant = left.get<constant_type>()) + { + const std::shared_ptr<constant_type> right_constant = right.get<constant_type>(); + const type right_unqualified = right_constant != nullptr + ? right_constant->unqualified + : right; + + return bind_arguments(parameters, bindings, left_constant->unqualified, right_unqualified); + } + else if (const auto left_instance = left.get<instantiated_type>()) + { + for (type candidate = right; !candidate.empty(); candidate = base_of(candidate)) + { + const std::shared_ptr<instantiated_type> right_instance = + resolve_aliases(candidate).get<instantiated_type>(); + + if (right_instance == nullptr + || !(left_instance->generic == right_instance->generic) + || left_instance->arguments.size() != right_instance->arguments.size()) + { + continue; + } + for (std::size_t i = 0; i < left_instance->arguments.size(); ++i) + { + if (!bind_arguments(parameters, bindings, + left_instance->arguments.at(i), right_instance->arguments.at(i))) + { + return false; + } + } + return true; + } + return false; + } + else if (const auto left_procedure = left.get<procedure_type>()) + { + const std::shared_ptr<procedure_type> right_procedure = right.get<procedure_type>(); + + if (right_procedure == nullptr + || left_procedure->parameters.size() != right_procedure->parameters.size()) + { + return false; + } + for (std::size_t i = 0; i < left_procedure->parameters.size(); ++i) + { + if (!bind_arguments(parameters, bindings, + left_procedure->parameters.at(i), right_procedure->parameters.at(i))) + { + return false; + } + } + return left_procedure->return_type.empty() + || bind_arguments(parameters, bindings, left_procedure->return_type, right_procedure->return_type); + } + return true; + } + type instantiate(const std::shared_ptr<instantiated_type>& instance) { std::shared_ptr<generic_type> const generic = unwrap_generic(instance->generic); |
