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: