aboutsummaryrefslogtreecommitdiff
path: root/boot/name_analysis.cc
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-29 01:16:18 +0200
committerEugen Wissner <belka@caraus.de>2026-07-29 01:16:18 +0200
commitf8daedce5c73e02dfb2fc59d75777190185584df (patch)
tree9ec211b96c3d7efd2fca0da37baeaa8b341e31fb /boot/name_analysis.cc
parent36a274c9a8bca944234589220def025d3920b3ef (diff)
downloadelna-f8daedce5c73e02dfb2fc59d75777190185584df.tar.gz
Validate cast compatibility during semantic analysis
Diffstat (limited to 'boot/name_analysis.cc')
-rw-r--r--boot/name_analysis.cc136
1 files changed, 58 insertions, 78 deletions
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<decltype(payload)>;
- if constexpr (std::is_same_v<T, undeclared>)
+ if constexpr (std::is_same_v<T, redefinition>)
{
- return "Type '" + payload.name + "' not declared";
+ return "Symbol '" + this->name + "' has been already defined";
}
- else if constexpr (std::is_same_v<T, local_export>)
+ else if constexpr (std::is_same_v<T, kind>)
{
- return "Local symbol '" + payload.name + "' cannot be exported";
- }
- else if constexpr (std::is_same_v<T, redefinition>)
- {
- 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<decltype(pay)>;
+ return std::visit([this](const auto& payload) -> std::string {
+ using T = std::decay_t<decltype(payload)>;
+
if constexpr (std::is_same_v<T, not_found>)
{
- const type resolved = resolve_underlying_type(pay.composite);
+ const type resolved = resolve_underlying_type(this->composite);
const bool is_enum = resolved.get<enumeration_type>() != nullptr;
const bool is_record = resolved.get<record_type>() != nullptr;
if (is_enum || is_record)
{
std::string message = is_enum ? "Enumeration" : "Record";
- if (auto alias = pay.composite.template get<alias_type>())
+ if (auto alias = this->composite.get<alias_type>())
{
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<T, duplicate>)
{
- const type resolved = resolve_underlying_type(pay.aggregate);
+ const type resolved = resolve_underlying_type(this->composite);
const bool is_enum = resolved.get<enumeration_type>() != nullptr;
const std::string kind = is_enum ? "member" : "field";
std::string message = is_enum ? "Enumeration" : "Record";
- if (auto alias = pay.aggregate.template get<alias_type>())
+ if (auto alias = this->composite.get<alias_type>())
{
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<type> name_analysis_visitor::build_composite_type(
- const std::vector<field_declaration>& fields,
- ordered_map<field_origin>& field_names,
- const type& aggregate)
+ ordered_map<type> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields,
+ ordered_map<field_origin>& field_names, const type& aggregate)
{
ordered_map<type> 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<std::string> 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<alias_type>())
@@ -347,9 +343,8 @@ namespace elna::boot
base_name = alias->name;
}
}
- add_error<member_error>(field_name.position(),
- member_error::duplicate{.name = field_name.name(), .aggregate = aggregate,
- .original = existing->second.declaration, .base = base_name});
+ add_error<member_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<declaration_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<declaration_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<declaration_error>(initializer.id().position(),
- declaration_error::undeclared{.name = initializer.id().name()});
+ add_error<member_error>(initializer.id().position(), initializer.id().name(),
+ expression->type_decoration, member_error::not_found{});
}
}
}
@@ -469,7 +464,7 @@ namespace elna::boot
{
std::vector<std::string> 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_error>(member.position(),
- member_error::duplicate{.name = member.name(), .aggregate = aggregate,
- .original = existing->second, .base = std::nullopt});
+ add_error<member_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<declaration_error>(position,
- declaration_error::redefinition{.name = name, .original = original->position});
+ add_error<declaration_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<enumeration_type>() == nullptr
- && !is_primitive_type(resolved, "Float")
- && !is_discrete_type(resolved))
- {
- add_error<unsupported_trait_type_error>(trait->name,
- trait->type_decoration);
- trait->type_decoration = type();
- }
- }
}
else
{
add_error<declaration_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<member_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<declaration_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<alias_type>(type_identifier) }).second)
{
add_error<declaration_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<declaration_error>(variable_identifier.id().position(),
- declaration_error::local_export{.name = variable_identifier.id().name()});
+ variable_identifier.id().name(), declaration_error::kind::local_export);
}
}
}