Skip to content

Commit 5a6de3f

Browse files
b-nabereholk
andcommitted
add compiler support for RFC 3243
Co-authored-by: Eric Holk <[email protected]>
1 parent 3c877f6 commit 5a6de3f

20 files changed

+586
-34
lines changed

compiler/rustc_resolve/src/build_reduced_graph.rs

+123-9
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use rustc_middle::metadata::ModChild;
2323
use rustc_middle::ty::Feed;
2424
use rustc_middle::{bug, ty};
2525
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
26-
use rustc_span::{Ident, Span, Symbol, kw, sym};
27-
use tracing::debug;
26+
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
27+
use tracing::{debug, instrument};
2828

2929
use crate::Namespace::{MacroNS, TypeNS, ValueNS};
3030
use crate::def_collector::collect_definitions;
@@ -70,6 +70,7 @@ impl<'ra, Id: Into<DefId>> ToNameBinding<'ra> for (Res, ty::Visibility<Id>, Span
7070
impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
7171
/// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
7272
/// otherwise, reports an error.
73+
#[instrument(level = "debug", skip(self, def))]
7374
pub(crate) fn define<T>(&mut self, parent: Module<'ra>, ident: Ident, ns: Namespace, def: T)
7475
where
7576
T: ToNameBinding<'ra>,
@@ -118,6 +119,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
118119
return module.copied();
119120
}
120121

122+
//if def_id.is_crate_root() && !def_id.is_local() {
123+
// bug!(
124+
// "expected module for external crate {:?} to be created via `create_module_for_external_crate`",
125+
// def_id
126+
// );
127+
//}
128+
121129
if !def_id.is_local() {
122130
// Query `def_kind` is not used because query system overhead is too expensive here.
123131
let def_kind = self.cstore().def_kind_untracked(def_id);
@@ -143,6 +151,52 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
143151
None
144152
}
145153

154+
/// Creates `Module` instance for an external crate.
155+
/// `Module`s are usually created via `get_module`, but namespaced crates depend
156+
/// on the crate name passed via `--extern`, whereas `get_module` uses the metadata's
157+
/// crate name. We need a way to pass that name to a `Module` and this method does that.
158+
#[instrument(level = "debug", skip(self))]
159+
pub(crate) fn create_module_for_external_crate(
160+
&mut self,
161+
def_id: DefId,
162+
ident: Ident,
163+
finalize: bool,
164+
) -> Module<'ra> {
165+
if let Some(module) = self.module_map.get(&def_id) {
166+
return *module;
167+
}
168+
169+
if finalize {
170+
self.crate_loader(|c| c.process_path_extern(ident.name, ident.span));
171+
}
172+
173+
let def_kind = self.cstore().def_kind_untracked(def_id);
174+
match def_kind {
175+
DefKind::Mod => {
176+
let parent = self
177+
.tcx
178+
.opt_parent(def_id)
179+
.map(|parent_id| self.get_nearest_non_block_module(parent_id));
180+
// Query `expn_that_defined` is not used because
181+
// hashing spans in its result is expensive.
182+
let expn_id = self.cstore().expn_that_defined_untracked(def_id, self.tcx.sess);
183+
let module = self.new_module(
184+
parent,
185+
ModuleKind::Def(def_kind, def_id, Some(ident.name)),
186+
expn_id,
187+
self.def_span(def_id),
188+
// FIXME: Account for `#[no_implicit_prelude]` attributes.
189+
parent.is_some_and(|module| module.no_implicit_prelude),
190+
);
191+
192+
return module;
193+
}
194+
_ => {
195+
bug!("expected DefKind::Mod for external crate");
196+
}
197+
}
198+
}
199+
146200
pub(crate) fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'ra> {
147201
match expn_id.expn_data().macro_def_id {
148202
Some(def_id) => self.macro_def_scope(def_id),
@@ -196,7 +250,33 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
196250
visitor.parent_scope.macro_rules
197251
}
198252

253+
// Builds the reduced graph for an external crate.
254+
// It builds the graph for each child module of the crate and it also checks whether
255+
// namespaced crates with the same base name exist. If any exist it builds a graph for
256+
// each of those crates.
257+
#[instrument(level = "debug", skip(self))]
199258
pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'ra>) {
259+
// Check if `module`'s name is the base crate name for a namespaced crate
260+
// (i.e. `my_api` for the namespaced crates `my_api::utils` and `my_api::core`),
261+
// and process the extern crate for the namespaced crates if they exist.
262+
if let Some(module_name) = module.kind.name() {
263+
let namespace_crate_name = self
264+
.namespaced_crate_names
265+
.get(module_name.as_str())
266+
.map(|names| names.iter().map(|s| s.to_string().clone()).collect::<Vec<String>>());
267+
268+
if let Some(namespaced_crate_names) = namespace_crate_name {
269+
debug!(?namespaced_crate_names);
270+
for namespaced_crate_name in namespaced_crate_names {
271+
let parent_scope = ParentScope::module(module, self);
272+
self.build_reduced_graph_for_namespaced_crate(
273+
&namespaced_crate_name,
274+
parent_scope,
275+
);
276+
}
277+
}
278+
}
279+
200280
for child in self.tcx.module_children(module.def_id()) {
201281
let parent_scope = ParentScope::module(module, self);
202282
self.build_reduced_graph_for_external_crate_res(child, parent_scope)
@@ -273,6 +353,43 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
273353
| Res::Err => bug!("unexpected resolution: {:?}", res),
274354
}
275355
}
356+
357+
// Builds the reduced graph for a namespaced external crate (crate names like `foo::bar`).
358+
#[instrument(level = "debug", skip(self))]
359+
fn build_reduced_graph_for_namespaced_crate(
360+
&mut self,
361+
name: &str,
362+
parent_scope: ParentScope<'ra>,
363+
) {
364+
let crate_id = self.crate_loader(|c| {
365+
c.maybe_process_path_extern(Symbol::intern(&name))
366+
.expect(&format!("no crate_num for namespaced crate {}", name))
367+
});
368+
369+
let module = self.create_module_for_external_crate(
370+
crate_id.as_def_id(),
371+
Ident::from_str(name),
372+
false,
373+
);
374+
let ident = Ident::from_str(
375+
name.split("::").nth(1).expect("namespaced crate name has unexpected form"),
376+
);
377+
let parent = parent_scope.module;
378+
let res = module.res().expect("namespaced crate has no Res").expect_non_local();
379+
let expansion = parent_scope.expansion;
380+
381+
match res {
382+
Res::Def(DefKind::Mod, _) => {
383+
self.define(
384+
parent,
385+
ident,
386+
TypeNS,
387+
(module, ty::Visibility::<DefId>::Public, DUMMY_SP, expansion),
388+
);
389+
}
390+
_ => bug!("expected namespaced crate to have Res Def(DefKind::Mod)"),
391+
}
392+
}
276393
}
277394

278395
struct BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
@@ -469,6 +586,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
469586
}
470587
}
471588

589+
#[instrument(level = "debug", skip(self))]
472590
fn build_reduced_graph_for_use_tree(
473591
&mut self,
474592
// This particular use tree
@@ -482,11 +600,6 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
482600
vis: ty::Visibility,
483601
root_span: Span,
484602
) {
485-
debug!(
486-
"build_reduced_graph_for_use_tree(parent_prefix={:?}, use_tree={:?}, nested={})",
487-
parent_prefix, use_tree, nested
488-
);
489-
490603
// Top level use tree reuses the item's id and list stems reuse their parent
491604
// use tree's ids, so in both cases their visibilities are already filled.
492605
if nested && !list_stem {
@@ -589,7 +702,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
589702
let crate_root = self.r.resolve_crate_root(source.ident);
590703
let crate_name = match crate_root.kind {
591704
ModuleKind::Def(.., name) => name,
592-
ModuleKind::Block => unreachable!(),
705+
ModuleKind::Block | ModuleKind::NamespaceCrate(..) => unreachable!(),
593706
};
594707
// HACK(eddyb) unclear how good this is, but keeping `$crate`
595708
// in `source` breaks `tests/ui/imports/import-crate-var.rs`,
@@ -897,6 +1010,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
8971010
}
8981011
}
8991012

1013+
#[instrument(level = "debug", skip(self))]
9001014
fn build_reduced_graph_for_extern_crate(
9011015
&mut self,
9021016
orig_name: Option<Symbol>,
@@ -922,7 +1036,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
9221036
});
9231037
crate_id.map(|crate_id| {
9241038
self.r.extern_crate_map.insert(local_def_id, crate_id);
925-
self.r.expect_module(crate_id.as_def_id())
1039+
self.r.create_module_for_external_crate(crate_id.as_def_id(), ident, false)
9261040
})
9271041
}
9281042
.map(|module| {

compiler/rustc_resolve/src/diagnostics.rs

+4
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
222222
// indirectly *calls* the resolver, and would cause a query cycle.
223223
ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()),
224224
ModuleKind::Block => "block",
225+
ModuleKind::NamespaceCrate(..) => {
226+
// `NamespaceCrate`s are virtual modules
227+
unreachable!();
228+
}
225229
};
226230

227231
let (name, span) =

compiler/rustc_resolve/src/ident.rs

+111-6
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
575575
None => Err(Determinacy::Determined),
576576
},
577577
Scope::ExternPrelude => {
578-
match this.extern_prelude_get(ident, finalize.is_some()) {
578+
match this.extern_prelude_get(
579+
ident,
580+
finalize.is_some(),
581+
Some(*parent_scope),
582+
) {
579583
Some(binding) => Ok((binding, Flags::empty())),
580584
None => Err(Determinacy::determined(
581585
this.graph_root.unexpanded_invocations.borrow().is_empty(),
@@ -796,8 +800,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
796800
)
797801
}
798802

799-
/// Attempts to resolve `ident` in namespaces `ns` of `module`.
800-
/// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
801803
#[instrument(level = "debug", skip(self))]
802804
fn resolve_ident_in_module_unadjusted(
803805
&mut self,
@@ -831,7 +833,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
831833
assert_eq!(shadowing, Shadowing::Unrestricted);
832834
return if ns != TypeNS {
833835
Err((Determined, Weak::No))
834-
} else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) {
836+
} else if let Some(binding) =
837+
self.extern_prelude_get(ident, finalize.is_some(), Some(*parent_scope))
838+
{
835839
Ok(binding)
836840
} else if !self.graph_root.unexpanded_invocations.borrow().is_empty() {
837841
// Macro-expanded `extern crate` items can add names to extern prelude.
@@ -866,9 +870,79 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
866870
}
867871
};
868872

873+
let result = self.try_resolve_ident_in_module_unadjusted(
874+
module,
875+
ident,
876+
ns,
877+
parent_scope,
878+
shadowing,
879+
finalize,
880+
ignore_binding,
881+
ignore_import,
882+
);
883+
884+
// If resolution failed for the module, check if we can resolve ident to a
885+
// namespaced crate. Suppose we have the crate `my_api` and the namespaced
886+
// crate `my_api::utils` as dependencies and `my_api` doesn't have a module
887+
// named `utils`. The `Ident` `utils` will be unresolvable for the `Module`
888+
// corresponding to `my_api`. If there's a crate named `my_api::utils` in the
889+
// extern prelude we resolve `my_api::utils` to that crate then. If `my_api`
890+
// does have a module named `utils` and a namespaced crate `my_api::utils`
891+
// exists, then we report a name conflict error (via `build_reduced_graph_external`).
892+
if let Err(err) = result {
893+
if let Some(module_name) = module.kind.name() {
894+
let Some(namespaced_crate_names) =
895+
self.namespaced_crate_names.get(module_name.as_str())
896+
else {
897+
return Err(err);
898+
};
899+
900+
debug!(?namespaced_crate_names);
901+
for namespaced_crate_name in namespaced_crate_names {
902+
if namespaced_crate_name
903+
.split("::")
904+
.nth(1)
905+
.expect("namespaced crate name has wrong form")
906+
== ident.as_str()
907+
{
908+
return self.resolve_namespaced_crate(
909+
namespaced_crate_name,
910+
finalize,
911+
module.parent.map(|m| ParentScope::module(m, self)),
912+
);
913+
}
914+
}
915+
916+
return Err(err);
917+
} else {
918+
return Err(err);
919+
}
920+
}
921+
922+
result
923+
}
924+
925+
/// Attempts to resolve `ident` in namespaces `ns` of `module`.
926+
/// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
927+
#[instrument(level = "debug", skip(self))]
928+
fn try_resolve_ident_in_module_unadjusted(
929+
&mut self,
930+
module: Module<'ra>,
931+
ident: Ident,
932+
ns: Namespace,
933+
parent_scope: &ParentScope<'ra>,
934+
shadowing: Shadowing,
935+
finalize: Option<Finalize>,
936+
// This binding should be ignored during in-module resolution, so that we don't get
937+
// "self-confirming" import resolutions during import validation and checking.
938+
ignore_binding: Option<NameBinding<'ra>>,
939+
ignore_import: Option<Import<'ra>>,
940+
) -> Result<NameBinding<'ra>, (Determinacy, Weak)> {
869941
let key = BindingKey::new(ident, ns);
870-
let resolution =
871-
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
942+
let resolution = self.resolution(module, key).try_borrow_mut().map_err(|_| {
943+
debug!("got resolution error for module {:?}", module);
944+
(Determined, Weak::No)
945+
})?; // This happens when there is a cycle of imports.
872946

873947
// If the primary binding is unusable, search further and return the shadowed glob
874948
// binding if it exists. What we really want here is having two separate scopes in
@@ -1034,6 +1108,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10341108
}
10351109
}
10361110

1111+
debug!("no resolution");
10371112
// --- From now on we have no resolution. ---
10381113

10391114
// Now we are in situation when new item/import can appear only from a glob or a macro
@@ -1100,6 +1175,34 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
11001175
Err((Determined, Weak::No))
11011176
}
11021177

1178+
// Resolves a namespaced crate.
1179+
// In general namespaced crates, i.e. those with names of the form `foo::bar`,
1180+
// are resolved in the following way. When intializing the `Resolver`, we
1181+
// collect information on them in the field `namespaced_crate_names`,
1182+
// which maps the base crate name to a list of namespaced crates with that
1183+
// base. Suppose we have a base crate named `my_api` and two namespaced
1184+
// crates named `my_api::utils` and `my_api::core`. When building the reduced
1185+
// graph for the base crate `my_api` (in `build_reduced_graph_for_external`),
1186+
// we check whether any namespaced crates exist for it. If any do, we use
1187+
// `build_reduced_graph_for_namespaced_crate` to build a graph for them.
1188+
// In the case in which the base crate isn't a dependency and we try to resolve
1189+
// `my_api` in the path `my_api::utils`, then we resolve the base name to a virtual
1190+
// module `ModuleKind::NamespaceCrate`. `resolve_ident_in_module`
1191+
#[instrument(skip(self, finalize))]
1192+
fn resolve_namespaced_crate(
1193+
&mut self,
1194+
namespaced_crate_name: &str,
1195+
finalize: Option<Finalize>,
1196+
parent_scope: Option<ParentScope<'ra>>,
1197+
) -> Result<NameBinding<'ra>, (Determinacy, Weak)> {
1198+
self.extern_prelude_get(
1199+
Ident::from_str(namespaced_crate_name),
1200+
finalize.is_some(),
1201+
parent_scope,
1202+
)
1203+
.ok_or((Determinacy::Determined, Weak::No))
1204+
}
1205+
11031206
/// Validate a local resolution (from ribs).
11041207
#[instrument(level = "debug", skip(self, all_ribs))]
11051208
fn validate_res_from_ribs(
@@ -1550,6 +1653,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15501653
);
15511654
}
15521655

1656+
debug!(?module);
15531657
let binding = if let Some(module) = module {
15541658
self.resolve_ident_in_module(
15551659
module,
@@ -1597,6 +1701,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15971701
)
15981702
};
15991703

1704+
debug!("resolve_path_with_ribs binding: {:?}", binding);
16001705
match binding {
16011706
Ok(binding) => {
16021707
if segment_idx == 1 {

0 commit comments

Comments
 (0)