Skip to content

Commit e844c24

Browse files
committed
resolve namespaced crates with open modules
1 parent d2218f5 commit e844c24

32 files changed

Lines changed: 481 additions & 54 deletions

compiler/rustc_hir/src/def.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,13 @@ pub enum Res<Id = hir::HirId> {
590590
/// **Belongs to the type namespace.**
591591
ToolMod,
592592

593+
/// The resolution for an open module in a namespaced crate. E.g. `my_api`
594+
/// in the namespaced crate `my_api::utils` when `my_api` isn't part of the
595+
/// extern prelude.
596+
///
597+
/// **Belongs to the type namespace.**
598+
OpenMod(Symbol),
599+
593600
// Macro namespace
594601
/// An attribute that is *not* implemented via macro.
595602
/// E.g., `#[inline]` and `#[rustfmt::skip]`, which are essentially directives,
@@ -838,6 +845,7 @@ impl<Id> Res<Id> {
838845
| Res::SelfTyAlias { .. }
839846
| Res::SelfCtor(..)
840847
| Res::ToolMod
848+
| Res::OpenMod(..)
841849
| Res::NonMacroAttr(..)
842850
| Res::Err => None,
843851
}
@@ -869,6 +877,7 @@ impl<Id> Res<Id> {
869877
Res::Local(..) => "local variable",
870878
Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => "self type",
871879
Res::ToolMod => "tool module",
880+
Res::OpenMod(..) => "namespaced crate",
872881
Res::NonMacroAttr(attr_kind) => attr_kind.descr(),
873882
Res::Err => "unresolved item",
874883
}
@@ -895,6 +904,7 @@ impl<Id> Res<Id> {
895904
Res::SelfTyAlias { alias_to, is_trait_impl }
896905
}
897906
Res::ToolMod => Res::ToolMod,
907+
Res::OpenMod(sym) => Res::OpenMod(sym),
898908
Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
899909
Res::Err => Res::Err,
900910
}
@@ -911,6 +921,7 @@ impl<Id> Res<Id> {
911921
Res::SelfTyAlias { alias_to, is_trait_impl }
912922
}
913923
Res::ToolMod => Res::ToolMod,
924+
Res::OpenMod(sym) => Res::OpenMod(sym),
914925
Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
915926
Res::Err => Res::Err,
916927
})
@@ -936,9 +947,11 @@ impl<Id> Res<Id> {
936947
pub fn ns(&self) -> Option<Namespace> {
937948
match self {
938949
Res::Def(kind, ..) => kind.ns(),
939-
Res::PrimTy(..) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::ToolMod => {
940-
Some(Namespace::TypeNS)
941-
}
950+
Res::PrimTy(..)
951+
| Res::SelfTyParam { .. }
952+
| Res::SelfTyAlias { .. }
953+
| Res::ToolMod
954+
| Res::OpenMod(..) => Some(Namespace::TypeNS),
942955
Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
943956
Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
944957
Res::Err => None,

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,6 +2786,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
27862786
| Res::SelfCtor(_)
27872787
| Res::Local(_)
27882788
| Res::ToolMod
2789+
| Res::OpenMod(..)
27892790
| Res::NonMacroAttr(_)
27902791
| Res::Err) => Const::new_error_with_message(
27912792
tcx,

compiler/rustc_passes/src/dead.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
157157
Res::Def(_, def_id) => self.check_def_id(def_id),
158158
Res::SelfTyParam { trait_: t } => self.check_def_id(t),
159159
Res::SelfTyAlias { alias_to: i, .. } => self.check_def_id(i),
160-
Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
160+
Res::ToolMod | Res::NonMacroAttr(..) | Res::OpenMod(..) | Res::Err => {}
161161
}
162162
}
163163

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
357357
| Res::SelfTyParam { .. }
358358
| Res::SelfTyAlias { .. }
359359
| Res::SelfCtor(..)
360+
| Res::OpenMod(..)
360361
| Res::Err => bug!("unexpected resolution: {:?}", res),
361362
}
362363
}

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore-tidy-filelength
12
use std::ops::ControlFlow;
23

34
use itertools::Itertools as _;
@@ -1735,8 +1736,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
17351736
Res::Def(DefKind::Macro(kinds), _) => {
17361737
format!("{} {}", kinds.article(), kinds.descr())
17371738
}
1738-
Res::ToolMod => {
1739-
// Don't confuse the user with tool modules.
1739+
Res::ToolMod | Res::OpenMod(..) => {
1740+
// Don't confuse the user with tool modules or open modules.
17401741
continue;
17411742
}
17421743
Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
@@ -1973,7 +1974,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19731974
let (built_in, from) = match scope {
19741975
Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"),
19751976
Scope::ExternPreludeFlags
1976-
if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() =>
1977+
if self.tcx.sess.opts.externs.get(ident.as_str()).is_some()
1978+
|| matches!(res, Res::OpenMod(..)) =>
19771979
{
19781980
("", " passed with `--extern`")
19791981
}

compiler/rustc_resolve/src/ident.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::{
2626
AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingKey, CmResolver, Decl, DeclKind,
2727
Determinacy, Finalize, IdentKey, ImportKind, LateDecl, Module, ModuleKind, ModuleOrUniformRoot,
2828
ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet,
29-
Segment, Stage, Used, errors,
29+
Segment, Stage, Symbol, Used, errors,
3030
};
3131

3232
#[derive(Copy, Clone)]
@@ -386,7 +386,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
386386
}
387387

388388
/// Resolve an identifier in the specified set of scopes.
389-
#[instrument(level = "debug", skip(self))]
390389
pub(crate) fn resolve_ident_in_scope_set<'r>(
391390
self: CmResolver<'r, 'ra, 'tcx>,
392391
orig_ident: Ident,
@@ -976,6 +975,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
976975
ignore_import,
977976
)
978977
}
978+
ModuleOrUniformRoot::OpenModule(sym) => {
979+
let open_ns_name = format!("{}::{}", sym.as_str(), ident.name);
980+
let ns_ident = IdentKey::with_root_ctxt(Symbol::intern(&open_ns_name));
981+
match self.extern_prelude_get_flag(ns_ident, ident.span, finalize.is_some()) {
982+
Some(decl) => Ok(decl),
983+
None => Err(Determinacy::Determined),
984+
}
985+
}
979986
ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set(
980987
ident,
981988
ScopeSet::ModuleAndExternPrelude(ns, module),
@@ -1962,7 +1969,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19621969
}
19631970

19641971
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
1965-
if let Some(def_id) = binding.res().module_like_def_id() {
1972+
if let Res::OpenMod(sym) = binding.res() {
1973+
module = Some(ModuleOrUniformRoot::OpenModule(sym));
1974+
record_segment_res(self.reborrow(), finalize, res, id);
1975+
} else if let Some(def_id) = binding.res().module_like_def_id() {
19661976
if self.mods_with_parse_errors.contains(&def_id) {
19671977
module_had_parse_errors = true;
19681978
}

compiler/rustc_resolve/src/imports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type Res = def::Res<NodeId>;
4141

4242
/// A potential import declaration in the process of being planted into a module.
4343
/// Also used for lazily planting names from `--extern` flags to extern prelude.
44-
#[derive(Clone, Copy, Default, PartialEq)]
44+
#[derive(Clone, Copy, Default, PartialEq, Debug)]
4545
pub(crate) enum PendingDecl<'ra> {
4646
Ready(Option<Decl<'ra>>),
4747
#[default]

compiler/rustc_resolve/src/lib.rs

Lines changed: 101 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use rustc_hir::definitions::DisambiguatorState;
6464
use rustc_hir::{PrimTy, TraitCandidate, find_attr};
6565
use rustc_index::bit_set::DenseBitSet;
6666
use rustc_metadata::creader::CStore;
67+
use rustc_middle::bug;
6768
use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport};
6869
use rustc_middle::middle::privacy::EffectiveVisibilities;
6970
use rustc_middle::query::Providers;
@@ -448,6 +449,11 @@ enum ModuleOrUniformRoot<'ra> {
448449
/// Used only for resolving single-segment imports. The reason it exists is that import paths
449450
/// are always split into two parts, the first of which should be some kind of module.
450451
CurrentScope,
452+
453+
/// Virtual module for the resolution of base names of namespaced crates,
454+
/// where the base name doesn't correspond to a module in the extern prelude.
455+
/// E.g. `my_api::utils` is in the prelude, but `my_api` is not.
456+
OpenModule(Symbol),
451457
}
452458

453459
#[derive(Debug)]
@@ -1108,13 +1114,20 @@ impl<'ra> DeclData<'ra> {
11081114
}
11091115
}
11101116

1117+
#[derive(Debug)]
11111118
struct ExternPreludeEntry<'ra> {
11121119
/// Name declaration from an `extern crate` item.
11131120
/// The boolean flag is true is `item_decl` is non-redundant, happens either when
11141121
/// `flag_decl` is `None`, or when `extern crate` introducing `item_decl` used renaming.
11151122
item_decl: Option<(Decl<'ra>, Span, /* introduced by item */ bool)>,
11161123
/// Name declaration from an `--extern` flag, lazily populated on first use.
1117-
flag_decl: Option<CacheCell<(PendingDecl<'ra>, /* finalized */ bool)>>,
1124+
flag_decl: Option<
1125+
CacheCell<(
1126+
PendingDecl<'ra>,
1127+
/* finalized */ bool,
1128+
/* open flag (namespaced crate) */ bool,
1129+
)>,
1130+
>,
11181131
}
11191132

11201133
impl ExternPreludeEntry<'_> {
@@ -1125,7 +1138,14 @@ impl ExternPreludeEntry<'_> {
11251138
fn flag() -> Self {
11261139
ExternPreludeEntry {
11271140
item_decl: None,
1128-
flag_decl: Some(CacheCell::new((PendingDecl::Pending, false))),
1141+
flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, false))),
1142+
}
1143+
}
1144+
1145+
fn open_flag() -> Self {
1146+
ExternPreludeEntry {
1147+
item_decl: None,
1148+
flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, true))),
11291149
}
11301150
}
11311151

@@ -1637,35 +1657,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
16371657
let mut invocation_parents = FxHashMap::default();
16381658
invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT);
16391659

1640-
let mut extern_prelude: FxIndexMap<_, _> = tcx
1641-
.sess
1642-
.opts
1643-
.externs
1644-
.iter()
1645-
.filter_map(|(name, entry)| {
1646-
// Make sure `self`, `super`, `_` etc do not get into extern prelude.
1647-
// FIXME: reject `--extern self` and similar in option parsing instead.
1648-
if entry.add_prelude
1649-
&& let name = Symbol::intern(name)
1650-
&& name.can_be_raw()
1651-
{
1652-
let ident = IdentKey::with_root_ctxt(name);
1653-
Some((ident, ExternPreludeEntry::flag()))
1654-
} else {
1655-
None
1656-
}
1657-
})
1658-
.collect();
1659-
1660-
if !attr::contains_name(attrs, sym::no_core) {
1661-
let ident = IdentKey::with_root_ctxt(sym::core);
1662-
extern_prelude.insert(ident, ExternPreludeEntry::flag());
1663-
if !attr::contains_name(attrs, sym::no_std) {
1664-
let ident = IdentKey::with_root_ctxt(sym::std);
1665-
extern_prelude.insert(ident, ExternPreludeEntry::flag());
1666-
}
1667-
}
1668-
1660+
let extern_prelude = build_extern_prelude(tcx, attrs);
16691661
let registered_tools = tcx.registered_tools(());
16701662
let edition = tcx.sess.edition();
16711663

@@ -2320,10 +2312,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
23202312
) -> Option<Decl<'ra>> {
23212313
let entry = self.extern_prelude.get(&ident);
23222314
entry.and_then(|entry| entry.flag_decl.as_ref()).and_then(|flag_decl| {
2323-
let (pending_decl, finalized) = flag_decl.get();
2315+
let (pending_decl, finalized, is_open) = flag_decl.get();
23242316
let decl = match pending_decl {
23252317
PendingDecl::Ready(decl) => {
2326-
if finalize && !finalized {
2318+
if finalize && !finalized && !is_open {
23272319
self.cstore_mut().process_path_extern(
23282320
self.tcx,
23292321
ident.name,
@@ -2334,18 +2326,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
23342326
}
23352327
PendingDecl::Pending => {
23362328
debug_assert!(!finalized);
2337-
let crate_id = if finalize {
2338-
self.cstore_mut().process_path_extern(self.tcx, ident.name, orig_ident_span)
2329+
if is_open {
2330+
let res = Res::OpenMod(ident.name);
2331+
Some(self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT))
23392332
} else {
2340-
self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
2341-
};
2342-
crate_id.map(|crate_id| {
2343-
let res = Res::Def(DefKind::Mod, crate_id.as_def_id());
2344-
self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT)
2345-
})
2333+
let crate_id = if finalize {
2334+
self.cstore_mut().process_path_extern(
2335+
self.tcx,
2336+
ident.name,
2337+
orig_ident_span,
2338+
)
2339+
} else {
2340+
self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)
2341+
};
2342+
crate_id.map(|crate_id| {
2343+
let def_id = crate_id.as_def_id();
2344+
let res = Res::Def(DefKind::Mod, def_id);
2345+
self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT)
2346+
})
2347+
}
23462348
}
23472349
};
2348-
flag_decl.set((PendingDecl::Ready(decl), finalize || finalized));
2350+
flag_decl.set((PendingDecl::Ready(decl), finalize || finalized, is_open));
23492351
decl.or_else(|| finalize.then_some(self.dummy_decl))
23502352
})
23512353
}
@@ -2387,7 +2389,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
23872389
PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => {
23882390
None
23892391
}
2390-
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
2392+
path_result @ (PathResult::Module(..) | PathResult::Indeterminate) => {
2393+
bug!("got invalid path_result: {path_result:?}")
2394+
}
23912395
}
23922396
}
23932397

@@ -2505,6 +2509,60 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
25052509
}
25062510
}
25072511

2512+
fn build_extern_prelude<'tcx, 'ra>(
2513+
tcx: TyCtxt<'tcx>,
2514+
attrs: &[ast::Attribute],
2515+
) -> FxIndexMap<IdentKey, ExternPreludeEntry<'ra>> {
2516+
let mut extern_prelude: FxIndexMap<IdentKey, ExternPreludeEntry<'ra>> = tcx
2517+
.sess
2518+
.opts
2519+
.externs
2520+
.iter()
2521+
.filter_map(|(name, entry)| {
2522+
// Make sure `self`, `super`, `_` etc do not get into extern prelude.
2523+
// FIXME: reject `--extern self` and similar in option parsing instead.
2524+
if entry.add_prelude
2525+
&& let sym = Symbol::intern(name)
2526+
&& sym.can_be_raw()
2527+
{
2528+
Some((IdentKey::with_root_ctxt(sym), ExternPreludeEntry::flag()))
2529+
} else {
2530+
None
2531+
}
2532+
})
2533+
.collect();
2534+
2535+
// Add open base entries for namespaced crates whose base segment
2536+
// is missing from the prelude (e.g. `foo::bar` without `foo`).
2537+
// These are necessary in order to resolve the open modules, whereas
2538+
// the namespaced names are necessary in `extern_prelude` for actually
2539+
// resolving the namespaced crates.
2540+
let missing_open_bases: Vec<IdentKey> = extern_prelude
2541+
.keys()
2542+
.filter_map(|ident| {
2543+
let (base, _) = ident.name.as_str().split_once("::")?;
2544+
let base_sym = Symbol::intern(base);
2545+
base_sym.can_be_raw().then(|| IdentKey::with_root_ctxt(base_sym))
2546+
})
2547+
.filter(|base_ident| !extern_prelude.contains_key(base_ident))
2548+
.collect();
2549+
2550+
extern_prelude.extend(
2551+
missing_open_bases.into_iter().map(|ident| (ident, ExternPreludeEntry::open_flag())),
2552+
);
2553+
2554+
// Inject `core` / `std` unless suppressed by attributes.
2555+
if !attr::contains_name(attrs, sym::no_core) {
2556+
extern_prelude.insert(IdentKey::with_root_ctxt(sym::core), ExternPreludeEntry::flag());
2557+
2558+
if !attr::contains_name(attrs, sym::no_std) {
2559+
extern_prelude.insert(IdentKey::with_root_ctxt(sym::std), ExternPreludeEntry::flag());
2560+
}
2561+
}
2562+
2563+
extern_prelude
2564+
}
2565+
25082566
fn names_to_string(names: impl Iterator<Item = Symbol>) -> String {
25092567
let mut result = String::new();
25102568
for (i, name) in names.enumerate().filter(|(_, name)| *name != kw::PathRoot) {

compiler/rustc_session/src/config/externs.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ pub(crate) fn split_extern_opt<'a>(
4343
}
4444
};
4545

46+
// Reject paths with more than two segments.
47+
if unstable_opts.namespaced_crates && crate_name.split("::").count() > 2 {
48+
return Err(early_dcx.early_struct_fatal(format!(
49+
"crate name `{crate_name}` passed to `--extern` can have at most two segments."
50+
)));
51+
}
52+
4653
if !valid_crate_name(&crate_name, unstable_opts) {
4754
let mut error = early_dcx.early_struct_fatal(format!(
4855
"crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier"

0 commit comments

Comments
 (0)