diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-17 10:18:58 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-17 10:18:58 +0200 |
| commit | a0e1740227535adc3151c02eb102c4f96b4f9731 (patch) | |
| tree | 0e29cd3719370eca8c74fb67480f3bfc1a207106 /boot/semantic.cc | |
| parent | d0a666507a6f11b65170c257fd483613f462f3f2 (diff) | |
| download | elna-a0e1740227535adc3151c02eb102c4f96b4f9731.tar.gz | |
Implement type constness
Diffstat (limited to 'boot/semantic.cc')
| -rw-r--r-- | boot/semantic.cc | 338 |
1 files changed, 161 insertions, 177 deletions
diff --git a/boot/semantic.cc b/boot/semantic.cc index 9137053..c987cfd 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -73,6 +73,18 @@ namespace elna::boot + "', but got '" + actual.to_string() + "'"; } + constant_assignment_error::constant_assignment_error(const source_position position, + type assignee) + : error(position), assignee(assignee) + { + } + + 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"; + } + field_not_found_error::field_not_found_error(const identifier& field_name, type composite_type) : error(field_name.position()), field_name(field_name.name()), composite_type(composite_type) @@ -81,7 +93,7 @@ namespace elna::boot std::string field_not_found_error::what() const { - type resolved = inner_aliased_type(composite_type); + type resolved = resolve_underlying_type(composite_type); bool is_enum = resolved.get<enumeration_type>() != nullptr; bool is_record = resolved.get<record_type>() != nullptr; @@ -112,7 +124,7 @@ namespace elna::boot std::string duplicate_member_error::what() const { - type resolved = inner_aliased_type(aggregate); + type resolved = resolve_underlying_type(aggregate); bool is_enum = resolved.get<enumeration_type>() != nullptr; std::string kind = is_enum ? "member" : "field"; std::string message = is_enum ? "Enumeration" : "Record"; @@ -208,7 +220,7 @@ namespace elna::boot unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, type actual) - : error(trait.position()), trait_name(trait.name()), actual(actual) + : error(trait.position()), actual(actual), trait_name(trait.name()) { } @@ -218,6 +230,119 @@ namespace elna::boot + "' 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) + { + if (resolve_aliases(aggregate).get<constant_type>() != nullptr + && resolve_aliases(element).get<constant_type>() == nullptr) + { + return type(std::make_shared<constant_type>(element)); + } + else + { + return element; + } + } + + /* + * 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); + + if (referent.get<constant_type>() != nullptr) + { + return true; + } + else if (auto record = referent.get<record_type>()) + { + for (const type_field& field : record->fields) + { + if (contains_constant_member(field.second)) + { + return true; + } + } + return !record->base.empty() && contains_constant_member(record->base); + } + else if (auto array = referent.get<array_type>()) + { + return contains_constant_member(array->base); + } + return false; + } + + bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, + std::vector<std::string>& alias_path) + { + if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) + { + return false; + } + alias_path.push_back(alias->name); + + if (auto another_alias = alias->reference.get<alias_type>()) + { + return check_unresolved_symbol(another_alias, alias_path); + } + return true; + } + + bool type_analysis_visitor::is_base_of(const std::shared_ptr<record_type>& base, + const std::shared_ptr<record_type>& derived) + { + if (derived != nullptr) + { + if (auto current_record = resolve_underlying_type(derived->base).get<record_type>()) + { + return current_record == base || is_base_of(base, current_record); + } + } + return false; + } + + bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment) + { + type resolved_assignee = resolve_underlying_type(assignee); + type resolved_assignment = resolve_underlying_type(assignment); + + if (resolved_assignee == resolved_assignment + || (is_primitive_type(resolved_assignee, "Pointer") && is_any_pointer_type(resolved_assignment)) + || (is_primitive_type(resolved_assignment, "Pointer") && is_any_pointer_type(resolved_assignee))) + { + return true; + } + std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>(); + std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>(); + + if (assignee_pointer == nullptr || assignment_pointer == nullptr) + { + return false; + } + auto assignee_pointee_const = resolve_aliases(assignee_pointer->base).get<constant_type>(); + auto assignment_pointee_const = resolve_aliases(assignment_pointer->base).get<constant_type>(); + + // Constness can be added at the first indirection level, but not removed. + if (assignee_pointee_const != nullptr + && assignee_pointee_const->unqualified == assignment_pointer->base) + { + return true; + } + if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr) + { + return false; + } + // A pointer to a record can be assigned to a pointer to its base type. + std::shared_ptr<record_type> assignee_record = + resolve_underlying_type(assignee_pointer->base).get<record_type>(); + + return assignee_record != nullptr + && is_base_of(assignee_record, resolve_underlying_type(assignment_pointer->base).get<record_type>()); + } + type_analysis_visitor::type_analysis_visitor(symbol_bag bag) : error_container(), bag(bag) { @@ -272,7 +397,12 @@ namespace elna::boot { walking_visitor::visit(statement); - if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) + if (contains_constant_member(statement->lvalue().type_decoration)) + { + add_error<constant_assignment_error>(statement->position(), + statement->lvalue().type_decoration); + } + 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); @@ -302,7 +432,7 @@ namespace elna::boot void type_analysis_visitor::visit(case_statement *statement) { walking_visitor::visit(statement); - type condition_type = inner_aliased_type(statement->condition().type_decoration); + type condition_type = resolve_underlying_type(statement->condition().type_decoration); for (const switch_case& case_block : statement->cases) { @@ -317,22 +447,6 @@ namespace elna::boot } } - bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, - std::vector<std::string>& alias_path) - { - if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) - { - return false; - } - alias_path.push_back(alias->name); - - if (auto another_alias = alias->reference.get<alias_type>()) - { - return check_unresolved_symbol(another_alias, alias_path); - } - return true; - } - void type_analysis_visitor::visit(type_declaration *declaration) { std::vector<std::string> alias_path; @@ -352,7 +466,7 @@ namespace elna::boot { if (expression->base.has_value()) { - type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); + type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); if (base_type.get<record_type>() == nullptr) { add_error<base_type_error>(base_type, expression->position()); @@ -393,7 +507,7 @@ namespace elna::boot void type_analysis_visitor::visit(record_constructor_expression *expression) { - auto record = inner_aliased_type(expression->type_decoration).get<record_type>(); + auto record = resolve_underlying_type(expression->type_decoration).get<record_type>(); if (record == nullptr) { @@ -422,7 +536,7 @@ namespace elna::boot void type_analysis_visitor::visit(array_constructor_expression *expression) { - auto array = inner_aliased_type(expression->type_decoration).get<array_type>(); + auto array = resolve_underlying_type(expression->type_decoration).get<array_type>(); if (array == nullptr) { @@ -490,49 +604,9 @@ namespace elna::boot return this->bag.lookup(name)->is_type()->symbol; } - type name_analysis_visitor::type_of_constant(const constant_info::variant& value) - { - return std::visit([this](auto&& arg) -> type { - using T = std::decay_t<decltype(arg)>; - - if constexpr (std::is_same_v<T, std::int32_t>) - { - return lookup_primitive_type("Int"); - } - else if constexpr (std::is_same_v<T, std::uint32_t>) - { - return lookup_primitive_type("Word"); - } - else if constexpr (std::is_same_v<T, double>) - { - return lookup_primitive_type("Float"); - } - else if constexpr (std::is_same_v<T, bool>) - { - return lookup_primitive_type("Bool"); - } - else if constexpr (std::is_same_v<T, unsigned char>) - { - return lookup_primitive_type("Char"); - } - else if constexpr (std::is_same_v<T, std::nullptr_t>) - { - return lookup_primitive_type("Pointer"); - } - else if constexpr (std::is_same_v<T, std::string>) - { - return lookup_primitive_type("String"); - } - else - { - static_assert(!sizeof(T*), "Unhandled constant type"); - } - }, value); - } - type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name) { - type resolved_type = inner_aliased_type(composite_type); + type resolved_type = resolve_underlying_type(composite_type); if (auto record = resolved_type.get<record_type>()) { @@ -579,6 +653,12 @@ namespace elna::boot this->current_type = type(std::make_shared<pointer_type>(this->current_type)); } + void name_analysis_visitor::visit(constant_type_expression *expression) + { + walking_visitor::visit(expression); + this->current_type = type(std::make_shared<constant_type>(this->current_type)); + } + void name_analysis_visitor::visit(array_type_expression *expression) { walking_visitor::visit(expression); @@ -593,7 +673,7 @@ namespace elna::boot static void collect_field_names(const type& composite_type, std::map<std::string, field_origin>& names) { - auto record = inner_aliased_type(composite_type).get<record_type>(); + auto record = resolve_underlying_type(composite_type).get<record_type>(); if (record == nullptr) { return; @@ -803,15 +883,6 @@ namespace elna::boot } } - void name_analysis_visitor::visit(constant_declaration *declaration) - { - walking_visitor::visit(declaration); - auto constant_symbol = std::make_shared<constant_info>(this->current_literal); - - constant_symbol->exported = declaration->identifier.exported(); - constant_symbol->position.emplace(declaration->position()); - this->bag.enter(declaration->identifier.name(), constant_symbol); - } void name_analysis_visitor::visit(procedure_declaration *declaration) { @@ -833,10 +904,6 @@ namespace elna::boot ++name_iterator; ++type_iterator; } - for (constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -887,10 +954,6 @@ namespace elna::boot { variable->accept(this); } - for (constant_declaration *const constant : unit->constants) - { - constant->accept(this); - } for (procedure_declaration *const procedure : unit->procedures) { procedure->accept(this); @@ -932,7 +995,7 @@ namespace elna::boot if (!trait->type_decoration.empty()) { - type resolved = inner_aliased_type(trait->type_decoration); + type resolved = resolve_underlying_type(trait->type_decoration); bool is_enum = resolved.get<enumeration_type>() != nullptr; bool is_integral = false; @@ -1005,7 +1068,7 @@ namespace elna::boot void name_analysis_visitor::visit(array_access_expression *expression) { walking_visitor::visit(expression); - auto resolved_base= inner_aliased_type(expression->base().type_decoration); + auto resolved_base = resolve_underlying_type(expression->base().type_decoration); if (auto array = resolved_base.get<array_type>()) { @@ -1015,6 +1078,13 @@ namespace elna::boot { expression->type_decoration = lookup_primitive_type("Char"); } + // Elements of a constant array are constant themselves since a static + // array is a holistic type. + if (!expression->type_decoration.empty()) + { + expression->type_decoration = qualify_member_type(expression->type_decoration, + expression->base().type_decoration); + } } void name_analysis_visitor::visit(field_access_expression *expression) @@ -1029,7 +1099,11 @@ namespace elna::boot } if (expression->type_decoration.empty()) { - add_error<field_not_found_error>(expression->field(), + add_error<field_not_found_error>(expression->field(), expression->base().type_decoration); + } + else + { + expression->type_decoration = qualify_member_type(expression->type_decoration, expression->base().type_decoration); } } @@ -1038,7 +1112,7 @@ namespace elna::boot { walking_visitor::visit(expression); - if (auto pointer = inner_aliased_type(expression->base().type_decoration).get<pointer_type>()) + if (auto pointer = resolve_underlying_type(expression->base().type_decoration).get<pointer_type>()) { expression->type_decoration = pointer->base; } @@ -1068,10 +1142,6 @@ namespace elna::boot { expression->type_decoration = variable_symbol->symbol; } - else if (auto constant_symbol = from_symbol_table->is_constant()) - { - expression->type_decoration = type_of_constant(constant_symbol->symbol); - } else if (auto procedure_symbol = from_symbol_table->is_procedure()) { expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol)); @@ -1086,43 +1156,36 @@ namespace elna::boot void name_analysis_visitor::visit(literal<std::int32_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Int"); } void name_analysis_visitor::visit(literal<std::uint32_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Word"); } void name_analysis_visitor::visit(literal<double> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Float"); } void name_analysis_visitor::visit(literal<bool> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Bool"); } void name_analysis_visitor::visit(literal<unsigned char> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Char"); } void name_analysis_visitor::visit(literal<std::nullptr_t> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("Pointer"); } void name_analysis_visitor::visit(literal<std::string> *literal) { - this->current_literal = literal->value; literal->type_decoration = lookup_primitive_type("String"); } @@ -1168,10 +1231,6 @@ namespace elna::boot { return; } - for (constant_declaration *const constant : declaration->body.value().constants) - { - constant->accept(this); - } for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); @@ -1189,79 +1248,4 @@ namespace elna::boot } } } - - void declaration_visitor::visit(constant_declaration *declaration) - { - if (declaration->identifier.exported()) - { - add_error<declaration_error>(declaration_error::kind::local_export, - declaration->identifier.id()); - } - } - - bool is_base_of(std::shared_ptr<record_type> base, std::shared_ptr<record_type> derived) - { - type current = derived->base; - - while (!current.empty()) - { - auto current_record = inner_aliased_type(current).get<record_type>(); - if (current_record == nullptr) - { - return false; - } - if (current_record == base) - { - return true; - } - current = current_record->base; - } - return false; - } - - bool are_compatible_pointers(type lhs_type, type rhs_type) - { - std::shared_ptr<primitive_type> lhs_primitive = lhs_type.get<primitive_type>(); - std::shared_ptr<primitive_type> rhs_primitive = rhs_type.get<primitive_type>(); - - bool lhs_is_primitive_pointer = lhs_primitive != nullptr && lhs_primitive->identifier == "Pointer"; - bool rhs_is_primitive_pointer = rhs_primitive != nullptr && rhs_primitive->identifier == "Pointer"; - - return (lhs_is_primitive_pointer && rhs_type.get<pointer_type>() != nullptr) - || (rhs_is_primitive_pointer && lhs_type.get<pointer_type>() != nullptr) - || (lhs_is_primitive_pointer && rhs_type.get<procedure_type>() != nullptr) - || (rhs_is_primitive_pointer && lhs_type.get<procedure_type>() != nullptr) - || (lhs_is_primitive_pointer && rhs_is_primitive_pointer); - } - - bool is_assignable_from(const type& assignee, const type& assignment) - { - type resolved_assignee = inner_aliased_type(assignee); - type resolved_assignment = inner_aliased_type(assignment); - - if (resolved_assignee == resolved_assignment) - { - return true; - } - // If the types do not match, only some pointers can be assigned. - if (are_compatible_pointers(resolved_assignee, resolved_assignment)) - { - return true; - } - std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>(); - std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>(); - - if (assignee_pointer == nullptr || assignment_pointer == nullptr) - { - return false; - } - // A pointer to a record can be assigned to a pointer to its base type. - type assignee_pointee = inner_aliased_type(assignee_pointer->base); - type assignment_pointee = inner_aliased_type(assignment_pointer->base); - std::shared_ptr<record_type> assignee_record = assignee_pointee.get<record_type>(); - std::shared_ptr<record_type> assignment_record = assignment_pointee.get<record_type>(); - - return assignee_record != nullptr && assignment_record != nullptr - && is_base_of(assignee_record, assignment_record); - } } |
