Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions compiler/rustc_hir/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,13 @@ pub enum Res<Id = hir::HirId> {
/// **Belongs to the type namespace.**
ToolMod,

/// The resolution for an open 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.**
OpenMod(Symbol),

// Macro namespace
/// An attribute that is *not* implemented via macro.
/// E.g., `#[inline]` and `#[rustfmt::skip]`, which are essentially directives,
Expand Down Expand Up @@ -838,6 +845,7 @@ impl<Id> Res<Id> {
| Res::SelfTyAlias { .. }
| Res::SelfCtor(..)
| Res::ToolMod
| Res::OpenMod(..)
| Res::NonMacroAttr(..)
| Res::Err => None,
}
Expand Down Expand Up @@ -869,6 +877,7 @@ impl<Id> Res<Id> {
Res::Local(..) => "local variable",
Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => "self type",
Res::ToolMod => "tool module",
Res::OpenMod(..) => "namespaced crate",
Res::NonMacroAttr(attr_kind) => attr_kind.descr(),
Res::Err => "unresolved item",
}
Expand All @@ -895,6 +904,7 @@ impl<Id> Res<Id> {
Res::SelfTyAlias { alias_to, is_trait_impl }
}
Res::ToolMod => Res::ToolMod,
Res::OpenMod(sym) => Res::OpenMod(sym),
Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
Res::Err => Res::Err,
}
Expand All @@ -911,6 +921,7 @@ impl<Id> Res<Id> {
Res::SelfTyAlias { alias_to, is_trait_impl }
}
Res::ToolMod => Res::ToolMod,
Res::OpenMod(sym) => Res::OpenMod(sym),
Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
Res::Err => Res::Err,
})
Expand All @@ -936,9 +947,11 @@ impl<Id> Res<Id> {
pub fn ns(&self) -> Option<Namespace> {
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::OpenMod(..) => Some(Namespace::TypeNS),
Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
Res::Err => None,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2786,6 +2786,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
| Res::SelfCtor(_)
| Res::Local(_)
| Res::ToolMod
| Res::OpenMod(..)
| Res::NonMacroAttr(_)
| Res::Err) => Const::new_error_with_message(
tcx,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,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::OpenMod(..) | Res::Err => {}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
| Res::SelfTyParam { .. }
| Res::SelfTyAlias { .. }
| Res::SelfCtor(..)
| Res::OpenMod(..)
| Res::Err => bug!("unexpected resolution: {:?}", res),
}
}
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ignore-tidy-filelength
use std::ops::ControlFlow;

use itertools::Itertools as _;
Expand Down Expand Up @@ -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::OpenMod(..) => {
// Don't confuse the user with tool modules or open modules.
continue;
}
Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
Expand Down Expand Up @@ -1973,7 +1974,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let (built_in, from) = match scope {
Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"),
Scope::ExternPreludeFlags
if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() =>
if self.tcx.sess.opts.externs.get(ident.as_str()).is_some()
|| matches!(res, Res::OpenMod(..)) =>
{
("", " passed with `--extern`")
}
Expand Down
16 changes: 13 additions & 3 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ 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,
Segment, Stage, Symbol, Used, errors,
};

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -386,7 +386,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,
Expand Down Expand Up @@ -976,6 +975,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ignore_import,
)
}
ModuleOrUniformRoot::OpenModule(sym) => {
let open_ns_name = format!("{}::{}", sym.as_str(), ident.name);
let ns_ident = IdentKey::with_root_ctxt(Symbol::intern(&open_ns_name));
match self.extern_prelude_get_flag(ns_ident, ident.span, finalize.is_some()) {
Some(decl) => Ok(decl),
None => Err(Determinacy::Determined),
}
}
ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set(
ident,
ScopeSet::ModuleAndExternPrelude(ns, module),
Expand Down Expand Up @@ -1962,7 +1969,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::OpenMod(sym) = binding.res() {
module = Some(ModuleOrUniformRoot::OpenModule(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;
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Res = def::Res<NodeId>;

/// 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<Decl<'ra>>),
#[default]
Expand Down
144 changes: 101 additions & 43 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use rustc_hir::definitions::DisambiguatorState;
use rustc_hir::{PrimTy, TraitCandidate, find_attr};
use rustc_index::bit_set::DenseBitSet;
use rustc_metadata::creader::CStore;
use rustc_middle::bug;
use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport};
use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::query::Providers;
Expand Down Expand Up @@ -448,6 +449,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.
OpenModule(Symbol),
}

#[derive(Debug)]
Expand Down Expand Up @@ -1108,13 +1114,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<CacheCell<(PendingDecl<'ra>, /* finalized */ bool)>>,
flag_decl: Option<
CacheCell<(
PendingDecl<'ra>,
/* finalized */ bool,
/* open flag (namespaced crate) */ bool,
)>,
>,
}

impl ExternPreludeEntry<'_> {
Expand All @@ -1125,7 +1138,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 open_flag() -> Self {
ExternPreludeEntry {
item_decl: None,
flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, true))),
}
}

Expand Down Expand Up @@ -1637,35 +1657,7 @@ 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 extern_prelude = build_extern_prelude(tcx, attrs);
let registered_tools = tcx.registered_tools(());
let edition = tcx.sess.edition();

Expand Down Expand Up @@ -2320,10 +2312,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
) -> Option<Decl<'ra>> {
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_open) = flag_decl.get();
let decl = match pending_decl {
PendingDecl::Ready(decl) => {
if finalize && !finalized {
if finalize && !finalized && !is_open {
self.cstore_mut().process_path_extern(
self.tcx,
ident.name,
Expand All @@ -2334,18 +2326,28 @@ 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_open {
let res = Res::OpenMod(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.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_open));
decl.or_else(|| finalize.then_some(self.dummy_decl))
})
}
Expand Down Expand Up @@ -2387,7 +2389,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => {
None
}
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
path_result @ (PathResult::Module(..) | PathResult::Indeterminate) => {
bug!("got invalid path_result: {path_result:?}")
}
}
}

Expand Down Expand Up @@ -2505,6 +2509,60 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}

fn build_extern_prelude<'tcx, 'ra>(
tcx: TyCtxt<'tcx>,
attrs: &[ast::Attribute],
) -> FxIndexMap<IdentKey, ExternPreludeEntry<'ra>> {
let mut extern_prelude: FxIndexMap<IdentKey, ExternPreludeEntry<'ra>> = 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 open base entries for namespaced crates whose base segment
// is missing from the prelude (e.g. `foo::bar` without `foo`).
// These are necessary in order to resolve the open modules, whereas
// the namespaced names are necessary in `extern_prelude` for actually
// resolving the namespaced crates.
let missing_open_bases: Vec<IdentKey> = 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_open_bases.into_iter().map(|ident| (ident, ExternPreludeEntry::open_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
}

fn names_to_string(names: impl Iterator<Item = Symbol>) -> String {
let mut result = String::new();
for (i, name) in names.enumerate().filter(|(_, name)| *name != kw::PathRoot) {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_session/src/config/externs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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` can have 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"
Expand Down
Loading
Loading