diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-29 01:16:18 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-29 01:16:18 +0200 |
| commit | f8daedce5c73e02dfb2fc59d75777190185584df (patch) | |
| tree | 9ec211b96c3d7efd2fca0da37baeaa8b341e31fb /boot | |
| parent | 36a274c9a8bca944234589220def025d3920b3ef (diff) | |
| download | elna-f8daedce5c73e02dfb2fc59d75777190185584df.tar.gz | |
Validate cast compatibility during semantic analysis
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ast.cc | 47 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 136 | ||||
| -rw-r--r-- | boot/symbol.cc | 8 | ||||
| -rw-r--r-- | boot/type_check.cc | 406 |
4 files changed, 327 insertions, 270 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) |
