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
7 changes: 7 additions & 0 deletions compiler/rustc_ast_lowering/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
self.visit_body(body);
}

fn visit_use(&mut self, tree: &'hir UseTree<'hir>, hir_id: HirId) {
if !hir_id.is_owner() {
self.insert(tree.prefix.span, hir_id, Node::NestedUseTree(tree));
}
intravisit::walk_use(self, tree, hir_id);
}

fn visit_param(&mut self, param: &'hir Param<'hir>) {
let node = Node::Param(param);
self.insert(param.pat.span, param.hir_id, node);
Expand Down
117 changes: 21 additions & 96 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
use rustc_hir::attrs::{AttributeKind, EiiImplResolution};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
self as hir, CRATE_OWNER_ID, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target,
find_attr,
};
use rustc_middle::middle::resolve::ResolverAstLowering;
use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::data_structures::IndexMap;
use rustc_span::def_id::{DefId, LocalDefId};
Expand Down Expand Up @@ -244,11 +243,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ItemKind::ExternCrate(*orig_name, ident)
}
ItemKind::Use(use_tree) => {
// Start with an empty prefix.
let prefix =
Path { segments: ThinVec::new(), span: use_tree.prefix.span.shrink_to_lo() };

self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs)
hir::ItemKind::Use(self.lower_use_tree(use_tree, id, vis_span, attrs))
}
ItemKind::Static(ast::StaticItem {
ident,
Expand Down Expand Up @@ -599,13 +594,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_use_tree(
&mut self,
tree: &UseTree,
prefix: &Path,
id: NodeId,
vis_span: Span,
attrs: &'hir [hir::Attribute],
) -> hir::ItemKind<'hir> {
) -> hir::UseTree<'hir> {
let path = &tree.prefix;
let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
let segments = path.segments.iter().cloned().collect();

match tree.kind {
UseTreeKind::Simple(rename) => {
Expand All @@ -627,104 +621,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
let res = self.lower_import_res(id, path.span);
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
let ident = self.lower_ident(ident);
hir::ItemKind::Use(path, hir::UseKind::Single(ident))
hir::UseTree { prefix: path, kind: hir::UseKind::Single(ident) }
}
UseTreeKind::Glob(_) => {
let res = self.expect_full_res(id);
let res = self.lower_res(res);
// Put the result in the appropriate namespace.
let res = match res {
Res::Def(DefKind::Mod | DefKind::Trait, _) => {
PerNS { type_ns: Some(res), value_ns: None, macro_ns: None }
}
Res::Def(DefKind::Enum, _) => {
PerNS { type_ns: None, value_ns: Some(res), macro_ns: None }
}
Res::Err => {
// Propagate the error to all namespaces, just to be sure.
let err = Some(Res::Err);
PerNS { type_ns: err, value_ns: err, macro_ns: err }
}
_ => span_bug!(path.span, "bad glob res {:?}", res),
};
let res = res.in_namespace();
let path = Path { segments, span: path.span };
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
hir::ItemKind::Use(path, hir::UseKind::Glob)
hir::UseTree { prefix: path, kind: hir::UseKind::Glob }
}
UseTreeKind::Nested { items: ref trees, .. } => {
// Nested imports are desugared into simple imports.
// So, if we start with
//
// ```
// pub(x) use foo::{a, b};
// ```
//
// we will create three items:
//
// ```
// pub(x) use foo::a;
// pub(x) use foo::b;
// pub(x) use foo::{}; // <-- this is called the `ListStem`
// ```
//
// The first two are produced by recursively invoking
// `lower_use_tree` (and indeed there may be things
// like `use foo::{a::{b, c}}` and so forth). They
// wind up being directly added to
// `self.items`. However, the structure of this
// function also requires us to return one item, and
// for that we return the `{}` import (called the
// `ListStem`).

let span = prefix.span.to(path.span);
let prefix = Path { segments, span };
let res = self.expect_full_res(id);
let res = self.lower_res(res);
// Put the result in the appropriate namespace.
let res = res.in_namespace();
let prefix = self.lower_use_path(res, &path, ParamMode::Explicit);

// Add all the nested `PathListItem`s to the HIR.
for &(ref use_tree, id) in trees {
let owner_id = self.owner_id(id);

// Each `use` import is an item and thus are owners of the
// names in the path. Up to this point the nested import is
// the current owner, since we want each desugared import to
// own its own names, we have to adjust the owner before
// lowering the rest of the import.
self.with_hir_id_owner(id, |this| {
// `prefix` is lowered multiple times, but in different HIR owners.
// So each segment gets renewed `HirId` with the same
// `ItemLocalId` and the new owner. (See `lower_node_id`)
let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, attrs);
if !attrs.is_empty() {
this.curr_owner.attrs.insert(hir::ItemLocalId::ZERO, attrs);
}

let item = hir::Item {
owner_id,
kind,
vis_span,
span: this.lower_span(use_tree.span()),
eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});
}
let items = self.arena.alloc_from_iter(trees.iter().map(|&(ref use_tree, id)| {
let hir_id = self.lower_node_id(id);
let def_id = self.curr_owner.owner.node_id_to_def_id[&id];
if !attrs.is_empty() {
self.curr_owner.attrs.insert(hir_id.local_id, attrs);
}
(self.lower_use_tree(use_tree, id, vis_span, attrs), hir_id, def_id)
}));

// Condition should match `build_reduced_graph_for_use_tree`.
let path = if trees.is_empty()
&& !(prefix.segments.is_empty()
|| prefix.segments.len() == 1
&& prefix.segments[0].ident.name == kw::PathRoot)
{
// For empty lists we need to lower the prefix so it is checked for things
// like stability later.
let res = self.lower_import_res(id, span);
self.lower_use_path(res, &prefix, ParamMode::Explicit)
} else {
// For non-empty lists we can just drop all the data, the prefix is already
// present in HIR as a part of nested imports.
let span = self.lower_span(span);
self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span })
};
hir::ItemKind::Use(path, hir::UseKind::ListStem)
hir::UseTree { prefix, kind: hir::UseKind::Nested { items } }
}
}
}
Expand Down
119 changes: 25 additions & 94 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hash::{StableHash, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::tagged_ptr::TaggedRef;
use rustc_data_structures::unord::ExtendUnord;
use rustc_errors::codes::*;
use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, ErrorGuaranteed};
use rustc_hir::attrs::lang_items::LangItem;
Expand Down Expand Up @@ -663,27 +662,6 @@ fn index_ast<'tcx>(
let item = mem::replace(item, *dummy);
self.insert(item.id, node(Box::new(item)));
}

#[tracing::instrument(level = "trace", skip(self))]
fn visit_item_id_use_tree(
&mut self,
tree: &UseTree,
parent: LocalDefId,
items: &mut SmallVec<[Box<Item>; 1]>,
) {
match tree.kind {
UseTreeKind::Glob(_) | UseTreeKind::Simple(_) => {}
UseTreeKind::Nested { items: ref nested_vec, span } => {
for &(ref nested, id) in nested_vec {
self.insert(id, AstOwner::NestedUseTree(parent));
items.push(self.make_dummy(id, span, ItemKind::MacCall));

let def_id = self.owners[&id].def_id;
self.visit_item_id_use_tree(nested, def_id, items);
}
}
}
}
}

impl MutVisitor for Indexer<'_, '_> {
Expand All @@ -693,37 +671,13 @@ fn index_ast<'tcx>(
}

fn flat_map_item(&mut self, mut item: Box<Item>) -> SmallVec<[Box<Item>; 1]> {
let def_id = self.owners[&item.id].def_id;
mut_visit::walk_item(self, &mut *item);
let dummy = self.make_dummy(item.id, item.span, ItemKind::MacCall);
let mut items = smallvec![dummy];
if let ItemKind::Use(ref use_tree) = item.kind {
self.visit_item_id_use_tree(use_tree, def_id, &mut items);
}
let items = smallvec![dummy];
self.insert(item.id, AstOwner::Item(item));
items
}

fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
let Stmt { id, span, kind } = stmt;
let mut id = Some(id);
mut_visit::walk_flat_map_stmt_kind(self, kind)
.into_iter()
.map(|kind| {
// Expanding the current statement is a nested `use` item,
// it is expanded into several flat `use` items.
// Create new NodeIds for the corresponding statements
// as two statements cannot have the same.
let id = id.take().unwrap_or_else(|| {
let next = self.next_node_id;
self.next_node_id.increment_by(1);
next
});
Stmt { id, kind, span }
})
.collect()
}

fn visit_assoc_item(&mut self, item: &mut AssocItem, ctxt: visit::AssocCtxt) {
mut_visit::walk_assoc_item(self, item, ctxt);
match ctxt {
Expand All @@ -750,12 +704,12 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> {
let ast_index = tcx.index_ast(());
let resolver_and_node = ast_index.get(def_id).map(Steal::steal);

let fallback_to_ancestor = |parent_id| {
let fallback_to_ancestor = || {
// The item did not exist in the AST, it was created while lowering another item.
// `parent_id` may be different from the direct parent of `def_id`,
// for instance use-trees are lowered by the first sibling.

let parent_id = tcx.local_parent(def_id);
let mut parent_info = tcx.lower_to_hir(parent_id);
if let hir::MaybeOwner::NonOwner(hir_id) = parent_info {
while let hir::MaybeOwner::NonOwner(hir_id) = parent_info {
// `parent_id` could also not be a owner either.
// For instance if `def_id` is an enum variant field,
// the direct parent is the enum variant.
Expand All @@ -766,7 +720,8 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> {

let parent_info = parent_info.unwrap();
*parent_info.children.get(&def_id).unwrap_or_else(|| {
panic!(
span_bug!(
tcx.source_span(def_id),
"{:?} does not appear in children of {:?}",
def_id,
parent_info.nodes.node().def_id()
Expand All @@ -778,7 +733,7 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> {
// `ast_index` does not contain all definitions, only up-to the highest
// `LocalDefId` which has a non-trivial `AstOwner`. Gracefully handle
// other definitions, in particular those nested inside this highest definition.
return fallback_to_ancestor(tcx.local_parent(def_id));
return fallback_to_ancestor();
};

let mut item_lowerer = item::ItemLowerer { tcx, resolver: &*resolver };
Expand All @@ -790,10 +745,9 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> {
AstOwner::TraitItem(item) => item_lowerer.lower_trait_item(&item),
AstOwner::ImplItem(item) => item_lowerer.lower_impl_item(&item),
AstOwner::ForeignItem(item) => item_lowerer.lower_foreign_item(&item),
AstOwner::NestedUseTree(owner_id) => fallback_to_ancestor(*owner_id),
// The item existed in the AST, but is not a HIR owner.
// Fetch the correct information from its parent.
AstOwner::NonOwner => fallback_to_ancestor(tcx.local_parent(def_id)),
AstOwner::NonOwner => fallback_to_ancestor(),
};

tcx.sess.time("drop_ast", || mem::drop(node));
Expand Down Expand Up @@ -902,43 +856,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::OwnerId { def_id: self.resolver.owners[&node].def_id }
}

/// Freshen the `LoweringContext` and ready it to lower a nested item.
/// The lowered item is registered into `self.curr_owner.children`.
///
/// This function sets up `HirId` lowering infrastructure,
/// and stashes the per-owner state to avoid pollution by the closure.
#[instrument(level = "debug", skip(self, f))]
fn with_hir_id_owner(
&mut self,
owner: NodeId,
f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>,
) {
let child_owner = PerOwnerLoweringState::new(self.resolver, owner);
let parent_owner = mem::replace(&mut self.curr_owner, child_owner);

// Do not reset `next_node_id` and `node_id_to_def_id`:
// we want `f` to be able to refer to the `LocalDefId`s that the caller created.
// and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s.

// Always allocate the first `HirId` for the owner itself.
#[cfg(debug_assertions)]
self.curr_owner
.relowering_checker
.assert_node_is_not_relowered(owner, hir::ItemLocalId::ZERO);

let item = f(self);
let completed_child_owner = mem::replace(&mut self.curr_owner, parent_owner);
let owner_id = completed_child_owner.owner_id;
let info = completed_child_owner.into_owner_info(self.tcx, item);

self.curr_owner
.children
.extend_unord(info.children.items().map(|(&def_id, &info)| (def_id, info)));

debug_assert!(!self.curr_owner.children.contains_key(&owner_id.def_id));
self.curr_owner.children.insert(owner_id.def_id, hir::MaybeOwner::Owner(info));
}

/// This method allocates a new `HirId` for the given `NodeId`.
/// Take care not to call this method if the resulting `HirId` is then not
/// actually used in the HIR, as that would trigger an assertion in the
Expand Down Expand Up @@ -1002,8 +919,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
}

fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS<Option<Res>> {
debug_assert_eq!(id, self.curr_owner.owner.id);
let per_ns = self.curr_owner.owner.import_res.map(|res| res.map(|res| self.lower_res(res)));
let per_ns = self
.curr_owner
.owner
.import_res
.get(&id)
.unwrap_or_else(|| {
let sp = self.tcx.source_span(self.curr_owner.owner.def_id);
self.tcx.dcx().span_delayed_bug(
sp,
"no import_res entry for import, \
this should only happen if it already errored in resolve",
);
&PerNS { value_ns: None, type_ns: None, macro_ns: None }
})
.map(|res| res.map(|res| self.lower_res(res)));

if per_ns.is_empty() {
// Propagate the error to all namespaces, just to be sure.
self.dcx().span_delayed_bug(span, "no resolution for an import");
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_hir/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,25 @@ pub enum Res<Id = HirId> {
Err,
}

impl Res {
pub fn in_namespace(self) -> PerNS<Option<Res>> {
match self {
Res::Def(DefKind::Mod | DefKind::Trait, _) => {
PerNS { type_ns: Some(self), value_ns: None, macro_ns: None }
}
Res::Def(DefKind::Enum, _) => {
PerNS { type_ns: None, value_ns: Some(self), macro_ns: None }

@petrochenkov petrochenkov Sep 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value namespace for DefKind::Enum is pre-existing, but clearly incorrect.
It probably doesn't cause issues because in HIR nobody actually inspects the namespace parts of PerNS.

View changes since the review

}
Res::Err => {
// Propagate the error to all namespaces, just to be sure.
let err = Some(Res::Err);
PerNS { type_ns: err, value_ns: err, macro_ns: err }
}
_ => panic!("bad path segment res {self:?}"),
}
}
}

impl<Id> IntoDiagArg for Res<Id> {
fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
DiagArgValue::Str(Cow::Borrowed(self.descr()))
Expand Down
Loading
Loading