aboutsummaryrefslogtreecommitdiff
path: root/boot/semantic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/semantic.cc')
-rw-r--r--boot/semantic.cc338
1 files changed, 161 insertions, 177 deletions
diff --git a/boot/semantic.cc b/boot/semantic.cc
index 9137053..c987cfd 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -73,6 +73,18 @@ namespace elna::boot
+ "', but got '" + actual.to_string() + "'";
}
+ constant_assignment_error::constant_assignment_error(const source_position position,
+ type assignee)
+ : error(position), assignee(assignee)
+ {
+ }
+
+ std::string constant_assignment_error::what() const
+ {
+ return "Cannot assign to a value of type '" + assignee.to_string()
+ + "', because it is constant or contains constant members";
+ }
+
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)
@@ -81,7 +93,7 @@ namespace elna::boot
std::string field_not_found_error::what() const
{
- type resolved = inner_aliased_type(composite_type);
+ type resolved = resolve_underlying_type(composite_type);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
bool is_record = resolved.get<record_type>() != nullptr;
@@ -112,7 +124,7 @@ namespace elna::boot
std::string duplicate_member_error::what() const
{
- type resolved = inner_aliased_type(aggregate);
+ type resolved = resolve_underlying_type(aggregate);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
std::string kind = is_enum ? "member" : "field";
std::string message = is_enum ? "Enumeration" : "Record";
@@ -208,7 +220,7 @@ namespace elna::boot
unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait,
type actual)
- : error(trait.position()), trait_name(trait.name()), actual(actual)
+ : error(trait.position()), actual(actual), trait_name(trait.name())
{
}
@@ -218,6 +230,119 @@ namespace elna::boot
+ "' does not support trait '#" + trait_name + "'";
}
+ // Members of a constant aggregate are constant themselves.
+ static type qualify_member_type(const type& element, const type& aggregate)
+ {
+ if (resolve_aliases(aggregate).get<constant_type>() != nullptr
+ && resolve_aliases(element).get<constant_type>() == nullptr)
+ {
+ return type(std::make_shared<constant_type>(element));
+ }
+ else
+ {
+ return element;
+ }
+ }
+
+ /*
+ * Whether the type itself is constant or has a constant member at any
+ * nesting level, so that values of this type cannot be reassigned as a
+ * whole. Pointers to constants do not make the type itself constant.
+ */
+ static bool contains_constant_member(const type& checked)
+ {
+ auto referent = resolve_aliases(checked);
+
+ if (referent.get<constant_type>() != nullptr)
+ {
+ return true;
+ }
+ else if (auto record = referent.get<record_type>())
+ {
+ for (const type_field& field : record->fields)
+ {
+ if (contains_constant_member(field.second))
+ {
+ return true;
+ }
+ }
+ return !record->base.empty() && contains_constant_member(record->base);
+ }
+ else if (auto array = referent.get<array_type>())
+ {
+ return contains_constant_member(array->base);
+ }
+ return false;
+ }
+
+ bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
+ std::vector<std::string>& alias_path)
+ {
+ if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
+ {
+ return false;
+ }
+ alias_path.push_back(alias->name);
+
+ if (auto another_alias = alias->reference.get<alias_type>())
+ {
+ return check_unresolved_symbol(another_alias, alias_path);
+ }
+ return true;
+ }
+
+ bool type_analysis_visitor::is_base_of(const std::shared_ptr<record_type>& base,
+ const std::shared_ptr<record_type>& derived)
+ {
+ if (derived != nullptr)
+ {
+ if (auto current_record = resolve_underlying_type(derived->base).get<record_type>())
+ {
+ return current_record == base || is_base_of(base, current_record);
+ }
+ }
+ return false;
+ }
+
+ bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment)
+ {
+ type resolved_assignee = resolve_underlying_type(assignee);
+ type resolved_assignment = resolve_underlying_type(assignment);
+
+ if (resolved_assignee == resolved_assignment
+ || (is_primitive_type(resolved_assignee, "Pointer") && is_any_pointer_type(resolved_assignment))
+ || (is_primitive_type(resolved_assignment, "Pointer") && is_any_pointer_type(resolved_assignee)))
+ {
+ return true;
+ }
+ std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>();
+ std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>();
+
+ if (assignee_pointer == nullptr || assignment_pointer == nullptr)
+ {
+ return false;
+ }
+ auto assignee_pointee_const = resolve_aliases(assignee_pointer->base).get<constant_type>();
+ auto assignment_pointee_const = resolve_aliases(assignment_pointer->base).get<constant_type>();
+
+ // Constness can be added at the first indirection level, but not removed.
+ if (assignee_pointee_const != nullptr
+ && assignee_pointee_const->unqualified == assignment_pointer->base)
+ {
+ return true;
+ }
+ if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr)
+ {
+ return false;
+ }
+ // A pointer to a record can be assigned to a pointer to its base type.
+ std::shared_ptr<record_type> assignee_record =
+ resolve_underlying_type(assignee_pointer->base).get<record_type>();
+
+ return assignee_record != nullptr
+ && is_base_of(assignee_record, resolve_underlying_type(assignment_pointer->base).get<record_type>());
+ }
+
type_analysis_visitor::type_analysis_visitor(symbol_bag bag)
: error_container(), bag(bag)
{
@@ -272,7 +397,12 @@ namespace elna::boot
{
walking_visitor::visit(statement);
- if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
+ if (contains_constant_member(statement->lvalue().type_decoration))
+ {
+ add_error<constant_assignment_error>(statement->position(),
+ statement->lvalue().type_decoration);
+ }
+ else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
{
add_error<type_mismatch_error>(statement->position(),
statement->lvalue().type_decoration, statement->rvalue().type_decoration);
@@ -302,7 +432,7 @@ namespace elna::boot
void type_analysis_visitor::visit(case_statement *statement)
{
walking_visitor::visit(statement);
- type condition_type = inner_aliased_type(statement->condition().type_decoration);
+ type condition_type = resolve_underlying_type(statement->condition().type_decoration);
for (const switch_case& case_block : statement->cases)
{
@@ -317,22 +447,6 @@ namespace elna::boot
}
}
- bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
- std::vector<std::string>& alias_path)
- {
- if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
- {
- return false;
- }
- alias_path.push_back(alias->name);
-
- if (auto another_alias = alias->reference.get<alias_type>())
- {
- return check_unresolved_symbol(another_alias, alias_path);
- }
- return true;
- }
-
void type_analysis_visitor::visit(type_declaration *declaration)
{
std::vector<std::string> alias_path;
@@ -352,7 +466,7 @@ namespace elna::boot
{
if (expression->base.has_value())
{
- type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
+ type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
add_error<base_type_error>(base_type, expression->position());
@@ -393,7 +507,7 @@ namespace elna::boot
void type_analysis_visitor::visit(record_constructor_expression *expression)
{
- auto record = inner_aliased_type(expression->type_decoration).get<record_type>();
+ auto record = resolve_underlying_type(expression->type_decoration).get<record_type>();
if (record == nullptr)
{
@@ -422,7 +536,7 @@ namespace elna::boot
void type_analysis_visitor::visit(array_constructor_expression *expression)
{
- auto array = inner_aliased_type(expression->type_decoration).get<array_type>();
+ auto array = resolve_underlying_type(expression->type_decoration).get<array_type>();
if (array == nullptr)
{
@@ -490,49 +604,9 @@ namespace elna::boot
return this->bag.lookup(name)->is_type()->symbol;
}
- type name_analysis_visitor::type_of_constant(const constant_info::variant& value)
- {
- return std::visit([this](auto&& arg) -> type {
- using T = std::decay_t<decltype(arg)>;
-
- if constexpr (std::is_same_v<T, std::int32_t>)
- {
- return lookup_primitive_type("Int");
- }
- else if constexpr (std::is_same_v<T, std::uint32_t>)
- {
- return lookup_primitive_type("Word");
- }
- else if constexpr (std::is_same_v<T, double>)
- {
- return lookup_primitive_type("Float");
- }
- else if constexpr (std::is_same_v<T, bool>)
- {
- return lookup_primitive_type("Bool");
- }
- else if constexpr (std::is_same_v<T, unsigned char>)
- {
- return lookup_primitive_type("Char");
- }
- else if constexpr (std::is_same_v<T, std::nullptr_t>)
- {
- return lookup_primitive_type("Pointer");
- }
- else if constexpr (std::is_same_v<T, std::string>)
- {
- return lookup_primitive_type("String");
- }
- else
- {
- static_assert(!sizeof(T*), "Unhandled constant type");
- }
- }, value);
- }
-
type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name)
{
- type resolved_type = inner_aliased_type(composite_type);
+ type resolved_type = resolve_underlying_type(composite_type);
if (auto record = resolved_type.get<record_type>())
{
@@ -579,6 +653,12 @@ namespace elna::boot
this->current_type = type(std::make_shared<pointer_type>(this->current_type));
}
+ void name_analysis_visitor::visit(constant_type_expression *expression)
+ {
+ walking_visitor::visit(expression);
+ this->current_type = type(std::make_shared<constant_type>(this->current_type));
+ }
+
void name_analysis_visitor::visit(array_type_expression *expression)
{
walking_visitor::visit(expression);
@@ -593,7 +673,7 @@ namespace elna::boot
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>();
+ auto record = resolve_underlying_type(composite_type).get<record_type>();
if (record == nullptr)
{
return;
@@ -803,15 +883,6 @@ namespace elna::boot
}
}
- void name_analysis_visitor::visit(constant_declaration *declaration)
- {
- walking_visitor::visit(declaration);
- auto constant_symbol = std::make_shared<constant_info>(this->current_literal);
-
- 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)
{
@@ -833,10 +904,6 @@ namespace elna::boot
++name_iterator;
++type_iterator;
}
- for (constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -887,10 +954,6 @@ namespace elna::boot
{
variable->accept(this);
}
- for (constant_declaration *const constant : unit->constants)
- {
- constant->accept(this);
- }
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
@@ -932,7 +995,7 @@ namespace elna::boot
if (!trait->type_decoration.empty())
{
- type resolved = inner_aliased_type(trait->type_decoration);
+ type resolved = resolve_underlying_type(trait->type_decoration);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
bool is_integral = false;
@@ -1005,7 +1068,7 @@ namespace elna::boot
void name_analysis_visitor::visit(array_access_expression *expression)
{
walking_visitor::visit(expression);
- auto resolved_base= inner_aliased_type(expression->base().type_decoration);
+ auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
if (auto array = resolved_base.get<array_type>())
{
@@ -1015,6 +1078,13 @@ namespace elna::boot
{
expression->type_decoration = lookup_primitive_type("Char");
}
+ // Elements of a constant array are constant themselves since a static
+ // array is a holistic type.
+ if (!expression->type_decoration.empty())
+ {
+ expression->type_decoration = qualify_member_type(expression->type_decoration,
+ expression->base().type_decoration);
+ }
}
void name_analysis_visitor::visit(field_access_expression *expression)
@@ -1029,7 +1099,11 @@ namespace elna::boot
}
if (expression->type_decoration.empty())
{
- add_error<field_not_found_error>(expression->field(),
+ add_error<field_not_found_error>(expression->field(), expression->base().type_decoration);
+ }
+ else
+ {
+ expression->type_decoration = qualify_member_type(expression->type_decoration,
expression->base().type_decoration);
}
}
@@ -1038,7 +1112,7 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- if (auto pointer = inner_aliased_type(expression->base().type_decoration).get<pointer_type>())
+ if (auto pointer = resolve_underlying_type(expression->base().type_decoration).get<pointer_type>())
{
expression->type_decoration = pointer->base;
}
@@ -1068,10 +1142,6 @@ namespace elna::boot
{
expression->type_decoration = variable_symbol->symbol;
}
- else if (auto constant_symbol = from_symbol_table->is_constant())
- {
- expression->type_decoration = type_of_constant(constant_symbol->symbol);
- }
else if (auto procedure_symbol = from_symbol_table->is_procedure())
{
expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol));
@@ -1086,43 +1156,36 @@ namespace elna::boot
void name_analysis_visitor::visit(literal<std::int32_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Int");
}
void name_analysis_visitor::visit(literal<std::uint32_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Word");
}
void name_analysis_visitor::visit(literal<double> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Float");
}
void name_analysis_visitor::visit(literal<bool> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Bool");
}
void name_analysis_visitor::visit(literal<unsigned char> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Char");
}
void name_analysis_visitor::visit(literal<std::nullptr_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Pointer");
}
void name_analysis_visitor::visit(literal<std::string> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("String");
}
@@ -1168,10 +1231,6 @@ namespace elna::boot
{
return;
}
- for (constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -1189,79 +1248,4 @@ namespace elna::boot
}
}
}
-
- void declaration_visitor::visit(constant_declaration *declaration)
- {
- if (declaration->identifier.exported())
- {
- add_error<declaration_error>(declaration_error::kind::local_export,
- declaration->identifier.id());
- }
- }
-
- bool is_base_of(std::shared_ptr<record_type> base, std::shared_ptr<record_type> derived)
- {
- type current = derived->base;
-
- while (!current.empty())
- {
- auto current_record = inner_aliased_type(current).get<record_type>();
- if (current_record == nullptr)
- {
- return false;
- }
- if (current_record == base)
- {
- return true;
- }
- current = current_record->base;
- }
- return false;
- }
-
- bool are_compatible_pointers(type lhs_type, type rhs_type)
- {
- std::shared_ptr<primitive_type> lhs_primitive = lhs_type.get<primitive_type>();
- std::shared_ptr<primitive_type> rhs_primitive = rhs_type.get<primitive_type>();
-
- bool lhs_is_primitive_pointer = lhs_primitive != nullptr && lhs_primitive->identifier == "Pointer";
- bool rhs_is_primitive_pointer = rhs_primitive != nullptr && rhs_primitive->identifier == "Pointer";
-
- return (lhs_is_primitive_pointer && rhs_type.get<pointer_type>() != nullptr)
- || (rhs_is_primitive_pointer && lhs_type.get<pointer_type>() != nullptr)
- || (lhs_is_primitive_pointer && rhs_type.get<procedure_type>() != nullptr)
- || (rhs_is_primitive_pointer && lhs_type.get<procedure_type>() != nullptr)
- || (lhs_is_primitive_pointer && rhs_is_primitive_pointer);
- }
-
- bool is_assignable_from(const type& assignee, const type& assignment)
- {
- type resolved_assignee = inner_aliased_type(assignee);
- type resolved_assignment = inner_aliased_type(assignment);
-
- if (resolved_assignee == resolved_assignment)
- {
- return true;
- }
- // If the types do not match, only some pointers can be assigned.
- if (are_compatible_pointers(resolved_assignee, resolved_assignment))
- {
- return true;
- }
- std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>();
- std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>();
-
- if (assignee_pointer == nullptr || assignment_pointer == nullptr)
- {
- return false;
- }
- // A pointer to a record can be assigned to a pointer to its base type.
- type assignee_pointee = inner_aliased_type(assignee_pointer->base);
- type assignment_pointee = inner_aliased_type(assignment_pointer->base);
- std::shared_ptr<record_type> assignee_record = assignee_pointee.get<record_type>();
- std::shared_ptr<record_type> assignment_record = assignment_pointee.get<record_type>();
-
- return assignee_record != nullptr && assignment_record != nullptr
- && is_base_of(assignee_record, assignment_record);
- }
}