aboutsummaryrefslogtreecommitdiff
path: root/boot/name_analysis.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/name_analysis.cc')
-rw-r--r--boot/name_analysis.cc123
1 files changed, 116 insertions, 7 deletions
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<field_origin>& names)
{
- auto record = resolve_underlying_type(composite_type).get<record_type>();
+ auto record = erase_generic(composite_type).get<record_type>();
if (record == nullptr)
{
return;
@@ -446,6 +446,38 @@ namespace elna::boot
}
}
+ std::vector<type> resolving_visitor::enter_parameters(const std::vector<identifier>& parameters)
+ {
+ std::vector<type> result;
+
+ result.reserve(parameters.size());
+ for (const identifier& parameter : parameters)
+ {
+ const type parameter_symbol = type(std::make_shared<parameter_type>(parameter.name()));
+ auto info = std::make_shared<type_info>(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<symbol_declaration_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<record_type> 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<record_type>(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<record_type>(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<instantiated_type>(base_type,
+ resolve_arguments(expression->base_arguments)));
+ }
+ result_type = std::make_shared<record_type>(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<instantiated_type>(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_type>(procedure_symbol->symbol));
+ type procedure = type(std::make_shared<procedure_type>(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<symbol_declaration_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<instantiated_type>(generic,
+ std::vector<type>(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<type> resolving_visitor::resolve_arguments(const std::vector<type_expression *>& arguments)
+ {
+ std::vector<type> 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<generic_type>())
+ {
+ return find_alias_cycle(being_resolved, link->referent, alias_path, state);
+ }
+ else if (auto link = referent.get<instantiated_type>())
+ {
+ // 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<procedure_type>())
{
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<type> 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<generic_type>(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<symbol_table> scope = this->bag.enter();
+ const std::vector<type> type_parameters = enter_parameters(declaration->parameters);
auto [heading, parameter_names] = build_procedure(declaration->heading());
std::shared_ptr<procedure_info> info;
if (declaration->body.has_value())
{
- info = std::make_shared<procedure_info>(heading, std::move(parameter_names), this->bag.enter());
+ info = std::make_shared<procedure_info>(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<procedure_info>(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;