[COMMITTED 07/19] gccrs: nr: Move path resolution from ForeverStack to NRCtx

arthur.cohen@embecosm.com arthur.cohen@embecosm.com
Mon Jun 1 13:40:45 GMT 2026


From: Arthur Cohen <arthur.cohen@embecosm.com>

gcc/rust/ChangeLog:

	* resolve/rust-forever-stack.h: Move declarations from ForeverStack to NRCtx, make most
	of the ForeverStack members public as it helps the Ctx a lot.
	* resolve/rust-forever-stack.hxx: Move implementation of resolve_path methods to NRCtx.
	* resolve/rust-name-resolution-context.h: Declare resolve_path methods.
	* resolve/rust-name-resolution-context.hxx: New file with resolve_path impls.
---
 gcc/rust/resolve/rust-forever-stack.h         |  30 +--
 gcc/rust/resolve/rust-forever-stack.hxx       | 214 ---------------
 .../resolve/rust-name-resolution-context.h    |  62 +++--
 .../resolve/rust-name-resolution-context.hxx  | 249 ++++++++++++++++++
 4 files changed, 296 insertions(+), 259 deletions(-)
 create mode 100644 gcc/rust/resolve/rust-name-resolution-context.hxx

diff --git a/gcc/rust/resolve/rust-forever-stack.h b/gcc/rust/resolve/rust-forever-stack.h
index f6e351a1c4f..242b588a394 100644
--- a/gcc/rust/resolve/rust-forever-stack.h
+++ b/gcc/rust/resolve/rust-forever-stack.h
@@ -749,23 +749,6 @@ public:
   tl::optional<Rib::Definition> get_from_prelude (NodeId prelude,
 						  const Identifier &name);
 
-  /**
-   * Resolve a path to its definition in the current `ForeverStack`
-   *
-   * // TODO: Add documentation for `segments`
-   *
-   * @return a valid option with the Definition if the path is present in the
-   *         current map, an empty one otherwise.
-   */
-  tl::optional<Rib::Definition> resolve_path (
-    const ResolutionPath &path, ResolutionMode mode,
-    std::function<void (Usage, Definition)> insert_segment_resolution,
-    std::vector<Error> &collect_errors);
-  tl::optional<Rib::Definition> resolve_path (
-    const ResolutionPath &path, ResolutionMode mode,
-    std::function<void (Usage, Definition)> insert_segment_resolution,
-    std::vector<Error> &collect_errors, NodeId starting_point_id);
-
   // FIXME: Documentation
   tl::optional<Rib &> to_rib (NodeId rib_id);
   tl::optional<const Rib &> to_rib (NodeId rib_id) const;
@@ -778,7 +761,6 @@ public:
    */
   bool is_module_descendant (NodeId parent, NodeId child) const;
 
-private:
   /**
    * A link between two Nodes in our trie data structure. This class represents
    * the edges of the graph
@@ -826,18 +808,8 @@ private:
     tl::optional<Node &> parent; // `None` only if the node is a root
   };
 
-  /**
-   * Private overloads which allow specifying a starting point
-   */
-
   tl::optional<Rib::Definition> get (Node &start, const Identifier &name);
 
-  tl::optional<Rib::Definition> resolve_path (
-    const ResolutionPath &path, ResolutionMode mode,
-    std::function<void (Usage, Definition)> insert_segment_resolution,
-    std::vector<Error> &collect_errors,
-    std::reference_wrapper<Node> starting_point);
-
   /* Should we keep going upon seeing a Rib? */
   enum class KeepGoing
   {
@@ -859,6 +831,7 @@ private:
 
   Node &cursor ();
   const Node &cursor () const;
+
   void update_cursor (Node &new_cursor);
 
   /* The forever stack's actual nodes */
@@ -930,7 +903,6 @@ private:
   tl::optional<const Node &> dfs_node (const Node &starting_point,
 				       NodeId to_find) const;
 
-public:
   bool forward_declared (NodeId definition, NodeId usage)
   {
     if (peek ().kind != Rib::Kind::ForwardTypeParamBan)
diff --git a/gcc/rust/resolve/rust-forever-stack.hxx b/gcc/rust/resolve/rust-forever-stack.hxx
index c7f90ae3a21..e3cd5b7add3 100644
--- a/gcc/rust/resolve/rust-forever-stack.hxx
+++ b/gcc/rust/resolve/rust-forever-stack.hxx
@@ -667,220 +667,6 @@ ForeverStack<N>::resolve_final_segment (Node &final_node, std::string &seg_name,
   return final_node.rib.get (seg_name);
 }
 
-template <Namespace N>
-tl::optional<Rib::Definition>
-ForeverStack<N>::resolve_path (
-  const ResolutionPath &path, ResolutionMode mode,
-  std::function<void (Usage, Definition)> insert_segment_resolution,
-  std::vector<Error> &collect_errors, NodeId starting_point_id)
-{
-  auto starting_point = dfs_node (root, starting_point_id);
-
-  // We may have a prelude, but haven't visited it yet and thus it's not in our
-  // nodes
-  if (!starting_point)
-    return tl::nullopt;
-
-  return resolve_path (path, mode, insert_segment_resolution, collect_errors,
-		       *starting_point);
-}
-
-template <Namespace N>
-tl::optional<Rib::Definition>
-ForeverStack<N>::resolve_path (
-  const ResolutionPath &path, ResolutionMode mode,
-  std::function<void (Usage, Definition)> insert_segment_resolution,
-  std::vector<Error> &collect_errors)
-{
-  std::reference_wrapper<Node> starting_point = cursor ();
-
-  return resolve_path (path, mode, insert_segment_resolution, collect_errors,
-		       starting_point);
-}
-
-template <Namespace N>
-tl::optional<Rib::Definition>
-ForeverStack<N>::resolve_path (
-  const ResolutionPath &path, ResolutionMode mode,
-  std::function<void (Usage, Definition)> insert_segment_resolution,
-  std::vector<Error> &collect_errors,
-  std::reference_wrapper<Node> starting_point)
-{
-  bool can_descend = true;
-
-  rust_debug ("resolving %s", path.as_string ().c_str ());
-
-  if (auto lang_item = path.get_lang_prefix ())
-    {
-      NodeId seg_id
-	= Analysis::Mappings::get ().get_lang_item_node (lang_item->first);
-
-      insert_segment_resolution (Usage (lang_item->second),
-				 Definition (seg_id));
-
-      if (path.get_segments ().empty ())
-	return Rib::Definition::NonShadowable (seg_id);
-
-      auto new_start = dfs_node (root, seg_id);
-      rust_assert (new_start.has_value ());
-      starting_point = new_start.value ();
-
-      can_descend = false;
-    }
-  else
-    {
-      switch (mode)
-	{
-	case ResolutionMode::Normal:
-	  break; // default
-	case ResolutionMode::FromRoot:
-	  starting_point = root;
-	  break;
-	case ResolutionMode::FromExtern:
-	  starting_point = extern_prelude;
-	  break;
-	default:
-	  rust_unreachable ();
-	}
-    }
-
-  if (path.get_segments ().empty ())
-    {
-      return Rib::Definition::NonShadowable (starting_point.get ().id);
-    }
-
-  auto &segments = path.get_segments ();
-
-  // if there's only one segment, we just use `get`
-  if (can_descend && segments.size () == 1)
-    {
-      auto &seg = segments.front ();
-
-      tl::optional<Rib::Definition> res = get (starting_point.get (), seg.name);
-
-      if (!res)
-	res = get_lang_prelude (seg.name);
-
-      if (N == Namespace::Types && !res)
-	{
-	  if (seg.is_crate_path_seg ())
-	    {
-	      insert_segment_resolution (Usage (seg.node_id),
-					 Definition (root.id));
-	      // TODO: does NonShadowable matter?
-	      return Rib::Definition::NonShadowable (root.id);
-	    }
-	  else if (seg.is_lower_self_seg ())
-	    {
-	      NodeId id = find_closest_module (starting_point.get ()).id;
-	      insert_segment_resolution (Usage (seg.node_id), Definition (id));
-	      // TODO: does NonShadowable matter?
-	      return Rib::Definition::NonShadowable (id);
-	    }
-	  else if (seg.is_super_path_seg ())
-	    {
-	      Node &closest_module
-		= find_closest_module (starting_point.get ());
-	      if (closest_module.is_root ())
-		{
-		  rust_error_at (seg.locus, ErrorCode::E0433,
-				 "too many leading %<super%> keywords");
-		  return tl::nullopt;
-		}
-
-	      NodeId id
-		= find_closest_module (closest_module.parent.value ()).id;
-	      insert_segment_resolution (Usage (seg.node_id), Definition (id));
-	      // TODO: does NonShadowable matter?
-	      return Rib::Definition::NonShadowable (id);
-	    }
-	  else
-	    {
-	      // HACK: check for a module after we check the language prelude
-	      for (auto &kv :
-		   find_closest_module (starting_point.get ()).children)
-		{
-		  auto &link = kv.first;
-
-		  if (link.path.map_or (
-			[&seg] (Identifier path) {
-			  auto &path_str = path.as_string ();
-			  return path_str == seg.name;
-			},
-			false))
-		    {
-		      insert_segment_resolution (Usage (seg.node_id),
-						 Definition (kv.second.id));
-		      return Rib::Definition::NonShadowable (kv.second.id);
-		    }
-		}
-	    }
-	}
-
-      if (res && !res->is_ambiguous ())
-	insert_segment_resolution (Usage (seg.node_id),
-				   Definition (res->get_node_id ()));
-      return res;
-    }
-
-  auto iterator = segments.begin ();
-  if (can_descend)
-    {
-      if (auto res
-	  = find_starting_point (segments, starting_point,
-				 insert_segment_resolution, collect_errors))
-	iterator = *res;
-      else
-	return tl::nullopt;
-    }
-
-  return resolve_segments (starting_point.get (), segments, iterator,
-			   insert_segment_resolution, collect_errors)
-    .and_then ([this, &segments, &insert_segment_resolution] (
-		 Node &final_node) -> tl::optional<Rib::Definition> {
-      // leave resolution within impl blocks to type checker
-      if (final_node.rib.kind == Rib::Kind::TraitOrImpl)
-	return tl::nullopt;
-
-      auto &seg = segments.back ();
-      std::string seg_name = seg.name;
-
-      tl::optional<Rib::Definition> res
-	= resolve_final_segment (final_node, seg_name,
-				 seg.is_lower_self_seg ());
-      // Ok we didn't find it in the rib, Lets try the prelude...
-      if (!res)
-	res = get_lang_prelude (seg_name);
-
-      if (N == Namespace::Types && !res)
-	{
-	  // HACK: check for a module after we check the language prelude
-	  for (auto &kv : final_node.children)
-	    {
-	      auto &link = kv.first;
-
-	      if (link.path.map_or (
-		    [&seg_name] (Identifier path) {
-		      auto &path_str = path.as_string ();
-		      return path_str == seg_name;
-		    },
-		    false))
-		{
-		  insert_segment_resolution (Usage (seg.node_id),
-					     Definition (kv.second.id));
-		  return Rib::Definition::NonShadowable (kv.second.id);
-		}
-	    }
-	}
-
-      if (res && !res->is_ambiguous ())
-	insert_segment_resolution (Usage (seg.node_id),
-				   Definition (res->get_node_id ()));
-
-      return res;
-    });
-}
-
 template <Namespace N>
 tl::optional<typename ForeverStack<N>::DfsResult>
 ForeverStack<N>::dfs (ForeverStack<N>::Node &starting_point, NodeId to_find)
diff --git a/gcc/rust/resolve/rust-name-resolution-context.h b/gcc/rust/resolve/rust-name-resolution-context.h
index eac38eb62d6..c5b2d0b1c00 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.h
+++ b/gcc/rust/resolve/rust-name-resolution-context.h
@@ -543,20 +543,20 @@ public:
     switch (ns)
       {
       case Namespace::Values:
-	resolved = values.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors);
+	resolved = resolve_path (values, path, mode, insert_segment_resolution,
+				 collect_errors);
 	break;
       case Namespace::Types:
-	resolved = types.resolve_path (path, mode, insert_segment_resolution,
-				       collect_errors);
+	resolved = resolve_path (types, path, mode, insert_segment_resolution,
+				 collect_errors);
 	break;
       case Namespace::Macros:
-	resolved = macros.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors);
+	resolved = resolve_path (macros, path, mode, insert_segment_resolution,
+				 collect_errors);
 	break;
       case Namespace::Labels:
-	resolved = labels.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors);
+	resolved = resolve_path (labels, path, mode, insert_segment_resolution,
+				 collect_errors);
 	break;
       default:
 	rust_unreachable ();
@@ -569,17 +569,17 @@ public:
 	switch (ns)
 	  {
 	  case Namespace::Values:
-	    return values.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors, *prelude);
+	    return resolve_path (values, path, mode, insert_segment_resolution,
+				 collect_errors, *prelude);
 	  case Namespace::Types:
-	    return types.resolve_path (path, mode, insert_segment_resolution,
-				       collect_errors, *prelude);
+	    return resolve_path (types, path, mode, insert_segment_resolution,
+				 collect_errors, *prelude);
 	  case Namespace::Macros:
-	    return macros.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors, *prelude);
+	    return resolve_path (macros, path, mode, insert_segment_resolution,
+				 collect_errors, *prelude);
 	  case Namespace::Labels:
-	    return labels.resolve_path (path, mode, insert_segment_resolution,
-					collect_errors, *prelude);
+	    return resolve_path (labels, path, mode, insert_segment_resolution,
+				 collect_errors, *prelude);
 	  default:
 	    rust_unreachable ();
 	  }
@@ -774,6 +774,34 @@ public:
 			 std::forward<Args> (args)...);
   }
 
+  /**
+   * Resolve a path to its definition in the current `ForeverStack`
+   *
+   * // TODO: Add documentation for `segments`
+   *
+   * @return a valid option with the Definition if the path is present in the
+   *         current map, an empty one otherwise.
+   */
+  template <Namespace N>
+  tl::optional<Rib::Definition> resolve_path (
+    ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+    std::function<void (Usage, Definition)> insert_segment_resolution,
+    std::vector<Error> &collect_errors);
+
+  template <Namespace N>
+  tl::optional<Rib::Definition> resolve_path (
+    ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+    std::function<void (Usage, Definition)> insert_segment_resolution,
+    std::vector<Error> &collect_errors, NodeId starting_point_id);
+
+  /* TODO: Make private? */
+  template <Namespace N>
+  tl::optional<Rib::Definition> resolve_path (
+    ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+    std::function<void (Usage, Definition)> insert_segment_resolution,
+    std::vector<Error> &collect_errors,
+    std::reference_wrapper<typename ForeverStack<N>::Node> starting_point);
+
   /* If declared with #[prelude_import], the current standard library module */
   tl::optional<NodeId> prelude;
 
@@ -785,4 +813,6 @@ private:
 } // namespace Resolver2_0
 } // namespace Rust
 
+#include "rust-name-resolution-context.hxx"
+
 #endif // ! RUST_NAME_RESOLVER_2_0_CTX_H
diff --git a/gcc/rust/resolve/rust-name-resolution-context.hxx b/gcc/rust/resolve/rust-name-resolution-context.hxx
new file mode 100644
index 00000000000..ae89126153d
--- /dev/null
+++ b/gcc/rust/resolve/rust-name-resolution-context.hxx
@@ -0,0 +1,249 @@
+// Copyright (C) 2026 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/>.
+
+#include "rust-name-resolution-context.h"
+
+/**
+ * Split the actual path resolution logic in its own file because it's a lot,
+ * and it could get even worse for certain edge cases.
+ */
+
+namespace Rust {
+namespace Resolver2_0 {
+
+template <Namespace N>
+tl::optional<Rib::Definition>
+NameResolutionContext::resolve_path (
+  ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+  std::function<void (Usage, Definition)> insert_segment_resolution,
+  std::vector<Error> &collect_errors)
+{
+  std::reference_wrapper<typename ForeverStack<N>::Node> starting_point
+    = stack.cursor ();
+
+  return NameResolutionContext::resolve_path (stack, path, mode,
+					      insert_segment_resolution,
+					      collect_errors, starting_point);
+}
+
+template <Namespace N>
+tl::optional<Rib::Definition>
+NameResolutionContext::resolve_path (
+  ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+  std::function<void (Usage, Definition)> insert_segment_resolution,
+  std::vector<Error> &collect_errors, NodeId starting_point_id)
+
+{
+  auto starting_point = stack.dfs_node (stack.root, starting_point_id);
+
+  // We may have a prelude, but haven't visited it yet and thus it's not in
+  // our nodes
+  if (!starting_point)
+    return tl::nullopt;
+
+  return NameResolutionContext::resolve_path (stack, path, mode,
+					      insert_segment_resolution,
+					      collect_errors, *starting_point);
+}
+
+template <Namespace N>
+tl::optional<Rib::Definition>
+NameResolutionContext::resolve_path (
+  ForeverStack<N> &stack, const ResolutionPath &path, ResolutionMode mode,
+  std::function<void (Usage, Definition)> insert_segment_resolution,
+  std::vector<Error> &collect_errors,
+  std::reference_wrapper<typename ForeverStack<N>::Node> starting_point)
+{
+  bool can_descend = true;
+
+  rust_debug ("resolving %s", path.as_string ().c_str ());
+
+  if (auto lang_item = path.get_lang_prefix ())
+    {
+      NodeId seg_id
+	= Analysis::Mappings::get ().get_lang_item_node (lang_item->first);
+
+      insert_segment_resolution (Usage (lang_item->second),
+				 Definition (seg_id));
+
+      if (path.get_segments ().empty ())
+	return Rib::Definition::NonShadowable (seg_id);
+
+      auto new_start = stack.dfs_node (stack.root, seg_id);
+      rust_assert (new_start.has_value ());
+      starting_point = new_start.value ();
+
+      can_descend = false;
+    }
+  else
+    {
+      switch (mode)
+	{
+	case ResolutionMode::Normal:
+	  break; // default
+	case ResolutionMode::FromRoot:
+	  starting_point = stack.root;
+	  break;
+	case ResolutionMode::FromExtern:
+	  starting_point = stack.extern_prelude;
+	  break;
+	default:
+	  rust_unreachable ();
+	}
+    }
+
+  if (path.get_segments ().empty ())
+    return Rib::Definition::NonShadowable (starting_point.get ().id);
+
+  auto &segments = path.get_segments ();
+
+  // if there's only one segment, we just use `get`
+  if (can_descend && segments.size () == 1)
+    {
+      auto &seg = segments.front ();
+
+      tl::optional<Rib::Definition> res
+	= stack.get (starting_point.get (), seg.name);
+
+      if (!res)
+	res = stack.get_lang_prelude (seg.name);
+
+      if (N == Namespace::Types && !res)
+	{
+	  if (seg.is_crate_path_seg ())
+	    {
+	      insert_segment_resolution (Usage (seg.node_id),
+					 Definition (stack.root.id));
+	      // TODO: does NonShadowable matter?
+	      return Rib::Definition::NonShadowable (stack.root.id);
+	    }
+	  else if (seg.is_lower_self_seg ())
+	    {
+	      NodeId id = stack.find_closest_module (starting_point.get ()).id;
+	      insert_segment_resolution (Usage (seg.node_id), Definition (id));
+	      // TODO: does NonShadowable matter?
+	      return Rib::Definition::NonShadowable (id);
+	    }
+	  else if (seg.is_super_path_seg ())
+	    {
+	      auto &closest_module
+		= stack.find_closest_module (starting_point.get ());
+	      if (closest_module.is_root ())
+		{
+		  rust_error_at (seg.locus, ErrorCode::E0433,
+				 "too many leading %<super%> keywords");
+		  return tl::nullopt;
+		}
+
+	      NodeId id
+		= stack.find_closest_module (closest_module.parent.value ()).id;
+	      insert_segment_resolution (Usage (seg.node_id), Definition (id));
+	      // TODO: does NonShadowable matter?
+	      return Rib::Definition::NonShadowable (id);
+	    }
+	  else
+	    {
+	      // HACK: check for a module after we check the language prelude
+	      for (auto &kv :
+		   stack.find_closest_module (starting_point.get ()).children)
+		{
+		  auto &link = kv.first;
+
+		  if (link.path.map_or (
+			[&seg] (Identifier path) {
+			  auto &path_str = path.as_string ();
+			  return path_str == seg.name;
+			},
+			false))
+		    {
+		      insert_segment_resolution (Usage (seg.node_id),
+						 Definition (kv.second.id));
+		      return Rib::Definition::NonShadowable (kv.second.id);
+		    }
+		}
+	    }
+	}
+
+      if (res && !res->is_ambiguous ())
+	insert_segment_resolution (Usage (seg.node_id),
+				   Definition (res->get_node_id ()));
+      return res;
+    }
+
+  auto iterator = segments.begin ();
+  if (can_descend)
+    {
+      if (auto res = stack.find_starting_point (segments, starting_point,
+						insert_segment_resolution,
+						collect_errors))
+	iterator = *res;
+      else
+	return tl::nullopt;
+    }
+
+  return stack
+    .resolve_segments (starting_point.get (), segments, iterator,
+		       insert_segment_resolution, collect_errors)
+    .and_then ([&segments, &insert_segment_resolution,
+		&stack] (typename ForeverStack<N>::Node &final_node)
+		 -> tl::optional<Rib::Definition> {
+      // leave resolution within impl blocks to type checker
+      if (final_node.rib.kind == Rib::Kind::TraitOrImpl)
+	return tl::nullopt;
+
+      auto &seg = segments.back ();
+      std::string seg_name = seg.name;
+
+      tl::optional<Rib::Definition> res
+	= stack.resolve_final_segment (final_node, seg_name,
+				       seg.is_lower_self_seg ());
+      // Ok we didn't find it in the rib, Lets try the prelude...
+      if (!res)
+	res = stack.get_lang_prelude (seg_name);
+
+      if (N == Namespace::Types && !res)
+	{
+	  // HACK: check for a module after we check the language prelude
+	  for (auto &kv : final_node.children)
+	    {
+	      auto &link = kv.first;
+
+	      if (link.path.map_or (
+		    [&seg_name] (Identifier path) {
+		      auto &path_str = path.as_string ();
+		      return path_str == seg_name;
+		    },
+		    false))
+		{
+		  insert_segment_resolution (Usage (seg.node_id),
+					     Definition (kv.second.id));
+		  return Rib::Definition::NonShadowable (kv.second.id);
+		}
+	    }
+	}
+
+      if (res && !res->is_ambiguous ())
+	insert_segment_resolution (Usage (seg.node_id),
+				   Definition (res->get_node_id ()));
+
+      return res;
+    });
+}
+
+} // namespace Resolver2_0
+} // namespace Rust
-- 
2.50.1



More information about the Gcc-rust mailing list