// Copyright (C) 2020-2025 Free Software Foundation, Inc. // This file is part of GCC. // 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 // <http://www.gnu.org/licenses/>. #ifndef RUST_RIB_H #define RUST_RIB_H #include "rust-system.h" #include "rust-ast.h" #include "optional.h" #include "expected.h" namespace Rust { namespace Resolver2_0 { /** pub enum Namespace { /// The type namespace includes `struct`s, `enum`s, `union`s, `trait`s, and `mod`s /// (and, by extension, crates). /// /// Note that the type namespace includes other items; this is not an /// exhaustive list. TypeNS, /// The value namespace includes `fn`s, `const`s, `static`s, and local variables (including function arguments). ValueNS, /// The macro namespace includes `macro_rules!` macros, declarative `macro`s, /// procedural macros, attribute macros, `derive` macros, and non-macro attributes /// like `#[inline]` and `#[rustfmt::skip]`. MacroNS, } */ // FIXME: There's no `labels` namespace, not sure if we need one or how to keep // one // FIXME: And where are things like loop labels kept? /** * All namespaces that Rust's name resolution needs to handle */ // TODO: Move to `rust-forever-stack.h`? enum class Namespace { Values, Types, Labels, Macros, // TODO: Which namespaces are we missing? }; /** * Error returned by `Rib::insert` when the key was already present in the Rib's * map. The class contains the previously-inserted NodeId as well as the name of * the node. */ struct DuplicateNameError { // TODO: We might need multiple kinds of errors later down the line DuplicateNameError (std::string name, NodeId existing); std::string name; NodeId existing; }; /** * A rib is a container of nodes, either declaration or usages, as well as the * identifier each node uses. They are used to delimit lexical scopes, and have * an impact on name resolution - they restrict certain name accesses and serve * as boundaries between scopes. * For example, if we are resolving the following *variable* use: * * ```rust * fn outer() { * let a = 15; // decl * fn inner() -> i32 { * a // use * } * } * ``` * * The `Function` rib we will have pushed will restrict the access to `outer`'s * `a` declaration: Variable uses cannot cross function boundaries. On the other * hand, if we were resolving a type usage, this would be perfectly allowed. */ class Rib { public: // TODO: Rename the class? to what? Binding? Declaration? // This is useful for items which are in namespaces where shadowing is not // allowed, but which are still shadowable! for example, when you do a glob // import, if a later import has the same name as an item imported in the glob // import, that glob imported item will need to get shadowed class Definition { public: static Definition NonShadowable (NodeId id); static Definition Shadowable (NodeId id); static Definition Globbed (NodeId id); // checked shadowable -> non_shadowable -> globbed // we have shadowable *and* globbed in order to control // resolution priority // we *could* use a single vector with 2 indices here // but it's probably not worth it for now std::vector<NodeId> ids_shadowable; std::vector<NodeId> ids_non_shadowable; std::vector<NodeId> ids_globbed; Definition () = default; Definition &operator= (const Definition &) = default; Definition (Definition const &) = default; bool is_ambiguous () const; NodeId get_node_id () const { if (!ids_shadowable.empty ()) return ids_shadowable.back (); rust_assert (!is_ambiguous ()); if (!ids_non_shadowable.empty ()) return ids_non_shadowable.back (); rust_assert (!ids_globbed.empty ()); return ids_globbed.back (); } std::string to_string () const; private: enum class Mode { SHADOWABLE, NON_SHADOWABLE, GLOBBED }; Definition (NodeId id, Mode mode); }; enum class Kind { Normal, Module, Function, ConstantItem, // -> this variant has a boolean TraitOrImpl, /* Any item other than a Module, Function, Constant, Trait or Impl block */ Item, Closure, MacroDefinition, /* Ban the use of forward-declared generic parameters in defaults */ ForwardTypeParamBan, /* Const generic, as in the following example: fn foo<T, const X: T>() {} */ ConstParamType, } kind; Rib (Kind kind); Rib (Kind kind, std::string identifier, NodeId id); Rib (Kind kind, std::unordered_map<std::string, NodeId> values); // TODO: What's the correctbehavior if the key already exists? What if a decl // and use are in the same rib? Is that possible? Okay based on RibKind? /** * Insert a new node in the rib * * @param name The name associated with the AST node * @param def The `Definition` to insert * * @return `DuplicateNameError` if the node is already present in the rib. The * `DuplicateNameError` class contains the NodeId of the existing * node. Returns the new NodeId on success. */ tl::expected<NodeId, DuplicateNameError> insert (std::string name, Definition def); /** * Access an inserted NodeId. * * @return tl::nullopt if the key does not exist, the NodeId otherwise */ tl::optional<Rib::Definition> get (const std::string &name); /* View all the values stored in the rib */ const std::unordered_map<std::string, Definition> &get_values () const; private: // TODO: Switch this to (NodeId, shadowable = false); std::unordered_map<std::string, Definition> values; }; } // namespace Resolver2_0 } // namespace Rust #endif // !RUST_RIB_H