aboutsummaryrefslogtreecommitdiff
path: root/boot/type_check.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/type_check.cc')
-rw-r--r--boot/type_check.cc406
1 files changed, 261 insertions, 145 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 343b5aa..07deb2d 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -22,9 +22,75 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
+ static char unary_operator_symbol(unary_operator operation)
+ {
+ switch (operation)
+ {
+ using enum unary_operator;
+ case reference:
+ return '@';
+ case negation:
+ case bitwise_negation:
+ case logical_negation:
+ return '~';
+ case minus:
+ return '-';
+ case plus:
+ return '+';
+ }
+ __builtin_unreachable();
+ }
+
+ static std::string print_binary_operator(const binary_operator operation)
+ {
+ switch (operation)
+ {
+ using enum binary_operator;
+ case sum:
+ return "+";
+ case subtraction:
+ return "-";
+ case multiplication:
+ return "*";
+ case division:
+ return "/";
+ case remainder:
+ return "%";
+ case equals:
+ return "=";
+ case not_equals:
+ return "<>";
+ case less:
+ return "<";
+ case less_equal:
+ return "<=";
+ case greater:
+ return ">";
+ case greater_equal:
+ return ">=";
+ case conjunction:
+ case logical_conjunction:
+ case bitwise_conjunction:
+ return "&";
+ case disjunction:
+ case logical_disjunction:
+ case bitwise_disjunction:
+ return "or";
+ case exclusive_disjunction:
+ case logical_exclusive_disjunction:
+ case bitwise_exclusive_disjunction:
+ return "xor";
+ case shift_left:
+ return "<<";
+ case shift_right:
+ return ">>";
+ }
+ __builtin_unreachable();
+ };
+
trait_error::trait_error(const source_position position, const std::string& trait_name,
payload_type payload)
- : error(position), trait_name(trait_name), m_payload(payload)
+ : error(position), trait_name(trait_name), payload(std::move(payload))
{
}
@@ -41,35 +107,94 @@ namespace elna::boot
+ (payload.expected != 1 ? "s" : "") + ", got "
+ std::to_string(payload.actual);
}
- else
+ else if constexpr (std::is_same_v<T, offset_not_field_name>)
{
- return "The second argument to the #" + this->trait_name + " trait must be a field name";
+ return "The second argument to the #" + this->trait_name
+ + " trait must be a field name";
}
- }, this->m_payload);
+ else if constexpr (std::is_same_v<T, unsupported_type>)
+ {
+ return "Type '" + payload.actual.to_string()
+ + "' does not support trait '#" + this->trait_name + "'";
+ }
+ }, this->payload);
}
type_mismatch_error::type_mismatch_error(const source_position position,
- type expected, type actual)
- : error(position), expected(std::move(expected)), actual(std::move(actual))
+ type actual, payload_type payload)
+ : error(position), actual(std::move(actual)), payload(std::move(payload))
{
}
std::string type_mismatch_error::what() const
{
- return "Expected type '" + expected.to_string()
- + "', but got '" + actual.to_string() + "'";
- }
+ return std::visit([this](auto&& payload) -> std::string
+ {
+ using T = std::decay_t<decltype(payload)>;
- constant_assignment_error::constant_assignment_error(const source_position position,
- type assignee)
- : error(position), assignee(std::move(assignee))
- {
- }
+ if constexpr (std::is_same_v<T, expected_type>)
+ {
+ return "Expected type '" + payload.value.to_string()
+ + "', but got '" + this->actual.to_string() + "'";
+ }
+ else if constexpr (std::is_same_v<T, return_type>)
+ {
+ if (!this->actual.empty())
+ {
+ return "Procedure '" + payload.identifier
+ + "' does not return a value, but return expression has type '"
+ + this->actual.to_string() + "'";
+ }
+ return "Procedure '" + payload.identifier
+ + "' is expected to return, but does not have a return statement";
- 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";
+ }
+ else if constexpr (std::is_same_v<T, unary>)
+ {
+ return "Type '" + this->actual.to_string() + "' cannot be used with unary '"
+ + unary_operator_symbol(payload.operation) + "'";
+ }
+ else if constexpr (std::is_same_v<T, binary>)
+ {
+ return "Invalid operands of type '" + this->actual.to_string()
+ + "' and '" + payload.right.to_string()
+ + "' for operator " + print_binary_operator(payload.operation);
+ }
+ else if constexpr (std::is_same_v<T, invalid_cast>)
+ {
+ return "Type '" + this->actual.to_string()
+ + "' cannot be converted to '" + payload.target.to_string() + "'";
+ }
+ else if constexpr (std::is_same_v<T, kind>)
+ {
+ switch (payload)
+ {
+ case kind::record_base:
+ return "Expected a record type, but got '"
+ + this->actual.to_string() + "'";
+ case kind::for_range:
+ return "Expected an array or slice type, but got '"
+ + this->actual.to_string() + "'";
+ case kind::condition:
+ return "Condition must be a boolean expression, but got '"
+ + this->actual.to_string() + "'";
+ case kind::constant_assignment:
+ return "Cannot assign to a value of type '" + this->actual.to_string()
+ + "', because it is constant or contains constant members";
+ case kind::array_index:
+ return "Array index must be an integral type, but got '"
+ + this->actual.to_string() + "'";
+ case kind::non_indexable:
+ return "Indexing is not allowed on type '"
+ + this->actual.to_string() + "'";
+ case kind::dereference_of_non_pointer:
+ return "Type '" + this->actual.to_string()
+ + "' cannot be dereferenced, it is not a pointer";
+ default:
+ __builtin_unreachable();
+ }
+ }
+ }, this->payload);
}
cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle,
@@ -91,46 +216,6 @@ namespace elna::boot
return message;
}
- return_error::return_error(const std::string& identifier, const source_position position,
- type return_type)
- : error(position), identifier(identifier), return_type(std::move(return_type))
- {
- }
-
- std::string return_error::what() const
- {
- 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";
- }
-
- type_kind_error::type_kind_error(const source_position position, kind type_kind, const type& actual)
- : error(position), type_kind(type_kind), actual(actual)
- {
- }
-
- std::string type_kind_error::what() const
- {
- switch (this->type_kind)
- {
- case kind::record_base:
- return "'" + this->actual.to_string() + "' is not a record type";
- case kind::for_loop:
- return "for-loop variable must be an array or a slice, got '"
- + this->actual.to_string() + "'";
- case kind::condition:
- return "Condition must be a boolean, got '"
- + this->actual.to_string() + "'";
- default:
- __builtin_unreachable();
- }
- }
-
argument_count_error::argument_count_error(std::size_t expected, std::size_t actual,
const source_position position)
: error(position), expected(expected), actual(actual)
@@ -151,43 +236,6 @@ namespace elna::boot
}
}
- unary_operation_error::unary_operation_error(const source_position position,
- type actual, unary_operator operation)
- : error(position), actual(std::move(actual)), op(operation)
- {
- }
-
- char unary_operation_error::unary_operator_symbol(unary_operator operation)
- {
- switch (operation)
- {
- using enum unary_operator;
- case reference:
- return '@';
- case negation:
- case bitwise_negation:
- case logical_negation:
- return '~';
- case minus:
- return '-';
- case plus:
- return '+';
- }
- __builtin_unreachable();
- }
-
- std::string unary_operation_error::what() const
- {
- return "Type '" + actual.to_string()
- + "' cannot be used with unary '"
- + unary_operator_symbol(op) + "'";
- }
-
- /*
- * 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);
@@ -248,8 +296,8 @@ namespace elna::boot
condition.accept(this);
if (!is_primitive_type(condition.type_decoration, "Bool"))
{
- add_error<type_kind_error>(condition.position(),
- type_kind_error::kind::condition, condition.type_decoration);
+ add_error<type_mismatch_error>(condition.position(),
+ condition.type_decoration, type_mismatch_error::kind::condition);
}
}
@@ -421,19 +469,22 @@ namespace elna::boot
{
if (!is_assignable_from(return_type, return_expr->type_decoration))
{
- add_error<type_mismatch_error>(
- return_expr->position(), return_type, return_expr->type_decoration);
+ add_error<type_mismatch_error>(return_expr->position(),
+ return_expr->type_decoration,
+ type_mismatch_error::expected_type{ return_type });
}
}
else
{
- add_error<return_error>(declaration->identifier.name(),
- return_expr->position(), return_expr->type_decoration);
+ add_error<type_mismatch_error>(return_expr->position(),
+ return_expr->type_decoration,
+ type_mismatch_error::return_type{ .identifier = declaration->identifier.name() });
}
}
else if (declaration->heading().return_type.proper_type != nullptr)
{
- add_error<return_error>(declaration->identifier.name(), declaration->position());
+ add_error<type_mismatch_error>(declaration->position(), type(),
+ type_mismatch_error::return_type{ .identifier = declaration->identifier.name() });
}
this->bag.leave();
}
@@ -451,13 +502,14 @@ namespace elna::boot
if (contains_constant_member(statement->lvalue().type_decoration))
{
- add_error<constant_assignment_error>(statement->position(),
- statement->lvalue().type_decoration);
+ add_error<type_mismatch_error>(statement->position(), statement->lvalue().type_decoration,
+ type_mismatch_error::kind::constant_assignment);
}
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);
+ statement->rvalue().type_decoration,
+ type_mismatch_error::expected_type{ statement->lvalue().type_decoration });
}
}
@@ -475,7 +527,8 @@ namespace elna::boot
if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration))
{
add_error<type_mismatch_error>(declaration->initializer->position(),
- variable_symbol->symbol, declaration->initializer->type_decoration);
+ declaration->initializer->type_decoration,
+ type_mismatch_error::expected_type{ variable_symbol->symbol });
}
}
}
@@ -491,9 +544,11 @@ namespace elna::boot
{
if (!is_equality_compatible(condition_type, case_label->type_decoration))
{
- add_error<binary_operation_error>(
- case_label->position(), condition_type,
- case_label->type_decoration, binary_operator::equals);
+ type_mismatch_error::binary binary_error{
+ .right = case_label->type_decoration,
+ .operation = binary_operator::equals
+ };
+ add_error<type_mismatch_error>(case_label->position(), condition_type, binary_error);
}
}
}
@@ -506,8 +561,8 @@ namespace elna::boot
if (!get_range_base_type(resolved_range))
{
- add_error<type_kind_error>(statement->range().position(), type_kind_error::kind::for_loop,
- statement->range().type_decoration);
+ add_error<type_mismatch_error>(statement->range().position(),
+ statement->range().type_decoration, type_mismatch_error::kind::for_range);
}
this->bag.enter(statement->symbols);
for (auto *body_statement : statement->body)
@@ -594,16 +649,16 @@ namespace elna::boot
auto const base_symbol = this->bag.lookup(expression->base.value().name());
if (base_symbol == nullptr || base_symbol->is_type() == nullptr)
{
- add_error<type_kind_error>(expression->position(),
- type_kind_error::kind::record_base, type());
+ add_error<type_mismatch_error>(expression->position(),
+ type(), type_mismatch_error::kind::record_base);
}
else
{
type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
- add_error<type_kind_error>(expression->position(),
- type_kind_error::kind::record_base, base_type);
+ add_error<type_mismatch_error>(expression->position(),
+ base_type, type_mismatch_error::kind::record_base);
}
}
}
@@ -626,8 +681,9 @@ namespace elna::boot
if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration))
{
add_error<type_mismatch_error>(
- (*argument_iterator)->position(), *type_iterator,
- (*argument_iterator)->type_decoration);
+ (*argument_iterator)->position(),
+ (*argument_iterator)->type_decoration,
+ type_mismatch_error::expected_type{ *type_iterator });
}
++argument_iterator;
++type_iterator;
@@ -641,8 +697,8 @@ namespace elna::boot
else if (!call->callable().type_decoration.empty())
{
add_error<type_mismatch_error>(call->position(),
- type(std::make_shared<procedure_type>()),
- call->callable().type_decoration);
+ call->callable().type_decoration,
+ type_mismatch_error::expected_type{ type(std::make_shared<procedure_type>()) });
}
// else callable is not declared which is already reported.
}
@@ -654,8 +710,8 @@ namespace elna::boot
if (record == nullptr)
{
add_error<type_mismatch_error>(
- expression->position(), type(std::make_shared<record_type>()),
- expression->type_decoration);
+ expression->position(), expression->type_decoration,
+ type_mismatch_error::expected_type{ type(std::make_shared<record_type>()) });
return;
}
for (const field_initializer& initializer : expression->field_initializers)
@@ -667,8 +723,7 @@ namespace elna::boot
if (!is_assignable_from(field_type, initializer.value().type_decoration))
{
add_error<type_mismatch_error>(
- initializer.value().position(), field_type,
- initializer.value().type_decoration);
+ initializer.value().position(), initializer.value().type_decoration, type_mismatch_error::expected_type{field_type});
}
break;
}
@@ -683,8 +738,8 @@ namespace elna::boot
if (array == nullptr)
{
add_error<type_mismatch_error>(
- expression->position(), type(std::make_shared<array_type>(type(), 0)),
- expression->type_decoration);
+ expression->position(), expression->type_decoration,
+ type_mismatch_error::expected_type{ type(std::make_shared<array_type>(type(), 0)) });
return;
}
if (expression->elements.size() > array->size)
@@ -698,7 +753,8 @@ namespace elna::boot
if (!is_assignable_from(array->base, element->type_decoration))
{
add_error<type_mismatch_error>(
- element->position(), array->base, element->type_decoration);
+ element->position(), element->type_decoration,
+ type_mismatch_error::expected_type{ array->base });
}
}
}
@@ -708,6 +764,24 @@ namespace elna::boot
walking_visitor::visit(expression);
}
+ void type_analysis_visitor::visit(array_access_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
+ if (resolved_base.get<array_type>() == nullptr
+ && resolved_base.get<slice_type>() == nullptr)
+ {
+ add_error<type_mismatch_error>(expression->position(),
+ expression->base().type_decoration, type_mismatch_error::kind::non_indexable);
+ }
+ if (!is_integral_type(resolve_underlying_type(expression->index().type_decoration)))
+ {
+ add_error<type_mismatch_error>(expression->index().position(),
+ expression->index().type_decoration, type_mismatch_error::kind::array_index);
+ }
+ }
+
void type_analysis_visitor::visit(unary_expression *expression)
{
walking_visitor::visit(expression);
@@ -719,8 +793,9 @@ namespace elna::boot
{
if (!is_numeric_type(resolved))
{
- add_error<unary_operation_error>(expression->position(),
- expression->operand().type_decoration, operation);
+ add_error<type_mismatch_error>(expression->position(),
+ expression->operand().type_decoration,
+ type_mismatch_error::unary{ .operation = operation });
}
}
else if (operation == unary_operator::minus)
@@ -728,8 +803,9 @@ namespace elna::boot
if (!is_primitive_type(resolved, "Int")
&& !is_primitive_type(resolved, "Float"))
{
- add_error<unary_operation_error>(expression->position(),
- expression->operand().type_decoration, operation);
+ add_error<type_mismatch_error>(expression->position(),
+ expression->operand().type_decoration,
+ type_mismatch_error::unary{ .operation = operation });
}
}
else if (operation == unary_operator::negation)
@@ -744,8 +820,9 @@ namespace elna::boot
}
else
{
- add_error<unary_operation_error>(expression->position(),
- expression->operand().type_decoration, operation);
+ add_error<type_mismatch_error>(expression->position(),
+ expression->operand().type_decoration,
+ type_mismatch_error::unary{ .operation = operation });
}
}
else if (operation == unary_operator::reference)
@@ -753,23 +830,40 @@ namespace elna::boot
auto *designator = expression->operand().is_designator();
if (designator == nullptr || designator->is_slicing() != nullptr)
{
- add_error<unary_operation_error>(expression->position(),
- expression->operand().type_decoration, operation);
+ add_error<type_mismatch_error>(expression->position(),
+ expression->operand().type_decoration,
+ type_mismatch_error::unary{ .operation = operation });
}
}
}
- binary_operation_error::binary_operation_error(const source_position position,
- type left, type right, binary_operator operation)
- : error(position), left(std::move(left)), right(std::move(right)), op(operation)
+ void type_analysis_visitor::visit(dereference_expression *expression)
{
+ walking_visitor::visit(expression);
+
+ if (resolve_underlying_type(expression->base().type_decoration).get<pointer_type>() == nullptr)
+ {
+ add_error<type_mismatch_error>(expression->position(),
+ expression->base().type_decoration,
+ type_mismatch_error::kind::dereference_of_non_pointer);
+ }
}
- std::string binary_operation_error::what() const
+ void type_analysis_visitor::visit(cast_expression *expression)
{
- return "Invalid operands of type '" + left.to_string()
- + "' and '" + right.to_string()
- + "' for operator " + print_binary_operator(op);
+ walking_visitor::visit(expression);
+
+ auto source = resolve_underlying_type(expression->value().type_decoration);
+ auto target = resolve_underlying_type(expression->type_decoration);
+
+ if (source != target // const cast.
+ && (!is_scalar_type(source) || !is_scalar_type(target))
+ && (source.get<slice_type>() == nullptr || target.get<slice_type>() == nullptr))
+ {
+ add_error<type_mismatch_error>(expression->position(),
+ expression->value().type_decoration,
+ type_mismatch_error::invalid_cast{ expression->type_decoration });
+ }
}
void type_analysis_visitor::visit(binary_expression *expression)
@@ -860,8 +954,11 @@ namespace elna::boot
}
if (!valid)
{
- add_error<binary_operation_error>(expression->position(),
- expression->lhs().type_decoration, expression->rhs().type_decoration, operation);
+ type_mismatch_error::binary binary_error{
+ .right = expression->rhs().type_decoration,
+ .operation = operation
+ };
+ add_error<type_mismatch_error>(expression->position(), expression->lhs().type_decoration, binary_error);
}
}
@@ -869,8 +966,7 @@ namespace elna::boot
{
walking_visitor::visit(trait);
- if (trait->name == "size" || trait->name == "alignment"
- || trait->name == "min" || trait->name == "max")
+ if (trait->name == "size" || trait->name == "alignment")
{
if (trait->arguments.size() != 1)
{
@@ -878,6 +974,26 @@ namespace elna::boot
trait_error::argument_count{ .expected = 1, .actual = trait->arguments.size() });
}
}
+ else if ((trait->name == "min" || trait->name == "max") && !trait->type_decoration.empty())
+ {
+ if (trait->arguments.size() != 1)
+ {
+ add_error<trait_error>(trait->position(), trait->name.name(),
+ trait_error::argument_count{ .expected = 1, .actual = trait->arguments.size() });
+ }
+ else
+ {
+ const type resolved = resolve_underlying_type(trait->type_decoration);
+
+ if (resolved.get<enumeration_type>() == nullptr
+ && !is_primitive_type(resolved, "Float")
+ && !is_discrete_type(resolved))
+ {
+ add_error<trait_error>(trait->name.position(), trait->name.name(),
+ trait_error::unsupported_type{ trait->type_decoration });
+ }
+ }
+ }
else if (trait->name == "offset")
{
if (trait->arguments.size() != 2)