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]>
---
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 a56db9241b3..c1a9b9956a4 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 67baebec158..514ddbac467 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 00000000000..ba048bcf0ce
--- /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 00000000000..8eac300284c
--- /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 3b724c9e6e7..10f506bb236 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 c652e37f684..07290feb489 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 e05c1c665d4..89c724a2f41 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 3bda8178a83..3041dccb5f9 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 60426122a24..7fb9630490a 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 5eda91da9f2..64fd8e22139 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 3d109112c47..620a14f8ec3 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.50.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.