/* Symbol definitions. Copyright (C) 2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "elna/boot/symbol.h" #include #include #include namespace elna::boot { type::type(std::shared_ptr alias) : payload(alias) { } template std::shared_ptr type::get() const { if constexpr (std::is_same_v) { if (const auto *weak_pointer = std::get_if>(&payload)) { return weak_pointer->lock(); } return nullptr; } else { if (auto *shared_pointer = std::get_if>(&payload)) { return *shared_pointer; } return nullptr; } } // Explicit instantiation for all types used by callers. template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; template std::shared_ptr type::get() const; bool type::operator==(const std::nullptr_t&) const { return empty(); } bool type::operator==(const type& other) const { type const resolved_this = resolve_aliases(*this); type const resolved_that = resolve_aliases(other); if (auto left_record = resolved_this.get()) { return left_record == resolved_that.get(); } if (auto left_primitive = resolved_this.get()) { auto right_primitive = resolved_that.get(); return right_primitive != nullptr && left_primitive->identifier == right_primitive->identifier; } if (auto left_enumeration = resolved_this.get()) { return left_enumeration == resolved_that.get(); } if (auto left_extern = resolved_this.get()) { return left_extern == resolved_that.get(); } if (auto left_pointer = resolved_this.get()) { auto right_pointer = resolved_that.get(); return right_pointer != nullptr && left_pointer->base == right_pointer->base; } if (auto left_const = resolved_this.get()) { auto right_const = resolved_that.get(); return right_const != nullptr && left_const->unqualified == right_const->unqualified; } if (auto left_array = resolved_this.get()) { auto right_array = resolved_that.get(); return right_array != nullptr && left_array->size == right_array->size && left_array->base == right_array->base; } if (auto left_slice = resolved_this.get()) { auto right_slice = resolved_that.get(); return right_slice != nullptr && left_slice->base == right_slice->base; } if (auto left_procedure = resolved_this.get()) { auto right_procedure = resolved_that.get(); return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type && left_procedure->parameters == right_procedure->parameters && left_procedure->variadic == right_procedure->variadic; } if (auto left_parameter = resolved_this.get()) { return left_parameter == resolved_that.get(); } if (auto left_generic = resolved_this.get()) { return left_generic == resolved_that.get(); } if (auto left_instance = resolved_this.get()) { auto right_instance = resolved_that.get(); return right_instance != nullptr && left_instance->generic == right_instance->generic && left_instance->arguments == right_instance->arguments; } return resolved_this.empty() && resolved_that.empty(); } type::operator bool() const { return !empty(); } bool type::empty() const { return std::holds_alternative(payload); } std::string type::to_string() const { return std::visit([](const auto& payload) -> std::string { using T = std::decay_t; if constexpr (std::is_same_v) { return ""; } else if constexpr (std::is_same_v>) { return payload.lock()->name; } else if constexpr (std::is_same_v>) { return payload->identifier; } else if constexpr (std::is_same_v>) { return "^" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return "const " + payload->unqualified.to_string(); } else if constexpr (std::is_same_v>) { return "[" + std::to_string(payload->size) + "]" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return "[]" + payload->base.to_string(); } else if constexpr (std::is_same_v>) { return payload->base.empty() ? "record ... end" : "record(" + payload->base.to_string() + ") ... end"; } else if constexpr (std::is_same_v>) { std::string result = "proc(" + join(payload->parameters) + ")"; if (payload->variadic) { result += " #{varargs}"; } if (payload->return_type.no_return) { result += " -> !"; } else if (!payload->return_type.proper_type.empty()) { result += " -> " + payload->return_type.proper_type.to_string(); } return result; } else if constexpr (std::is_same_v>) { return "(enumeration)"; } else if constexpr (std::is_same_v>) { return "extern"; } else if constexpr (std::is_same_v>) { return payload->name; } else if constexpr (std::is_same_v>) { return "generic"; } else if constexpr (std::is_same_v>) { return payload->generic.to_string() + "#[" + join(payload->arguments) + "]"; } }, payload); } alias_type::alias_type(const std::string& name, type referent) : name(name), referent(std::move(referent)) { } pointer_type::pointer_type(type base) : base(std::move(base)) { } constant_type::constant_type(type unqualified) : unqualified(std::move(unqualified)) { } array_type::array_type(type base, std::uint64_t size) : base(std::move(base)), size(size) { } slice_type::slice_type(type base) : base(std::move(base)) { } primitive_type::primitive_type(const std::string& identifier, const type_properties& properties) : identifier(identifier), properties(properties) { } field_info::field_info(type field_type, std::optional alignment) : field_type(std::move(field_type)), alignment(alignment) { } record_type::record_type(type base) : base(std::move(base)) { } procedure_type::procedure_type(return_t return_type) : return_type(std::move(return_type)) { } enumeration_type::enumeration_type(const std::vector& members) : members(members) { } parameter_type::parameter_type(const std::string& name) : name(name) { } generic_type::generic_type(std::vector&& parameters, type referent) : parameters(std::move(parameters)), referent(std::move(referent)) { } instantiated_type::instantiated_type(type generic, std::vector&& arguments) : generic(std::move(generic)), arguments(std::move(arguments)) { } info::~info() = default; std::shared_ptr info::is_type() { return nullptr; } std::shared_ptr info::is_procedure() { return nullptr; } std::shared_ptr info::is_variable() { return nullptr; } type_info::type_info(const type& symbol) : symbol(symbol), owner(symbol.get()) { } std::shared_ptr type_info::is_type() { return std::static_pointer_cast(shared_from_this()); } procedure_info::procedure_info(const procedure_type& symbol, const std::vector& names, std::shared_ptr scope) : symbol(symbol), names(names), scope(std::move(scope)) { } std::shared_ptr procedure_info::is_procedure() { return std::static_pointer_cast(shared_from_this()); } bool procedure_info::is_extern() const { return this->scope == nullptr; } variable_info::variable_info(const type& symbol, bool is_extern) : symbol(symbol), is_extern(is_extern) { } std::shared_ptr variable_info::is_variable() { return std::static_pointer_cast(shared_from_this()); } static void builtin_integers(const std::shared_ptr& symbols, const std::array& properties, const std::string& integer_name) { for (std::size_t i = 1; i < properties.size(); ++i) { const std::size_t bit_size = properties[i].size * CHAR_BIT; const std::string type_name = integer_name + std::to_string(bit_size); const type variant_type = type(std::make_shared(type_name, properties[i])); symbols->enter(type_name, std::make_shared(variant_type)); // The unsuffixed integer type is an alias of the fixed-size type // matching the machine word. if (bit_size == properties.front().size * CHAR_BIT) { auto alias = std::make_shared(integer_name, variant_type); symbols->enter(integer_name, std::make_shared(type(alias))); } } if (!symbols->contains(integer_name)) { auto variant_type = type(std::make_shared(integer_name, properties.front())); symbols->enter(integer_name, std::make_shared(variant_type)); } } std::shared_ptr builtin_symbol_table(const target_info& target) { auto result = std::make_shared(); builtin_integers(result, target.int_properties, "Int"); builtin_integers(result, target.word_properties, "Word"); result->enter("Char", std::make_shared(type(std::make_shared("Char", target.char_properties)))); const type pointer = type(std::make_shared("Pointer", target.pointer_properties)); result->enter("Pointer", std::make_shared(pointer)); result->enter("Single", std::make_shared(type(std::make_shared("Single", target.single_properties)))); result->enter("Double", std::make_shared(type(std::make_shared("Double", target.double_properties)))); const type boolean = type(std::make_shared("Bool", target.bool_properties)); result->enter("Bool", std::make_shared(boolean)); procedure_type assert_symbol{ procedure_type::return_t() }; assert_symbol.parameters.push_back(boolean); std::shared_ptr const assert_info = std::make_shared(assert_symbol, std::vector{ "condition" }); result->enter("assert", assert_info); const type load_parameter = type(std::make_shared("T")); procedure_type volatile_load_symbol{ procedure_type::return_t(load_parameter) }; volatile_load_symbol.parameters.emplace_back(std::make_shared(load_parameter)); std::shared_ptr const volatile_load_info = std::make_shared(volatile_load_symbol, std::vector{ "source" }); volatile_load_info->parameters.push_back(load_parameter); result->enter("volatile_load", volatile_load_info); const type store_parameter = type(std::make_shared("T")); procedure_type volatile_store_symbol{ procedure_type::return_t() }; volatile_store_symbol.parameters.emplace_back(std::make_shared(store_parameter)); volatile_store_symbol.parameters.push_back(store_parameter); std::shared_ptr const volatile_store_info = std::make_shared(volatile_store_symbol, std::vector{ "target", "value" }); volatile_store_info->parameters.push_back(store_parameter); result->enter("volatile_store", volatile_store_info); return result; } symbol_bag::symbol_bag(forward_table&& unresolved, const std::shared_ptr& global_table) : unresolved(unresolved) { this->symbols = std::make_shared(global_table); } std::shared_ptr symbol_bag::lookup_import(const std::string& name) const { const auto found = std::ranges::find_if(this->imports, [&name](const std::shared_ptr& import_bag) { return import_bag->lookup(name) != nullptr; } ); return found == this->imports.cend() ? nullptr : (*found)->lookup(name); } std::shared_ptr symbol_bag::lookup(const std::string& name) { if (auto result = this->lookup_import(name)) { return result; } return this->symbols->lookup(name); } bool symbol_bag::enter(const std::string& name, const std::shared_ptr& entry) { return this->lookup_import(name) == nullptr && this->symbols->enter(name, entry); } std::shared_ptr symbol_bag::enter() { this->symbols = std::make_shared(this->symbols); return this->symbols; } void symbol_bag::enter(std::shared_ptr child) { this->symbols = std::move(child); } std::shared_ptr symbol_bag::leave() { std::shared_ptr result = this->symbols; this->symbols = result->scope(); return result; } std::shared_ptr symbol_bag::declared(const std::string& symbol_name) { auto unresolved_alias = this->unresolved.find(symbol_name); return unresolved_alias == this->unresolved.end() ? std::shared_ptr() : unresolved_alias->second; } std::shared_ptr symbol_bag::resolve(const std::string& symbol_name, type& resolution) { auto unresolved_node = this->unresolved.extract(symbol_name); assert(!unresolved_node.empty()); auto unresolved_declaration = unresolved_node.mapped(); unresolved_declaration->referent = resolution; // The alias is owned by the type_info the caller enters into the // symbol table from now on. return unresolved_declaration; } bool symbol_bag::forward_declare(const std::string& symbol_name, std::shared_ptr forward_declaration) { return this->unresolved.insert({ symbol_name, std::move(forward_declaration) }).second; } void symbol_bag::add_import(const std::shared_ptr& symbols) { this->imports.push_front(symbols); } bool symbol_bag::is_global() const { return this->symbols->scope() != nullptr && this->symbols->scope()->scope() == nullptr; } std::shared_ptr symbol_bag::exported_symbols() const { if (!m_exported) { m_exported = std::make_shared(symbols->scope()); for (const auto& [name, info] : *symbols) { if (info->exported) { m_exported->enter(name, info); } } } return m_exported; } type resolve_underlying_type(const std::shared_ptr& alias) { return resolve_underlying_type(alias->referent); } type resolve_underlying_type(const type& alias) { type resolved_alias = resolve_aliases(alias); if (auto qualified = resolved_alias.get()) { return resolve_underlying_type(qualified->unqualified); } else { return resolved_alias; } } type unwrap_aliases(const type& checked) { if (auto alias = checked.get()) { return unwrap_aliases(alias->referent); } return checked; } std::shared_ptr unwrap_generic(const type& checked) { return unwrap_aliases(checked).get(); } type substitute(const type& subject, const std::vector& parameters, const std::vector& arguments) { if (auto parameter = subject.get()) { for (std::size_t i = 0; i < parameters.size() && i < arguments.size(); ++i) { if (parameters[i].get() == parameter) { return arguments[i]; } } return subject; } if (auto pointer = subject.get()) { return type(std::make_shared( substitute(pointer->base, parameters, arguments))); } if (auto slice = subject.get()) { return type(std::make_shared( substitute(slice->base, parameters, arguments))); } if (auto array = subject.get()) { return type(std::make_shared( substitute(array->base, parameters, arguments), array->size)); } if (auto qualified = subject.get()) { return type(std::make_shared( substitute(qualified->unqualified, parameters, arguments))); } if (auto procedure = subject.get()) { procedure_type::return_t result_return = procedure->return_type; if (!result_return.no_return && !result_return.proper_type.empty()) { result_return = procedure_type::return_t( substitute(result_return.proper_type, parameters, arguments)); } auto result = std::make_shared(std::move(result_return)); result->variadic = procedure->variadic; result->parameters.reserve(procedure->parameters.size()); for (const type& parameter_type : procedure->parameters) { result->parameters.push_back(substitute(parameter_type, parameters, arguments)); } return type(result); } if (auto instance = subject.get()) { std::vector substituted; substituted.reserve(instance->arguments.size()); for (const type& argument : instance->arguments) { substituted.push_back(substitute(argument, parameters, arguments)); } return type(std::make_shared(instance->generic, std::move(substituted))); } return subject; } type instantiate(const std::shared_ptr& instance) { std::shared_ptr const generic = unwrap_generic(instance->generic); return generic == nullptr ? type() : substitute(generic->referent, generic->parameters, instance->arguments); } type erase_generic(const type& checked) { type resolved = resolve_underlying_type(checked); if (auto instance = resolved.get()) { std::shared_ptr const generic = unwrap_generic(instance->generic); return generic == nullptr ? type() : resolve_underlying_type(generic->referent); } if (auto generic = resolved.get()) { return resolve_underlying_type(generic->referent); } return resolved; } /* * Rewrites a type taken from an instantiation's erased referent so that it * reads as the instantiation sees it. The identity for anything else. */ static type substitute_from(const type& subject, const type& taken) { auto instance = subject.get(); if (instance == nullptr) { return taken; } std::shared_ptr const generic = unwrap_generic(instance->generic); return generic == nullptr ? type() : substitute(taken, generic->parameters, instance->arguments); } type base_of(const type& derived) { const type resolved = resolve_underlying_type(derived); auto record = erase_generic(resolved).get(); return record == nullptr || record->base.empty() ? type() : substitute_from(resolved, record->base); } bool is_base_of(const type& base, const type& derived) { for (type current = base_of(derived); !current.empty(); current = base_of(current)) { if (current == base) { return true; } } return false; } type resolve_aliases(const type& checked) { if (auto alias = checked.get()) { return resolve_aliases(alias->referent); } if (auto instance = checked.get()) { std::shared_ptr const generic = unwrap_generic(instance->generic); // A record referent makes the instantiation nominal, so resolution // stops here and Stack#[^File] stays distinct from Stack#[^Socket]. if (generic == nullptr || unwrap_aliases(generic->referent).get() != nullptr) { return checked; } return resolve_aliases(instantiate(instance)); } return checked; } bool is_primitive_type(const type& checked, const std::string& name) { if (auto primitive_checked = checked.get()) { return primitive_checked->identifier == name; } return false; } bool is_integer_type(const type& checked) { if (auto primitive_checked = checked.get()) { return primitive_checked->identifier.starts_with("Int"); } return false; } bool is_word_type(const type& checked) { if (auto primitive_checked = checked.get()) { return primitive_checked->identifier.starts_with("Word"); } return false; } bool is_float_type(const type& checked) { return is_primitive_type(checked, "Single") || is_primitive_type(checked, "Double"); } bool is_numeric_type(const type& checked) { return is_integral_type(checked) || is_float_type(checked); } bool is_integral_type(const type& checked) { return is_integer_type(checked) || is_word_type(checked); } bool is_discrete_type(const type& checked) { return is_integral_type(checked) || is_primitive_type(checked, "Bool") || is_primitive_type(checked, "Char"); } bool is_any_pointer_type(const type& checked) { return checked.get() != nullptr || checked.get() != nullptr || checked.get() != nullptr || is_primitive_type(checked, "Pointer"); } bool is_scalar_type(const type& checked) { return is_discrete_type(checked) || is_float_type(checked) || is_any_pointer_type(checked) || checked.get() != nullptr; } bool is_string_type(const type& checked) { if (auto slice = checked.get()) { if (auto base = resolve_aliases(slice->base).get()) { return is_primitive_type(resolve_aliases(base->unqualified), "Word8"); } } return false; } type get_range_base_type(const type& range) { if (auto array = range.get()) { return array->base; } else if (auto slice = range.get()) { return slice->base; } return type(); } type lookup_field(const type& composite_type, const std::string& field_name) { const type resolved_type = resolve_underlying_type(composite_type); if (auto record = erase_generic(resolved_type).get()) { for (const auto& [name, field] : record->fields) { if (name == field_name) { return substitute_from(resolved_type, field.field_type); } } } const type base = base_of(resolved_type); return base.empty() ? type() : lookup_field(base, field_name); } }