From 15f42e261340158db4e1d59ce7afe8d9283147b9 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Thu, 12 Mar 2026 14:51:08 +0300 Subject: [PATCH 1/2] Move `FromDyn` closer to `is_dyn_thread_safe` --- compiler/rustc_data_structures/src/marker.rs | 47 ------------------ compiler/rustc_data_structures/src/sync.rs | 51 +++++++++++++++++++- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index e2193a97a0f43..997077ac4402e 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -188,53 +188,6 @@ pub fn assert_dyn_send() {} pub fn assert_dyn_send_val(_t: &T) {} pub fn assert_dyn_send_sync_val(_t: &T) {} -#[derive(Copy, Clone)] -pub struct FromDyn(T); - -impl FromDyn { - #[inline(always)] - pub fn from(val: T) -> Self { - // Check that `sync::is_dyn_thread_safe()` is true on creation so we can - // implement `Send` and `Sync` for this structure when `T` - // implements `DynSend` and `DynSync` respectively. - assert!(crate::sync::is_dyn_thread_safe()); - FromDyn(val) - } - - #[inline(always)] - pub fn derive(&self, val: O) -> FromDyn { - // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` - FromDyn(val) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0 - } -} - -// `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. -unsafe impl Send for FromDyn {} - -// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. -unsafe impl Sync for FromDyn {} - -impl std::ops::Deref for FromDyn { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for FromDyn { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - // A wrapper to convert a struct that is already a `Send` or `Sync` into // an instance of `DynSend` and `DynSync`, since the compiler cannot infer // it automatically in some cases. (e.g. Box) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 327c28fd13890..7967b8c9a452e 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -34,7 +34,7 @@ pub use self::atomic::AtomicU64; pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; #[doc(no_inline)] pub use self::lock::{Lock, LockGuard, Mode}; -pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +pub use self::mode::{is_dyn_thread_safe, FromDyn, set_dyn_thread_safe_mode}; pub use self::parallel::{ broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn, try_par_for_each_in, @@ -64,6 +64,8 @@ mod atomic { mod mode { use std::sync::atomic::{AtomicU8, Ordering}; + use crate::sync::{DynSend, DynSync}; + const UNINITIALIZED: u8 = 0; const DYN_NOT_THREAD_SAFE: u8 = 1; const DYN_THREAD_SAFE: u8 = 2; @@ -99,6 +101,53 @@ mod mode { // Check that the mode was either uninitialized or was already set to the requested mode. assert!(previous.is_ok() || previous == Err(set)); } + + #[derive(Copy, Clone)] + pub struct FromDyn(T); + + impl FromDyn { + #[inline(always)] + pub fn from(val: T) -> Self { + // Check that `sync::is_dyn_thread_safe()` is true on creation so we can + // implement `Send` and `Sync` for this structure when `T` + // implements `DynSend` and `DynSync` respectively. + assert!(crate::sync::is_dyn_thread_safe()); + FromDyn(val) + } + + #[inline(always)] + pub fn derive(&self, val: O) -> FromDyn { + // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` + FromDyn(val) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0 + } + } + + // `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. + unsafe impl Send for FromDyn {} + + // `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. + unsafe impl Sync for FromDyn {} + + impl std::ops::Deref for FromDyn { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for FromDyn { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } } /// This makes locks panic if they are already held. From 3973d9f558ae2104b5f7dc75e1cd8a859447634e Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 17 Mar 2026 14:46:12 +0300 Subject: [PATCH 2/2] Remove FromDyn::from and add check_dyn_thread_safe --- compiler/rustc_data_structures/src/sync.rs | 19 +++--- .../src/sync/parallel.rs | 63 +++++++++++-------- compiler/rustc_interface/src/util.rs | 9 ++- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 7967b8c9a452e..3d5bc85278286 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -34,7 +34,9 @@ pub use self::atomic::AtomicU64; pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; #[doc(no_inline)] pub use self::lock::{Lock, LockGuard, Mode}; -pub use self::mode::{is_dyn_thread_safe, FromDyn, set_dyn_thread_safe_mode}; +pub use self::mode::{ + FromDyn, check_dyn_thread_safe, is_dyn_thread_safe, set_dyn_thread_safe_mode, +}; pub use self::parallel::{ broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn, try_par_for_each_in, @@ -72,6 +74,12 @@ mod mode { static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED); + // Whether thread safety is enabled (due to running under multiple threads). + #[inline] + pub fn check_dyn_thread_safe() -> Option> { + is_dyn_thread_safe().then_some(FromDyn(())) + } + // Whether thread safety is enabled (due to running under multiple threads). #[inline] pub fn is_dyn_thread_safe() -> bool { @@ -106,15 +114,6 @@ mod mode { pub struct FromDyn(T); impl FromDyn { - #[inline(always)] - pub fn from(val: T) -> Self { - // Check that `sync::is_dyn_thread_safe()` is true on creation so we can - // implement `Send` and `Sync` for this structure when `T` - // implements `DynSend` and `DynSync` respectively. - assert!(crate::sync::is_dyn_thread_safe()); - FromDyn(val) - } - #[inline(always)] pub fn derive(&self, val: O) -> FromDyn { // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 2ab4a7f75b6bd..cbf88efea18d1 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -57,8 +57,8 @@ where } pub fn spawn(func: impl FnOnce() + DynSend + 'static) { - if mode::is_dyn_thread_safe() { - let func = FromDyn::from(func); + if let Some(proof) = mode::check_dyn_thread_safe() { + let func = proof.derive(func); rustc_thread_pool::spawn(|| { (func.into_inner())(); }); @@ -73,8 +73,8 @@ pub fn spawn(func: impl FnOnce() + DynSend + 'static) { /// Use that for the longest running function for better scheduling. pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { parallel_guard(|guard: &ParallelGuard| { - if mode::is_dyn_thread_safe() { - let funcs = FromDyn::from(funcs); + if let Some(proof) = mode::check_dyn_thread_safe() { + let funcs = proof.derive(funcs); rustc_thread_pool::scope(|s| { let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else { return; @@ -84,7 +84,7 @@ pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { // order when using a single thread. This ensures the execution order matches // that of a single threaded rustc. for f in rest.iter_mut().rev() { - let f = FromDyn::from(f); + let f = proof.derive(f); s.spawn(|_| { guard.run(|| (f.into_inner())()); }); @@ -108,13 +108,13 @@ where A: FnOnce() -> RA + DynSend, B: FnOnce() -> RB + DynSend, { - if mode::is_dyn_thread_safe() { - let oper_a = FromDyn::from(oper_a); - let oper_b = FromDyn::from(oper_b); + if let Some(proof) = mode::check_dyn_thread_safe() { + let oper_a = proof.derive(oper_a); + let oper_b = proof.derive(oper_b); let (a, b) = parallel_guard(|guard| { rustc_thread_pool::join( - move || guard.run(move || FromDyn::from(oper_a.into_inner()())), - move || guard.run(move || FromDyn::from(oper_b.into_inner()())), + move || guard.run(move || proof.derive(oper_a.into_inner()())), + move || guard.run(move || proof.derive(oper_b.into_inner()())), ) }); (a.unwrap().into_inner(), b.unwrap().into_inner()) @@ -127,8 +127,9 @@ fn par_slice( items: &mut [I], guard: &ParallelGuard, for_each: impl Fn(&mut I) + DynSync + DynSend, + proof: FromDyn<()>, ) { - let for_each = FromDyn::from(for_each); + let for_each = proof.derive(for_each); let mut items = for_each.derive(items); rustc_thread_pool::scope(|s| { let proof = items.derive(()); @@ -150,9 +151,9 @@ pub fn par_for_each_in>( for_each: impl Fn(&I) + DynSync + DynSend, ) { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { + if let Some(proof) = mode::check_dyn_thread_safe() { let mut items: Vec<_> = t.into_iter().collect(); - par_slice(&mut items, guard, |i| for_each(&*i)) + par_slice(&mut items, guard, |i| for_each(&*i), proof) } else { t.into_iter().for_each(|i| { guard.run(|| for_each(&i)); @@ -173,16 +174,21 @@ where ::Item: DynSend, { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { + if let Some(proof) = mode::check_dyn_thread_safe() { let mut items: Vec<_> = t.into_iter().collect(); let error = Mutex::new(None); - par_slice(&mut items, guard, |i| { - if let Err(err) = for_each(&*i) { - *error.lock() = Some(err); - } - }); + par_slice( + &mut items, + guard, + |i| { + if let Err(err) = for_each(&*i) { + *error.lock() = Some(err); + } + }, + proof, + ); if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) } } else { @@ -196,15 +202,20 @@ pub fn par_map, R: DynSend, C: FromIterato map: impl Fn(I) -> R + DynSync + DynSend, ) -> C { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { - let map = FromDyn::from(map); + if let Some(proof) = mode::check_dyn_thread_safe() { + let map = proof.derive(map); let mut items: Vec<(Option, Option)> = t.into_iter().map(|i| (Some(i), None)).collect(); - par_slice(&mut items, guard, |i| { - i.1 = Some(map(i.0.take().unwrap())); - }); + par_slice( + &mut items, + guard, + |i| { + i.1 = Some(map(i.0.take().unwrap())); + }, + proof, + ); items.into_iter().filter_map(|i| i.1).collect() } else { @@ -214,8 +225,8 @@ pub fn par_map, R: DynSend, C: FromIterato } pub fn broadcast(op: impl Fn(usize) -> R + DynSync) -> Vec { - if mode::is_dyn_thread_safe() { - let op = FromDyn::from(op); + if let Some(proof) = mode::check_dyn_thread_safe() { + let op = proof.derive(op); let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index()))); results.into_iter().map(|r| r.into_inner()).collect() } else { diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index c5344ee66cd03..0cd0275f96bbb 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -183,7 +183,6 @@ pub(crate) fn run_in_thread_pool_with_globals< use std::process; use rustc_data_structures::defer; - use rustc_data_structures::sync::FromDyn; use rustc_middle::ty::tls; use rustc_query_impl::break_query_cycles; @@ -191,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); - if !sync::is_dyn_thread_safe() { + let Some(proof) = sync::check_dyn_thread_safe() else { return run_in_thread_with_globals( thread_stack_size, edition, @@ -204,9 +203,9 @@ pub(crate) fn run_in_thread_pool_with_globals< f(current_gcx, jobserver_proxy) }, ); - } + }; - let current_gcx = FromDyn::from(CurrentGcx::new()); + let current_gcx = proof.derive(CurrentGcx::new()); let current_gcx2 = current_gcx.clone(); let proxy = Proxy::new(); @@ -278,7 +277,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; // `Send` in the parallel compiler. rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || { rustc_span::with_session_globals(|session_globals| { - let session_globals = FromDyn::from(session_globals); + let session_globals = proof.derive(session_globals); builder .build_scoped( // Initialize each new worker thread when created.