authorbors <bors@rust-lang.org> 2026-07-01 10:14:42 UTC
committerbors <bors@rust-lang.org> 2026-07-01 10:14:42 UTC
loge2b71ade2db4ea263ab0d561d889f3e3795a500d
treef8586b6a65035485e56ce97c9c1e05fd2c7b9283
parent7c5dac9c7476a30e09a5aa87b4df2c5663fe7734
parentd737d297a7f471e9478e163e1581f70ec291651c

Auto merge of #158604 - LorrensP-2158466:less-alloc-during-res, r=petrochenkov

resolve: no allocation in `resolve_ident_in(_local)_module_*` Somewhat of a follow up of rust-lang/rust#158207 and rust-lang/rust#158035. Remove the `or_default` call of `NameResolutions` which does an arena allocation. Prep work for parallel import resolution. - cycle detection now works on `module` and `bindingkey` instead of the `NameResolution`. This should have no impact as `NameResolutions` are found by that pair. - Now that we get an `Option<NameResolution>` the logic changed a bit to work with `None`. Did this by following the logic of the functions to see what would happen with a completely empty resolution (`or_default`). r? @petrochenkov

2 files changed, 44 insertions(+), 38 deletions(-)

compiler/rustc_resolve/src/ident.rs+34-30
......@@ -1148,10 +1148,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
11481148 ignore_import: Option<Import<'ra>>,
11491149 ) -> Result<Decl<'ra>, ControlFlow<Determinacy, Determinacy>> {
11501150 let key = BindingKey::new(ident, ns);
1151 let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span);
1152 let resolution = resolution_ref.borrow();
1151 let resolution = self.resolution(module.to_module(), key);
11531152
1154 let binding = resolution.non_glob_decl.filter(|b| Some(*b) != ignore_decl);
1153 let binding =
1154 resolution.as_ref().and_then(|r| r.non_glob_decl).filter(|b| Some(*b) != ignore_decl);
11551155
11561156 if let Some(finalize) = finalize {
11571157 return self.get_mut().finalize_module_binding(
......@@ -1170,21 +1170,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
11701170 return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) };
11711171 }
11721172
1173 // We need to detect resolution cycles to avoid infinite recursion. The guard ensures
1174 // the resolution is removed when this resolve call ends.
1175 let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref)
1176 .map_err(|_| ControlFlow::Continue(Determined))?;
1173 if let Some(resolution) = resolution {
1174 // We need to detect resolution cycles to avoid infinite recursion. The guard ensures
1175 // the resolution is removed when this resolve call ends.
1176 let _cycle_guard = cycle_detection::enter_cycle_detector(module, key)
1177 .map_err(|_| ControlFlow::Continue(Determined))?;
11771178
1178 // Check if one of single imports can still define the name, block if it can.
1179 if self.reborrow().single_import_can_define_name(
1180 &resolution,
1181 None,
1182 ns,
1183 ignore_import,
1184 ignore_decl,
1185 parent_scope,
1186 ) {
1187 return Err(ControlFlow::Break(Undetermined));
1179 // Check if one of single imports can still define the name, block if it can.
1180 if self.reborrow().single_import_can_define_name(
1181 &resolution,
1182 None,
1183 ns,
1184 ignore_import,
1185 ignore_decl,
1186 parent_scope,
1187 ) {
1188 return Err(ControlFlow::Break(Undetermined));
1189 }
11881190 }
11891191
11901192 // Check if one of unexpanded macros can still define the name.
......@@ -1210,10 +1212,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
12101212 ignore_import: Option<Import<'ra>>,
12111213 ) -> Result<Decl<'ra>, ControlFlow<Determinacy, Determinacy>> {
12121214 let key = BindingKey::new(ident, ns);
1213 let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span);
1214 let resolution = resolution_ref.borrow();
1215 let resolution = self.resolution(module.to_module(), key);
12151216
1216 let binding = resolution.glob_decl.filter(|b| Some(*b) != ignore_decl);
1217 let binding =
1218 resolution.as_ref().and_then(|r| r.glob_decl).filter(|b| Some(*b) != ignore_decl);
12171219
12181220 if let Some(finalize) = finalize {
12191221 return self.get_mut().finalize_module_binding(
......@@ -1228,20 +1230,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
12281230
12291231 // We need to detect resolution cycles to avoid infinite recursion. The guard ensures
12301232 // the resolution is removed when this resolve call ends.
1231 let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref)
1233 let _cycle_guard = cycle_detection::enter_cycle_detector(module, key)
12321234 .map_err(|_| ControlFlow::Continue(Determined))?;
12331235
12341236 // Check if one of single imports can still define the name,
12351237 // if it can then our result is not determined and can be invalidated.
1236 if self.reborrow().single_import_can_define_name(
1237 &resolution,
1238 binding,
1239 ns,
1240 ignore_import,
1241 ignore_decl,
1242 parent_scope,
1243 ) {
1244 return Err(ControlFlow::Break(Undetermined));
1238 if let Some(resolution) = resolution {
1239 if self.reborrow().single_import_can_define_name(
1240 &resolution,
1241 binding,
1242 ns,
1243 ignore_import,
1244 ignore_decl,
1245 parent_scope,
1246 ) {
1247 return Err(ControlFlow::Break(Undetermined));
1248 }
12451249 }
12461250
12471251 // So we have a resolution that's from a glob import. This resolution is determined
compiler/rustc_resolve/src/imports.rs+10-8
......@@ -326,23 +326,23 @@ impl<'ra> NameResolution<'ra> {
326326pub(crate) mod cycle_detection {
327327 use std::ptr;
328328
329 use crate::CacheRefCell;
330 use crate::imports::NameResolutionRef;
329 use crate::{BindingKey, CacheRefCell, LocalModule};
331330
332331 thread_local!(
333332 /// During import resolution, recursive imports can form cycles.
334333 /// This set stores the active resolution stack for the current thread.
335 /// So it's essentially a recursion stack.
334 /// By keeping track of the module and `BindingKey` pair that identifies
335 /// the specific resolution.
336336 ///
337 /// The key is the interned address of a `RefCell<NameResolution<'ra>>` allocated
337 /// The pointer is the interned address of a `Interned<'ra, ModuleData>` allocated
338338 /// in the `Resolver Arenas` (lifetime `'ra`), it is thus stable and allows casting
339339 /// to a `*const ()` for comparison. This is done because we can't use lifetimes
340340 /// other than `'static` in thread local storage.
341 static ACTIVE_RESOLUTIONS: CacheRefCell<Vec<*const ()>> = Default::default();
341 static ACTIVE_RESOLUTIONS: CacheRefCell<Vec<(*const (), BindingKey)>> = Default::default();
342342 );
343343
344344 pub(crate) struct ActiveResolutionGuard {
345 key: *const (),
345 key: (*const (), BindingKey),
346346 }
347347
348348 impl Drop for ActiveResolutionGuard {
......@@ -360,9 +360,11 @@ pub(crate) mod cycle_detection {
360360 /// Returns `Err(())` if a cycle is detected, otherwise this returns a
361361 /// guard that will remove the resolution when dropped.
362362 pub(crate) fn enter_cycle_detector<'ra>(
363 resolution: NameResolutionRef<'ra>,
363 module: LocalModule<'ra>,
364 binding_key: BindingKey,
364365 ) -> Result<ActiveResolutionGuard, ()> {
365 let key = ptr::from_ref(resolution.0).cast::<()>();
366 let module_key = ptr::from_ref(module.0.0).cast();
367 let key = (module_key, binding_key);
366368 ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| {
367369 if ar.contains(&key) {
368370 return Err(());