https://gcc.gnu.org/g:c0e2fc3c0af6b01486608ab8a74cc3feee7f6e82

commit r17-3062-gc0e2fc3c0af6b01486608ab8a74cc3feee7f6e82
Author: Pierre-Emmanuel Patry <[email protected]>
Date:   Wed Jun 17 22:16:21 2026 +0200

    gccrs: Name resolve extern crate independently
    
    Extern crates are exported using their own dump. When resolved, we may
    encounter `crate::` path, which point to an item from the extern crate's
    root instead of the current crate's root. This means we need the crate
    context within the name resolution. We chose to resolve crates
    independently because extern crates do not require late name resolution
    and keeping them isolated keeps things easy.
    
    gcc/rust/ChangeLog:
    
            * Make-lang.in: Add a new file with a visitor to collect and load
            extern crates.
            * expand/rust-cfg-strip.cc (CfgStrip::visit): Remove extern crate
            loading from cfgstrip step.
            * resolve/rust-default-resolver.cc 
(DefaultResolver::visit_extern_crate):
            Do not visit the extern crate. We do not need to resolve it since 
it is
            already done.
            * resolve/rust-forever-stack.hxx: Do not assign unused result.
            * resolve/rust-name-resolution-context.cc 
(NameResolutionContext::merge):
            Add a function to merge an extern crate's nr context at some 
location
            within another name resolution context.
            * resolve/rust-name-resolution-context.h: Add function prototype.
            * resolve/rust-toplevel-name-resolver-2.0.cc 
(TopLevel::visit_extern_crate):
            Do not visit extern crate.
            * rust-session-manager.cc (Session::expansion): Collect extern 
crates,
            load them and merge them into the main crate.
            (Session::dump_hir_pretty): Change function return type.
            (Session::load_extern_crate): Change return type to collect extern
            crates name resolution context.
            * rust-session-manager.h: Update function prototype.
            * expand/rust-extern-crate-loader.cc: New file.
            * expand/rust-extern-crate-loader.h: New file.
    
    Signed-off-by: Pierre-Emmanuel Patry <[email protected]>

Diff:
---
 gcc/rust/Make-lang.in                              |  1 +
 gcc/rust/expand/rust-cfg-strip.cc                  |  7 ----
 gcc/rust/expand/rust-extern-crate-loader.cc        | 47 ++++++++++++++++++++++
 gcc/rust/expand/rust-extern-crate-loader.h         | 42 +++++++++++++++++++
 gcc/rust/resolve/rust-default-resolver.cc          |  3 +-
 gcc/rust/resolve/rust-forever-stack.hxx            |  2 +-
 gcc/rust/resolve/rust-name-resolution-context.cc   | 30 ++++++++++++++
 gcc/rust/resolve/rust-name-resolution-context.h    | 14 ++++++-
 .../resolve/rust-toplevel-name-resolver-2.0.cc     |  3 +-
 gcc/rust/rust-session-manager.cc                   | 25 +++++++++---
 gcc/rust/rust-session-manager.h                    | 41 ++++++++++++++++++-
 11 files changed, 196 insertions(+), 19 deletions(-)

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index a56db9241b38..c1a9b9956a4b 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -87,6 +87,7 @@ GRS_OBJS = \
     rust/rust-hir-visitor.o \
     rust/rust-hir-dump.o \
     rust/rust-session-manager.o \
+    rust/rust-extern-crate-loader.o \
     rust/rust-compile.o \
     rust/rust-mangle.o \
     rust/rust-mangle-v0.o \
diff --git a/gcc/rust/expand/rust-cfg-strip.cc 
b/gcc/rust/expand/rust-cfg-strip.cc
index 67baebec158d..514ddbac4674 100644
--- a/gcc/rust/expand/rust-cfg-strip.cc
+++ b/gcc/rust/expand/rust-cfg-strip.cc
@@ -1793,13 +1793,6 @@ CfgStrip::visit (AST::ExternCrate &extern_crate)
       extern_crate.mark_for_strip ();
       return;
     }
-
-  if (!extern_crate.references_self ())
-    {
-      Session &session = Session::get_instance ();
-      session.load_extern_crate (extern_crate.get_referenced_crate (),
-                                extern_crate.get_locus ());
-    }
 }
 
 void
diff --git a/gcc/rust/expand/rust-extern-crate-loader.cc 
b/gcc/rust/expand/rust-extern-crate-loader.cc
new file mode 100644
index 000000000000..ba048bcf0ce3
--- /dev/null
+++ b/gcc/rust/expand/rust-extern-crate-loader.cc
@@ -0,0 +1,47 @@
+// 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-extern-crate-loader.h"
+#include "rust-session-manager.h"
+
+namespace Rust {
+ExternCrateLoaderVisitor::ExternCrateLoaderVisitor (
+  std::vector<Session::LoadedCrate> &loaded_crate)
+  : loaded_crates (loaded_crate)
+{}
+
+void
+ExternCrateLoaderVisitor::go (AST::Crate &crate)
+{
+  visit (crate);
+}
+
+void
+ExternCrateLoaderVisitor::visit (AST::ExternCrate &extern_crate)
+{
+  if (extern_crate.references_self ())
+    return;
+
+  Session &session = Session::get_instance ();
+  auto crate = session.load_extern_crate (extern_crate.get_referenced_crate (),
+                                         extern_crate.get_locus ());
+  if (crate)
+    loaded_crates.push_back (std::move (crate.value ()));
+}
+
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-extern-crate-loader.h 
b/gcc/rust/expand/rust-extern-crate-loader.h
new file mode 100644
index 000000000000..8eac300284c6
--- /dev/null
+++ b/gcc/rust/expand/rust-extern-crate-loader.h
@@ -0,0 +1,42 @@
+// 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/>.
+
+#ifndef RUST_EXTERN_CRATE_LOADER_H
+#define RUST_EXTERN_CRATE_LOADER_H
+
+#include "rust-ast-visitor.h"
+#include "rust-session-manager.h"
+
+namespace Rust {
+
+class ExternCrateLoaderVisitor : public AST::DefaultASTVisitor
+{
+  std::vector<Session::LoadedCrate> &loaded_crates;
+
+public:
+  using DefaultASTVisitor::visit;
+  void go (AST::Crate &crate);
+
+  void visit (AST::ExternCrate &extern_crate) override;
+
+  ExternCrateLoaderVisitor (std::vector<Session::LoadedCrate> &loaded_crates);
+};
+
+} // namespace Rust
+
+#endif /* ! RUST_EXTERN_CRATE_LOADER */
diff --git a/gcc/rust/resolve/rust-default-resolver.cc 
b/gcc/rust/resolve/rust-default-resolver.cc
index 3b724c9e6e74..10f506bb2364 100644
--- a/gcc/rust/resolve/rust-default-resolver.cc
+++ b/gcc/rust/resolve/rust-default-resolver.cc
@@ -442,7 +442,8 @@ void
 DefaultResolver::visit_extern_crate (AST::ExternCrate &extern_crate,
                                     AST::Crate &crate, CrateNum num)
 {
-  visit (crate);
+  // We do *NOT* visit the crate because loaded crates are resolved
+  // independently.
 }
 
 void
diff --git a/gcc/rust/resolve/rust-forever-stack.hxx 
b/gcc/rust/resolve/rust-forever-stack.hxx
index c652e37f684b..07290feb4892 100644
--- a/gcc/rust/resolve/rust-forever-stack.hxx
+++ b/gcc/rust/resolve/rust-forever-stack.hxx
@@ -53,7 +53,7 @@ template <Namespace N>
 void
 ForeverStack<N>::Node::insert_child (Link link, Node child)
 {
-  auto res = children.insert ({link, child});
+  children.insert ({link, child});
 
   // Do we want to error if the child already exists? Probably not, right?
   // That's kinda the point, isn't it. So this method always succeeds, right?
diff --git a/gcc/rust/resolve/rust-name-resolution-context.cc 
b/gcc/rust/resolve/rust-name-resolution-context.cc
index e05c1c665d4f..89c724a2f414 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.cc
+++ b/gcc/rust/resolve/rust-name-resolution-context.cc
@@ -384,6 +384,36 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, 
Namespace ns,
     }
 }
 
+void
+NameResolutionContext::merge (NameResolutionContext &other, NodeId at)
+{
+  auto merge_fstack = [&] (auto &stack, auto &other_stack) {
+    auto node = stack.dfs_node (stack.root, at);
+    if (node)
+      {
+       auto &extern_crate_node = node.value ();
+       for (auto kv : other_stack.root.children)
+         {
+           auto link = kv.first;
+           auto child = kv.second;
+           extern_crate_node.insert_child (link, child);
+           child.parent = extern_crate_node;
+         }
+       for (auto kv : other_stack.root.rib.get_values ())
+         {
+           auto name = kv.first;
+           auto def = kv.second;
+           extern_crate_node.rib.insert (name, def);
+         }
+      }
+  };
+
+  merge_fstack (values, other.values);
+  merge_fstack (types, other.types);
+  merge_fstack (macros, other.macros);
+  merge_fstack (labels, other.labels);
+}
+
 #if 0
 void
 NameResolutionContext::flatten ()
diff --git a/gcc/rust/resolve/rust-name-resolution-context.h 
b/gcc/rust/resolve/rust-name-resolution-context.h
index 3bda8178a835..3041dccb5f94 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.h
+++ b/gcc/rust/resolve/rust-name-resolution-context.h
@@ -844,8 +844,18 @@ public:
                         std::forward<Args> (args)...);
   }
 
-  // We disable this function for now as it causes regressions, but I think it
-  // is important for a more proper final nameres context - need to investigate
+  /*
+   * Merge a name resolution context within another one at a given location.
+   *
+   * @param other The other name resolution context to merge within the current
+   * one.
+   * @param at The node id of the container were the nr context should be
+   * merged. Usually an extern crate node.
+   */
+  void merge (NameResolutionContext &other, NodeId at);
+
+// We disable this function for now as it causes regressions, but I think it
+// is important for a more proper final nameres context - need to investigate
 #if 0
   /**
    * We've now collected every definition and import, and errored out when
diff --git a/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc 
b/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc
index 60426122a24f..7fb9630490ac 100644
--- a/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc
+++ b/gcc/rust/resolve/rust-toplevel-name-resolver-2.0.cc
@@ -192,7 +192,8 @@ TopLevel::visit_extern_crate (AST::ExternCrate 
&extern_crate, AST::Crate &crate,
        mappings.insert_bang_proc_macro_def (macro);
     }
 
-  visit (crate);
+  // We do *NOT* visit the crate because loaded crates are resolved
+  // independently.
 }
 
 static bool
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
index 5eda91da9f2a..64fd8e22139c 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -62,6 +62,7 @@
 #include "rust-tyty-variance-analysis.h"
 #include "rust-attribute-checker.h"
 #include "rust-builtin-attribute-checker.h"
+#include "rust-extern-crate-loader.h"
 
 #include "input.h"
 #include "selftest.h"
@@ -1070,11 +1071,16 @@ Session::expansion (AST::Crate &crate, 
Resolver2_0::NameResolutionContext &ctx)
 
   while (!fixed_point_reached && iterations < cfg.recursion_limit)
     {
+      std::vector<Session::LoadedCrate> loaded_crates;
       CfgStrip (cfg).go (crate);
       // Errors might happen during cfg strip pass
 
+      ExternCrateLoaderVisitor (loaded_crates).go (crate);
+
       Resolver2_0::Early early (ctx);
       early.go (crate);
+      for (auto &loaded_crate : loaded_crates)
+       ctx.merge (loaded_crate.ctx, loaded_crate.node_id);
       macro_errors = early.get_macro_resolve_errors ();
 
       if (saw_errors ())
@@ -1234,7 +1240,7 @@ Session::dump_hir_pretty (HIR::Crate &crate) const
 
 // imports
 
-NodeId
+tl::expected<Session::LoadedCrate, Session::AlreadyLoadedError>
 Session::load_extern_crate (const std::string &crate_name, location_t locus)
 {
   // has it already been loaded?
@@ -1243,7 +1249,8 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
       auto resolved_node_id = mappings.crate_num_to_nodeid (*crate_num);
       rust_assert (resolved_node_id);
 
-      return *resolved_node_id;
+      return tl::make_unexpected (
+       AlreadyLoadedError::make_already_loaded (*resolved_node_id));
     }
 
   std::string relative_import_path = "";
@@ -1273,7 +1280,7 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
       && proc_macros.empty ()) // no proc macros
     {
       rust_error_at (locus, "failed to locate crate %qs", import_name.c_str 
());
-      return UNKNOWN_NODEID;
+      return tl::make_unexpected (AlreadyLoadedError::make_failed_to_locate 
());
     }
 
   auto extern_crate
@@ -1287,7 +1294,8 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
       if (!ok)
        {
          rust_error_at (locus, "failed to load crate metadata");
-         return UNKNOWN_NODEID;
+         return tl::make_unexpected (
+           AlreadyLoadedError::make_failed_to_locate ());
        }
     }
 
@@ -1297,7 +1305,7 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
     {
       rust_error_at (locus, "current crate name %qs collides with this",
                     current_crate_name.c_str ());
-      return UNKNOWN_NODEID;
+      return tl::make_unexpected (AlreadyLoadedError::make_collision ());
     }
 
   // setup mappings
@@ -1314,6 +1322,11 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
   AST::Crate &parsed_crate
     = mappings.insert_ast_crate (std::move (metadata_crate), crate_num);
 
+  auto ctx = Resolver2_0::NameResolutionContext ();
+
+  Resolver2_0::Early early (ctx);
+  early.go (parsed_crate);
+
   std::vector<AttributeProcMacro> attribute_macros;
   std::vector<CustomDeriveProcMacro> derive_macros;
   std::vector<BangProcMacro> bang_macros;
@@ -1343,7 +1356,7 @@ Session::load_extern_crate (const std::string 
&crate_name, location_t locus)
   // always restore the crate_num
   mappings.set_current_crate (saved_crate_num);
 
-  return parsed_crate.get_node_id ();
+  return LoadedCrate{crate_name, parsed_crate.get_node_id (), std::move (ctx)};
 }
 //
 
diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h
index 3d109112c476..620a14f8ec38 100644
--- a/gcc/rust/rust-session-manager.h
+++ b/gcc/rust/rust-session-manager.h
@@ -433,7 +433,46 @@ public:
     return extra_files.back ().c_str ();
   }
 
-  NodeId load_extern_crate (const std::string &crate_name, location_t locus);
+  struct LoadedCrate
+  {
+    LoadedCrate (const LoadedCrate &) = delete;
+    LoadedCrate &operator= (const LoadedCrate &) = delete;
+    LoadedCrate (LoadedCrate &&other) = default;
+
+    std::string name;
+    NodeId node_id;
+    Resolver2_0::NameResolutionContext ctx;
+  };
+
+  struct AlreadyLoadedError
+  {
+  public:
+    enum class Kind
+    {
+      ALREADY_LOADED,
+      FAILED_TO_LOCATE,
+      COLLISION,
+    } kind;
+
+    static AlreadyLoadedError make_already_loaded (NodeId node_id)
+    {
+      return AlreadyLoadedError{Kind::ALREADY_LOADED, node_id};
+    }
+
+    static AlreadyLoadedError make_failed_to_locate ()
+    {
+      return AlreadyLoadedError{Kind::FAILED_TO_LOCATE, UNKNOWN_NODEID};
+    }
+
+    static AlreadyLoadedError make_collision ()
+    {
+      return AlreadyLoadedError{Kind::COLLISION, UNKNOWN_NODEID};
+    }
+    NodeId node_id;
+  };
+
+  tl::expected<LoadedCrate, AlreadyLoadedError>
+  load_extern_crate (const std::string &crate_name, location_t locus);
 
 private:
   Session () : mappings (Analysis::Mappings::get ()) {}

Reply via email to