aboutsummaryrefslogtreecommitdiff
path: root/boot/semantic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/semantic.cc')
-rw-r--r--boot/semantic.cc331
1 files changed, 242 insertions, 89 deletions
diff --git a/boot/semantic.cc b/boot/semantic.cc
index dd9022b..031adfc 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -22,8 +22,8 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
declaration_error::declaration_error(const kind error_kind,
- const std::string& identifier, const struct position position)
- : error(position), identifier(identifier), error_kind(error_kind)
+ const boot::identifier& identifier)
+ : error(identifier.position()), identifier(identifier.name()), error_kind(error_kind)
{
}
@@ -33,8 +33,6 @@ namespace elna::boot
{
case kind::undeclared:
return "Type '" + identifier + "' not declared";
- case kind::already:
- return "Symbol '" + identifier + "' has been already declared";
case kind::local_export:
return "Local symbol '" + this->identifier + "' cannot be exported";
default:
@@ -42,38 +40,107 @@ namespace elna::boot
}
}
- type_expectation_error::type_expectation_error(const kind error_kind, const struct position position)
- : error(position), error_kind(error_kind)
+ redefinition_error::redefinition_error(const boot::identifier& identifier,
+ std::optional<source_position> original)
+ : error(identifier.position()), identifier(identifier.name()), original(original)
{
}
- std::string type_expectation_error::what() const
+ std::string redefinition_error::what() const
{
- switch (this->error_kind)
+ return "Symbol '" + identifier + "' has been already defined";
+ }
+
+ std::optional<std::pair<std::string, source_position>>
+ redefinition_error::note() const
+ {
+ if (original.has_value() && original->start().available())
{
- case kind::assignment:
- return "Assinging an incompatible type";
- case kind::result:
- return "Procedure return type and type of the return expression are not compatible";
- case kind::argument:
- return "Argument type does not match parameter";
- default:
- __builtin_unreachable();
+ return std::make_pair("previously declared here", *original);
+ }
+ return std::nullopt;
+ }
+
+ type_mismatch_error::type_mismatch_error(const source_position position,
+ type expected, type actual)
+ : error(position), expected(expected), actual(actual)
+ {
+ }
+
+ std::string type_mismatch_error::what() const
+ {
+ return "Expected type '" + expected.to_string()
+ + "', but got '" + actual.to_string() + "'";
+ }
+
+ field_not_found_error::field_not_found_error(const identifier& field_name,
+ type composite_type)
+ : error(field_name.position()), field_name(field_name.name()), composite_type(composite_type)
+ {
+ }
+
+ std::string field_not_found_error::what() const
+ {
+ type resolved = inner_aliased_type(composite_type);
+ bool is_enum = resolved.get<enumeration_type>() != nullptr;
+ bool is_record = resolved.get<record_type>() != nullptr;
+
+ if (is_enum || is_record)
+ {
+ std::string message = is_enum ? "Enumeration" : "Record";
+
+ if (auto alias = composite_type.get<alias_type>())
+ {
+ message += " '" + alias->name + "'";
+ }
+ message += " does not have a ";
+ message += is_enum ? "member" : "field";
+ message += " named '" + field_name + "'";
+ return message;
}
+ return "Type '" + composite_type.to_string()
+ + "' does not have a field named '" + field_name + "'";
}
- field_duplication_error::field_duplication_error(const std::string& field_name, const struct position position)
- : error(position), field_name(field_name)
+ duplicate_member_error::duplicate_member_error(const boot::identifier& member_name,
+ type aggregate, std::optional<source_position> original,
+ std::optional<std::string> base_name)
+ : error(member_name.position()), member_name(member_name.name()), aggregate(aggregate),
+ original(original), base_name(base_name)
{
}
- std::string field_duplication_error::what() const
+ std::string duplicate_member_error::what() const
{
- return "Repeated field name '" + field_name + "'";
+ type resolved = inner_aliased_type(aggregate);
+ bool is_enum = resolved.get<enumeration_type>() != nullptr;
+ std::string kind = is_enum ? "member" : "field";
+ std::string message = is_enum ? "Enumeration" : "Record";
+
+ if (auto alias = aggregate.get<alias_type>())
+ {
+ message += " '" + alias->name + "'";
+ }
+ message += " already has a " + kind + " named '" + member_name + "'";
+
+ if (base_name.has_value())
+ {
+ message += " (defined in base type '" + *base_name + "')";
+ }
+ return message;
+ }
+
+ std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const
+ {
+ if (original.has_value() && original->start().available())
+ {
+ return std::make_pair("previously declared here", *original);
+ }
+ return std::nullopt;
}
cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle,
- const struct position position)
+ const source_position position)
: error(position), cycle(cycle)
{
}
@@ -91,28 +158,36 @@ namespace elna::boot
return message;
}
- return_error::return_error(const std::string& identifier, const struct position position)
- : error(position), identifier(identifier)
+ return_error::return_error(const std::string& identifier, const source_position position,
+ type return_type)
+ : error(position), identifier(identifier), return_type(return_type)
{
}
std::string return_error::what() const
{
- return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement";
+ if (!return_type.empty())
+ {
+ return "Procedure '" + this->identifier
+ + "' does not return a value, but return expression has type '"
+ + return_type.to_string() + "'";
+ }
+ return "Procedure '" + this->identifier
+ + "' is expected to return, but does not have a return statement";
}
- base_type_error::base_type_error(const std::string& base, const struct position position)
- : error(position), base(base)
+ base_type_error::base_type_error(type actual, const source_position position)
+ : error(position), actual(actual)
{
}
std::string base_type_error::what() const
{
- return "Record base type '" + this->base + "' is not a record.";
+ return "'" + actual.to_string() + "' is not a record type";
}
argument_count_error::argument_count_error(std::size_t expected, std::size_t actual,
- const struct position position)
+ const source_position position)
: error(position), expected(expected), actual(actual)
{
}
@@ -138,7 +213,7 @@ namespace elna::boot
void type_analysis_visitor::visit(procedure_declaration *declaration)
{
- this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure();
+ this->current_procedure = this->bag.lookup(declaration->identifier.name())->is_procedure();
if (declaration->body.has_value())
{
@@ -157,17 +232,19 @@ namespace elna::boot
{
if (!is_assignable_from(return_type, return_expr->type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position());
+ add_error<type_mismatch_error>(
+ return_expr->position(), return_type, return_expr->type_decoration);
}
}
else
{
- add_error<type_expectation_error>(type_expectation_error::kind::result, return_expr->position());
+ add_error<return_error>(declaration->identifier.name(),
+ return_expr->position(), return_expr->type_decoration);
}
}
else if (declaration->heading().return_type.proper_type != nullptr)
{
- add_error<return_error>(declaration->identifier.name, declaration->position());
+ add_error<return_error>(declaration->identifier.name(), declaration->position());
}
this->bag.leave();
}
@@ -185,7 +262,8 @@ namespace elna::boot
if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::assignment, statement->position());
+ add_error<type_mismatch_error>(statement->position(),
+ statement->lvalue().type_decoration, statement->rvalue().type_decoration);
}
}
@@ -199,11 +277,12 @@ namespace elna::boot
}
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
- auto variable_symbol = this->bag.lookup(variable_identifier.name)->is_variable();
+ auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::assignment,
- declaration->initializer->position());
+ add_error<type_mismatch_error>(
+ declaration->initializer->position(),
+ variable_symbol->symbol, declaration->initializer->type_decoration);
}
}
}
@@ -219,8 +298,8 @@ namespace elna::boot
{
if (!is_assignable_from(condition_type, case_label->type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::assignment,
- case_label->position());
+ add_error<type_mismatch_error>(
+ case_label->position(), condition_type, case_label->type_decoration);
}
}
}
@@ -245,7 +324,7 @@ namespace elna::boot
void type_analysis_visitor::visit(type_declaration *declaration)
{
std::vector<std::string> alias_path;
- auto unresolved_type = this->bag.lookup(declaration->identifier.name)->is_type()->symbol.get<alias_type>();
+ auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>();
if (!check_unresolved_symbol(unresolved_type, alias_path))
{
@@ -261,10 +340,10 @@ namespace elna::boot
{
if (expression->base.has_value())
{
- type base_type = inner_aliased_type(this->bag.lookup(expression->base.value())->is_type()->symbol);
+ type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
- add_error<base_type_error>(expression->base.value(), expression->position());
+ add_error<base_type_error>(base_type, expression->position());
}
}
walking_visitor::visit(expression);
@@ -285,8 +364,9 @@ namespace elna::boot
(*argument_iterator)->accept(this);
if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::argument,
- (*argument_iterator)->position());
+ add_error<type_mismatch_error>(
+ (*argument_iterator)->position(), *type_iterator,
+ (*argument_iterator)->type_decoration);
}
++argument_iterator;
++type_iterator;
@@ -305,19 +385,22 @@ namespace elna::boot
if (record == nullptr)
{
- add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position());
+ add_error<type_mismatch_error>(
+ expression->position(), type(std::make_shared<record_type>()),
+ expression->type_decoration);
return;
}
for (const field_initializer& initializer : expression->field_initializers)
{
for (const type_field& field : record->fields)
{
- if (field.first == initializer.name)
+ if (field.first == initializer.name())
{
- if (!is_assignable_from(field.second, initializer.value->type_decoration))
+ if (!is_assignable_from(field.second, initializer.value().type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::argument,
- initializer.value->position());
+ add_error<type_mismatch_error>(
+ initializer.value().position(), field.second,
+ initializer.value().type_decoration);
}
break;
}
@@ -331,7 +414,9 @@ namespace elna::boot
if (array == nullptr)
{
- add_error<type_expectation_error>(type_expectation_error::kind::result, expression->position());
+ add_error<type_mismatch_error>(
+ expression->position(), type(std::make_shared<array_type>(type(), 0)),
+ expression->type_decoration);
return;
}
if (expression->elements.size() > array->size)
@@ -344,8 +429,8 @@ namespace elna::boot
{
if (!is_assignable_from(array->base, element->type_decoration))
{
- add_error<type_expectation_error>(type_expectation_error::kind::argument,
- element->position());
+ add_error<type_mismatch_error>(
+ element->position(), array->base, element->type_decoration);
}
}
}
@@ -382,7 +467,7 @@ namespace elna::boot
for (auto& parameter_name : parameter_names)
{
result_type.first.parameters.push_back(this->current_type);
- result_type.second.push_back(parameter_name);
+ result_type.second.push_back(parameter_name.name());
}
}
return result_type;
@@ -468,11 +553,12 @@ namespace elna::boot
void name_analysis_visitor::visit(type_declaration *declaration)
{
walking_visitor::visit(declaration);
- auto resolved = this->bag.resolve(declaration->identifier.name, this->current_type);
+ auto resolved = this->bag.resolve(declaration->identifier.name(), this->current_type);
auto info = std::make_shared<type_info>(type(resolved));
- info->exported = declaration->identifier.exported;
- this->bag.enter(declaration->identifier.name, info);
+ info->exported = declaration->identifier.exported();
+ info->position.emplace(declaration->position());
+ this->bag.enter(declaration->identifier.name(), info);
}
void name_analysis_visitor::visit(pointer_type_expression *expression)
@@ -492,7 +578,8 @@ namespace elna::boot
/**
* Collects field names from a record type recursively, base first.
*/
- static void collect_field_names(const type& composite_type, std::set<std::string>& names)
+ static void collect_field_names(const type& composite_type,
+ std::map<std::string, field_origin>& names)
{
auto record = inner_aliased_type(composite_type).get<record_type>();
if (record == nullptr)
@@ -505,12 +592,14 @@ namespace elna::boot
}
for (auto& field : record->fields)
{
- names.insert(field.first);
+ names.insert({ field.first, field_origin{ std::nullopt, composite_type } });
}
}
std::vector<type_field> name_analysis_visitor::build_composite_type(
- const std::vector<field_declaration>& fields, std::set<std::string>& field_names)
+ const std::vector<field_declaration>& fields,
+ std::map<std::string, field_origin>& field_names,
+ type aggregate)
{
std::vector<type_field> result;
@@ -519,13 +608,27 @@ namespace elna::boot
field.second->accept(this);
for (auto& field_name : field.first)
{
- if (!field_names.insert(field_name).second)
+ auto existing = field_names.find(field_name.name());
+ if (existing != field_names.end())
{
- add_error<field_duplication_error>(field_name, field.second->position());
+ std::optional<std::string> base_name;
+
+ if (!existing->second.declaration.has_value()
+ && !existing->second.base_type.empty())
+ {
+ if (auto alias = existing->second.base_type.get<alias_type>())
+ {
+ base_name = alias->name;
+ }
+ }
+ add_error<duplicate_member_error>(field_name, aggregate,
+ existing->second.declaration, base_name);
}
else
{
- result.push_back(std::make_pair(field_name, this->current_type));
+ field_names.insert({ field_name.name(),
+ field_origin{ field.second->position(), type() } });
+ result.push_back(std::make_pair(field_name.name(), this->current_type));
}
}
}
@@ -537,11 +640,11 @@ namespace elna::boot
std::shared_ptr<record_type> result_type;
if (expression->base.has_value())
{
- if (auto unresolved_alias = this->bag.declared(expression->base.value()))
+ if (auto unresolved_alias = this->bag.declared(expression->base.value().name()))
{
result_type = std::make_shared<record_type>(type(unresolved_alias));
}
- else if (auto base_symbol = this->bag.lookup(expression->base.value()))
+ else if (auto base_symbol = this->bag.lookup(expression->base.value().name()))
{
if (auto base_type_info = base_symbol->is_type())
{
@@ -549,7 +652,17 @@ namespace elna::boot
}
else
{
- add_error<base_type_error>(expression->base.value(), expression->position());
+ type actual;
+
+ if (auto var = base_symbol->is_variable())
+ {
+ actual = var->symbol;
+ }
+ else if (auto proc = base_symbol->is_procedure())
+ {
+ actual = type(std::make_shared<procedure_type>(proc->symbol));
+ }
+ add_error<base_type_error>(actual, expression->position());
this->current_type = type();
return;
}
@@ -557,7 +670,7 @@ namespace elna::boot
else
{
add_error<declaration_error>(declaration_error::kind::undeclared,
- expression->base.value(), expression->position());
+ expression->base.value());
this->current_type = type();
return;
}
@@ -567,30 +680,35 @@ namespace elna::boot
result_type = std::make_shared<record_type>();
}
- std::set<std::string> field_names;
+ std::map<std::string, field_origin> field_names;
collect_field_names(result_type->base, field_names);
- result_type->fields = build_composite_type(expression->fields, field_names);
+ result_type->fields = build_composite_type(expression->fields, field_names, type(result_type));
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(record_constructor_expression *expression)
{
- if (auto type_symbol = this->bag.lookup(expression->type_name))
+ if (auto type_symbol = this->bag.lookup(expression->type_name.name()))
{
if (auto type_info = type_symbol->is_type())
{
expression->type_decoration = type_info->symbol;
}
}
+ else
+ {
+ add_error<declaration_error>(declaration_error::kind::undeclared,
+ expression->type_name);
+ }
for (const field_initializer& initializer : expression->field_initializers)
{
- initializer.value->accept(this);
+ initializer.value().accept(this);
if (!expression->type_decoration.empty()
- && lookup_field(expression->type_decoration, initializer.name).empty())
+ && lookup_field(expression->type_decoration, initializer.name()).empty())
{
add_error<declaration_error>(declaration_error::kind::undeclared,
- initializer.name, expression->position());
+ initializer.id());
}
}
}
@@ -616,19 +734,42 @@ namespace elna::boot
void name_analysis_visitor::visit(enumeration_type_expression *expression)
{
- std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(expression->members);
+ std::vector<std::string> member_names;
+ for (auto& member : expression->members)
+ {
+ member_names.emplace_back(member.name());
+ }
+ std::shared_ptr<enumeration_type> result_type = std::make_shared<enumeration_type>(
+ member_names);
+ std::map<std::string, source_position> seen;
+ type aggregate(result_type);
+ for (auto& member : expression->members)
+ {
+ auto existing = seen.find(member.name());
+ if (existing != seen.end())
+ {
+ add_error<duplicate_member_error>(member, aggregate, existing->second);
+ }
+ else
+ {
+ seen.insert({ member.name(), member.position() });
+ }
+ }
this->current_type = type(result_type);
}
std::shared_ptr<variable_info> name_analysis_visitor::register_variable(const std::string& name,
- const bool is_extern, const struct position position)
+ const bool is_extern, const source_position position)
{
auto variable_symbol = std::make_shared<variable_info>(this->current_type, is_extern);
+ variable_symbol->position.emplace(position);
if (!this->bag.enter(name, variable_symbol))
{
- add_error<declaration_error>(declaration_error::kind::already, name, position);
+ auto original = this->bag.lookup(name);
+ add_error<redefinition_error>(boot::identifier(name, position),
+ original->position);
}
return variable_symbol;
}
@@ -644,9 +785,9 @@ namespace elna::boot
}
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
- auto variable_symbol = register_variable(variable_identifier.name, declaration->is_extern,
+ auto variable_symbol = register_variable(variable_identifier.name(), declaration->is_extern,
declaration->position());
- variable_symbol->exported = variable_identifier.exported;
+ variable_symbol->exported = variable_identifier.exported();
}
}
@@ -655,8 +796,9 @@ namespace elna::boot
walking_visitor::visit(declaration);
auto constant_symbol = std::make_shared<constant_info>(this->current_literal);
- constant_symbol->exported = declaration->identifier.exported;
- this->bag.enter(declaration->identifier.name, constant_symbol);
+ constant_symbol->exported = declaration->identifier.exported();
+ constant_symbol->position.emplace(declaration->position());
+ this->bag.enter(declaration->identifier.name(), constant_symbol);
}
void name_analysis_visitor::visit(procedure_declaration *declaration)
@@ -701,8 +843,9 @@ namespace elna::boot
{
info = std::make_shared<procedure_info>(heading, std::move(parameter_names));
}
- info->exported = declaration->identifier.exported;
- this->bag.enter(declaration->identifier.name, info);
+ info->exported = declaration->identifier.exported();
+ info->position.emplace(declaration->position());
+ this->bag.enter(declaration->identifier.name(), info);
}
void name_analysis_visitor::visit(procedure_call *call)
@@ -775,6 +918,11 @@ namespace elna::boot
{
trait->type_decoration = trait->types.empty() ? type() : trait->types.front();
}
+ else
+ {
+ add_error<declaration_error>(declaration_error::kind::undeclared,
+ trait->name);
+ }
}
void name_analysis_visitor::visit(binary_expression *expression)
@@ -841,13 +989,18 @@ namespace elna::boot
void name_analysis_visitor::visit(field_access_expression *expression)
{
walking_visitor::visit(expression);
- expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field());
+ expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field().name());
auto is_designator = expression->base().is_designator();
if (expression->type_decoration.empty() && is_designator != nullptr && is_designator->is_named() != nullptr)
{
expression->type_decoration = this->current_type;
}
+ if (expression->type_decoration.empty())
+ {
+ add_error<field_not_found_error>(expression->field(),
+ expression->base().type_decoration);
+ }
}
void name_analysis_visitor::visit(dereference_expression *expression)
@@ -896,7 +1049,7 @@ namespace elna::boot
else
{
add_error<declaration_error>(declaration_error::kind::undeclared,
- expression->name, expression->position());
+ boot::identifier(expression->name, expression->position()));
}
}
@@ -969,12 +1122,12 @@ namespace elna::boot
void declaration_visitor::visit(type_declaration *declaration)
{
- const std::string& type_identifier = declaration->identifier.name;
+ const std::string& type_identifier = declaration->identifier.name();
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
{
- add_error<declaration_error>(declaration_error::kind::already,
- declaration->identifier.name, declaration->position());
+ add_error<redefinition_error>(declaration->identifier.id(),
+ declaration->position());
}
}
@@ -998,20 +1151,20 @@ namespace elna::boot
{
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
- if (variable_identifier.exported)
+ if (variable_identifier.exported())
{
add_error<declaration_error>(declaration_error::kind::local_export,
- variable_identifier.name, declaration->position());
+ variable_identifier.id());
}
}
}
void declaration_visitor::visit(constant_declaration *declaration)
{
- if (declaration->identifier.exported)
+ if (declaration->identifier.exported())
{
add_error<declaration_error>(declaration_error::kind::local_export,
- declaration->identifier.name, declaration->position());
+ declaration->identifier.id());
}
}