diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 7f04136fe63..27123a0133a 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -146,6 +146,7 @@ GRS_OBJS = \ rust/rust-ast-resolve-path.o \ rust/rust-ast-resolve-stmt.o \ rust/rust-ast-resolve-struct-expr-field.o \ + rust/rust-forever-stack.o \ rust/rust-hir-type-check.o \ rust/rust-privacy-check.o \ rust/rust-privacy-ctx.o \ diff --git a/gcc/rust/resolve/rust-forever-stack.cc b/gcc/rust/resolve/rust-forever-stack.cc new file mode 100644 index 00000000000..5e47dfc6a64 --- /dev/null +++ b/gcc/rust/resolve/rust-forever-stack.cc @@ -0,0 +1,336 @@ +// Copyright (C) 2024 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 +// . + +#include "expected.h" +#include "rust-ast.h" +#include "rust-diagnostics.h" +#include "rust-forever-stack.h" +#include "rust-rib.h" +#include "optional.h" + +namespace Rust { +namespace Resolver2_0 { + +bool +ForeverStackStore::Node::is_root () const +{ + return !parent.has_value (); +} + +bool +ForeverStackStore::Node::is_leaf () const +{ + return children.empty (); +} + +NodeId +ForeverStackStore::Node::get_id () const +{ + return id; +} + +ForeverStackStore::Node & +ForeverStackStore::Node::insert_child (NodeId id, tl::optional path, + Rib::Kind kind) +{ + auto res = children.insert ({Link (id, path), Node (kind, id, *this)}); + + rust_debug ("inserting link: Link(%d [%s]): existed? %s", id, + path.has_value () ? path.value ().as_string ().c_str () + : "", + !res.second ? "yes" : "no"); + + // sanity check on rib kind + // pick the value rib, since all ribs should have the same kind anyways + rust_assert (res.second || res.first->second.value_rib.kind == kind); + + // verify, if we're using an existing node, our paths don't contradict + if (!res.second && path.has_value ()) + { + auto other_path = res.first->first.path; + rust_assert (!other_path.has_value () + || other_path.value ().as_string () + == path.value ().as_string ()); + } + + return res.first->second; +} + +tl::optional +ForeverStackStore::Node::get_child (const Identifier &path) +{ + for (auto &ent : children) + { + if (ent.first.path.has_value () + && ent.first.path->as_string () == path.as_string ()) + return ent.second; + } + return tl::nullopt; +} + +tl::optional +ForeverStackStore::Node::get_child (const Identifier &path) const +{ + for (auto &ent : children) + { + if (ent.first.path.has_value () + && ent.first.path->as_string () == path.as_string ()) + return ent.second; + } + return tl::nullopt; +} + +tl::optional +ForeverStackStore::Node::get_parent () +{ + return parent; +} + +tl::optional +ForeverStackStore::Node::get_parent () const +{ + if (parent) + return *parent; + else + return tl::nullopt; +} + +tl::optional +ForeverStackStore::Node::get_parent_path () const +{ + if (parent.has_value ()) + for (auto &ent : parent->children) + if (ent.first.id == id && ent.first.path.has_value ()) + return ent.first.path.value (); + return tl::nullopt; +} + +Rib & +ForeverStackStore::Node::get_rib (Namespace ns) +{ + switch (ns) + { + case Namespace::Values: + return value_rib; + case Namespace::Types: + return type_rib; + case Namespace::Labels: + return label_rib; + case Namespace::Macros: + return macro_rib; + } + rust_unreachable (); +} + +const Rib & +ForeverStackStore::Node::get_rib (Namespace ns) const +{ + switch (ns) + { + case Namespace::Values: + return value_rib; + case Namespace::Types: + return type_rib; + case Namespace::Labels: + return label_rib; + case Namespace::Macros: + return macro_rib; + } + rust_unreachable (); +} + +tl::expected +ForeverStackStore::Node::insert (const Identifier &name, NodeId node, + Namespace ns) +{ + // So what do we do here - if the Rib has already been pushed in an earlier + // pass, we might end up in a situation where it is okay to re-add new names. + // Do we just ignore that here? Do we keep track of if the Rib is new or not? + // should our cursor have info on the current node like "is it newly pushed"? + return get_rib (ns).insert (name.as_string (), + Rib::Definition::NonShadowable (node)); +} + +tl::expected +ForeverStackStore::Node::insert_shadowable (const Identifier &name, NodeId node, + Namespace ns) +{ + return get_rib (ns).insert (name.as_string (), + Rib::Definition::Shadowable (node)); +} + +tl::expected +ForeverStackStore::Node::insert_globbed (const Identifier &name, NodeId node, + Namespace ns) +{ + return get_rib (ns).insert (name.as_string (), + Rib::Definition::Globbed (node)); +} + +void +ForeverStackStore::Node::reverse_iter (std::function lambda) +{ + Node *tmp = this; + + while (true) + { + auto keep_going = lambda (*tmp); + if (keep_going == KeepGoing::No) + return; + + if (tmp->is_root ()) + return; + + tmp = &tmp->parent.value (); + } +} + +void +ForeverStackStore::Node::reverse_iter ( + std::function lambda) const +{ + const Node *tmp = this; + + while (true) + { + auto keep_going = lambda (*tmp); + if (keep_going == KeepGoing::No) + return; + + if (tmp->is_root ()) + return; + + tmp = &tmp->parent.value (); + } +} + +void +ForeverStackStore::Node::child_iter ( + std::function, Node &)> + lambda) +{ + for (auto &ent : children) + { + tl::optional path; + if (ent.first.path.has_value ()) + path = ent.first.path.value (); + auto keep_going = lambda (ent.first.id, path, ent.second); + if (keep_going == KeepGoing::No) + return; + } +} + +void +ForeverStackStore::Node::child_iter ( + std::function, + const Node &)> + lambda) const +{ + for (auto &ent : children) + { + tl::optional path; + if (ent.first.path.has_value ()) + path = ent.first.path.value (); + auto keep_going = lambda (ent.first.id, path, ent.second); + if (keep_going == KeepGoing::No) + return; + } +} + +ForeverStackStore::Node & +ForeverStackStore::Node::find_closest_module () +{ + // get kind of value_rib + // but all ribs should share the same kind anyways + if (value_rib.kind == Rib::Kind::Module || !parent.has_value ()) + return *this; + else + return parent->find_closest_module (); +} + +const ForeverStackStore::Node & +ForeverStackStore::Node::find_closest_module () const +{ + // get kind of value_rib + // but all ribs should share the same kind anyways + if (value_rib.kind != Rib::Kind::Module || !parent.has_value ()) + return *this; + else + return parent->find_closest_module (); +} + +tl::optional +ForeverStackStore::Node::dfs_node (NodeId to_find) +{ + if (id == to_find) + return *this; + + for (auto &child : children) + { + auto candidate = child.second.dfs_node (to_find); + + if (candidate.has_value ()) + return candidate; + } + + return tl::nullopt; +} + +tl::optional +ForeverStackStore::Node::dfs_node (NodeId to_find) const +{ + if (id == to_find) + return *this; + + for (auto &child : children) + { + auto candidate = child.second.dfs_node (to_find); + + if (candidate.has_value ()) + return candidate; + } + + return tl::nullopt; +} + +ForeverStackStore::Node & +ForeverStackStore::get_root () +{ + return root; +} + +const ForeverStackStore::Node & +ForeverStackStore::get_root () const +{ + return root; +} + +tl::optional +ForeverStackStore::get_node (NodeId node_id) +{ + return root.dfs_node (node_id); +} + +tl::optional +ForeverStackStore::get_node (NodeId node_id) const +{ + return root.dfs_node (node_id); +} + +} // namespace Resolver2_0 +} // namespace Rust diff --git a/gcc/rust/resolve/rust-forever-stack.h b/gcc/rust/resolve/rust-forever-stack.h index 623e256d32c..d7289f57c82 100644 --- a/gcc/rust/resolve/rust-forever-stack.h +++ b/gcc/rust/resolve/rust-forever-stack.h @@ -392,6 +392,157 @@ not contain any imports, macro definitions or macro invocations. You can look at this pass's documentation for more details on this resolution process. **/ + +/** + * Intended for use by ForeverStack to store Nodes + * Unlike ForeverStack, does not store a cursor reference + * Intended to make path resolution in multiple namespaces simpler + **/ +class ForeverStackStore +{ +public: + ForeverStackStore (NodeId crate_id) : root (Rib::Kind::Normal, crate_id) + { + rust_assert (root.is_root ()); + rust_assert (root.is_leaf ()); + } + +private: + /** + * A link between two Nodes in our trie data structure. This class represents + * the edges of the graph + */ + class Link + { + public: + Link (NodeId id, tl::optional path) : id (id), path (path) {} + + bool compare (const Link &other) const { return id < other.id; } + + NodeId id; + tl::optional path; + }; + + /* Link comparison class, which we use in a Node's `children` map */ + class LinkCmp + { + public: + bool operator() (const Link &lhs, const Link &rhs) const + { + return lhs.compare (rhs); + } + }; + +public: + class Node; + + struct DfsResult + { + Node &first; + std::string second; + }; + + struct ConstDfsResult + { + const Node &first; + std::string second; + }; + + /* Should we keep going upon seeing a Rib? */ + enum class KeepGoing + { + Yes, + No, + }; + + class Node + { + private: + friend class ForeverStackStore::ForeverStackStore; + + Node (Rib::Kind rib_kind, NodeId id, tl::optional parent) + : value_rib (rib_kind), type_rib (rib_kind), label_rib (rib_kind), + macro_rib (rib_kind), id (id), parent (parent) + {} + Node (Rib::Kind rib_kind, NodeId id) : Node (rib_kind, id, tl::nullopt) {} + Node (Rib::Kind rib_kind, NodeId id, Node &parent) + : Node (rib_kind, id, tl::optional (parent)) + {} + + public: + Node (const Node &) = default; + Node (Node &&) = default; + Node &operator= (const Node &) = delete; + Node &operator= (Node &&) = default; + + bool is_root () const; + bool is_leaf () const; + + NodeId get_id () const; + + Node &insert_child (NodeId id, tl::optional path, + Rib::Kind kind); + + tl::optional get_child (const Identifier &path); + tl::optional get_child (const Identifier &path) const; + + tl::optional get_parent (); + tl::optional get_parent () const; + + // finds the identifier, if any, used to link + // this node's parent to this node + tl::optional get_parent_path () const; + + Rib &get_rib (Namespace ns); + const Rib &get_rib (Namespace ns) const; + + tl::expected insert (const Identifier &name, + NodeId node, Namespace ns); + tl::expected + insert_shadowable (const Identifier &name, NodeId node, Namespace ns); + tl::expected + insert_globbed (const Identifier &name, NodeId node, Namespace ns); + + void reverse_iter (std::function lambda); + void reverse_iter (std::function lambda) const; + + void child_iter (std::function, Node &)> + lambda); + void child_iter (std::function, const Node &)> + lambda) const; + + Node &find_closest_module (); + const Node &find_closest_module () const; + + tl::optional dfs_node (NodeId to_find); + tl::optional dfs_node (NodeId to_find) const; + + private: + // per-namespace ribs + Rib value_rib; + Rib type_rib; + Rib label_rib; + Rib macro_rib; + // all linked nodes + std::map children; + + NodeId id; // The node id of the Node's scope + + tl::optional parent; // `None` only if the node is a root + }; + + Node &get_root (); + const Node &get_root () const; + + tl::optional get_node (NodeId node_id); + tl::optional get_node (NodeId node_id) const; + +private: + Node root; +}; + template class ForeverStack { public: