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
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
// also nested delegations may need to access information about this code (#154332),
// so it is better to leave this code as opposed to bodies of extern functions,
// which are completely erased from existence.
// FIXME(fn_delegation): fix `help` in error message (see `inner-attr.stderr`)
if param_count == 0
&& let Some(block) = block
{
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub(super) enum Owners<'a, 'hir> {
}

impl<'hir> Owners<'_, 'hir> {
fn get_or_insert_mut(&mut self, def_id: LocalDefId) -> &mut hir::MaybeOwner<'hir> {
pub(super) fn get_or_insert_mut(&mut self, def_id: LocalDefId) -> &mut hir::MaybeOwner<'hir> {
match self {
Owners::IndexVec(index_vec) => {
index_vec.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom)
Expand Down
37 changes: 30 additions & 7 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use std::mem;
use std::sync::Arc;

use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::{self as ast, *};
use rustc_attr_parsing::{AttributeParser, Late, OmitDoc};
use rustc_data_structures::fingerprint::Fingerprint;
Expand All @@ -53,8 +54,8 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
use rustc_hir::definitions::{DefPathData, DisambiguatorState};
use rustc_hir::lints::{AttributeLint, DelayedLint};
use rustc_hir::{
self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource,
LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr,
self as hir, AngleBrackets, ConstArg, DelayedOwner, GenericArg, HirId, ItemLocalMap,
LifetimeSource, LifetimeSyntax, ParamName, Target, TraitCandidate, find_attr,
};
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_macros::extension;
Expand Down Expand Up @@ -633,13 +634,35 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> mid_hir::Crate<'_> {
let mut delayed_ids: FxIndexSet<LocalDefId> = Default::default();

for def_id in ast_index.indices() {
match &ast_index[def_id] {
AstOwner::Item(Item { kind: ItemKind::Delegation { .. }, .. })
| AstOwner::AssocItem(Item { kind: AssocItemKind::Delegation { .. }, .. }, _) => {
delayed_ids.insert(def_id);
let delayed_owner = match &ast_index[def_id] {
AstOwner::Item(Item {
kind: ItemKind::Delegation(box Delegation { ident, .. }),
..
}) => Some(DelayedOwner { kind: hir::DelayedOwnerKind::Item, ident: *ident }),
AstOwner::AssocItem(
Item { kind: AssocItemKind::Delegation(box Delegation { ident, .. }), .. },
ctx,
) => {
let kind = match ctx {
AssocCtxt::Trait => hir::DelayedOwnerKind::TraitItem,
AssocCtxt::Impl { .. } => hir::DelayedOwnerKind::ImplItem,
};

Some(DelayedOwner { kind, ident: *ident })
}
_ => lowerer.lower_node(def_id),
_ => None,
};

if let Some(delayed_owner) = delayed_owner {
delayed_ids.insert(def_id);

let owner = lowerer.owners.get_or_insert_mut(def_id);
if let hir::MaybeOwner::Phantom = owner {
*owner = hir::MaybeOwner::Delayed(delayed_owner)
}
} else {
lowerer.lower_node(def_id);
}
}

// Don't hash unless necessary, because it's expensive.
Expand Down
22 changes: 20 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1641,10 +1641,24 @@ impl<'tcx> OwnerInfo<'tcx> {
}
}

#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub enum DelayedOwnerKind {
Item,
ImplItem,
TraitItem,
}

#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub struct DelayedOwner {
pub ident: Ident,
pub kind: DelayedOwnerKind,
}

#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub enum MaybeOwner<'tcx> {
Owner(&'tcx OwnerInfo<'tcx>),
NonOwner(HirId),
Delayed(DelayedOwner),
/// Used as a placeholder for unused LocalDefId.
Phantom,
}
Expand All @@ -1653,12 +1667,16 @@ impl<'tcx> MaybeOwner<'tcx> {
pub fn as_owner(self) -> Option<&'tcx OwnerInfo<'tcx>> {
match self {
MaybeOwner::Owner(i) => Some(i),
MaybeOwner::NonOwner(_) | MaybeOwner::Phantom => None,
_ => None,
}
}

pub fn unwrap(self) -> &'tcx OwnerInfo<'tcx> {
self.as_owner().unwrap_or_else(|| panic!("Not a HIR owner"))
self.as_owner().unwrap_or_else(|| panic!("not a HIR owner"))
}

pub fn expect_delayed(self) -> DelayedOwner {
if let MaybeOwner::Delayed(owner) = self { owner } else { panic!("not a delayed owner") }
}
}

Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub trait HirTyCtxt<'hir> {
fn hir_trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir>;
fn hir_impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir>;
fn hir_foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir>;
fn is_delayed(&self, id: LocalDefId) -> bool;
}

// Used when no tcx is actually available, forcing manual implementation of nested visitors.
Expand All @@ -139,6 +140,9 @@ impl<'hir> HirTyCtxt<'hir> for ! {
fn hir_foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> {
unreachable!();
}
fn is_delayed(&self, _: LocalDefId) -> bool {
unreachable!()
}
}

pub mod nested_filter {
Expand Down Expand Up @@ -226,6 +230,8 @@ pub trait Visitor<'v>: Sized {
/// or `ControlFlow<T>`.
type Result: VisitorResult = ();

const VISIT_DELAYED: bool = true;

/// If `type NestedFilter` is set to visit nested items, this method
/// must also be overridden to provide a map to retrieve nested items.
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
Expand All @@ -244,18 +250,23 @@ pub trait Visitor<'v>: Sized {
/// this method is if you want a nested pattern but cannot supply a
/// `TyCtxt`; see `maybe_tcx` for advice.
fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
if Self::NestedFilter::INTER {
if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) {
let item = self.maybe_tcx().hir_item(id);
try_visit!(self.visit_item(item));
}
Self::Result::output()
}

// Now delayed owners are only delegations, which are either item, trait item or impl item.
fn should_visit_maybe_delayed_inter(&mut self, id: LocalDefId) -> bool {
Self::NestedFilter::INTER && (Self::VISIT_DELAYED || !self.maybe_tcx().is_delayed(id))
}

/// Like `visit_nested_item()`, but for trait items. See
/// `visit_nested_item()` for advice on when to override this
/// method.
fn visit_nested_trait_item(&mut self, id: TraitItemId) -> Self::Result {
if Self::NestedFilter::INTER {
if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) {
let item = self.maybe_tcx().hir_trait_item(id);
try_visit!(self.visit_trait_item(item));
}
Expand All @@ -266,7 +277,7 @@ pub trait Visitor<'v>: Sized {
/// `visit_nested_item()` for advice on when to override this
/// method.
fn visit_nested_impl_item(&mut self, id: ImplItemId) -> Self::Result {
if Self::NestedFilter::INTER {
if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) {
let item = self.maybe_tcx().hir_impl_item(id);
try_visit!(self.visit_impl_item(item));
}
Expand Down
9 changes: 4 additions & 5 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,10 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) {
/// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses.
/// This function never fails.
fn run_required_analyses(tcx: TyCtxt<'_>) {
// Forces all delayed owners to be lowered and drops AST crate after it.
// Also refetches hir_crate_items to prevent multiple threads from blocking on it later.
tcx.force_delayed_owners_lowering();

if tcx.sess.opts.unstable_opts.input_stats {
rustc_passes::input_stats::print_hir_stats(tcx);
}
Expand All @@ -1062,11 +1066,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
#[cfg(all(not(doc), debug_assertions))]
rustc_passes::hir_id_validator::check_crate(tcx);

// Prefetch this to prevent multiple threads from blocking on it later.
// This is needed since the `hir_id_validator::check_crate` call above is not guaranteed
// to use `hir_crate_items`.
tcx.ensure_done().hir_crate_items(());

let sess = tcx.sess;
sess.time("misc_checking_1", || {
par_fns(&mut [
Expand Down
76 changes: 51 additions & 25 deletions compiler/rustc_middle/src/hir/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_ast::visit::{VisitorResult, walk_list};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, spawn, try_par_for_each_in};
use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, try_par_for_each_in};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
Expand Down Expand Up @@ -835,7 +835,14 @@ impl<'tcx> TyCtxt<'tcx> {
}

#[inline]
fn hir_opt_ident(self, id: HirId) -> Option<Ident> {
pub fn hir_opt_ident(self, id: HirId) -> Option<Ident> {
// If possible don't force lowering of delayed owner, as it can lead to cycles.
if let MaybeOwner::Delayed(delayed_owner) =
self.hir_maybe_owner_unprocessed(id.owner.def_id)
Copy link
Copy Markdown
Contributor Author

@aerooneqq aerooneqq Mar 31, 2026

Choose a reason for hiding this comment

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

@rustbot ready
This should fix incremental compilation issues, however it may bring other perf regressions as now we call it every time we want to get Ident. Let's try run perf, If it will be OK then we can leave this thus adjusting all logic connected to getting identifier.

Honestly speaking I am not sure that this is a correct way of solving cycle problems during delegation function resolution, after some experiments with inherent impls I found several other scenarios where cycles are possible, for example one is connected to const evaluation which eventually comes to lints_that_dont_need_to_run which creates query cycle.
Maybe we need infrastructural solution, for example fill synthetic owners for delegations before delayed lowering, then execute delayed lowering in a special query system mode, in this mode we will use synthetic owners in all queries connected to delegations, and after this mode we will replace them with lowered delegations and invalidate all queries that used synthetic delegations. But I still need to experiment more with that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's not clear to me why delegation needs a special "unprocessed HIR ID" at all. Can't you use type-dependent name resolution like the other things that can't be resolved without typeck info?

Copy link
Copy Markdown
Contributor Author

@aerooneqq aerooneqq Apr 1, 2026

Choose a reason for hiding this comment

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

If by type-dependent name resolution you mean resolution through rustc_hir_typeck::method::probe::ProbeContext thats what I am trying to do now, the problem is that we need this resolution in order to generate HIR of delegation, so we invoke it during delayed AST -> HIR lowering, however, ProbeContext can eventually invoke queries that can walk for example all items and access their HIR, but as we are doing it while lowering delegation, its HIR is not ready, so this creates cycle.
Most of information about delegation can be retrieved without lowered delegation HIR, in this example we know delegation identifier without its HIR version, so now we stored it in MaybeOwner::Delayed variant and it can be accessed without forcing delegation's AST -> HIR lowering.

{
return Some(delayed_owner.ident);
}

match self.hir_node(id) {
Node::Pat(&Pat { kind: PatKind::Binding(_, _, ident, _), .. }) => Some(ident),
// A `Ctor` doesn't have an identifier itself, but its parent
Expand Down Expand Up @@ -1115,6 +1122,10 @@ impl<'tcx> intravisit::HirTyCtxt<'tcx> for TyCtxt<'tcx> {
fn hir_foreign_item(&self, id: ForeignItemId) -> &'tcx ForeignItem<'tcx> {
(*self).hir_foreign_item(id)
}

fn is_delayed(&self, id: LocalDefId) -> bool {
(*self).hir_crate(()).delayed_ids.contains(&id)
}
}

impl<'tcx> pprust_hir::PpAnn for TyCtxt<'tcx> {
Expand Down Expand Up @@ -1212,8 +1223,14 @@ fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> {
upstream_crates
}

#[derive(Clone, Copy)]
enum ItemCollectionKind {
Crate,
Mod(LocalModDefId),
}

pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> ModuleItems {
let mut collector = ItemCollector::new(tcx, false);
let mut collector = ItemCollector::new(tcx, ItemCollectionKind::Mod(module_id));

let (hir_mod, span, hir_id) = tcx.hir_get_module(module_id);
collector.visit_mod(hir_mod, span, hir_id);
Expand Down Expand Up @@ -1245,26 +1262,8 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
}
}

fn force_delayed_owners_lowering(tcx: TyCtxt<'_>) {
let krate = tcx.hir_crate(());
for &id in &krate.delayed_ids {
tcx.ensure_done().lower_delayed_owner(id);
}

let (_, krate) = krate.delayed_resolver.steal();
let prof = tcx.sess.prof.clone();

// Drop AST to free memory. It can be expensive so try to drop it on a separate thread.
spawn(move || {
let _timer = prof.verbose_generic_activity("drop_ast");
drop(krate);
});
}

pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
force_delayed_owners_lowering(tcx);

let mut collector = ItemCollector::new(tcx, true);
let mut collector = ItemCollector::new(tcx, ItemCollectionKind::Crate);

// A "crate collector" and "module collector" start at a
// module item (the former starts at the crate root) but only
Expand Down Expand Up @@ -1327,9 +1326,9 @@ struct ItemCollector<'tcx> {
}

impl<'tcx> ItemCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>, crate_collector: bool) -> ItemCollector<'tcx> {
ItemCollector {
crate_collector,
fn new(tcx: TyCtxt<'tcx>, collection_kind: ItemCollectionKind) -> ItemCollector<'tcx> {
let mut collector = ItemCollector {
crate_collector: matches!(collection_kind, ItemCollectionKind::Crate),
tcx,
submodules: Vec::default(),
items: Vec::default(),
Expand All @@ -1341,12 +1340,39 @@ impl<'tcx> ItemCollector<'tcx> {
nested_bodies: Vec::default(),
delayed_lint_items: Vec::default(),
eiis: Vec::default(),
};

let krate = tcx.hir_crate(());
let delayed_kinds = krate
.delayed_ids
.iter()
.copied()
.map(|id| (id, krate.owners[id].expect_delayed().kind))
.filter(|(id, _)| match collection_kind {
ItemCollectionKind::Crate => true,
ItemCollectionKind::Mod(mod_id) => tcx.parent_module_from_def_id(*id) == mod_id,
});

// FIXME(fn_delegation): need to add delayed lints, eiis
for (def_id, kind) in delayed_kinds {
let owner_id = OwnerId { def_id };

match kind {
DelayedOwnerKind::Item => collector.items.push(ItemId { owner_id }),
DelayedOwnerKind::ImplItem => collector.impl_items.push(ImplItemId { owner_id }),
DelayedOwnerKind::TraitItem => collector.trait_items.push(TraitItemId { owner_id }),
};

collector.body_owners.push(def_id);
}

collector
}
}

impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
type NestedFilter = nested_filter::All;
const VISIT_DELAYED: bool = false;

fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
Expand Down
27 changes: 24 additions & 3 deletions compiler/rustc_middle/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in};
use rustc_data_structures::sync::{DynSend, DynSync, spawn, try_par_for_each_in};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::lints::DelayedLint;
Expand Down Expand Up @@ -64,7 +64,8 @@ impl<'hir> Crate<'hir> {
// which is greater than delayed LocalDefId, we use IndexVec for owners,
// so we will call ensure_contains_elem which will grow it.
if let Some(owner) = self.owners.get(def_id)
&& (self.delayed_ids.is_empty() || !matches!(owner, MaybeOwner::Phantom))
&& (self.delayed_ids.is_empty()
|| !matches!(owner, MaybeOwner::Phantom | MaybeOwner::Delayed(_)))
{
return *owner;
}
Expand Down Expand Up @@ -207,6 +208,24 @@ impl ModuleItems {
}

impl<'tcx> TyCtxt<'tcx> {
pub fn force_delayed_owners_lowering(self) {
let krate = self.hir_crate(());
self.ensure_done().hir_crate_items(());

for &id in &krate.delayed_ids {
self.ensure_done().lower_delayed_owner(id);
}

let (_, krate) = krate.delayed_resolver.steal();
let prof = self.sess.prof.clone();

// Drop AST to free memory. It can be expensive so try to drop it on a separate thread.
spawn(move || {
let _timer = prof.verbose_generic_activity("drop_ast");
drop(krate);
});
}

pub fn parent_module(self, id: HirId) -> LocalModDefId {
if !id.is_owner() && self.def_kind(id.owner) == DefKind::Mod {
LocalModDefId::new_unchecked(id.owner.def_id)
Expand Down Expand Up @@ -472,10 +491,12 @@ pub fn provide(providers: &mut Providers) {
providers.hir_crate_items = map::hir_crate_items;
providers.crate_hash = map::crate_hash;
providers.hir_module_items = map::hir_module_items;
providers.hir_maybe_owner_unprocessed = |tcx, id| &tcx.hir_crate(()).owners[id];
providers.local_def_id_to_hir_id = |tcx, def_id| match tcx.hir_crate(()).owner(tcx, def_id) {
MaybeOwner::Owner(_) => HirId::make_owner(def_id),
MaybeOwner::NonOwner(hir_id) => hir_id,
MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id),
MaybeOwner::Phantom => bug!("no HirId for {:?}", def_id),
MaybeOwner::Delayed(_) => bug!("delayed owner should be lowered {:?}", def_id),
};
providers.opt_hir_owner_nodes =
|tcx, id| tcx.hir_crate(()).owner(tcx, id).as_owner().map(|i| &i.nodes);
Expand Down
Loading
Loading