From 5cdaceb77af6a1d98145f8afb34ce6884e21a3d6 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 26 Jul 2026 19:47:59 +0200 Subject: Type check traits properly --- boot/evaluator.cc | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 330 insertions(+), 10 deletions(-) (limited to 'boot/evaluator.cc') diff --git a/boot/evaluator.cc b/boot/evaluator.cc index bb50bd3..6d3153e 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -19,11 +19,22 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/ast.h" #include +#include #include #include namespace elna::boot { + non_constant_initializer_error::non_constant_initializer_error(const source_position position) + : error(position) + { + } + + std::string non_constant_initializer_error::what() const + { + return "Variable initializers must be constant expressions"; + } + std::optional get_type_properties(const type& subject, const target_info& target) { auto resolved = resolve_underlying_type(subject); @@ -127,9 +138,8 @@ namespace elna::boot }; } - evaluator::evaluator(symbol_bag& bag, const target_info& target, - const std::map& evaluated_initializers) - : bag(bag), target(target), evaluated_initializers(evaluated_initializers) + evaluator::evaluator(symbol_bag& bag, const target_info& target) + : bag(bag), target(target) { } @@ -145,6 +155,18 @@ namespace elna::boot { return evaluate_named(*named); } + if (auto *array_access = designator->is_array_access()) + { + return evaluate_array_access(*array_access); + } + if (auto *field_access = designator->is_field_access()) + { + return evaluate_field_access(*field_access); + } + if (auto *slicing = designator->is_slicing()) + { + return evaluate_slicing(*slicing); + } } else if (auto *unary = subject.is_unary()) { @@ -240,20 +262,109 @@ namespace elna::boot { return std::nullopt; } - auto initializer = this->evaluated_initializers.find(subject.name); + return variable->value; + } + + std::optional evaluator::evaluate_array_access(array_access_expression& subject) + { + auto base = evaluate(subject.base()); + auto index = evaluate(subject.index()); + if (!base.has_value() || !index.has_value()) + { + return std::nullopt; + } + auto *array = std::get_if>(&base.value()); + if (array == nullptr) + { + return std::nullopt; + } + std::size_t position; + if (auto *int_index = std::get_if(&index.value())) + { + if (*int_index < 0) + { + return std::nullopt; + } + position = static_cast(*int_index); + } + else if (auto *word_index = std::get_if(&index.value())) + { + position = *word_index; + } + else + { + return std::nullopt; + } + if (position >= (*array)->size()) + { + return std::nullopt; + } + return (*array)->at(position); + } + + std::optional evaluator::evaluate_field_access(field_access_expression& subject) + { + auto base = evaluate(subject.base()); + if (!base.has_value()) + { + return std::nullopt; + } + auto *record = std::get_if>(&base.value()); + if (record == nullptr) + { + return std::nullopt; + } + auto pos = (*record)->find(subject.field().name()); + if (pos == (*record)->end()) + { + return std::nullopt; + } + return pos->second; + } - return initializer == this->evaluated_initializers.end() - ? std::nullopt - : evaluate(*initializer->second); + std::optional evaluator::evaluate_slicing(slicing_expression& subject) + { + auto base = evaluate(subject.base()); + auto start = evaluate(subject.start()); + auto end = evaluate(subject.end()); + if (!base.has_value() || !start.has_value() || !end.has_value()) + { + return std::nullopt; + } + auto *array = std::get_if>(&base.value()); + auto *start_idx = std::get_if(&start.value()); + auto *end_idx = std::get_if(&end.value()); + if (array == nullptr || start_idx == nullptr || end_idx == nullptr + || *start_idx < 0 || *end_idx < 0) + { + return std::nullopt; + } + auto start_pos = static_cast(*start_idx); + auto end_pos = static_cast(*end_idx); + if (start_pos > end_pos || end_pos > (*array)->size()) + { + return std::nullopt; + } + auto slice_begin = std::next((*array)->begin(), static_cast(start_pos)); + auto slice_end = std::next((*array)->begin(), static_cast(end_pos)); + + return constant_value{ + constant_aggregate{ std::vector(slice_begin, slice_end) } + }; } std::optional evaluator::evaluate_unary(unary_expression& subject) { if (subject.operation() == unary_operator::reference) { - // The address of a module-level entity is a compile-time - // constant. The caller (type analysis) validates the context. - return constant_value{ std::nullptr_t{} }; + if (auto *designator = subject.operand().is_designator()) + { + if (auto *named = designator->is_named()) + { + return constant_value{ global_address{ .name = named->name } }; + } + } + return std::nullopt; } auto operand = evaluate(subject.operand()); @@ -367,6 +478,19 @@ namespace elna::boot return std::nullopt; } } + else if constexpr (std::is_same_v) + { + switch (operation) + { + using enum binary_operator; + case equals: + return constant_value{ lhs == rhs }; + case not_equals: + return constant_value{ lhs != rhs }; + default: + return std::nullopt; + } + } else { switch (operation) @@ -645,4 +769,200 @@ namespace elna::boot } return std::nullopt; } + + /** + * Converts a constant_value to an AST literal expression. + * + * \param value Evaluated constant to convert. + * \param position Source position for the new literal node. + * \param decoration Type decoration to apply to the literal. + * \return A new literal expression, or \c nullptr if \p value + * is an aggregate (handled separately by the caller). + */ + static expression *value_to_expression(const constant_value& value, + const source_position& position, const type& decoration) + { + return std::visit([&position, &decoration](auto&& value) -> expression* + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + { + auto *lit = new literal(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v) + { + auto *lit = new literal(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v) + { + auto *lit = new literal(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v) + { + auto *lit = new literal(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v) + { + auto *lit = new literal(position, value); + lit->type_decoration = decoration; + return lit; + } + else if constexpr (std::is_same_v + || std::is_same_v) + { + auto *lit = new literal(position, nullptr); + lit->type_decoration = decoration; + return lit; + } + else + { + return nullptr; + } + }, value); + } + + expression *evaluator::fold(expression& original) + { + auto value = evaluate(original); + if (!value.has_value()) + { + return &original; + } + if (expression *literal = value_to_expression(value.value(), original.position(), original.type_decoration)) + { + delete &original; + return literal; + } + if (auto *record = original.is_record_constructor()) + { + for (auto& field_init : record->field_initializers) + { + fold_aggregate_field(field_init); + } + } + else if (auto *array = original.is_array_constructor()) + { + for (auto& element : array->elements) + { + element = fold(*element); + } + } + return &original; + } + + void evaluator::fold_aggregate_field(field_initializer& field_init) + { + auto value = evaluate(field_init.value()); + if (!value.has_value()) + { + return; + } + expression *literal = value_to_expression(value.value(), + field_init.value().position(), field_init.value().type_decoration); + if (literal != nullptr) + { + field_init.value(*literal); + return; + } + expression *folded = fold(field_init.value()); + if (folded != &field_init.value()) + { + field_init.value(*folded); + } + } + + constant_folder::constant_folder(symbol_bag& bag, const target_info& target) + : bag(bag), target(target), constant_evaluator(this->bag, this->target) + { + } + + void constant_folder::visit(variable_declaration* declaration) + { + if (declaration->initializer == nullptr || has_errors()) + { + return; + } + auto computed = this->constant_evaluator.evaluate(*declaration->initializer); + if (!computed) + { + add_error(declaration->initializer->position()); + return; + } + declaration->initializer = this->constant_evaluator.fold(*declaration->initializer); + + for (const auto& identifier : declaration->identifiers) + { + auto symbol = this->bag.lookup(identifier.name()); + if (symbol == nullptr) + { + continue; + } + if (auto var = symbol->is_variable(); + resolve_aliases(var->symbol).get() != nullptr) + { + var->value = computed; + } + } + } + + void constant_folder::visit(cast_expression *expr) + { + expr->target().accept(this); + expr->value().accept(this); + expr->value(fold_trait(expr->value())); + } + + void constant_folder::visit(binary_expression *expr) + { + expr->lhs().accept(this); + expr->lhs(fold_trait(expr->lhs())); + expr->rhs().accept(this); + expr->rhs(fold_trait(expr->rhs())); + } + + void constant_folder::visit(unary_expression *expr) + { + expr->operand().accept(this); + expr->operand(fold_trait(expr->operand())); + } + + void constant_folder::visit(procedure_call *call) + { + call->callable().accept(this); + for (auto& argument : call->arguments) + { + argument->accept(this); + expression& folded = fold_trait(*argument); + if (&folded != argument) + { + delete argument; + argument = &folded; + } + } + } + + expression& constant_folder::fold_trait(expression& expr) + { + if (auto *trait = expr.is_traits()) + { + if (auto value = this->constant_evaluator.evaluate_traits(*trait)) + { + if (auto *literal = value_to_expression(value.value(), trait->position(), trait->type_decoration)) + { + return *literal; + } + } + } + return expr; + } + } -- cgit v1.2.3