From: Pierre-Emmanuel Patry <[email protected]>

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]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/8c62dc7209837a86c175d35cf6580bfcc82dd29e

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4588

 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 +-
 .../resolve/rust-name-resolution-context.cc   | 30 ++++++++++++
 .../resolve/rust-name-resolution-context.h    | 14 +++++-
 .../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(-)
 create mode 100644 gcc/rust/expand/rust-extern-crate-loader.cc
 create mode 100644 gcc/rust/expand/rust-extern-crate-loader.h

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index a56db9241..c1a9b9956 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 67baebec1..514ddbac4 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 000000000..ba048bcf0
--- /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 000000000..8eac30028
--- /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 3b724c9e6..10f506bb2 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 c652e37f6..07290feb4 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 e05c1c665..89c724a2f 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 3bda8178a..3041dccb5 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 60426122a..7fb963049 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 5eda91da9..64fd8e221 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 3d109112c..620a14f8e 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 ()) {}
-- 
2.54.0

Reply via email to