aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-29 01:16:18 +0200
committerEugen Wissner <belka@caraus.de>2026-07-29 01:16:18 +0200
commitf8daedce5c73e02dfb2fc59d75777190185584df (patch)
tree9ec211b96c3d7efd2fca0da37baeaa8b341e31fb
parent36a274c9a8bca944234589220def025d3920b3ef (diff)
downloadelna-f8daedce5c73e02dfb2fc59d75777190185584df.tar.gz
Validate cast compatibility during semantic analysis
-rw-r--r--boot/ast.cc47
-rw-r--r--boot/name_analysis.cc136
-rw-r--r--boot/symbol.cc8
-rw-r--r--boot/type_check.cc406
-rw-r--r--gcc/gcc/elna-diagnostic.cc107
-rw-r--r--gcc/gcc/elna-generic.cc135
-rw-r--r--gcc/gcc/elna-tree.cc39
-rw-r--r--include/elna/boot/ast.h2
-rw-r--r--include/elna/boot/name_analysis.h43
-rw-r--r--include/elna/boot/symbol.h11
-rw-r--r--include/elna/boot/type_check.h139
-rw-r--r--include/elna/gcc/elna-diagnostic.h1
-rw-r--r--include/elna/gcc/elna-tree.h1
-rw-r--r--testsuite/fail_compilation/while_condition.elna2
-rw-r--r--testsuite/runnable/slice_cast.elna6
15 files changed, 464 insertions, 619 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index a2f108a..7f34243 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -1647,51 +1647,4 @@ namespace elna::boot
{
return *this->m_range;
}
-
- const char *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();
- };
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 077b5d6..89353e9 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -22,27 +22,35 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
- declaration_error::declaration_error(const source_position position, payload_type payload)
- : error(position), payload(std::move(payload))
+ declaration_error::declaration_error(const source_position position, const std::string& name, payload_type payload)
+ : error(position), name(name), payload(payload)
{
}
std::string declaration_error::what() const
{
- return std::visit([](const auto& payload) -> std::string {
+ return std::visit([this](const auto& payload) -> std::string {
using T = std::decay_t<decltype(payload)>;
- if constexpr (std::is_same_v<T, undeclared>)
+ if constexpr (std::is_same_v<T, redefinition>)
{
- return "Type '" + payload.name + "' not declared";
+ return "Symbol '" + this->name + "' has been already defined";
}
- else if constexpr (std::is_same_v<T, local_export>)
+ else if constexpr (std::is_same_v<T, kind>)
{
- return "Local symbol '" + payload.name + "' cannot be exported";
- }
- else if constexpr (std::is_same_v<T, redefinition>)
- {
- return "Symbol '" + payload.name + "' has been already defined";
+ switch (payload)
+ {
+ case kind::undeclared_type:
+ return "Type '" + this->name + "' not declared";
+ case kind::undeclared_trait:
+ return "Trait '#" + this->name + "' not declared";
+ case kind::undeclared_symbol:
+ return "Symbol '" + this->name + "' not declared";
+ case kind::local_export:
+ return "Local symbol '" + this->name + "' cannot be exported";
+ default:
+ __builtin_unreachable();
+ }
}
}, this->payload);
}
@@ -77,52 +85,54 @@ namespace elna::boot
}
}
- member_error::member_error(const source_position position, payload_type payload)
- : error(position), payload(std::move(payload))
+ member_error::member_error(const source_position position, const std::string& name,
+ const type& composite, payload_type payload)
+ : error(position), name(name), composite(composite), payload(std::move(payload))
{
}
std::string member_error::what() const
{
- return std::visit([](const auto& pay) -> std::string {
- using T = std::decay_t<decltype(pay)>;
+ return std::visit([this](const auto& payload) -> std::string {
+ using T = std::decay_t<decltype(payload)>;
+
if constexpr (std::is_same_v<T, not_found>)
{
- const type resolved = resolve_underlying_type(pay.composite);
+ const type resolved = resolve_underlying_type(this->composite);
const bool is_enum = resolved.get<enumeration_type>() != nullptr;
const bool is_record = resolved.get<record_type>() != nullptr;
if (is_enum || is_record)
{
std::string message = is_enum ? "Enumeration" : "Record";
- if (auto alias = pay.composite.template get<alias_type>())
+ if (auto alias = this->composite.get<alias_type>())
{
message += " '" + alias->name + "'";
}
message += " does not have a ";
message += is_enum ? "member" : "field";
- message += " named '" + pay.name + "'";
+ message += " named '" + this->name + "'";
return message;
}
- return "Type '" + pay.composite.to_string()
- + "' does not have a field named '" + pay.name + "'";
+ return "Type '" + this->composite.to_string()
+ + "' does not have a field named '" + this->name + "'";
}
else if constexpr (std::is_same_v<T, duplicate>)
{
- const type resolved = resolve_underlying_type(pay.aggregate);
+ const type resolved = resolve_underlying_type(this->composite);
const bool is_enum = resolved.get<enumeration_type>() != nullptr;
const std::string kind = is_enum ? "member" : "field";
std::string message = is_enum ? "Enumeration" : "Record";
- if (auto alias = pay.aggregate.template get<alias_type>())
+ if (auto alias = this->composite.get<alias_type>())
{
message += " '" + alias->name + "'";
}
- message += " already has a " + kind + " named '" + pay.name + "'";
+ message += " already has a " + kind + " named '" + this->name + "'";
- if (pay.base.has_value())
+ if (payload.base.has_value())
{
- message += " (defined in base type '" + *pay.base + "')";
+ message += " (defined in base type '" + *payload.base + "')";
}
return message;
}
@@ -141,18 +151,6 @@ namespace elna::boot
return std::nullopt;
}
- unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait,
- type actual)
- : error(trait.position()), actual(std::move(actual)), trait_name(trait.name())
- {
- }
-
- std::string unsupported_trait_type_error::what() const
- {
- return "Type '" + actual.to_string()
- + "' 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)
{
@@ -317,14 +315,12 @@ namespace elna::boot
}
for (auto& field : record->fields)
{
- names.insert(field.first, field_origin{ .declaration = std::nullopt, .base_type = composite_type });
+ names.insert(field.first, field_origin{ .position = std::nullopt, .base_type = composite_type });
}
}
- ordered_map<type> name_analysis_visitor::build_composite_type(
- const std::vector<field_declaration>& fields,
- ordered_map<field_origin>& field_names,
- const type& aggregate)
+ ordered_map<type> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields,
+ ordered_map<field_origin>& field_names, const type& aggregate)
{
ordered_map<type> result;
@@ -334,12 +330,12 @@ namespace elna::boot
for (const auto& field_name : field.first)
{
auto [existing, inserted] = field_names.insert(field_name.name(),
- field_origin{ .declaration = field.second->position(), .base_type = type() });
+ field_origin{ .position = field.second->position(), .base_type = type() });
if (!inserted)
{
std::optional<std::string> base_name;
- if (!existing->second.declaration.has_value()
+ if (!existing->second.position.has_value()
&& !existing->second.base_type.empty())
{
if (auto alias = existing->second.base_type.get<alias_type>())
@@ -347,9 +343,8 @@ namespace elna::boot
base_name = alias->name;
}
}
- add_error<member_error>(field_name.position(),
- member_error::duplicate{.name = field_name.name(), .aggregate = aggregate,
- .original = existing->second.declaration, .base = base_name});
+ add_error<member_error>(field_name.position(), field_name.name(), aggregate,
+ member_error::duplicate{ .original = existing->second.position, .base = base_name });
}
else
{
@@ -384,7 +379,7 @@ namespace elna::boot
else
{
add_error<declaration_error>(expression->base.value().position(),
- declaration_error::undeclared{.name = expression->base.value().name()});
+ expression->base.value().name(), declaration_error::kind::undeclared_type);
this->current_type = type();
return;
}
@@ -413,7 +408,7 @@ namespace elna::boot
else
{
add_error<declaration_error>(expression->type_name.position(),
- declaration_error::undeclared{.name = expression->type_name.name()});
+ expression->type_name.name(), declaration_error::kind::undeclared_type);
}
for (const field_initializer& initializer : expression->field_initializers)
{
@@ -421,8 +416,8 @@ namespace elna::boot
if (!expression->type_decoration.empty()
&& lookup_field(expression->type_decoration, initializer.name()).empty())
{
- add_error<declaration_error>(initializer.id().position(),
- declaration_error::undeclared{.name = initializer.id().name()});
+ add_error<member_error>(initializer.id().position(), initializer.id().name(),
+ expression->type_decoration, member_error::not_found{});
}
}
}
@@ -469,7 +464,7 @@ namespace elna::boot
{
std::vector<std::string> member_names;
member_names.reserve(expression->members.size());
-for (const auto& member : expression->members)
+ for (const auto& member : expression->members)
{
member_names.emplace_back(member.name());
}
@@ -483,9 +478,8 @@ for (const auto& member : expression->members)
auto existing = seen.find(member.name());
if (existing != seen.end())
{
- add_error<member_error>(member.position(),
- member_error::duplicate{.name = member.name(), .aggregate = aggregate,
- .original = existing->second, .base = std::nullopt});
+ add_error<member_error>(member.position(), member.name(), aggregate,
+ member_error::duplicate{ .original = existing->second, .base = std::nullopt });
}
else
{
@@ -504,8 +498,8 @@ for (const auto& member : expression->members)
if (!this->bag.enter(name, variable_symbol))
{
auto original = this->bag.lookup(name);
- add_error<declaration_error>(position,
- declaration_error::redefinition{.name = name, .original = original->position});
+ add_error<declaration_error>(position, name,
+ declaration_error::redefinition{ .original = original->position });
}
return variable_symbol;
}
@@ -632,25 +626,11 @@ for (const auto& member : expression->members)
else if (trait->name == "min" || trait->name == "max")
{
trait->type_decoration = trait->types.empty() ? type() : trait->types.front();
-
- if (!trait->type_decoration.empty())
- {
- 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<unsupported_trait_type_error>(trait->name,
- trait->type_decoration);
- trait->type_decoration = type();
- }
- }
}
else
{
add_error<declaration_error>(trait->name.position(),
- declaration_error::undeclared{.name = trait->name.name()});
+ trait->name.name(), declaration_error::kind::undeclared_trait);
}
}
@@ -730,8 +710,8 @@ for (const auto& member : expression->members)
if (expression->type_decoration.empty())
{
add_error<member_error>(expression->field().position(),
- member_error::not_found{.name = expression->field().name(),
- .composite = expression->base().type_decoration});
+ expression->field().name(), expression->base().type_decoration,
+ member_error::not_found{});
}
else
{
@@ -805,7 +785,7 @@ for (const auto& member : expression->members)
else
{
add_error<declaration_error>(expression->position(),
- declaration_error::undeclared{.name = expression->name});
+ expression->name, declaration_error::kind::undeclared_symbol);
}
}
@@ -882,8 +862,8 @@ for (const auto& member : expression->members)
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
{
add_error<declaration_error>(declaration->identifier.id().position(),
- declaration_error::redefinition{.name = declaration->identifier.id().name(),
- .original = declaration->position()});
+ declaration->identifier.id().name(),
+ declaration_error::redefinition{ .original = declaration->position() });
}
}
@@ -906,7 +886,7 @@ for (const auto& member : expression->members)
if (variable_identifier.exported())
{
add_error<declaration_error>(variable_identifier.id().position(),
- declaration_error::local_export{.name = variable_identifier.id().name()});
+ variable_identifier.id().name(), declaration_error::kind::local_export);
}
}
}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index e0a89e6..a51ee01 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -448,6 +448,14 @@ namespace elna::boot
|| is_primitive_type(checked, "Pointer");
}
+ bool is_scalar_type(const type& checked)
+ {
+ return is_discrete_type(checked)
+ || is_primitive_type(checked, "Float")
+ || is_any_pointer_type(checked)
+ || checked.get<enumeration_type>() != nullptr;
+ }
+
type get_range_base_type(const type& range)
{
if (auto array = range.get<array_type>())
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)
diff --git a/gcc/gcc/elna-diagnostic.cc b/gcc/gcc/elna-diagnostic.cc
index 3dcdb98..909869c 100644
--- a/gcc/gcc/elna-diagnostic.cc
+++ b/gcc/gcc/elna-diagnostic.cc
@@ -16,8 +16,6 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/gcc/elna-diagnostic.h"
-#include "elna/gcc/elna-tree.h"
-#include "elna/gcc/elna1.h"
namespace elna::gcc
{
@@ -65,111 +63,6 @@ namespace elna::gcc
return make_location(caret, start, end);
}
- static std::string print_aggregate_name(tree type, const std::string& kind_name)
- {
- if (TYPE_IDENTIFIER(type) == NULL_TREE)
- {
- return kind_name;
- }
- else
- {
- return std::string(IDENTIFIER_POINTER(TYPE_IDENTIFIER(type)));
- }
- }
-
- std::string print_type(tree type)
- {
- gcc_assert(TYPE_P(type));
-
- tree unqualified_type = get_qualified_type(type, TYPE_UNQUALIFIED);
- tree_code code = TREE_CODE(type);
-
- if (unqualified_type == elna_int_type_node)
- {
- return "Int";
- }
- else if (unqualified_type == elna_word_type_node)
- {
- return "Word";
- }
- else if (unqualified_type == elna_bool_type_node)
- {
- return "Bool";
- }
- else if (unqualified_type == elna_pointer_type_node)
- {
- return "Pointer";
- }
- else if (unqualified_type == elna_float_type_node)
- {
- return "Float";
- }
- else if (unqualified_type == elna_char_type_node)
- {
- return "Char";
- }
- else if (is_void_type(unqualified_type)) // For procedures without a return type.
- {
- return "()";
- }
- else if (POINTER_TYPE_P(unqualified_type))
- {
- tree pointer_target_type = TREE_TYPE(type);
-
- if (TREE_CODE(pointer_target_type) == FUNCTION_TYPE)
- {
- return print_type(pointer_target_type);
- }
- else
- {
- return std::string("^" + print_type(pointer_target_type));
- }
- }
- else if (code == FUNCTION_TYPE)
- {
- std::string output = "proc(";
- tree parameter_type = TYPE_ARG_TYPES(type);
- while (TREE_VALUE(parameter_type) != void_type_node)
- {
- output += print_type(TREE_VALUE(parameter_type));
- parameter_type = TREE_CHAIN(parameter_type);
- if (TREE_VALUE(parameter_type) == void_type_node)
- {
- break;
- }
- else
- {
- output += ", ";
- }
- }
- output += ')';
- tree return_type = TREE_TYPE(type);
-
- if (!is_void_type(return_type))
- {
- output += " -> " + print_type(return_type);
- }
- return output;
- }
- else if (code == ARRAY_TYPE)
- {
- return "array";
- }
- else if (code == RECORD_TYPE)
- {
- return print_aggregate_name(unqualified_type, "record");
- }
- else if (code == ENUMERAL_TYPE)
- {
- return print_aggregate_name(unqualified_type, "enumeration");
- }
- else
- {
- return "<<unknown-type>>";
- }
- gcc_unreachable();
- }
-
void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors)
{
for (const auto& error : errors)
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 559d09c..1104cf1 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -62,37 +62,22 @@ namespace elna::gcc
void generic_visitor::build_assert_builtin(location_t call_location,
const std::vector<boot::expression *>& arguments)
{
- if (arguments.size() != 1)
+ arguments.at(0)->accept(this);
+ tree constant_expression = extract_constant(this->current_expression);
+
+ if (constant_expression == boolean_false_node)
{
- error_at(call_location, "assert expects exactly one boolean argument, got %lu", arguments.size());
- this->current_expression = error_mark_node;
+ this->current_expression = call_built_in(call_location, "__builtin_unreachable", void_type_node);
+ }
+ else if (constant_expression != boolean_true_node)
+ {
+ tree assert_expression = call_built_in(call_location, "__builtin_trap", void_type_node);
+ this->current_expression = build3(COND_EXPR, void_type_node, this->current_expression,
+ NULL_TREE, assert_expression);
}
else
{
- arguments.at(0)->accept(this);
- tree argument_type = TREE_TYPE(this->current_expression);
-
- if (argument_type != elna_bool_type_node)
- {
- error_at(call_location, "assert expects exactly one boolean argument, got %s",
- print_type(argument_type).c_str());
- this->current_expression = error_mark_node;
- }
- tree constant_expression = extract_constant(this->current_expression);
- if (constant_expression == boolean_false_node)
- {
- this->current_expression = call_built_in(call_location, "__builtin_unreachable", void_type_node);
- }
- else if (constant_expression != boolean_true_node)
- {
- tree assert_expression = call_built_in(call_location, "__builtin_trap", void_type_node);
- this->current_expression = build3(COND_EXPR, void_type_node, this->current_expression,
- NULL_TREE, assert_expression);
- }
- else
- {
- this->current_expression = NULL_TREE;
- }
+ this->current_expression = NULL_TREE;
}
}
@@ -145,18 +130,34 @@ namespace elna::gcc
tree cast_target = get_inner_alias(expression->type_decoration, this->symbols);
expression->value().accept(this);
- tree cast_source = TREE_TYPE(this->current_expression);
-
- if (is_castable_type(cast_target) && (is_castable_type(cast_source)))
- {
- this->current_expression = fold_convert_loc(get_location(&expression->position()),
- cast_target, this->current_expression);
+ location_t cast_location = get_location(&expression->position());
+ auto source_slice = resolve_underlying_type(expression->value().type_decoration)
+ .get<boot::slice_type>();
+ auto target_slice = resolve_underlying_type(expression->type_decoration)
+ .get<boot::slice_type>();
+
+ if (source_slice != nullptr && target_slice != nullptr)
+ {
+ tree slice_fields = TYPE_FIELDS(TREE_TYPE(this->current_expression));
+ tree ptr = build3_loc(cast_location, COMPONENT_REF, TREE_TYPE(slice_fields),
+ this->current_expression, slice_fields, NULL_TREE);
+ tree slice_chain = TREE_CHAIN(slice_fields);
+ tree old_length = build3_loc(cast_location, COMPONENT_REF, TREE_TYPE(slice_chain),
+ this->current_expression, slice_chain, NULL_TREE);
+
+ tree source_size = TYPE_SIZE_UNIT(get_inner_alias(source_slice->base, this->symbols));
+ tree target_size = TYPE_SIZE_UNIT(get_inner_alias(target_slice->base, this->symbols));
+ tree size_ratio = build2(TRUNC_DIV_EXPR, elna_word_type_node,
+ fold_convert(elna_word_type_node, source_size),
+ fold_convert(elna_word_type_node, target_size));
+ old_length = build2(MULT_EXPR, elna_word_type_node, old_length, size_ratio);
+ tree new_length = fold_convert(TREE_TYPE(slice_chain), old_length);
+
+ this->current_expression = build_slice(cast_target, ptr, new_length);
}
else
{
- error_at(get_location(&expression->position()), "Type '%s' cannot be converted to '%s'",
- print_type(cast_source).c_str(), print_type(cast_target).c_str());
- this->current_expression = error_mark_node;
+ this->current_expression = fold_convert_loc(cast_location, cast_target, this->current_expression);
}
}
@@ -209,7 +210,6 @@ namespace elna::gcc
location_t location = get_location(&expression->position());
tree slice_type = get_inner_alias(expression->type_decoration, this->symbols);
tree ptr_field = TYPE_FIELDS(slice_type);
- tree length_field = TREE_CHAIN(ptr_field);
expression->base().accept(this);
tree base = this->current_expression;
@@ -239,14 +239,10 @@ namespace elna::gcc
expression->end().accept(this);
tree end_index = fold_convert(elna_word_type_node, this->current_expression);
- tree slice_length = build2(MINUS_EXPR, elna_word_type_node,
- end_index, start_index);
+ tree slice_length = build2(MINUS_EXPR, elna_word_type_node, end_index, start_index);
slice_length = build2(PLUS_EXPR, elna_word_type_node, slice_length, elna_word_one_node);
- vec<constructor_elt, va_gc> *tree_arguments = nullptr;
- CONSTRUCTOR_APPEND_ELT(tree_arguments, ptr_field, slice_ptr);
- CONSTRUCTOR_APPEND_ELT(tree_arguments, length_field, slice_length);
- this->current_expression = build_constructor(slice_type, tree_arguments);
+ this->current_expression = build_slice(slice_type, slice_ptr, slice_length);
}
void generic_visitor::visit(boot::unit *unit)
@@ -541,11 +537,7 @@ namespace elna::gcc
string_literal, integer_zero_node, NULL_TREE, NULL_TREE);
string_literal = build1(ADDR_EXPR, ptr_type, string_literal);
- vec<constructor_elt, va_gc> *elms = nullptr;
- CONSTRUCTOR_APPEND_ELT(elms, ptr_field, string_literal);
- CONSTRUCTOR_APPEND_ELT(elms, TREE_CHAIN(ptr_field), index_constant);
-
- this->current_expression = build_constructor(slice_type, elms);
+ this->current_expression = build_slice(slice_type, string_literal, index_constant);
}
void generic_visitor::visit(boot::traits_expression *trait)
@@ -783,13 +775,6 @@ namespace elna::gcc
location_t location = get_location(&expression->position());
expression->index().accept(this);
- if (!is_integral_type(TREE_TYPE(this->current_expression)))
- {
- error_at(location, "Type '%s' cannot be used as index",
- print_type(TREE_TYPE(this->current_expression)).c_str());
- this->current_expression = error_mark_node;
- return;
- }
tree offset = fold_convert(elna_word_type_node, this->current_expression);
if (TREE_CODE(TREE_TYPE(designator)) == ARRAY_TYPE)
@@ -799,7 +784,7 @@ namespace elna::gcc
this->current_expression = build4_loc(location,
ARRAY_REF, element_type, designator, offset, elna_word_one_node, NULL_TREE);
}
- else if (expression->base().type_decoration.get<boot::slice_type>() != nullptr)
+ else
{
tree ptr_field = TYPE_FIELDS(TREE_TYPE(designator));
offset = build2(MINUS_EXPR, elna_word_type_node, offset, elna_word_one_node);
@@ -810,12 +795,6 @@ namespace elna::gcc
this->current_expression = build_simple_mem_ref_loc(location, target_pointer);
}
- else
- {
- error_at(location, "Indexing is not allowed on type '%s'",
- print_type(TREE_TYPE(designator)).c_str());
- this->current_expression = error_mark_node;
- }
}
void generic_visitor::visit(boot::field_access_expression *expression)
@@ -867,19 +846,9 @@ namespace elna::gcc
{
expression->base().accept(this);
location_t expression_location = get_location(&expression->position());
- tree expression_type = TREE_TYPE(this->current_expression);
- if (POINTER_TYPE_P(expression_type))
- {
- this->current_expression = build_simple_mem_ref_loc(expression_location,
- this->current_expression);
- }
- else
- {
- error_at(expression_location, "Type '%s' cannot be dereferenced, it is not a pointer",
- print_type(expression_type).c_str());
- this->current_expression = error_mark_node;
- }
+ this->current_expression = build_simple_mem_ref_loc(expression_location,
+ this->current_expression);
}
void generic_visitor::visit(boot::assign_statement *statement)
@@ -892,22 +861,8 @@ namespace elna::gcc
statement->rvalue().accept(this);
tree rvalue = prepare_rvalue(this->current_expression);
- if (TREE_CODE(lvalue) == CONST_DECL)
- {
- error_at(statement_location, "Cannot modify constant '%s'",
- statement->lvalue().is_named()->name.c_str());
- }
- else if (TYPE_READONLY(TREE_TYPE(lvalue)))
- {
- error_at(statement_location, "Cannot modify a constant expression of type '%s'",
- print_type(TREE_TYPE(lvalue)).c_str());
- }
- else
- {
- tree assignment = fold_build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue);
-
- append_statement(assignment);
- }
+ tree assignment = fold_build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue);
+ append_statement(assignment);
this->current_expression = NULL_TREE;
}
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index 352d413..7862eaa 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -193,37 +193,21 @@ namespace elna::gcc
gcc_unreachable();
}
- tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name)
+ tree find_field_by_name(location_t, tree type, const std::string& field_name)
{
- if (type == error_mark_node)
- {
- return type;
- }
- tree field_declaration = TYPE_FIELDS(type);
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
- if (!RECORD_OR_UNION_TYPE_P(type))
+ if (type == error_mark_node || !RECORD_OR_UNION_TYPE_P(type))
{
- error_at(expression_location, "Type '%s' does not have a field named '%s'",
- print_type(type).c_str(), field_name.c_str());
return error_mark_node;
}
- while (field_declaration != NULL_TREE)
+ for (tree field = TYPE_FIELDS(type); field != NULL_TREE; field = TREE_CHAIN(field))
{
- tree declaration_name = DECL_NAME(field_declaration);
- const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name);
-
- if (field_name == identifier_pointer)
+ if (field_name == IDENTIFIER_POINTER(DECL_NAME(field)))
{
- break;
+ return field;
}
- field_declaration = TREE_CHAIN(field_declaration);
- }
- if (field_declaration == NULL_TREE)
- {
- error_at(expression_location, "Aggregate type does not have a field '%s'", field_name.c_str());
- return error_mark_node;
}
- return field_declaration;
+ return error_mark_node;
}
tree build_static_array_type(tree type, const std::uint64_t size)
@@ -234,6 +218,17 @@ namespace elna::gcc
return build_array_type(type, range_type);
}
+ tree build_slice(tree slice_type, tree ptr, tree length)
+ {
+ tree ptr_field = TYPE_FIELDS(slice_type);
+ vec<constructor_elt, va_gc> *elements = nullptr;
+
+ CONSTRUCTOR_APPEND_ELT(elements, ptr_field, ptr);
+ CONSTRUCTOR_APPEND_ELT(elements, TREE_CHAIN(ptr_field), length);
+
+ return build_constructor(slice_type, elements);
+ }
+
tree build_enumeration_type(const std::vector<std::string>& members)
{
tree composite_type_node = make_node(ENUMERAL_TYPE);
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 9697d1c..cc5878b 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -972,6 +972,4 @@ namespace elna::boot
~unary_expression() override;
};
-
- const char *print_binary_operator(const binary_operator operation);
}
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index 8f53260..626e9f2 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -35,27 +35,20 @@ namespace elna::boot
class declaration_error : public error
{
public:
- struct undeclared
- {
- std::string name;
- };
- struct local_export
- {
- std::string name;
- };
+ enum class kind { undeclared_type, undeclared_trait, undeclared_symbol, local_export };
struct redefinition
{
- std::string name;
std::optional<source_position> original;
};
- using payload_type = std::variant<undeclared, local_export, redefinition>;
+ using payload_type = std::variant<redefinition, kind>;
- declaration_error(const source_position position, payload_type payload);
+ declaration_error(const source_position position, const std::string& name, payload_type payload);
std::string what() const override;
std::optional<std::pair<std::string, source_position>> note() const override;
private:
+ std::string name;
payload_type payload;
};
@@ -84,46 +77,31 @@ namespace elna::boot
public:
struct not_found
{
- std::string name;
- type composite;
};
struct duplicate {
- std::string name;
- type aggregate;
std::optional<source_position> original;
std::optional<std::string> base;
};
using payload_type = std::variant<not_found, duplicate>;
- member_error(const source_position position, payload_type payload);
+ member_error(const source_position position, const std::string& name,
+ const type& composite, payload_type payload);
std::string what() const override;
std::optional<std::pair<std::string, source_position>> note() const override;
private:
+ std::string name;
+ type composite;
payload_type payload;
};
/**
- * Trait is not applicable to the given type.
- */
- class unsupported_trait_type_error : public error
- {
- type actual;
- std::string trait_name;
-
- public:
- unsupported_trait_type_error(const identifier& trait, type actual);
-
- std::string what() const override;
- };
-
- /**
* Origin of a field in a composite type.
*/
struct field_origin
{
- std::optional<source_position> declaration;
+ std::optional<source_position> position;
type base_type;
};
@@ -139,8 +117,7 @@ namespace elna::boot
std::pair<procedure_type, std::vector<std::string>> build_procedure(
procedure_type_expression& expression);
ordered_map<type> build_composite_type(const std::vector<field_declaration>& fields,
- ordered_map<field_origin>& field_names,
- const type& aggregate);
+ ordered_map<field_origin>& field_names, const type& aggregate);
std::shared_ptr<variable_info> register_variable(const std::string& name,
const bool is_extern, const source_position position);
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index dd3e8ff..01eedb1 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -577,6 +577,17 @@ namespace elna::boot
bool is_any_pointer_type(const type& checked);
/**
+ * Checks whether the given type is a scalar type.
+ *
+ * Scalar types are discrete types, floating point numbers, enumerations,
+ * any pointers.
+ *
+ * \param checked The type to check.
+ * \return Whether the type is a scalar type.
+ */
+ bool is_scalar_type(const type& checked);
+
+ /**
* If \a range is an array or a slice gives its base type, otherwise
* returns an empty type.
*
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index bd32c08..628af06 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -32,33 +32,50 @@ namespace elna::boot
*/
class type_mismatch_error : public error
{
- type expected;
- type actual;
-
public:
+ struct expected_type
+ {
+ type value;
+ };
+ struct return_type
+ {
+ std::string identifier;
+ };
+ struct unary
+ {
+ unary_operator operation;
+ };
+ struct binary
+ {
+ type right;
+ binary_operator operation;
+ };
+ struct invalid_cast
+ {
+ type target;
+ };
+ enum class kind {
+ record_base,
+ for_range,
+ condition,
+ constant_assignment,
+ array_index,
+ non_indexable,
+ dereference_of_non_pointer
+ };
+ using payload_type = std::variant<expected_type, return_type, unary, binary, invalid_cast, kind>;
+
type_mismatch_error(const source_position position,
- type expected, type actual);
+ type actual, payload_type payload);
std::string what() const override;
- };
-
- /**
- * Attempted to assign a value whose type contains constant members.
- */
- class constant_assignment_error : public error
- {
- type assignee;
- public:
- constant_assignment_error(const source_position position, type assignee);
-
- std::string what() const override;
+ private:
+ type actual;
+ payload_type payload;
};
/**
- * Attempted to access a field that does not exist on the given type.
- */
- /**
* Cyclic type declaration.
*/
class cyclic_declaration_error : public error
@@ -72,42 +89,6 @@ namespace elna::boot
};
/**
- * Procedure with a return type does not return.
- */
- class return_error : public error
- {
- std::string identifier;
- type return_type;
-
- public:
- return_error(const std::string& identifier, const source_position position,
- type return_type = type());
-
- std::string what() const override;
- };
-
- /**
- * Unexpected kind of a type at specific position.
- */
- class type_kind_error : public error
- {
- public:
- enum class kind
- {
- record_base,
- for_loop,
- condition
- };
- type_kind_error(const source_position position, kind type_kind, const type& actual);
-
- std::string what() const override;
-
- private:
- kind type_kind;
- type actual;
- };
-
- /**
* Argument count in a procedure call or array constructor doesn't match
* the expected number of parameters or elements.
*/
@@ -124,21 +105,23 @@ namespace elna::boot
};
/**
- * A trait invocation has invalid arguments.
+ * A trait invocation is invalid.
*/
class trait_error : public error
{
public:
- /// Wrong number of arguments passed to a trait.
struct argument_count
{
std::size_t expected;
std::size_t actual;
};
- /// \c \#offset second argument is not a field name.
struct offset_not_field_name {};
+ struct unsupported_type
+ {
+ type actual;
+ };
- using payload_type = std::variant<argument_count, offset_not_field_name>;
+ using payload_type = std::variant<argument_count, offset_not_field_name, unsupported_type>;
trait_error(const source_position position, const std::string& trait_name,
payload_type payload);
@@ -147,40 +130,7 @@ namespace elna::boot
private:
std::string trait_name;
- payload_type m_payload;
- };
-
- /**
- * A unary operator is applied to an unsupported type.
- */
- class unary_operation_error : public error
- {
- type actual;
- unary_operator op;
-
- static char unary_operator_symbol(unary_operator operation);
-
- public:
- unary_operation_error(const source_position position, type actual,
- unary_operator operation);
-
- std::string what() const override;
- };
-
- /**
- * A binary operator is applied to unsupported types.
- */
- class binary_operation_error : public error
- {
- type left;
- type right;
- binary_operator op;
-
- public:
- binary_operation_error(const source_position position,
- type left, type right, binary_operator operation);
-
- std::string what() const override;
+ payload_type payload;
};
/**
@@ -258,6 +208,9 @@ namespace elna::boot
void visit(array_constructor_expression *expression) override;
void visit(traits_expression *trait) override;
void visit(slicing_expression *expression) override;
+ void visit(array_access_expression *expression) override;
+ void visit(dereference_expression *expression) override;
+ void visit(cast_expression *expression) override;
void visit(unary_expression *expression) override;
void visit(binary_expression *expression) override;
};
diff --git a/include/elna/gcc/elna-diagnostic.h b/include/elna/gcc/elna-diagnostic.h
index 2cb30f5..3e0dd51 100644
--- a/include/elna/gcc/elna-diagnostic.h
+++ b/include/elna/gcc/elna-diagnostic.h
@@ -43,6 +43,5 @@ namespace elna::gcc
location_t get_location(const boot::source_position *position);
location_t make_range(const boot::source_position& position);
- std::string print_type(tree type);
void report_errors(const std::deque<std::unique_ptr<boot::error>>& errors);
}
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index 71cc13b..41f5cc3 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -63,6 +63,7 @@ namespace elna::gcc
tree build_field(location_t location, tree record_type, const std::string& name, tree type);
tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name);
tree build_static_array_type(tree type, const std::uint64_t size);
+ tree build_slice(tree slice_type, tree ptr, tree length);
tree build_enumeration_type(const std::vector<std::string>& members);
const elna::boot::target_info& get_host_target();
diff --git a/testsuite/fail_compilation/while_condition.elna b/testsuite/fail_compilation/while_condition.elna
index 73fed95..1e17489 100644
--- a/testsuite/fail_compilation/while_condition.elna
+++ b/testsuite/fail_compilation/while_condition.elna
@@ -1,4 +1,4 @@
begin
- while 1 do (* @Error Condition must be a boolean, got 'Int' *)
+ while 1 do (* @Error Condition must be a boolean expression, but got 'Int' *)
end
end.
diff --git a/testsuite/runnable/slice_cast.elna b/testsuite/runnable/slice_cast.elna
new file mode 100644
index 0000000..5ab2c93
--- /dev/null
+++ b/testsuite/runnable/slice_cast.elna
@@ -0,0 +1,6 @@
+var
+ ints: [4]Int := [4]Int{ 1, 2, 3, 4 }
+
+begin
+ assert(cast(ints[1u to 4u]: []Char).length = #size(Int) * ints.length)
+end.