From d696abc97143061bf2abb05922188e12b59306c1 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 14 Aug 2026 17:22:17 +0200 Subject: Use the new .is_a() for literal kind checks in the evaluator --- boot/evaluator.cc | 50 ++++++-------- boot/name_analysis.cc | 65 ++++++++---------- boot/result.cc | 22 ++++++ boot/type_check.cc | 184 ++++++++++++++++++++++++++++---------------------- boot/validation.cc | 25 ++----- 5 files changed, 181 insertions(+), 165 deletions(-) (limited to 'boot') 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> non_constant_expression_error::note() const { - return std::visit([](const auto& payload) -> std::optional> { - using T = std::decay_t; - - if constexpr (std::is_same_v) - { - 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(this->payload)) + { + return identifier_list_note(std::get(this->payload).identifiers); + } + else + { + return std::nullopt; + } } std::optional get_type_properties(const type& subject, const target_info& target) @@ -239,33 +233,29 @@ namespace elna::boot std::optional 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()) { - return constant_value{ static_cast&>(subject).value }; + return constant_value{ integer_subject->value }; } - else if (is_primitive_type(decoration, "Float")) + else if (auto *double_subject = subject.is_a()) { - return constant_value{ static_cast&>(subject).value }; + return constant_value{ double_subject->value }; } - else if (is_primitive_type(decoration, "Bool")) + else if (auto *boolean_subject = subject.is_a()) { - return constant_value{ static_cast&>(subject).value }; + return constant_value{ boolean_subject->value }; } - else if (is_primitive_type(decoration, "Char")) + else if (auto *character_subject = subject.is_a()) { - return constant_value{ static_cast&>(subject).value }; + return constant_value{ character_subject->value }; } - else if (is_primitive_type(decoration, "Pointer")) + else if (auto *pointer_subject = subject.is_a()) { - 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()) { - return constant_value{ static_cast&>(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> declaration_error::note() const { - if (const auto *redef = std::get_if(&payload)) + if (std::holds_alternative(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(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; @@ -99,23 +99,16 @@ namespace elna::boot }, this->payload); } - std::optional> name_analysis_error::note() const + std::optional> const_qualifier_error::note() const { - return std::visit([](const auto& payload) -> std::optional> { - using T = std::decay_t; - - if constexpr (std::is_same_v) - { - 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(this->payload)) + { + return identifier_list_note(std::get(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> member_error::note() const { - if (const auto *dup = std::get_if(&payload)) + if (std::holds_alternative(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(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() != nullptr) { - add_error(expression->position(), - name_analysis_error::kind::duplicate); + add_error(expression->position(), + const_qualifier_error::kind::duplicate); } this->current_type = type(std::make_shared(this->current_type)); } @@ -330,8 +323,8 @@ namespace elna::boot if (array_base.get() != nullptr) { - add_error(expression->position(), - name_analysis_error::kind::array_position); + add_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(position_span, - name_analysis_error::not_initialized{ extract_identifiers(declaration->identifiers) }); + add_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> previous_declaration_note( + const std::optional& 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> identifier_list_note( + const std::vector& 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 extract_identifiers(const std::vector& identifiers) { std::vector 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; - if constexpr (std::is_same_v) - { - 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) + if constexpr (std::is_same_v) { 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) + else if constexpr (std::is_same_v) { - 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& 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& 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(condition.position(), - condition.type_decoration, type_mismatch_error::kind::condition); + add_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(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(statement->range().position(), - statement->range().type_decoration, type_mismatch_error::kind::for_range); + add_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(alias_path, declaration->position()); + add_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(expression->position(), - type(), type_mismatch_error::kind::record_base); + add_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() == nullptr) { - add_error(expression->position(), - base_type, type_mismatch_error::kind::record_base); + add_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(procedure->parameters.size(), - call->arguments.size(), call->position()); + if (auto *procedure_name = call->callable().is_named()) + { + add_error(call->position(), + argument_count_error::kind::call, procedure_name->name, + procedure->parameters.size(), call->arguments.size()); + } + else + { + add_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(array->size, expression->elements.size(), - expression->position()); + add_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(expression->start().position(), - expression->start().type_decoration, type_mismatch_error::kind::array_index); + add_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(expression->end().position(), - expression->end().type_decoration, type_mismatch_error::kind::array_index); + add_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() == nullptr && resolved_base.get() == nullptr) { - add_error(expression->position(), - expression->base().type_decoration, type_mismatch_error::kind::non_indexable); + add_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(expression->index().position(), - expression->index().type_decoration, type_mismatch_error::kind::array_index); + add_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() == nullptr) { - add_error(expression->position(), - expression->base().type_decoration, - type_mismatch_error::kind::dereference_of_non_pointer); + add_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->position(), trait->name.name(), - trait_error::argument_count{ .expected = 1, .actual = trait->arguments.size() }); + add_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->position(), trait->name.name(), - trait_error::argument_count{ .expected = 1, .actual = trait->arguments.size() }); + add_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->position(), trait->name.name(), - trait_error::argument_count{ .expected = 2, .actual = trait->arguments.size() }); + add_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; - - if constexpr (std::is_same_v) - { - return "Duplicate case label"; - } - }, this->payload); + return "Duplicate case label"; } std::optional> validation_error::note() const { - return std::visit([](const auto& payload) -> std::optional> { - using T = std::decay_t; - - if constexpr (std::is_same_v) - { - 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(label->position(), - validation_error::duplicate_case{ case_position->second }); + add_error(label->position(), case_position->second); } } } -- cgit v1.2.3