From 1247eeac470cc03a122e723d07957da3f8a3970c Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Mon, 6 Jul 2026 10:12:10 +0200 Subject: [PATCH] Parallelize the import resolution loop using `par_map`. With the accompanying changes to make datastructures concurrent: - CmRefCell now wraps a `rustc_datastructures::sync::RwLock` because of the borrow counters (does introduce synchronized access in single-threaded mode) - Cache(Ref)Cells are now changed into atomics/(rw)locks - populating external resolution tables now use `Once` to ensure only 1 thread builds the table and all others wait if they need it This is the bare minimum to make the loop work and is not at all optimized, this will follow :). --- compiler/rustc_data_structures/src/sync.rs | 10 +- .../rustc_resolve/src/build_reduced_graph.rs | 76 ++++++---- .../rustc_resolve/src/diagnostics/impls.rs | 2 +- compiler/rustc_resolve/src/ident.rs | 12 +- compiler/rustc_resolve/src/imports.rs | 57 +++++--- compiler/rustc_resolve/src/lib.rs | 132 +++++++++--------- compiler/rustc_resolve/src/macros.rs | 5 +- 7 files changed, 170 insertions(+), 124 deletions(-) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 3d5bc85278286..c97a84ebfec40 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -16,11 +16,11 @@ //! where noted otherwise, the type in column one is defined as a //! newtype around the type from column two or three. //! -//! | Type | Serial version | Parallel version | -//! | ----------------------- | ------------------- | ------------------------------- | -//! | `Lock` | `RefCell` | `RefCell` or | -//! | | | `parking_lot::Mutex` | -//! | `RwLock` | `RefCell` | `parking_lot::RwLock` | +//! | Type | Serial version | Parallel version | +//! | ----------------------- | ------------------------ | ------------------------------- | +//! | `Lock` | `RefCell` | `RefCell` or | +//! | | | `parking_lot::Mutex` | +//! | `RwLock` | `parking_lot::RwLock` | `parking_lot::RwLock` | use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3f4c260a496af..10790d25cf097 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -15,6 +15,7 @@ use rustc_ast::{ }; use rustc_attr_parsing::AttributeParser; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sync::WriteGuard; use rustc_expand::base::{ResolverExpand, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, MacroUseArgs}; @@ -112,38 +113,59 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match def_id.as_local() { Some(local_def_id) => self.local_module_map.get(&local_def_id).map(|m| m.to_module()), None => { - if let module @ Some(..) = self.extern_module_map.borrow().get(&def_id) { + if let module @ Some(..) = self.extern_module_map.read().get(&def_id) { return module.map(|m| m.to_module()); } + // We need the lock on the extern_module_map for the entire duration of this call. + // It is otherwise entirely possible 2 different threads will create and allocate + // the exact same module during speculative resolution. + // FIXME(parallel_import_resolution): We lock the entire map to make sure + // no 2+ threads try to create the exact same module. Could it be possible to + // only "lock on" `def_id`? + let mut lock = self.extern_module_map.write(); + // No reentrant locking possible, so do a recursive call with lock + // passed as argument. + self.get_extern_module_with_lock(def_id, &mut lock) + } + } + } - // Query `def_kind` is not used because query system overhead is too expensive here. - let def_kind = self.cstore().def_kind_untracked(def_id); - if def_kind.is_module_like() { - let parent = self.tcx.opt_parent(def_id).map(|parent_id| { - self.get_nearest_non_block_module(parent_id).expect_extern() - }); - // Query `expn_that_defined` is not used because - // hashing spans in its result is expensive. - let expn_id = self.cstore().expn_that_defined_untracked(self.tcx, def_id); - let module = self.new_extern_module( - parent, - ModuleKind::Def( - def_kind, - def_id, - DUMMY_NODE_ID, - Some(self.tcx.item_name(def_id)), - ), - expn_id, - self.def_span(def_id), - // FIXME: Account for `#[no_implicit_prelude]` attributes. - parent.is_some_and(|module| module.no_implicit_prelude), - ); - return Some(module.to_module()); + fn get_extern_module_with_lock( + &self, + def_id: DefId, + map_lock: &mut WriteGuard<'_, FxIndexMap>>, + ) -> Option> { + if let module @ Some(..) = map_lock.get(&def_id) { + return module.map(|m| m.to_module()); + } + // Query `def_kind` is not used because query system overhead is too expensive here. + let def_kind = self.cstore().def_kind_untracked(def_id); + if def_kind.is_module_like() { + let parent = self.tcx.opt_parent(def_id).map(|mut parent_id| { + loop { + match self.get_extern_module_with_lock(parent_id, map_lock) { + Some(module) => break module.expect_extern(), + None => parent_id = self.tcx.parent(parent_id), + } } - - None - } + }); + // Query `expn_that_defined` is not used because + // hashing spans in its result is expensive. + let expn_id = self.cstore().expn_that_defined_untracked(self.tcx, def_id); + let module = ExternModule::new( + parent, + ModuleKind::Def(def_kind, def_id, DUMMY_NODE_ID, Some(self.tcx.item_name(def_id))), + self.tcx.visibility(def_id), + expn_id, + self.def_span(def_id), + parent.is_some_and(|module| module.no_implicit_prelude), + self.arenas, + ); + map_lock.insert(def_id, module); + return Some(module.to_module()); } + + None } pub(crate) fn expn_def_scope(&self, expn_id: ExpnId) -> Module<'ra> { diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ab8554f2652ee..76e82a42f63b0 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1482,7 +1482,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Never recommend deprecated helper attributes. } Scope::MacroRules(macro_rules_scope) => { - if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { + if let MacroRulesScope::Def(macro_rules_def) = *macro_rules_scope.read() { let res = macro_rules_def.decl.res(); if filter_fn(res) { suggestions.push(TypoSuggestion::new( diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 12dd05bfe6e60..548a544d65405 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -142,9 +142,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. // As another consequence of this optimization visitors never observe invocation // scopes for macros that were already expanded. - while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() { - if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) { - macro_rules_scope.set(next_scope.get()); + let mut scope = *macro_rules_scope.read(); + while let MacroRulesScope::Invocation(invoc_id) = scope { + if let Some(next) = self.output_macro_rules_scopes.get(&invoc_id) { + scope = *next.read(); + *macro_rules_scope.write() = scope; } else { break; } @@ -185,7 +187,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules), - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { + Scope::MacroRules(macro_rules_scope) => match *macro_rules_scope.read() { MacroRulesScope::Def(binding) => { Scope::MacroRules(binding.parent_macro_rules_scope) } @@ -590,7 +592,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } result } - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { + Scope::MacroRules(macro_rules_scope) => match *macro_rules_scope.read() { MacroRulesScope::Def(macro_rules_def) if ident == macro_rules_def.ident => { Ok(macro_rules_def.decl) } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d62bf538133f2..f3366a0635d69 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -2,10 +2,12 @@ use std::cmp::Ordering; use std::mem; +use std::sync::atomic::{self, AtomicUsize}; use rustc_ast::NodeId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::intern::Interned; +use rustc_data_structures::sync::Lock; use rustc_errors::{Applicability, BufferedEarlyLint, Diagnostic}; use rustc_expand::base::SyntaxExtensionKind; use rustc_hir::def::{self, DefKind, PartialRes}; @@ -50,6 +52,7 @@ pub(crate) enum PendingDecl<'ra> { } enum ImportResolutionKind<'ra> { + // these are the decls the import imports, not the import declarations themselves Single(PerNS>), Glob(Vec<(Decl<'ra>, BindingKey, Span /* orig_ident_span */)>), } @@ -775,31 +778,44 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut indeterminate_count = self.indeterminate_imports.len() * 3; while indeterminate_count < prev_indeterminate_count { prev_indeterminate_count = indeterminate_count; - indeterminate_count = 0; - let mut resolutions = Vec::new(); + let atomic_indeterminate_count = AtomicUsize::new(0); + self.assert_speculative = true; - for import in mem::take(&mut self.indeterminate_imports) { - let (resolution, import_indeterminate_count) = self.cm().resolve_import(import); - indeterminate_count += import_indeterminate_count; + + let to_resolve_imports = mem::take(&mut self.indeterminate_imports); + let cm_resolver = self.cm(); + + let determined_imports = Lock::new(Vec::new()); + let indeterminate_imports = Lock::new(Vec::new()); + let resolutions = rustc_data_structures::sync::par_map(to_resolve_imports, |import| { + let (resolution, import_indeterminate_count) = + cm_resolver.reborrow_ref().resolve_import(import); + atomic_indeterminate_count + .fetch_add(import_indeterminate_count, atomic::Ordering::Relaxed); match import_indeterminate_count { - 0 => self.determined_imports.push(import), - _ => self.indeterminate_imports.push(import), - } - if let Some(resolution) = resolution { - resolutions.push((import, resolution)); + 0 => determined_imports.lock().push(import), + _ => indeterminate_imports.lock().push(import), } - } + (import, resolution) + }); + indeterminate_count = atomic_indeterminate_count.into_inner(); + self.determined_imports.extend(determined_imports.into_inner()); + self.indeterminate_imports.extend(indeterminate_imports.into_inner()); + self.assert_speculative = false; + self.write_import_resolutions(resolutions); } } fn write_import_resolutions( &mut self, - import_resolutions: Vec<(Import<'ra>, ImportResolution<'ra>)>, + import_resolutions: Vec<(Import<'ra>, Option>)>, ) { for (import, resolution) in &import_resolutions { - let ImportResolution { imported_module, .. } = resolution; + let Some(ImportResolution { imported_module, .. }) = resolution else { + continue; + }; import.imported_module.set(Some(*imported_module), self); if import.is_glob() @@ -812,7 +828,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } for (import, resolution) in import_resolutions { - let ImportResolution { imported_module, kind: resolution_kind } = resolution; + let Some(ImportResolution { imported_module, kind: resolution_kind }) = resolution + else { + continue; + }; match (&import.kind, resolution_kind) { ( @@ -1139,7 +1158,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => unreachable!(), }; - let mut import_decls = PerNS::default(); + let mut decls = PerNS::default(); let mut indeterminate_count = 0; self.per_ns_cm(|mut this, ns| { if bindings[ns].get() != PendingDecl::Pending { @@ -1160,12 +1179,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PendingDecl::Pending } }; - import_decls[ns] = pending_decl; + decls[ns] = pending_decl; }); - let import_resolution = ImportResolution { - imported_module: module, - kind: ImportResolutionKind::Single(import_decls), - }; + let import_resolution = + ImportResolution { imported_module: module, kind: ImportResolutionKind::Single(decls) }; (Some(import_resolution), indeterminate_count) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 13576c24e6c08..e88fd58bc61f7 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -21,10 +21,9 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -use std::cell::Ref; use std::collections::BTreeSet; use std::ops::ControlFlow; -use std::sync::Arc; +use std::sync::{Arc, Once}; use std::{fmt, mem}; use diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; @@ -46,7 +45,9 @@ use rustc_ast::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, default}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard, WorkerLocal}; +use rustc_data_structures::sync::{ + FreezeReadGuard, FreezeWriteGuard, ReadGuard, RwLock, WorkerLocal, +}; use rustc_data_structures::unord::{UnordItems, UnordMap, UnordSet}; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; @@ -655,7 +656,7 @@ struct ModuleData<'ra> { /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'ra>, /// True if this is a module from other crate that needs to be populated on access. - populate_on_access: CacheCell, + populate_on_access: Once, /// Used to disambiguate underscore items (`const _: T = ...`) in the module. underscore_disambiguator: CmCell, @@ -709,7 +710,6 @@ impl<'ra> ModuleData<'ra> { vis: Visibility, arenas: &'ra ResolverArenas<'ra>, ) -> Self { - let is_foreign = !kind.is_local(); let self_decl = match kind { ModuleKind::Def(def_kind, def_id, ..) => { let expn_id = expansion.as_local().unwrap_or(LocalExpnId::ROOT); @@ -721,7 +721,7 @@ impl<'ra> ModuleData<'ra> { parent, kind, lazy_resolutions: Default::default(), - populate_on_access: CacheCell::new(is_foreign), + populate_on_access: Once::new(), underscore_disambiguator: CmCell::new(0), unexpanded_invocations: Default::default(), no_implicit_prelude, @@ -1242,7 +1242,7 @@ struct ExternPreludeEntry<'ra> { 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<( + RwLock<( PendingDecl<'ra>, /* finalized */ bool, /* open flag (namespaced crate) */ bool, @@ -1258,14 +1258,14 @@ impl ExternPreludeEntry<'_> { fn flag() -> Self { ExternPreludeEntry { item_decl: None, - flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, false))), + flag_decl: Some(RwLock::new((PendingDecl::Pending, false, false))), } } fn open_flag() -> Self { ExternPreludeEntry { item_decl: None, - flag_decl: Some(CacheCell::new((PendingDecl::Pending, false, true))), + flag_decl: Some(RwLock::new((PendingDecl::Pending, false, true))), } } @@ -1367,7 +1367,7 @@ pub struct Resolver<'ra, 'tcx> { /// Eagerly populated map of all local non-block modules. local_module_map: FxIndexMap>, /// Lazily populated cache of modules loaded from external crates. - extern_module_map: CacheRefCell>>, + extern_module_map: RwLock>>, /// Maps glob imports to the names of items actually imported. glob_map: FxIndexMap>, @@ -1400,7 +1400,7 @@ pub struct Resolver<'ra, 'tcx> { /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap> = default::fx_hash_map(), /// Lazily populated cache of macro definitions loaded from external crates. - extern_macro_map: CacheRefCell>>, + extern_macro_map: RwLock>>, dummy_ext_bang: &'ra Arc, dummy_ext_derive: &'ra Arc, non_macro_attr: &'ra Arc, @@ -1573,7 +1573,7 @@ impl<'ra> ResolverArenas<'ra> { Interned::new_unchecked(self.name_resolutions.alloc(CmRefCell::new(resolution))) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { - self.dropless.alloc(CacheCell::new(scope)) + self.dropless.alloc(RwLock::new(scope)) } fn alloc_macro_rules_decl(&'ra self, decl: MacroRulesDecl<'ra>) -> &'ra MacroRulesDecl<'ra> { self.dropless.alloc(decl) @@ -1879,28 +1879,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module } - fn new_extern_module( - &self, - parent: Option>, - kind: ModuleKind, - expn_id: ExpnId, - span: Span, - no_implicit_prelude: bool, - ) -> ExternModule<'ra> { - let def_id = kind.def_id(); - let module = ExternModule::new( - parent, - kind, - self.tcx.visibility(def_id), - expn_id, - span, - no_implicit_prelude, - self.arenas, - ); - self.extern_module_map.borrow_mut().insert(def_id, module); - module - } - fn next_node_id(&mut self) -> NodeId { let start = self.next_node_id; let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); @@ -2170,11 +2148,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { - if module.populate_on_access.get() { - module.populate_on_access.set(false); - // unchecked because extern - *module.lazy_resolutions.borrow_mut_unchecked() = - self.build_reduced_graph_external(module.expect_extern()); + if !module.is_local() { + // as long as 1 thread is building this external table, all other threads will wait + module.populate_on_access.call_once(|| { + *module.lazy_resolutions.borrow_mut_unchecked() = + self.build_reduced_graph_external(module.expect_extern()); + }); } &module.0.0.lazy_resolutions } @@ -2183,10 +2162,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &self, module: Module<'ra>, key: BindingKey, - ) -> Option>> { + ) -> Option>> { self.resolutions(module).borrow().get(&key).map(|resolution| resolution.0.borrow()) } + #[track_caller] fn resolution_or_default( &self, module: Module<'ra>, @@ -2424,7 +2404,7 @@ 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, is_open) = flag_decl.get(); + let (pending_decl, finalized, is_open) = *flag_decl.read(); let decl = match pending_decl { PendingDecl::Ready(decl) => { if finalize && !finalized && !is_open { @@ -2459,7 +2439,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } }; - flag_decl.set((PendingDecl::Ready(decl), finalize || finalized, is_open)); + *flag_decl.write() = (PendingDecl::Ready(decl), finalize || finalized, is_open); decl.or_else(|| finalize.then_some(self.dummy_decl)) }) } @@ -2801,43 +2781,60 @@ type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; // FIXME: These are cells for caches that can be populated even during speculative resolution, // and should be replaced with mutexes, atomics, or other synchronized data when migrating to // parallel name resolution. -use std::cell::{Cell as CacheCell, RefCell as CacheRefCell}; +use std::cell::RefCell as CacheRefCell; mod ref_mut { - use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; - use std::fmt; + use std::cell::Cell; + use std::marker::PhantomData; use std::ops::Deref; + use std::{fmt, mem}; + + use rustc_data_structures::sync::{DynSend, DynSync, ReadGuard, RwLock, WriteGuard}; use crate::Resolver; /// A wrapper around a mutable reference that conditionally allows mutable access. pub(crate) struct RefOrMut<'a, T> { - p: &'a mut T, + p: *mut T, mutable: bool, + _marker: PhantomData<&'a mut T>, } + // SAFETY: these are safe because we never allow mutable access to the underlying + // `T` unless the user specifies that it is mutable. + // FIXME: make sure we never construct a mutable `RefOrMut` that is used in Send/Sync contexts? + unsafe impl<'a, T> DynSync for RefOrMut<'a, T> {} + unsafe impl<'a, T> DynSend for RefOrMut<'a, T> {} + impl<'a, T> Deref for RefOrMut<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { - self.p + // SAFETY: `RefOrMUt` is only constructable through a `&mut T`. + unsafe { self.p.as_ref_unchecked() } } } impl<'a, T> AsRef for RefOrMut<'a, T> { fn as_ref(&self) -> &T { - self.p + // SAFETY: `RefOrMUt` is only constructable through a `&mut T`. + unsafe { self.p.as_ref_unchecked() } } } impl<'a, T> RefOrMut<'a, T> { pub(crate) fn new(p: &'a mut T, mutable: bool) -> Self { - RefOrMut { p, mutable } + RefOrMut { p, mutable, _marker: PhantomData } + } + + pub(crate) fn reborrow_ref(&self) -> RefOrMut<'_, T> { + assert!(!self.mutable); + RefOrMut { p: self.p, mutable: self.mutable, _marker: PhantomData } } /// This is needed because this wraps a `&mut T` and is therefore not `Copy`. pub(crate) fn reborrow(&mut self) -> RefOrMut<'_, T> { - RefOrMut { p: self.p, mutable: self.mutable } + RefOrMut { p: self.p, mutable: self.mutable, _marker: PhantomData } } /// Returns a mutable reference to the inner value if allowed. @@ -2848,7 +2845,9 @@ mod ref_mut { pub(crate) fn get_mut(&mut self) -> &mut T { match self.mutable { false => panic!("can't mutably borrow speculative resolver"), - true => self.p, + // SAFETY: `RefOrMut` is only constructable through a `&mut T` and we + // have tested that it may indeed be used as a `&mut T` in this match. + true => unsafe { self.p.as_mut_unchecked() }, } } } @@ -2857,6 +2856,11 @@ mod ref_mut { #[derive(Default)] pub(crate) struct CmCell(Cell); + /// SAFETY: `CmCell` is used within the resolver to make sure we do not mutate during + /// speculative resolution. This is run in parallel, thus we never mutate when in "parallel" + /// mode. It is thus safe to implement `DynSync`. + unsafe impl DynSync for CmCell {} + impl fmt::Debug for CmCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("CmCell").field(&self.get()).finish() @@ -2900,44 +2904,44 @@ mod ref_mut { } } - /// A wrapper around a [`RefCell`] that only allows mutable borrows based on a condition in the resolver. + /// A wrapper around a [`RwLock`] that only allows writes (mutable borrows) based on a condition in the resolver. #[derive(Default)] - pub(crate) struct CmRefCell(RefCell); + pub(crate) struct CmRefCell(RwLock); impl CmRefCell { - pub(crate) const fn new(value: T) -> CmRefCell { - CmRefCell(RefCell::new(value)) + pub(crate) fn new(value: T) -> CmRefCell { + CmRefCell(RwLock::new(value)) } #[track_caller] // FIXME: this should be eliminated in the process of migration // to parallel name resolution. - pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { - self.0.borrow_mut() + pub(crate) fn borrow_mut_unchecked(&self) -> WriteGuard<'_, T> { + self.0.write() } #[track_caller] - pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> RefMut<'_, T> { + pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> WriteGuard<'_, T> { if r.assert_speculative { panic!("not allowed to mutably borrow a `CmRefCell` during speculative resolution"); } - self.0.borrow_mut() + self.0.write() } #[track_caller] pub(crate) fn try_borrow_mut<'ra, 'tcx>( &self, r: &Resolver<'ra, 'tcx>, - ) -> Result, BorrowMutError> { + ) -> Result, ()> { if r.assert_speculative { panic!("not allowed to mutably borrow a `CmRefCell` during speculative resolution"); } - self.0.try_borrow_mut() + self.0.try_write() } #[track_caller] - pub(crate) fn borrow(&self) -> Ref<'_, T> { - self.0.borrow() + pub(crate) fn borrow(&self) -> ReadGuard<'_, T> { + self.0.read() } } @@ -2946,7 +2950,7 @@ mod ref_mut { if r.assert_speculative { panic!("not allowed to mutate a CmRefCell during speculative resolution"); } - self.0.take() + mem::take(&mut *self.0.write()) } } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 595c2fdf011dd..40b28a4c94110 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use rustc_ast::{self as ast, Crate, DelegationSuffixes, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::sync::RwLock; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -42,7 +43,7 @@ use crate::diagnostics::{ use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::Import; use crate::{ - BindingKey, CacheCell, CmResolver, Decl, DeclKind, DeriveData, Determinacy, Finalize, IdentKey, + BindingKey, CmResolver, Decl, DeclKind, DeriveData, Determinacy, Finalize, IdentKey, InvocationParent, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, Res, ResolutionError, Resolver, ScopeSet, Segment, Used, }; @@ -80,7 +81,7 @@ pub(crate) enum MacroRulesScope<'ra> { /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// which usually grow linearly with the number of macro invocations /// in a module (including derives) and hurt performance. -pub(crate) type MacroRulesScopeRef<'ra> = &'ra CacheCell>; +pub(crate) type MacroRulesScopeRef<'ra> = &'ra RwLock>; /// Macro namespace is separated into two sub-namespaces, one for bang macros and /// one for attribute-like macros (attributes, derives).