diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e93deaa84944e..5f3d815321549 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -600,6 +600,13 @@ pub enum Res { /// /// **Not bound to a specific namespace.** Err, + + /// The resolution for a virtual module in a namespaced crate. E.g. `my_api` + /// in the namespaced crate `my_api::utils` when `my_api` isn't part of the + /// extern prelude. + /// + /// **Belongs to the type namespace.** + VirtualMod(Symbol), } impl IntoDiagArg for Res { @@ -834,6 +841,7 @@ impl Res { | Res::SelfTyAlias { .. } | Res::SelfCtor(..) | Res::ToolMod + | Res::VirtualMod(..) | Res::NonMacroAttr(..) | Res::Err => None, } @@ -865,6 +873,7 @@ impl Res { Res::Local(..) => "local variable", Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => "self type", Res::ToolMod => "tool module", + Res::VirtualMod(..) => "virtual module for namespaced crate", Res::NonMacroAttr(attr_kind) => attr_kind.descr(), Res::Err => "unresolved item", } @@ -891,6 +900,7 @@ impl Res { Res::SelfTyAlias { alias_to, is_trait_impl } } Res::ToolMod => Res::ToolMod, + Res::VirtualMod(sym) => Res::VirtualMod(sym), Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind), Res::Err => Res::Err, } @@ -907,6 +917,7 @@ impl Res { Res::SelfTyAlias { alias_to, is_trait_impl } } Res::ToolMod => Res::ToolMod, + Res::VirtualMod(sym) => Res::VirtualMod(sym), Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind), Res::Err => Res::Err, }) @@ -932,9 +943,11 @@ impl Res { pub fn ns(&self) -> Option { match self { Res::Def(kind, ..) => kind.ns(), - Res::PrimTy(..) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::ToolMod => { - Some(Namespace::TypeNS) - } + Res::PrimTy(..) + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } + | Res::ToolMod + | Res::VirtualMod(..) => Some(Namespace::TypeNS), Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS), Res::NonMacroAttr(..) => Some(Namespace::MacroNS), Res::Err => None, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 9f84f652698b4..632f635769987 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2769,6 +2769,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | Res::SelfCtor(_) | Res::Local(_) | Res::ToolMod + | Res::VirtualMod(..) | Res::NonMacroAttr(_) | Res::Err) => Const::new_error_with_message( tcx, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3294b6802a719..3f39a84908ff5 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -154,7 +154,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { Res::Def(_, def_id) => self.check_def_id(def_id), Res::SelfTyParam { trait_: t } => self.check_def_id(t), Res::SelfTyAlias { alias_to: i, .. } => self.check_def_id(i), - Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {} + Res::ToolMod | Res::NonMacroAttr(..) | Res::VirtualMod(..) | Res::Err => {} } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index f0dffd8829da3..0387e87bc03da 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -326,6 +326,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _, ) | Res::PrimTy(..) + | Res::VirtualMod(..) | Res::ToolMod => define_extern(TypeNS), Res::Def( DefKind::Fn diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index f09b987157996..f2cfbd156266f 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1204,6 +1204,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ModuleGlobs(..) => { // Already handled in `ModuleNonGlobs`. } + Scope::NamespacedCrates(..) => {} Scope::MacroUsePrelude => { suggestions.extend(this.macro_use_prelude.iter().filter_map( |(name, binding)| { @@ -1735,8 +1736,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Res::Def(DefKind::Macro(kinds), _) => { format!("{} {}", kinds.article(), kinds.descr()) } - Res::ToolMod => { - // Don't confuse the user with tool modules. + Res::ToolMod | Res::VirtualMod(..) => { + // Don't confuse the user with tool modules or virtual modules. continue; } Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index d4d373d820644..7df0f738be29a 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -25,8 +25,8 @@ use crate::macros::{MacroRulesScope, sub_namespace_match}; use crate::{ AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingKey, CmResolver, Decl, DeclKind, Determinacy, Finalize, IdentKey, ImportKind, LateDecl, Module, ModuleKind, ModuleOrUniformRoot, - ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, - Segment, Stage, Used, errors, + NamespacedCrateRoot, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, + Scope, ScopeSet, Segment, Stage, Symbol, Used, errors, }; #[derive(Copy, Clone)] @@ -109,26 +109,40 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ns, macro_kind) = match scope_set { ScopeSet::All(ns) | ScopeSet::Module(ns, _) - | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::NamespacedCrate(ns, _) => (ns, None), ScopeSet::ExternPrelude => (TypeNS, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; let module = match scope_set { // Start with the specified module. - ScopeSet::Module(_, module) | ScopeSet::ModuleAndExternPrelude(_, module) => module, + ScopeSet::Module(_, module) | ScopeSet::ModuleAndExternPrelude(_, module) => { + Some(module) + } + ScopeSet::NamespacedCrate(_, _) => None, // Jump out of trait or enum modules, they do not act as scopes. - _ => parent_scope.module.nearest_item_scope(), + _ => Some(parent_scope.module.nearest_item_scope()), }; let module_only = matches!(scope_set, ScopeSet::Module(..)); let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)); let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude); + let namespace_crate_only = matches!(scope_set, ScopeSet::NamespacedCrate(..)); let mut scope = match ns { - _ if module_only || module_and_extern_prelude => Scope::ModuleNonGlobs(module, None), + _ if module_only || module_and_extern_prelude => { + Scope::ModuleNonGlobs(module.unwrap(), None) + } + _ if namespace_crate_only => { + let ScopeSet::NamespacedCrate(_, root_name) = scope_set else { + unreachable!(); + }; + + Scope::NamespacedCrates(NamespacedCrateRoot::VirtualMod(root_name), None) + } _ if extern_prelude => Scope::ExternPreludeItems, - TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), + TypeNS | ValueNS => Scope::ModuleNonGlobs(module.unwrap(), None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut use_prelude = !module.no_implicit_prelude; + let mut use_prelude = module.map(|m| !m.no_implicit_prelude).unwrap_or_else(|| true); loop { let visit = match scope { @@ -151,7 +165,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } true } - Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, + Scope::ModuleNonGlobs(..) + | Scope::ModuleGlobs(..) + | Scope::NamespacedCrates(..) => true, Scope::MacroUsePrelude => use_prelude || orig_ident_span.is_rust_2015(), Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { @@ -192,11 +208,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Invocation(invoc_id) => { Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) } - MacroRulesScope::Empty => Scope::ModuleNonGlobs(module, None), + MacroRulesScope::Empty => Scope::ModuleNonGlobs(module.unwrap(), None), }, Scope::ModuleNonGlobs(module, lint_id) => Scope::ModuleGlobs(module, lint_id), - Scope::ModuleGlobs(..) if module_only => break, - Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns { + Scope::ModuleGlobs(module, lint_id) if module_only => { + Scope::NamespacedCrates(NamespacedCrateRoot::Mod(module), lint_id) + } + Scope::NamespacedCrates(..) if module_only | namespace_crate_only => break, + Scope::ModuleGlobs(module, lint_id) if module_and_extern_prelude => { + Scope::NamespacedCrates(NamespacedCrateRoot::Mod(module), lint_id) + } + Scope::NamespacedCrates(..) if module_and_extern_prelude => match ns { TypeNS => { ctxt.update_unchecked(|ctxt| ctxt.adjust(ExpnId::root())); Scope::ExternPreludeItems @@ -204,6 +226,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ValueNS | MacroNS => break, }, Scope::ModuleGlobs(module, prev_lint_id) => { + Scope::NamespacedCrates(NamespacedCrateRoot::Mod(module), prev_lint_id) + } + Scope::NamespacedCrates(namespace_crate_root, prev_lint_id) => { + let module = namespace_crate_root.get_mod(); use_prelude = !module.no_implicit_prelude; match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) { Some((parent_module, lint_id)) => { @@ -386,7 +412,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } /// Resolve an identifier in the specified set of scopes. - #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_ident_in_scope_set<'r>( self: CmResolver<'r, 'ra, 'tcx>, orig_ident: Ident, @@ -425,7 +450,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (ns, macro_kind) = match scope_set { ScopeSet::All(ns) | ScopeSet::Module(ns, _) - | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), + | ScopeSet::ModuleAndExternPrelude(ns, _) + | ScopeSet::NamespacedCrate(ns, _) => (ns, None), ScopeSet::ExternPrelude => (TypeNS, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; @@ -686,6 +712,65 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Err(ControlFlow::Break(..)) => return binding, } } + Scope::NamespacedCrates(ns_root, _) => { + let try_find_namespaced_crate = |root_name: &Symbol| { + self.namespaced_crate_names + .get(root_name.as_str()) + .into_iter() + .flatten() + .find(|s| s.split("::").nth(1) == Some(ident.name.as_str())) + }; + + match ns_root { + NamespacedCrateRoot::Mod(module) => { + if let Some(def_id) = module.opt_def_id() { + if let Some(ns_base_name) = + self.def_id_to_namespaced_crate_names.borrow().get(&def_id) + { + let ns_crate_cand = try_find_namespaced_crate(ns_base_name); + match ns_crate_cand { + Some(ns_cand) => { + let ns_ident = + IdentKey::with_root_ctxt(Symbol::intern(ns_cand)); + + match self.extern_prelude_get_flag( + ns_ident, + module.span, + finalize.is_some(), + ) { + Some(decl) => Ok(decl), + None => Err(Determinacy::Determined), + } + } + None => Err(Determinacy::Determined), + } + } else { + Err(Determinacy::Determined) + } + } else { + Err(Determinacy::Determined) + } + } + NamespacedCrateRoot::VirtualMod(sym) => { + let ns_crate_cand = try_find_namespaced_crate(&sym); + match ns_crate_cand { + Some(ns_cand) => { + let ns_ident = IdentKey::with_root_ctxt(Symbol::intern(ns_cand)); + + match self.extern_prelude_get_flag( + ns_ident, + orig_ident_span, + finalize.is_some(), + ) { + Some(decl) => Ok(decl), + None => Err(Determinacy::Determined), + } + } + None => Err(Determinacy::Determined), + } + } + } + } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { Some(decl) => Ok(decl), None => Err(Determinacy::determined( @@ -951,6 +1036,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import, ) } + ModuleOrUniformRoot::VirtualNamespacedCrate(sym) => self.resolve_ident_in_scope_set( + ident, + ScopeSet::NamespacedCrate(ns, sym), + parent_scope, + finalize, + ignore_decl, + ignore_import, + ), ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set( ident, ScopeSet::ModuleAndExternPrelude(ns, module), @@ -1905,7 +1998,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res); - if let Some(def_id) = binding.res().module_like_def_id() { + if let Res::VirtualMod(sym) = binding.res() { + module = Some(ModuleOrUniformRoot::VirtualNamespacedCrate(sym)); + record_segment_res(self.reborrow(), finalize, res, id); + } else if let Some(def_id) = binding.res().module_like_def_id() { if self.mods_with_parse_errors.contains(&def_id) { module_had_parse_errors = true; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 78ad139cff795..b807734cbb2b1 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -41,7 +41,7 @@ type Res = def::Res; /// A potential import declaration in the process of being planted into a module. /// Also used for lazily planting names from `--extern` flags to extern prelude. -#[derive(Clone, Copy, Default, PartialEq)] +#[derive(Clone, Copy, Default, PartialEq, Debug)] pub(crate) enum PendingDecl<'ra> { Ready(Option>), #[default] diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 659b74b74df77..7746ac6aee888 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -131,6 +131,8 @@ enum Scope<'ra> { /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` /// lint if it should be reported. ModuleGlobs(Module<'ra>, Option), + // Crate names of the form `foo::bar`. + NamespacedCrates(NamespacedCrateRoot<'ra>, Option), /// Names introduced by `#[macro_use]` attributes on `extern crate` items. MacroUsePrelude, /// Built-in attributes. @@ -161,6 +163,8 @@ enum ScopeSet<'ra> { ExternPrelude, /// Same as `All(MacroNS)`, but with the given macro kind restriction. Macro(MacroKind), + /// Only `Scope::NamespacedCrates` + NamespacedCrate(Namespace, Symbol), } /// Everything you need to know about a name's location to resolve it. @@ -461,6 +465,11 @@ enum ModuleOrUniformRoot<'ra> { /// Used only for resolving single-segment imports. The reason it exists is that import paths /// are always split into two parts, the first of which should be some kind of module. CurrentScope, + + /// Virtual module for the resolution of base names of namespaced crates, + /// where the base name doesn't correspond to a module in the extern prelude. + /// E.g. `my_api::utils` is in the prelude, but `my_api` is not. + VirtualNamespacedCrate(Symbol), } #[derive(Debug)] @@ -1112,13 +1121,20 @@ impl<'ra> DeclData<'ra> { } } +#[derive(Debug)] struct ExternPreludeEntry<'ra> { /// Name declaration from an `extern crate` item. /// The boolean flag is true is `item_decl` is non-redundant, happens either when /// `flag_decl` is `None`, or when `extern crate` introducing `item_decl` used renaming. item_decl: Option<(Decl<'ra>, Span, /* introduced by item */ bool)>, /// Name declaration from an `--extern` flag, lazily populated on first use. - flag_decl: Option, /* finalized */ bool)>>, + flag_decl: Option< + CacheCell<( + PendingDecl<'ra>, + /* finalized */ bool, + /* virtual flag (namespaced crate) */ bool, + )>, + >, } impl ExternPreludeEntry<'_> { @@ -1129,7 +1145,14 @@ impl ExternPreludeEntry<'_> { fn flag() -> Self { ExternPreludeEntry { item_decl: None, - flag_decl: Some(CacheCell::new((PendingDecl::Pending, false))), + flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, false))), + } + } + + fn virtual_flag() -> Self { + ExternPreludeEntry { + item_decl: None, + flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, true))), } } @@ -1384,6 +1407,11 @@ pub struct Resolver<'ra, 'tcx> { // that were encountered during resolution. These names are used to generate item names // for APITs, so we don't want to leak details of resolution into these names. impl_trait_names: FxHashMap = default::fx_hash_map(), + + // Extern crates with names of the form `foo::bar` + namespaced_crate_names: FxHashMap<&'tcx str, Vec<&'tcx str>>, + + def_id_to_namespaced_crate_names: CacheRefCell>, } /// This provides memory for the rest of the crate. The `'ra` lifetime that is @@ -1580,7 +1608,7 @@ impl<'tcx> Resolver<'_, 'tcx> { impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, - attrs: &[ast::Attribute], + attrs: &'ra [ast::Attribute], crate_span: Span, current_crate_outer_attr_insert_span: Span, arenas: &'ra ResolverArenas<'ra>, @@ -1613,35 +1641,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut invocation_parents = FxHashMap::default(); invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT); - let mut extern_prelude: FxIndexMap<_, _> = tcx - .sess - .opts - .externs - .iter() - .filter_map(|(name, entry)| { - // Make sure `self`, `super`, `_` etc do not get into extern prelude. - // FIXME: reject `--extern self` and similar in option parsing instead. - if entry.add_prelude - && let name = Symbol::intern(name) - && name.can_be_raw() - { - let ident = IdentKey::with_root_ctxt(name); - Some((ident, ExternPreludeEntry::flag())) - } else { - None - } - }) - .collect(); - - if !attr::contains_name(attrs, sym::no_core) { - let ident = IdentKey::with_root_ctxt(sym::core); - extern_prelude.insert(ident, ExternPreludeEntry::flag()); - if !attr::contains_name(attrs, sym::no_std) { - let ident = IdentKey::with_root_ctxt(sym::std); - extern_prelude.insert(ident, ExternPreludeEntry::flag()); - } - } - + let namespaced_crate_names = get_namespaced_crate_names(tcx); + let extern_prelude = build_extern_prelude(tcx, attrs); let registered_tools = tcx.registered_tools(()); let edition = tcx.sess.edition(); @@ -1709,6 +1710,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), current_crate_outer_attr_insert_span, + namespaced_crate_names, + def_id_to_namespaced_crate_names: Default::default(), .. }; @@ -1956,6 +1959,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ModuleGlobs(..) => { // Already handled in `ModuleNonGlobs` (but see #144993). } + Scope::NamespacedCrates(..) => { + // not sure what to do here + } Scope::StdLibPrelude => { if let Some(module) = this.prelude { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); @@ -2300,10 +2306,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Option> { let entry = self.extern_prelude.get(&ident); entry.and_then(|entry| entry.flag_decl.as_ref()).and_then(|flag_decl| { - let (pending_decl, finalized) = flag_decl.get(); + let (pending_decl, finalized, is_virtual) = flag_decl.get(); let decl = match pending_decl { PendingDecl::Ready(decl) => { - if finalize && !finalized { + if finalize && !finalized && !is_virtual { self.cstore_mut().process_path_extern( self.tcx, ident.name, @@ -2314,18 +2320,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } PendingDecl::Pending => { debug_assert!(!finalized); - let crate_id = if finalize { - self.cstore_mut().process_path_extern(self.tcx, ident.name, orig_ident_span) + if is_virtual { + let res = Res::VirtualMod(ident.name); + Some(self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT)) } else { - self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) - }; - crate_id.map(|crate_id| { - let res = Res::Def(DefKind::Mod, crate_id.as_def_id()); - self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT) - }) + let crate_id = if finalize { + self.cstore_mut().process_path_extern( + self.tcx, + ident.name, + orig_ident_span, + ) + } else { + self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) + }; + crate_id.map(|crate_id| { + let def_id = crate_id.as_def_id(); + let res = Res::Def(DefKind::Mod, def_id); + self.try_add_def_id_for_namespaced_crate(def_id, ident); + self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT) + }) + } } }; - flag_decl.set((PendingDecl::Ready(decl), finalize || finalized)); + flag_decl.set((PendingDecl::Ready(decl), finalize || finalized, is_virtual)); decl.or_else(|| finalize.then_some(self.dummy_decl)) }) } @@ -2359,7 +2376,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(); let Ok(segments) = segments else { return None }; - match self.cm().maybe_resolve_path(&segments, Some(ns), &parent_scope, None) { + let path_result = self.cm().maybe_resolve_path(&segments, Some(ns), &parent_scope, None); + match path_result { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) => { path_res.full_res().filter(|res| !matches!(res, Res::Def(DefKind::Ctor(..), _))) @@ -2367,7 +2385,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { None } - PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), + PathResult::Module(..) | PathResult::Indeterminate => { + panic!("got invalid path_result: {:?}", path_result) + } } } @@ -2483,6 +2503,88 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } self.main_def = Some(MainDefinition { res, is_import, span }); } + + fn try_add_def_id_for_namespaced_crate(&self, def_id: DefId, ident: IdentKey) { + let crate_name = ident.name; + if self.namespaced_crate_names.contains_key(crate_name.as_str()) { + self.def_id_to_namespaced_crate_names.borrow_mut().insert(def_id, crate_name); + } + } +} + +pub(crate) fn is_namespaced_crate(crate_name: &str) -> bool { + crate_name.contains("::") +} + +fn build_extern_prelude<'tcx, 'ra>( + tcx: TyCtxt<'tcx>, + attrs: &'ra [ast::Attribute], +) -> FxIndexMap> { + let mut extern_prelude: FxIndexMap> = tcx + .sess + .opts + .externs + .iter() + .filter_map(|(name, entry)| { + // Make sure `self`, `super`, `_` etc do not get into extern prelude. + // FIXME: reject `--extern self` and similar in option parsing instead. + if entry.add_prelude + && let sym = Symbol::intern(name) + && sym.can_be_raw() + { + Some((IdentKey::with_root_ctxt(sym), ExternPreludeEntry::flag())) + } else { + None + } + }) + .collect(); + + // Add virtual base entries for namespaced crates whose base segment + // is missing from the prelude (e.g. `foo::bar` without `foo`). + let missing_virtual_bases: Vec = extern_prelude + .keys() + .filter_map(|ident| { + let (base, _) = ident.name.as_str().split_once("::")?; + let base_sym = Symbol::intern(base); + base_sym.can_be_raw().then(|| IdentKey::with_root_ctxt(base_sym)) + }) + .filter(|base_ident| !extern_prelude.contains_key(base_ident)) + .collect(); + + extern_prelude.extend( + missing_virtual_bases.into_iter().map(|ident| (ident, ExternPreludeEntry::virtual_flag())), + ); + + // Inject `core` / `std` unless suppressed by attributes. + if !attr::contains_name(attrs, sym::no_core) { + extern_prelude.insert(IdentKey::with_root_ctxt(sym::core), ExternPreludeEntry::flag()); + + if !attr::contains_name(attrs, sym::no_std) { + extern_prelude.insert(IdentKey::with_root_ctxt(sym::std), ExternPreludeEntry::flag()); + } + } + + extern_prelude +} + +// Creates a map for base path segment -> full path of all namespaced crates. +fn get_namespaced_crate_names(tcx: TyCtxt<'_>) -> FxHashMap<&str, Vec<&str>> { + tcx.sess + .opts + .externs + .iter() + .filter(|(name, _)| is_namespaced_crate(name)) + .map(|(name, _)| { + let main_crate_name = name + .split("::") + .nth(0) + .expect(&format!("namespaced crate name has unexpected form {}", name)); + (main_crate_name, name.as_str()) + }) + .fold(FxHashMap::default(), |mut acc_map, (main_name, full_name)| { + acc_map.entry(main_name).or_insert_with(Vec::new).push(full_name); + acc_map + }) } fn names_to_string(names: impl Iterator) -> String { @@ -2572,6 +2674,22 @@ impl Finalize { } } +#[derive(Debug, Clone, Copy)] +enum NamespacedCrateRoot<'ra> { + Mod(Module<'ra>), + VirtualMod(Symbol), +} + +impl<'ra> NamespacedCrateRoot<'ra> { + /// Retrieves the `Module` of `NamespacedCrateRoot::Mod`. Panics if variant is `VirtualMod`. + pub(crate) fn get_mod(&self) -> Module<'ra> { + match self { + Self::Mod(m) => *m, + Self::VirtualMod(..) => panic!("expected NamespacedCrateRoot::Mod"), + } + } +} + pub fn provide(providers: &mut Providers) { providers.registered_tools = macros::registered_tools; } diff --git a/compiler/rustc_session/src/config/externs.rs b/compiler/rustc_session/src/config/externs.rs index d668d8b4203db..367516b4f6ae1 100644 --- a/compiler/rustc_session/src/config/externs.rs +++ b/compiler/rustc_session/src/config/externs.rs @@ -43,6 +43,13 @@ pub(crate) fn split_extern_opt<'a>( } }; + // Reject paths with more than two segments. + if unstable_opts.namespaced_crates && crate_name.split("::").count() > 2 { + return Err(early_dcx.early_struct_fatal(format!( + "crate name `{crate_name}` passed to `--extern` has too many segments; namespaced crates currently support at most two segments" + ))); + } + if !valid_crate_name(&crate_name, unstable_opts) { let mut error = early_dcx.early_struct_fatal(format!( "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier" diff --git a/tests/ui/resolve/auxiliary/open-ns-mod-my_api.rs b/tests/ui/resolve/auxiliary/open-ns-mod-my_api.rs new file mode 100644 index 0000000000000..dc8b5720c0c10 --- /dev/null +++ b/tests/ui/resolve/auxiliary/open-ns-mod-my_api.rs @@ -0,0 +1,9 @@ +pub mod utils { + pub fn root_helper() { + println!("root_helper"); + } +} + +pub fn root_function() -> String { + "my_api root!".to_string() +} diff --git a/tests/ui/resolve/auxiliary/open-ns-my_api.rs b/tests/ui/resolve/auxiliary/open-ns-my_api.rs new file mode 100644 index 0000000000000..be4bf31f0fbcd --- /dev/null +++ b/tests/ui/resolve/auxiliary/open-ns-my_api.rs @@ -0,0 +1,3 @@ +pub fn root_function() -> String { + "my_api root!".to_string() +} diff --git a/tests/ui/resolve/auxiliary/open-ns-my_api_core.rs b/tests/ui/resolve/auxiliary/open-ns-my_api_core.rs new file mode 100644 index 0000000000000..41418f1516f60 --- /dev/null +++ b/tests/ui/resolve/auxiliary/open-ns-my_api_core.rs @@ -0,0 +1,15 @@ +// #![crate_name = "my_api::core"] + +pub mod util { + pub fn core_mod_fn() -> String { + format!("core_fn from my_api::core::util",) + } +} + +pub fn core_fn() -> String { + format!("core_fn from my_api::core!",) +} + +pub fn core_fn2() -> String { + format!("core_fn2 from my_api::core!",) +} diff --git a/tests/ui/resolve/auxiliary/open-ns-my_api_utils.rs b/tests/ui/resolve/auxiliary/open-ns-my_api_utils.rs new file mode 100644 index 0000000000000..d2af20728bd5e --- /dev/null +++ b/tests/ui/resolve/auxiliary/open-ns-my_api_utils.rs @@ -0,0 +1,13 @@ +pub mod util { + pub fn util_mod_helper() -> String { + format!("Helper from my_api::utils::util",) + } +} + +pub fn utils_helper() -> String { + format!("Helper from my_api::utils!",) +} + +pub fn get_u32() -> u32 { + 1 +} diff --git a/tests/ui/resolve/open-ns-1.rs b/tests/ui/resolve/open-ns-1.rs new file mode 100644 index 0000000000000..0268990b17e0b --- /dev/null +++ b/tests/ui/resolve/open-ns-1.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ aux-crate:my_api=open-ns-my_api.rs +//@ aux-crate:my_api::utils=open-ns-my_api_utils.rs +//@ aux-crate:my_api::core=open-ns-my_api_core.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 + +use my_api::root_function; +use my_api::utils::util; + +fn main() { + let _ = root_function(); + let _ = my_api::root_function(); + let _ = my_api::utils::utils_helper(); + let _ = util::util_mod_helper(); + let _ = my_api::core::core_fn(); +} diff --git a/tests/ui/resolve/open-ns-2.rs b/tests/ui/resolve/open-ns-2.rs new file mode 100644 index 0000000000000..5cc0ea38fd1d3 --- /dev/null +++ b/tests/ui/resolve/open-ns-2.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ aux-crate: my_api=open-ns-my_api.rs +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ aux-crate: my_api::core=open-ns-my_api_core.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 + +use my_api::core::{core_fn, core_fn2}; +use my_api::utils::*; +use my_api::*; + +fn main() { + let _ = root_function(); + let _ = utils_helper(); + let _ = core_fn(); + let _ = core_fn2(); +} diff --git a/tests/ui/resolve/open-ns-3.rs b/tests/ui/resolve/open-ns-3.rs new file mode 100644 index 0000000000000..17ffea9f16519 --- /dev/null +++ b/tests/ui/resolve/open-ns-3.rs @@ -0,0 +1,14 @@ +// This test should fail with `utils_helper` being unresolvable in `my_api::utils`. +// If a crate contains a module that overlaps with a namespaced crate name, then +// the namespaced crate will not be used in name resolution. + +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ aux-crate: my_api=open-ns-mod-my_api.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 + +fn main() { + let _ = my_api::utils::root_helper(); + let _ = my_api::utils::utils_helper(); + //~^ ERROR E0425 +} diff --git a/tests/ui/resolve/open-ns-3.stderr b/tests/ui/resolve/open-ns-3.stderr new file mode 100644 index 0000000000000..8ae261af01429 --- /dev/null +++ b/tests/ui/resolve/open-ns-3.stderr @@ -0,0 +1,19 @@ +error[E0425]: cannot find function `utils_helper` in module `my_api::utils` + --> $DIR/open-ns-3.rs:12:28 + | +LL | let _ = my_api::utils::utils_helper(); + | ^^^^^^^^^^^^ not found in `my_api::utils` + | +help: consider importing this function + | +LL + use my_api::utils::utils_helper; + | +help: if you import `utils_helper`, refer to it directly + | +LL - let _ = my_api::utils::utils_helper(); +LL + let _ = utils_helper(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/open-ns-4.rs b/tests/ui/resolve/open-ns-4.rs new file mode 100644 index 0000000000000..0c0138d782946 --- /dev/null +++ b/tests/ui/resolve/open-ns-4.rs @@ -0,0 +1,13 @@ +// This test makes sure namespaced crates work if we don't use any use statements but instead fully +// qualify all references. + +//@ aux-crate: my_api=open-ns-my_api.rs +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 +//@ build-pass + +fn main() { + let _ = my_api::root_function(); + let _ = my_api::utils::utils_helper(); +} diff --git a/tests/ui/resolve/open-ns-5.rs b/tests/ui/resolve/open-ns-5.rs new file mode 100644 index 0000000000000..a980a0e657122 --- /dev/null +++ b/tests/ui/resolve/open-ns-5.rs @@ -0,0 +1,17 @@ +// Tests that namespaced crate names work inside macros. + +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 +//@ build-pass + +macro_rules! import_and_call { + ($import_path:path, $fn_name:ident) => {{ + use $import_path; + $fn_name(); + }}; +} + +fn main() { + import_and_call!(my_api::utils::utils_helper, utils_helper) +} diff --git a/tests/ui/resolve/open-ns-6.rs b/tests/ui/resolve/open-ns-6.rs new file mode 100644 index 0000000000000..f73e6499c1e87 --- /dev/null +++ b/tests/ui/resolve/open-ns-6.rs @@ -0,0 +1,13 @@ +// Tests that virtual modules are resolvable. + +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 +//@ build-pass + +use my_api; +use my_api::utils::utils_helper; + +fn main() { + let _ = utils_helper(); +} diff --git a/tests/ui/resolve/open-ns-7.rs b/tests/ui/resolve/open-ns-7.rs new file mode 100644 index 0000000000000..1cb64c184c830 --- /dev/null +++ b/tests/ui/resolve/open-ns-7.rs @@ -0,0 +1,13 @@ +// Tests that namespaced crates work with absolute paths. + +//@ aux-crate: my_api=open-ns-my_api.rs +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 +//@ build-pass + +use ::my_api::utils::utils_helper; + +fn main() { + let _ = my_api::utils::utils_helper(); +} diff --git a/tests/ui/resolve/open-ns-8.rs b/tests/ui/resolve/open-ns-8.rs new file mode 100644 index 0000000000000..1f9ed19eaa4d6 --- /dev/null +++ b/tests/ui/resolve/open-ns-8.rs @@ -0,0 +1,23 @@ +// Tests that namespaced crate names work inside macros. +//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs +//@ compile-flags: -Z namespaced-crates +//@ edition: 2024 +//@ build-pass + +macro_rules! define { + () => { + pub mod my_api { + pub mod utils { + pub fn get_u32() -> u32 { + 2 + } + } + } + }; +} + +fn main() { + define!(); + let res = my_api::utils::get_u32(); + assert_eq!(res, 2); +}