From f8daedce5c73e02dfb2fc59d75777190185584df Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 29 Jul 2026 01:16:18 +0200 Subject: Validate cast compatibility during semantic analysis --- boot/name_analysis.cc | 136 +++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 78 deletions(-) (limited to 'boot/name_analysis.cc') 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; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { - return "Type '" + payload.name + "' not declared"; + return "Symbol '" + this->name + "' has been already defined"; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return "Local symbol '" + payload.name + "' cannot be exported"; - } - else if constexpr (std::is_same_v) - { - 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; + return std::visit([this](const auto& payload) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { - const type resolved = resolve_underlying_type(pay.composite); + const type resolved = resolve_underlying_type(this->composite); const bool is_enum = resolved.get() != nullptr; const bool is_record = resolved.get() != nullptr; if (is_enum || is_record) { std::string message = is_enum ? "Enumeration" : "Record"; - if (auto alias = pay.composite.template get()) + if (auto alias = this->composite.get()) { 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) { - const type resolved = resolve_underlying_type(pay.aggregate); + const type resolved = resolve_underlying_type(this->composite); const bool is_enum = resolved.get() != nullptr; const std::string kind = is_enum ? "member" : "field"; std::string message = is_enum ? "Enumeration" : "Record"; - if (auto alias = pay.aggregate.template get()) + if (auto alias = this->composite.get()) { 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 name_analysis_visitor::build_composite_type( - const std::vector& fields, - ordered_map& field_names, - const type& aggregate) + ordered_map name_analysis_visitor::build_composite_type(const std::vector& fields, + ordered_map& field_names, const type& aggregate) { ordered_map 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 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()) @@ -347,9 +343,8 @@ namespace elna::boot base_name = alias->name; } } - add_error(field_name.position(), - member_error::duplicate{.name = field_name.name(), .aggregate = aggregate, - .original = existing->second.declaration, .base = base_name}); + add_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(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(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(initializer.id().position(), - declaration_error::undeclared{.name = initializer.id().name()}); + add_error(initializer.id().position(), initializer.id().name(), + expression->type_decoration, member_error::not_found{}); } } } @@ -469,7 +464,7 @@ namespace elna::boot { std::vector 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.position(), - member_error::duplicate{.name = member.name(), .aggregate = aggregate, - .original = existing->second, .base = std::nullopt}); + add_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(position, - declaration_error::redefinition{.name = name, .original = original->position}); + add_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() == nullptr - && !is_primitive_type(resolved, "Float") - && !is_discrete_type(resolved)) - { - add_error(trait->name, - trait->type_decoration); - trait->type_decoration = type(); - } - } } else { add_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(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(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(type_identifier) }).second) { add_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(variable_identifier.id().position(), - declaration_error::local_export{.name = variable_identifier.id().name()}); + variable_identifier.id().name(), declaration_error::kind::local_export); } } } -- cgit v1.2.3