diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-08 11:12:30 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-08 11:12:30 +0200 |
| commit | 8f9ba0c67479d8926edead8aa828458c5b3bc1be (patch) | |
| tree | 5c9831a9fcb4fa2828c4ec598f832152702f13fe | |
| parent | 72f0f9b9629013e83fc4346a35113e9eecae45b5 (diff) | |
| download | elna-8f9ba0c67479d8926edead8aa828458c5b3bc1be.tar.gz | |
Fix ICE when variable name is resolved as type
| -rw-r--r-- | boot/name_analysis.cc | 75 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 6 |
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, |
