diff options
| -rw-r--r-- | boot/evaluator.cc | 50 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 65 | ||||
| -rw-r--r-- | boot/result.cc | 22 | ||||
| -rw-r--r-- | boot/type_check.cc | 184 | ||||
| -rw-r--r-- | boot/validation.cc | 25 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 4 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 13 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 58 | ||||
| -rw-r--r-- | include/elna/boot/validation.h | 10 |
9 files changed, 239 insertions, 192 deletions
diff --git a/boot/evaluator.cc b/boot/evaluator.cc index d8d9ddc..07b2953 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -53,20 +53,14 @@ namespace elna::boot std::optional<std::pair<std::string, source_position>> non_constant_expression_error::note() const { - return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> { - using T = std::decay_t<decltype(payload)>; - - if constexpr (std::is_same_v<T, initializer>) - { - auto position_span = source_position(payload.identifiers.front().position().start(), - payload.identifiers.back().position().end()); - return std::make_pair(join(payload.identifiers), position_span); - } - else - { - return std::nullopt; - } - }, this->payload); + if (std::holds_alternative<initializer>(this->payload)) + { + return identifier_list_note(std::get<initializer>(this->payload).identifiers); + } + else + { + return std::nullopt; + } } std::optional<type_properties> get_type_properties(const type& subject, const target_info& target) @@ -239,33 +233,29 @@ namespace elna::boot std::optional<constant_value> evaluator::evaluate_literal(literal_expression& subject) { - // The type decoration here is trustable since the name analysis derives - // the decoration from literal's own value. - type const decoration = subject.type_decoration; - - if (is_primitive_type(decoration, "Int") || is_primitive_type(decoration, "Word")) + if (auto *integer_subject = subject.is_a<integer_literal>()) { - return constant_value{ static_cast<literal<integer_literal>&>(subject).value }; + return constant_value{ integer_subject->value }; } - else if (is_primitive_type(decoration, "Float")) + else if (auto *double_subject = subject.is_a<double>()) { - return constant_value{ static_cast<literal<double>&>(subject).value }; + return constant_value{ double_subject->value }; } - else if (is_primitive_type(decoration, "Bool")) + else if (auto *boolean_subject = subject.is_a<bool>()) { - return constant_value{ static_cast<literal<bool>&>(subject).value }; + return constant_value{ boolean_subject->value }; } - else if (is_primitive_type(decoration, "Char")) + else if (auto *character_subject = subject.is_a<unsigned char>()) { - return constant_value{ static_cast<literal<unsigned char>&>(subject).value }; + return constant_value{ character_subject->value }; } - else if (is_primitive_type(decoration, "Pointer")) + else if (auto *pointer_subject = subject.is_a<std::nullptr_t>()) { - return constant_value{ std::nullptr_t{} }; + return constant_value{ pointer_subject->value }; } - else if (is_string_type(decoration)) + else if (auto *string_subject = subject.is_a<std::string>()) { - return constant_value{ static_cast<literal<std::string>&>(subject).value }; + return constant_value{ string_subject->value }; } return std::nullopt; } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index ae6810d..4127466 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -59,22 +59,22 @@ namespace elna::boot std::optional<std::pair<std::string, source_position>> declaration_error::note() const { - if (const auto *redef = std::get_if<redefinition>(&payload)) + if (std::holds_alternative<redefinition>(this->payload)) { - if (redef->original.has_value() && redef->original->start().available()) - { - return std::make_pair("previously declared here", *redef->original); - } + return previous_declaration_note(std::get<redefinition>(this->payload).original); + } + else + { + return std::nullopt; } - return std::nullopt; } - name_analysis_error::name_analysis_error(const source_position position, payload_type payload) + const_qualifier_error::const_qualifier_error(const source_position position, payload_type payload) : error(position), payload(std::move(payload)) { } - std::string name_analysis_error::what() const + std::string const_qualifier_error::what() const { return std::visit([](const auto& payload) -> std::string { using T = std::decay_t<decltype(payload)>; @@ -99,23 +99,16 @@ namespace elna::boot }, this->payload); } - std::optional<std::pair<std::string, source_position>> name_analysis_error::note() const + std::optional<std::pair<std::string, source_position>> const_qualifier_error::note() const { - return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> { - using T = std::decay_t<decltype(payload)>; - - if constexpr (std::is_same_v<T, not_initialized>) - { - auto position_span = source_position(payload.identifiers.front().position().start(), - payload.identifiers.back().position().end()); - - return std::make_optional(std::make_pair(join(payload.identifiers), position_span)); - } - else - { - return std::nullopt; - } - }, this->payload); + if (std::holds_alternative<not_initialized>(this->payload)) + { + return identifier_list_note(std::get<not_initialized>(this->payload).identifiers); + } + else + { + return std::nullopt; + } } member_error::member_error(const source_position position, const std::string& name, @@ -184,14 +177,14 @@ namespace elna::boot std::optional<std::pair<std::string, source_position>> member_error::note() const { - if (const auto *dup = std::get_if<duplicate>(&payload)) + if (std::holds_alternative<duplicate>(this->payload)) { - if (dup->original.has_value() && dup->original->start().available()) - { - return std::make_pair("previously declared here", *dup->original); - } + return previous_declaration_note(std::get<duplicate>(this->payload).original); + } + else + { + return std::nullopt; } - return std::nullopt; } // Members of a constant aggregate are constant themselves. @@ -317,8 +310,8 @@ namespace elna::boot if (this->current_type.get<constant_type>() != nullptr) { - add_error<name_analysis_error>(expression->position(), - name_analysis_error::kind::duplicate); + add_error<const_qualifier_error>(expression->position(), + const_qualifier_error::kind::duplicate); } this->current_type = type(std::make_shared<constant_type>(this->current_type)); } @@ -330,8 +323,8 @@ namespace elna::boot if (array_base.get<constant_type>() != nullptr) { - add_error<name_analysis_error>(expression->position(), - name_analysis_error::kind::array_position); + add_error<const_qualifier_error>(expression->position(), + const_qualifier_error::kind::array_position); } expression->dimensions().accept(this); const auto size_constant = this->constant_evaluator.evaluate_index(expression->dimensions()); @@ -580,8 +573,8 @@ namespace elna::boot { auto position_span = source_position(declaration->identifiers.front().id().position().start(), declaration->identifiers.back().id().position().end()); - add_error<name_analysis_error>(position_span, - name_analysis_error::not_initialized{ extract_identifiers(declaration->identifiers) }); + add_error<const_qualifier_error>(position_span, + const_qualifier_error::not_initialized{ extract_identifiers(declaration->identifiers) }); } for (const identifier_definition& variable_identifier : declaration->identifiers) { diff --git a/boot/result.cc b/boot/result.cc index 15fe8b4..a5f0803 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -129,6 +129,28 @@ namespace elna::boot return this->m_exported; } + std::optional<std::pair<std::string, source_position>> previous_declaration_note( + const std::optional<source_position>& original, std::string_view label) + { + if (original.has_value() && original.value().start().available()) + { + return std::make_pair(std::string(label), original.value()); + } + else + { + return std::nullopt; + } + } + + std::optional<std::pair<std::string, source_position>> identifier_list_note( + const std::vector<identifier>& identifiers) + { + auto position_span = source_position(identifiers.front().position().start(), + identifiers.back().position().end()); + + return std::make_optional(std::make_pair(join(identifiers), position_span)); + } + std::vector<identifier> extract_identifiers(const std::vector<identifier_definition>& identifiers) { std::vector<identifier> result; diff --git a/boot/type_check.cc b/boot/type_check.cc index b1739d9..07e563f 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -100,14 +100,7 @@ namespace elna::boot { using T = std::decay_t<decltype(payload)>; - if constexpr (std::is_same_v<T, argument_count>) - { - return "Trait #" + this->trait_name + " expects " - + std::to_string(payload.expected) + " argument" - + (payload.expected != 1 ? "s" : "") + ", got " - + std::to_string(payload.actual); - } - else if constexpr (std::is_same_v<T, offset_not_field_name>) + 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"; @@ -170,41 +163,50 @@ namespace elna::boot return "The number " + payload.overflow.to_string() + " does not fit into the type '" + actual.to_string() + "'"; } - else if constexpr (std::is_same_v<T, kind>) + else if constexpr (std::is_same_v<T, constant_assignment>) { - switch (payload) - { - using enum kind; - case record_base: - return "Expected a record type, but got '" - + this->actual.to_string() + "'"; - case for_range: - return "Expected an array or slice type, but got '" - + this->actual.to_string() + "'"; - case condition: - return "Condition must be a boolean expression, but got '" - + this->actual.to_string() + "'"; - case constant_assignment: - return "Cannot assign to a value of type '" + this->actual.to_string() - + "', because it is constant or contains constant members"; - case array_index: - return "Array index must be an integral type, but got '" - + this->actual.to_string() + "'"; - case non_indexable: - return "Indexing is not allowed on type '" - + this->actual.to_string() + "'"; - case dereference_of_non_pointer: - return "Type '" + this->actual.to_string() - + "' cannot be dereferenced, it is not a pointer"; - default: - __builtin_unreachable(); - } + return "Cannot assign to a value of type '" + this->actual.to_string() + + "', because it is constant or contains constant members"; } }, this->payload); } - cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, - const source_position position) + type_requirement_error::type_requirement_error(const source_position position, + type actual, kind kind) + : error(position), actual(std::move(actual)), m_kind(kind) + { + } + + std::string type_requirement_error::what() const + { + switch (this->m_kind) + { + using enum kind; + case record_base: + return "Expected a record type, but got '" + + this->actual.to_string() + "'"; + case for_range: + return "Expected an array or slice type, but got '" + + this->actual.to_string() + "'"; + case condition: + return "Condition must be a boolean expression, but got '" + + this->actual.to_string() + "'"; + case array_index: + return "Array index must be an integral type, but got '" + + this->actual.to_string() + "'"; + case non_indexable: + return "Indexing is not allowed on type '" + + this->actual.to_string() + "'"; + case dereference_of_non_pointer: + return "Type '" + this->actual.to_string() + + "' cannot be dereferenced, it is not a pointer"; + default: + __builtin_unreachable(); + } + } + + cyclic_declaration_error::cyclic_declaration_error(const source_position position, + const std::vector<std::string>& cycle) : error(position), cycle(cycle) { } @@ -222,24 +224,35 @@ namespace elna::boot return message; } - argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, - const source_position position) - : error(position), expected(expected), actual(actual) + argument_count_error::argument_count_error(const source_position position, kind kind, + std::string applicand, std::size_t expected, std::size_t actual) + : error(position), m_kind(kind), applicand(std::move(applicand)), expected(expected), actual(actual) { } std::string argument_count_error::what() const { - if (actual > expected) + std::string entity; + + switch (this->m_kind) { - return "Too many arguments, expected " + std::to_string(expected) - + ", got " + std::to_string(actual); + using enum kind; + case trait: + entity = '#' + this->applicand; + break; + case call: + entity = '\'' + this->applicand + '\''; + break; + case array: + entity = "array initializer '" + this->applicand + "'"; + break; } - else - { - return "Too few arguments, expected " + std::to_string(expected) + const std::string noun = this->m_kind == kind::array ? "elements" : "arguments"; + const std::string quantifier = actual > expected ? "many" : "few"; + + return "Too " + quantifier + " " + noun + " for " + entity + + ", expected " + std::to_string(expected) + ", got " + std::to_string(actual); - } } static bool contains_constant_member(const type& checked) @@ -302,8 +315,8 @@ namespace elna::boot condition.accept(this); if (!is_primitive_type(condition.type_decoration, "Bool")) { - add_error<type_mismatch_error>(condition.position(), - condition.type_decoration, type_mismatch_error::kind::condition); + add_error<type_requirement_error>(condition.position(), + condition.type_decoration, type_requirement_error::kind::condition); } } @@ -516,7 +529,7 @@ namespace elna::boot if (contains_constant_member(statement->lvalue().type_decoration)) { add_error<type_mismatch_error>(statement->position(), statement->lvalue().type_decoration, - type_mismatch_error::kind::constant_assignment); + type_mismatch_error::constant_assignment{}); } else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) { @@ -574,8 +587,8 @@ namespace elna::boot if (!get_range_base_type(resolved_range)) { - add_error<type_mismatch_error>(statement->range().position(), - statement->range().type_decoration, type_mismatch_error::kind::for_range); + add_error<type_requirement_error>(statement->range().position(), + statement->range().type_decoration, type_requirement_error::kind::for_range); } this->bag.enter(statement->symbols); for (auto *body_statement : statement->body) @@ -647,7 +660,7 @@ namespace elna::boot if (!check_unresolved_symbol(unresolved_type, alias_path)) { - add_error<cyclic_declaration_error>(alias_path, declaration->position()); + add_error<cyclic_declaration_error>(declaration->position(), alias_path); } else { @@ -662,16 +675,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_mismatch_error>(expression->position(), - type(), type_mismatch_error::kind::record_base); + add_error<type_requirement_error>(expression->position(), + type(), type_requirement_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_mismatch_error>(expression->position(), - base_type, type_mismatch_error::kind::record_base); + add_error<type_requirement_error>(expression->position(), + base_type, type_requirement_error::kind::record_base); } } } @@ -703,8 +716,18 @@ namespace elna::boot } if (call->arguments.size() != procedure->parameters.size()) { - add_error<argument_count_error>(procedure->parameters.size(), - call->arguments.size(), call->position()); + if (auto *procedure_name = call->callable().is_named()) + { + add_error<argument_count_error>(call->position(), + argument_count_error::kind::call, procedure_name->name, + procedure->parameters.size(), call->arguments.size()); + } + else + { + add_error<argument_count_error>(call->position(), + argument_count_error::kind::call, call->callable().type_decoration.to_string(), + procedure->parameters.size(), call->arguments.size()); + } } } else if (!call->callable().type_decoration.empty()) @@ -757,8 +780,9 @@ namespace elna::boot } if (expression->elements.size() > array->size) { - add_error<argument_count_error>(array->size, expression->elements.size(), - expression->position()); + add_error<argument_count_error>(expression->position(), + argument_count_error::kind::array, expression->type_decoration.to_string(), + array->size, expression->elements.size()); return; } for (auto *element : expression->elements) @@ -778,13 +802,13 @@ namespace elna::boot if (!is_integral_type(resolve_underlying_type(expression->start().type_decoration))) { - add_error<type_mismatch_error>(expression->start().position(), - expression->start().type_decoration, type_mismatch_error::kind::array_index); + add_error<type_requirement_error>(expression->start().position(), + expression->start().type_decoration, type_requirement_error::kind::array_index); } if (!is_integral_type(resolve_underlying_type(expression->end().type_decoration))) { - add_error<type_mismatch_error>(expression->end().position(), - expression->end().type_decoration, type_mismatch_error::kind::array_index); + add_error<type_requirement_error>(expression->end().position(), + expression->end().type_decoration, type_requirement_error::kind::array_index); } } @@ -796,13 +820,13 @@ namespace elna::boot 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); + add_error<type_requirement_error>(expression->position(), + expression->base().type_decoration, type_requirement_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); + add_error<type_requirement_error>(expression->index().position(), + expression->index().type_decoration, type_requirement_error::kind::array_index); } } @@ -867,9 +891,8 @@ namespace elna::boot 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); + add_error<type_requirement_error>(expression->position(), expression->base().type_decoration, + type_requirement_error::kind::dereference_of_non_pointer); } } @@ -994,16 +1017,18 @@ namespace elna::boot { if (trait->arguments.size() != 1) { - add_error<trait_error>(trait->position(), trait->name.name(), - trait_error::argument_count{ .expected = 1, .actual = trait->arguments.size() }); + add_error<argument_count_error>(trait->position(), + argument_count_error::kind::trait, trait->name.name(), + 1, 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() }); + add_error<argument_count_error>(trait->position(), + argument_count_error::kind::trait, trait->name.name(), + 1, trait->arguments.size()); } else { @@ -1022,8 +1047,9 @@ namespace elna::boot { if (trait->arguments.size() != 2) { - add_error<trait_error>(trait->position(), trait->name.name(), - trait_error::argument_count{ .expected = 2, .actual = trait->arguments.size() }); + add_error<argument_count_error>(trait->position(), + argument_count_error::kind::trait, trait->name.name(), + 2, trait->arguments.size()); } else if (trait->arguments.at(1)->is_named() == nullptr) { diff --git a/boot/validation.cc b/boot/validation.cc index 0c03fd0..26d4f64 100644 --- a/boot/validation.cc +++ b/boot/validation.cc @@ -21,33 +21,19 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - validation_error::validation_error(const source_position position, payload_type payload) - : error(position), payload(payload) + validation_error::validation_error(const source_position position, source_position first) + : error(position), first(first) { } std::string validation_error::what() const { - return std::visit([](const auto& payload) -> std::string { - using T = std::decay_t<decltype(payload)>; - - if constexpr (std::is_same_v<T, duplicate_case>) - { - return "Duplicate case label"; - } - }, this->payload); + return "Duplicate case label"; } std::optional<std::pair<std::string, source_position>> validation_error::note() const { - return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> { - using T = std::decay_t<decltype(payload)>; - - if constexpr (std::is_same_v<T, duplicate_case>) - { - return std::make_pair("Previous label here", payload.first); - } - }, this->payload); + return previous_declaration_note(this->first, "Previous label here"); } validation_visitor::validation_visitor(symbol_bag& bag, const target_info& target) @@ -145,8 +131,7 @@ namespace elna::boot auto [case_position, inserted] = seen.try_emplace(value.value(), label->position()); if (!inserted) { - add_error<validation_error>(label->position(), - validation_error::duplicate_case{ case_position->second }); + add_error<validation_error>(label->position(), case_position->second); } } } diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 9e10fab..a9e7f24 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -63,7 +63,7 @@ namespace elna::boot * \c const qualifier used incorrectly — wrong position or * duplicate. */ - class name_analysis_error final : public error + class const_qualifier_error final : public error { public: enum class kind @@ -77,7 +77,7 @@ namespace elna::boot }; using payload_type = std::variant<not_initialized, kind>; - name_analysis_error(const source_position position, payload_type payload); + const_qualifier_error(const source_position position, payload_type payload); std::optional<std::pair<std::string, source_position>> note() const override; std::string what() const override; diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 03d589e..bf2149a 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -184,6 +184,19 @@ namespace elna::boot }; /** + * Creates an error note pointing to a previous declaration. + * + * \param original Source position of the previous declaration. + * \param label Description what was declared previously. + * \return Error note if the position of the previous declaration is available. + */ + std::optional<std::pair<std::string, source_position>> previous_declaration_note( + const std::optional<source_position>& original, std::string_view label = "previously declared here"); + + std::optional<std::pair<std::string, source_position>> identifier_list_note( + const std::vector<identifier>& identifiers); + + /** * Checks whether the givn object can be converted to a string using * the .to_string() method. */ diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index 93e891a..52eec61 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -58,14 +58,8 @@ namespace elna::boot { integer_literal overflow; }; - enum class kind { - record_base, - for_range, - condition, - constant_assignment, - array_index, - non_indexable, - dereference_of_non_pointer + struct constant_assignment + { }; using payload_type = std::variant< expected_type, @@ -74,7 +68,7 @@ namespace elna::boot binary, invalid_cast, integer_literal_overflow, - kind + constant_assignment >; type_mismatch_error(const source_position position, @@ -87,6 +81,29 @@ namespace elna::boot payload_type payload; }; + class type_requirement_error : public error + { + public: + enum class kind + { + record_base, + for_range, + condition, + array_index, + non_indexable, + dereference_of_non_pointer + }; + + type_requirement_error(const source_position position, + type actual, kind kind); + + std::string what() const override; + + private: + type actual; + kind m_kind; + }; + /** * Cyclic type declaration. */ @@ -95,7 +112,7 @@ namespace elna::boot std::vector<std::string> cycle; public: - cyclic_declaration_error(const std::vector<std::string>& cycle, const source_position position); + cyclic_declaration_error(const source_position position, const std::vector<std::string>& cycle); std::string what() const override; }; @@ -106,12 +123,22 @@ namespace elna::boot */ class argument_count_error final : public error { + public: + enum class kind + { + trait, + call, + array + }; + private: + kind m_kind; + std::string applicand; std::size_t expected; std::size_t actual; public: - argument_count_error(std::size_t expected, std::size_t actual, - const source_position position); + argument_count_error(const source_position position, kind kind, + std::string applicand, std::size_t expected, std::size_t actual); std::string what() const override; }; @@ -122,18 +149,15 @@ namespace elna::boot class trait_error final : public error { public: - struct argument_count + struct offset_not_field_name { - std::size_t expected; - std::size_t actual; }; - struct offset_not_field_name {}; struct unsupported_type { type actual; }; - using payload_type = std::variant<argument_count, offset_not_field_name, unsupported_type>; + using payload_type = std::variant<offset_not_field_name, unsupported_type>; trait_error(const source_position position, const std::string& trait_name, payload_type payload); diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h index 171ac1d..65972b7 100644 --- a/include/elna/boot/validation.h +++ b/include/elna/boot/validation.h @@ -30,19 +30,13 @@ namespace elna::boot class validation_error final : public error { public: - struct duplicate_case - { - source_position first; - }; - using payload_type = std::variant<duplicate_case>; - - validation_error(const source_position position, payload_type payload); + validation_error(const source_position position, source_position first); std::string what() const override; std::optional<std::pair<std::string, source_position>> note() const override; private: - payload_type payload; + source_position first; }; /** |
