From f8daedce5c73e02dfb2fc59d75777190185584df Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Jul 2026 01:16:18 +0200 Subject: Validate cast compatibility during semantic analysis --- boot/ast.cc | 47 --- boot/name_analysis.cc | 136 ++++---- boot/symbol.cc | 8 + boot/type_check.cc | 406 +++++++++++++++--------- gcc/gcc/elna-diagnostic.cc | 107 ------- gcc/gcc/elna-generic.cc | 135 +++----- gcc/gcc/elna-tree.cc | 39 +-- include/elna/boot/ast.h | 2 - include/elna/boot/name_analysis.h | 43 +-- include/elna/boot/symbol.h | 11 + include/elna/boot/type_check.h | 139 +++----- include/elna/gcc/elna-diagnostic.h | 1 - include/elna/gcc/elna-tree.h | 1 + testsuite/fail_compilation/while_condition.elna | 2 +- testsuite/runnable/slice_cast.elna | 6 + 15 files changed, 464 insertions(+), 619 deletions(-) create mode 100644 testsuite/runnable/slice_cast.elna 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; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { - return "Type '" + payload.name + "' not declared"; + return "Symbol '" + this->name + "' has been already defined"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return "Local symbol '" + payload.name + "' cannot be exported"; - } - else if constexpr (std::is_same_v) - { - 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; + return std::visit([this](const auto& payload) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { - const type resolved = resolve_underlying_type(pay.composite); + const type resolved = resolve_underlying_type(this->composite); const bool is_enum = resolved.get() != nullptr; const bool is_record = resolved.get() != nullptr; if (is_enum || is_record) { std::string message = is_enum ? "Enumeration" : "Record"; - if (auto alias = pay.composite.template get()) + if (auto alias = this->composite.get()) { 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) { - const type resolved = resolve_underlying_type(pay.aggregate); + const type resolved = resolve_underlying_type(this->composite); const bool is_enum = resolved.get() != nullptr; const std::string kind = is_enum ? "member" : "field"; std::string message = is_enum ? "Enumeration" : "Record"; - if (auto alias = pay.aggregate.template get()) + if (auto alias = this->composite.get()) { 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 name_analysis_visitor::build_composite_type( - const std::vector& fields, - ordered_map& field_names, - const type& aggregate) + ordered_map name_analysis_visitor::build_composite_type(const std::vector& fields, + ordered_map& field_names, const type& aggregate) { ordered_map 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 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()) @@ -347,9 +343,8 @@ namespace elna::boot base_name = alias->name; } } - add_error(field_name.position(), - member_error::duplicate{.name = field_name.name(), .aggregate = aggregate, - .original = existing->second.declaration, .base = base_name}); + add_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(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(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(initializer.id().position(), - declaration_error::undeclared{.name = initializer.id().name()}); + add_error(initializer.id().position(), initializer.id().name(), + expression->type_decoration, member_error::not_found{}); } } } @@ -469,7 +464,7 @@ namespace elna::boot { std::vector 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.position(), - member_error::duplicate{.name = member.name(), .aggregate = aggregate, - .original = existing->second, .base = std::nullopt}); + add_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(position, - declaration_error::redefinition{.name = name, .original = original->position}); + add_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() == nullptr - && !is_primitive_type(resolved, "Float") - && !is_discrete_type(resolved)) - { - add_error(trait->name, - trait->type_decoration); - trait->type_decoration = type(); - } - } } else { add_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(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(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(type_identifier) }).second) { add_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(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() != nullptr; + } + type get_range_base_type(const type& range) { if (auto array = range.get()) 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) { - 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) + { + 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; - constant_assignment_error::constant_assignment_error(const source_position position, - type assignee) - : error(position), assignee(std::move(assignee)) - { - } + if constexpr (std::is_same_v) + { + return "Expected type '" + payload.value.to_string() + + "', but got '" + this->actual.to_string() + "'"; + } + else if constexpr (std::is_same_v) + { + 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) + { + return "Type '" + this->actual.to_string() + "' cannot be used with unary '" + + unary_operator_symbol(payload.operation) + "'"; + } + else if constexpr (std::is_same_v) + { + 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) + { + return "Type '" + this->actual.to_string() + + "' cannot be converted to '" + payload.target.to_string() + "'"; + } + else if constexpr (std::is_same_v) + { + 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& 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(condition.position(), - type_kind_error::kind::condition, condition.type_decoration); + add_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( - return_expr->position(), return_type, return_expr->type_decoration); + add_error(return_expr->position(), + return_expr->type_decoration, + type_mismatch_error::expected_type{ return_type }); } } else { - add_error(declaration->identifier.name(), - return_expr->position(), return_expr->type_decoration); + add_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(declaration->identifier.name(), declaration->position()); + add_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(statement->position(), - statement->lvalue().type_decoration); + add_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(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(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( - 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(case_label->position(), condition_type, binary_error); } } } @@ -506,8 +561,8 @@ namespace elna::boot if (!get_range_base_type(resolved_range)) { - add_error(statement->range().position(), type_kind_error::kind::for_loop, - statement->range().type_decoration); + add_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(expression->position(), - type_kind_error::kind::record_base, type()); + add_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() == nullptr) { - add_error(expression->position(), - type_kind_error::kind::record_base, base_type); + add_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( - (*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(call->position(), - type(std::make_shared()), - call->callable().type_decoration); + call->callable().type_decoration, + type_mismatch_error::expected_type{ type(std::make_shared()) }); } // else callable is not declared which is already reported. } @@ -654,8 +710,8 @@ namespace elna::boot if (record == nullptr) { add_error( - expression->position(), type(std::make_shared()), - expression->type_decoration); + expression->position(), expression->type_decoration, + type_mismatch_error::expected_type{ type(std::make_shared()) }); 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( - 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( - expression->position(), type(std::make_shared(type(), 0)), - expression->type_decoration); + expression->position(), expression->type_decoration, + type_mismatch_error::expected_type{ type(std::make_shared(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( - 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() == nullptr + && resolved_base.get() == nullptr) + { + add_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(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(expression->position(), - expression->operand().type_decoration, operation); + add_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(expression->position(), - expression->operand().type_decoration, operation); + add_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(expression->position(), - expression->operand().type_decoration, operation); + add_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(expression->position(), - expression->operand().type_decoration, operation); + add_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() == nullptr) + { + add_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() == nullptr || target.get() == nullptr)) + { + add_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(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(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->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() == nullptr + && !is_primitive_type(resolved, "Float") + && !is_discrete_type(resolved)) + { + add_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 . */ #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 "<>"; - } - gcc_unreachable(); - } - void report_errors(const std::deque>& 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& 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(); + auto target_slice = resolve_underlying_type(expression->type_decoration) + .get(); + + 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 *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 *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() != 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 *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& 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 original; }; - using payload_type = std::variant; + using payload_type = std::variant; - 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> 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 original; std::optional base; }; using payload_type = std::variant; - 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> 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 declaration; + std::optional position; type base_type; }; @@ -139,8 +117,7 @@ namespace elna::boot std::pair> build_procedure( procedure_type_expression& expression); ordered_map build_composite_type(const std::vector& fields, - ordered_map& field_names, - const type& aggregate); + ordered_map& field_names, const type& aggregate); std::shared_ptr 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 @@ -576,6 +576,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,32 +32,49 @@ 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; + 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. */ @@ -71,42 +88,6 @@ namespace elna::boot std::string what() const override; }; - /** - * 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; + using payload_type = std::variant; 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>& 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& 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. -- cgit v1.2.3