aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-08 11:12:30 +0200
committerEugen Wissner <belka@caraus.de>2026-09-08 11:12:30 +0200
commit8f9ba0c67479d8926edead8aa828458c5b3bc1be (patch)
tree5c9831a9fcb4fa2828c4ec598f832152702f13fe
parent72f0f9b9629013e83fc4346a35113e9eecae45b5 (diff)
downloadelna-8f9ba0c67479d8926edead8aa828458c5b3bc1be.tar.gz
Fix ICE when variable name is resolved as type
-rw-r--r--boot/name_analysis.cc75
-rw-r--r--include/elna/boot/name_analysis.h6
2 files changed, 60 insertions, 21 deletions
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 37c7ced..a410ca8 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -48,6 +48,8 @@ namespace elna::boot
return "Trait '#" + this->name + "' not declared";
case undeclared_symbol:
return "Symbol '" + this->name + "' not declared";
+ case not_a_type:
+ return "'" + this->name + "' is not a type";
case local_export:
return "Local symbol '" + this->name + "' cannot be exported";
default:
@@ -283,8 +285,7 @@ namespace elna::boot
}
else if (return_type.proper_type != nullptr)
{
- return_type.proper_type->accept(this);
- return procedure_type::return_t(this->current_type);
+ return procedure_type::return_t(resolve_type(*return_type.proper_type));
}
else
{
@@ -301,10 +302,11 @@ namespace elna::boot
};
for (const auto& [parameter_names, parameters_type] : expression.parameters)
{
- parameters_type->accept(this);
+ const type parameter_type = resolve_type(*parameters_type);
+
for (const auto& parameter_name : parameter_names)
{
- result_type.first.parameters.push_back(this->current_type);
+ result_type.first.parameters.push_back(parameter_type);
result_type.second.push_back(parameter_name.name());
}
}
@@ -318,7 +320,8 @@ namespace elna::boot
for (const auto& field : fields)
{
- field.second->accept(this);
+ const type field_type = resolve_type(*field.second);
+
for (const auto& field_name : field.first)
{
auto [existing, inserted] = field_names.insert(field_name.name(),
@@ -340,7 +343,7 @@ namespace elna::boot
}
else
{
- result.insert(field_name.name(), this->current_type);
+ result.insert(field_name.name(), field_type);
}
}
}
@@ -413,8 +416,7 @@ namespace elna::boot
void resolving_visitor::visit(variable_declaration *declaration)
{
- declaration->variable_type().accept(this);
- auto variable_type = this->current_type;
+ const type variable_type = resolve_type(declaration->variable_type());
std::optional<constant_value> computed;
if (declaration->initializer != nullptr)
@@ -444,11 +446,33 @@ namespace elna::boot
}
}
+ type resolving_visitor::resolve_type(type_expression& expression)
+ {
+ expression.accept(this);
+
+ // An unknown name has reported itself already; only a name that resolves
+ // to something other than a type is left to report.
+ if (this->current_type.empty())
+ {
+ if (auto *named = expression.is_named();
+ named != nullptr && this->bag.lookup(named->name) != nullptr)
+ {
+ add_error<symbol_declaration_error>(named->position(),
+ named->name, symbol_declaration_error::kind::not_a_type);
+ }
+ }
+ return this->current_type;
+ }
+
void resolving_visitor::visit(array_type_expression *expression)
{
- expression->base().accept(this);
- auto array_base = this->current_type;
+ const type array_base = resolve_type(expression->base());
+ if (array_base.empty())
+ {
+ this->current_type = type();
+ return;
+ }
if (array_base.get<constant_type>() != nullptr)
{
add_error<declaration_format_error>(expression->position(),
@@ -476,25 +500,34 @@ namespace elna::boot
void resolving_visitor::visit(slice_type_expression *expression)
{
- expression->base().accept(this);
- this->current_type = type(std::make_shared<slice_type>(this->current_type));
+ const type slice_base = resolve_type(expression->base());
+
+ this->current_type = slice_base.empty()
+ ? type()
+ : type(std::make_shared<slice_type>(slice_base));
}
void resolving_visitor::visit(pointer_type_expression *expression)
{
- expression->base().accept(this);
- this->current_type = type(std::make_shared<pointer_type>(this->current_type));
+ const type pointer_base = resolve_type(expression->base());
+
+ this->current_type = pointer_base.empty()
+ ? type()
+ : type(std::make_shared<pointer_type>(pointer_base));
}
void resolving_visitor::visit(constant_type_expression *expression)
{
- expression->base().accept(this);
- if (this->current_type.get<constant_type>() != nullptr)
+ const type qualified_base = resolve_type(expression->base());
+
+ if (qualified_base.get<constant_type>() != nullptr)
{
add_error<declaration_format_error>(expression->position(),
declaration_format_error::kind::duplicate);
}
- this->current_type = type(std::make_shared<constant_type>(this->current_type));
+ this->current_type = qualified_base.empty()
+ ? type()
+ : type(std::make_shared<constant_type>(qualified_base));
}
void resolving_visitor::visit(record_type_expression *expression)
@@ -514,6 +547,8 @@ namespace elna::boot
}
else
{
+ add_error<symbol_declaration_error>(expression->base.value().position(),
+ expression->base.value().name(), symbol_declaration_error::kind::not_a_type);
this->current_type = type();
return;
}
@@ -760,8 +795,7 @@ namespace elna::boot
{
if (!trait->arguments.empty())
{
- trait->arguments.front()->accept(this);
- trait->types.push_back(this->current_type);
+ trait->types.push_back(resolve_type(*trait->arguments.front()));
}
if (trait->name == "size" || trait->name == "alignment" || trait->name == "offset")
@@ -1162,8 +1196,7 @@ namespace elna::boot
void declaration_visitor::visit(type_declaration *declaration)
{
- declaration->underlying_type().accept(this);
- type underlying = this->current_type;
+ type underlying = resolve_type(declaration->underlying_type());
// Reject the cycle and wire an empty referent: the declaration still
// resolves and is entered, so its uses degrade silently.
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index cf35ab6..4a22df7 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -43,6 +43,7 @@ namespace elna::boot
undeclared_type,
undeclared_trait,
undeclared_symbol,
+ not_a_type,
local_export
};
struct redefinition
@@ -180,6 +181,11 @@ namespace elna::boot
procedure_type_expression& expression);
ordered_map<type> build_composite_type(const std::vector<field_declaration>& fields,
ordered_map<field_origin>& field_names, const type& aggregate);
+ /*
+ * Resolves a type expression, reporting names that denote something
+ * other than a type.
+ */
+ type resolve_type(type_expression& expression);
type lookup_primitive_type(const std::string& name);
std::filesystem::path redefinition_file(const std::shared_ptr<info>& original) const;
std::shared_ptr<variable_info> register_variable(const std::string& name,