diff options
Diffstat (limited to 'library')
| -rw-r--r-- | library/alloc/src/boxed.rs | 31 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/mod.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/native/eager.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/native/lazy.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/native/mod.rs | 12 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/no_threads.rs | 69 | ||||
| -rw-r--r-- | library/std/src/sys/thread_local/os.rs | 155 | ||||
| -rw-r--r-- | library/std/src/thread/local.rs | 233 | ||||
| -rw-r--r-- | library/std/src/thread/mod.rs | 1 | ||||
| -rw-r--r-- | library/std/tests/thread.rs | 2 |
10 files changed, 441 insertions, 72 deletions
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 5a63d90b95f..49ff768bed1 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -619,6 +619,37 @@ impl<T, A: Allocator> Box<T, A> { pub fn into_inner(boxed: Self) -> T { *boxed } + + /// Consumes the `Box` without consuming its allocation, returning the wrapped value and a `Box` + /// to the uninitialized memory where the wrapped value used to live. + /// + /// This can be used together with [`write`](Box::write) to reuse the allocation for multiple + /// boxed values. + /// + /// # Examples + /// + /// ``` + /// #![feature(box_take)] + /// + /// let c = Box::new(5); + /// + /// // take the value out of the box + /// let (value, uninit) = Box::take(c); + /// assert_eq!(value, 5); + /// + /// // reuse the box for a second value + /// let c = Box::write(uninit, 6); + /// assert_eq!(*c, 6); + /// ``` + #[unstable(feature = "box_take", issue = "147212")] + pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) { + unsafe { + let (raw, alloc) = Box::into_raw_with_allocator(boxed); + let value = raw.read(); + let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc); + (value, uninit) + } + } } impl<T> Box<[T]> { diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index d5c795093cf..f7f051b1add 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -42,7 +42,7 @@ cfg_select! { } _ => { mod os; - pub use os::{Storage, thread_local_inner}; + pub use os::{Storage, thread_local_inner, value_align}; pub(crate) use os::{LocalPointer, local_pointer}; } } diff --git a/library/std/src/sys/thread_local/native/eager.rs b/library/std/src/sys/thread_local/native/eager.rs index fd48c4f7202..23abad645c1 100644 --- a/library/std/src/sys/thread_local/native/eager.rs +++ b/library/std/src/sys/thread_local/native/eager.rs @@ -10,9 +10,11 @@ enum State { } #[allow(missing_debug_implementations)] +#[repr(C)] pub struct Storage<T> { - state: Cell<State>, + // This field must be first, for correctness of `#[rustc_align_static]` val: UnsafeCell<T>, + state: Cell<State>, } impl<T> Storage<T> { diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs index b556dd9aa25..02939a74fc0 100644 --- a/library/std/src/sys/thread_local/native/lazy.rs +++ b/library/std/src/sys/thread_local/native/lazy.rs @@ -27,9 +27,11 @@ enum State<D> { } #[allow(missing_debug_implementations)] +#[repr(C)] pub struct Storage<T, D> { - state: Cell<State<D>>, + // This field must be first, for correctness of `#[rustc_align_static]` value: UnsafeCell<MaybeUninit<T>>, + state: Cell<State<D>>, } impl<T, D> Storage<T, D> diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index a5dffe3c458..5dc14240804 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -54,7 +54,7 @@ pub macro thread_local_inner { // test in `tests/thread.rs` if these types are renamed. // Used to generate the `LocalKey` value for const-initialized thread locals. - (@key $t:ty, const $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ const __INIT: $t = $init; unsafe { @@ -62,6 +62,7 @@ pub macro thread_local_inner { if $crate::mem::needs_drop::<$t>() { |_| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::EagerStorage<$t> = $crate::thread::local_impl::EagerStorage::new(__INIT); VAL.get() @@ -69,6 +70,7 @@ pub macro thread_local_inner { } else { |_| { #[thread_local] + $(#[$align_attr])* static VAL: $t = __INIT; &VAL } @@ -78,7 +80,7 @@ pub macro thread_local_inner { }}, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] fn __init() -> $t { $init @@ -89,6 +91,7 @@ pub macro thread_local_inner { if $crate::mem::needs_drop::<$t>() { |init| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) @@ -96,6 +99,7 @@ pub macro thread_local_inner { } else { |init| { #[thread_local] + $(#[$align_attr])* static VAL: $crate::thread::local_impl::LazyStorage<$t, !> = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) @@ -104,10 +108,6 @@ pub macro thread_local_inner { }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, } #[rustc_macro_transparency = "semitransparent"] diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 4da01a84acf..409dfb19518 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -2,6 +2,7 @@ //! thread locals and we can instead just use plain statics! use crate::cell::{Cell, UnsafeCell}; +use crate::mem::MaybeUninit; use crate::ptr; #[doc(hidden)] @@ -11,12 +12,13 @@ use crate::ptr; #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ const __INIT: $t = $init; // NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|_| { + $(#[$align_attr])* static VAL: $crate::thread::local_impl::EagerStorage<$t> = $crate::thread::local_impl::EagerStorage { value: __INIT }; &VAL.value @@ -25,27 +27,22 @@ pub macro thread_local_inner { }}, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ #[inline] fn __init() -> $t { $init } unsafe { - use $crate::thread::LocalKey; - use $crate::thread::local_impl::LazyStorage; - - LocalKey::new(|init| { - static VAL: LazyStorage<$t> = LazyStorage::new(); + $crate::thread::LocalKey::new(|init| { + $(#[$align_attr])* + static VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); VAL.get(init, __init) }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); - }, } #[allow(missing_debug_implementations)] +#[repr(transparent)] // Required for correctness of `#[rustc_align_static]` pub struct EagerStorage<T> { pub value: T, } @@ -53,14 +50,27 @@ pub struct EagerStorage<T> { // SAFETY: the target doesn't have threads. unsafe impl<T> Sync for EagerStorage<T> {} +#[derive(Clone, Copy, PartialEq, Eq)] +enum State { + Initial, + Alive, + Destroying, +} + #[allow(missing_debug_implementations)] +#[repr(C)] pub struct LazyStorage<T> { - value: UnsafeCell<Option<T>>, + // This field must be first, for correctness of `#[rustc_align_static]` + value: UnsafeCell<MaybeUninit<T>>, + state: Cell<State>, } impl<T> LazyStorage<T> { pub const fn new() -> LazyStorage<T> { - LazyStorage { value: UnsafeCell::new(None) } + LazyStorage { + value: UnsafeCell::new(MaybeUninit::uninit()), + state: Cell::new(State::Initial), + } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -70,24 +80,39 @@ impl<T> LazyStorage<T> { /// has occurred. #[inline] pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T { - let value = unsafe { &*self.value.get() }; - match value { - Some(v) => v, - None => self.initialize(i, f), + if self.state.get() == State::Alive { + self.value.get() as *const T + } else { + self.initialize(i, f) } } #[cold] fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T { let value = i.and_then(Option::take).unwrap_or_else(f); - // Destroy the old value, after updating the TLS variable as the - // destructor might reference it. + + // Destroy the old value if it is initialized // FIXME(#110897): maybe panic on recursive initialization. + if self.state.get() == State::Alive { + self.state.set(State::Destroying); + // Safety: we check for no initialization during drop below + unsafe { + ptr::drop_in_place(self.value.get() as *mut T); + } + self.state.set(State::Initial); + } + + // Guard against initialization during drop + if self.state.get() == State::Destroying { + panic!("Attempted to initialize thread-local while it is being dropped"); + } + unsafe { - self.value.get().replace(Some(value)); + self.value.get().write(MaybeUninit::new(value)); } - // SAFETY: we just set this to `Some`. - unsafe { (*self.value.get()).as_ref().unwrap_unchecked() } + self.state.set(State::Alive); + + self.value.get() as *const T } } diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index fe6af27db3a..88bb5ae7c65 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,8 +1,12 @@ use super::key::{Key, LazyKey, get, set}; use super::{abort_on_dtor_unwind, guard}; +use crate::alloc::{self, Layout}; use crate::cell::Cell; use crate::marker::PhantomData; -use crate::ptr; +use crate::mem::ManuallyDrop; +use crate::ops::Deref; +use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; +use crate::ptr::{self, NonNull}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -10,17 +14,12 @@ use crate::ptr; #[unstable(feature = "thread_local_internals", issue = "none")] #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => { - $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) - }, - // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user // provided type or type alias with a matching name. Please update the shadowing test in // `tests/thread.rs` if these types are renamed. // used to generate the `LocalKey` value for `thread_local!`. - (@key $t:ty, $init:expr) => {{ + (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{ #[inline] fn __init() -> $t { $init } @@ -29,37 +28,148 @@ pub macro thread_local_inner { // in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|init| { - static VAL: $crate::thread::local_impl::Storage<$t> + static VAL: $crate::thread::local_impl::Storage<$t, { + $({ + // Ensure that attributes have valid syntax + // and that the proper feature gate is enabled + $(#[$($align_attr)*])+ + #[allow(unused)] + static DUMMY: () = (); + })? + + #[allow(unused_mut)] + let mut final_align = $crate::thread::local_impl::value_align::<$t>(); + $($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)? + final_align + }> = $crate::thread::local_impl::Storage::new(); VAL.get(init, __init) }) } }}, - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); + + // process a single `rustc_align_static` attribute + (@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => { + let new_align: $crate::primitive::usize = $($align)*; + if new_align > $final_align { + $final_align = new_align; + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + // process a single `cfg_attr` attribute + // by translating it into a `cfg`ed block and recursing. + // https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate + + (@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg(true)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + (@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg(false)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? + }, + + (@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + #[cfg($cfg_pred)] + { + $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); + } + + $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? }, } /// Use a regular global static to store this key; the state provided will then be /// thread-local. +/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::<T>`. #[allow(missing_debug_implementations)] -pub struct Storage<T> { +pub struct Storage<T, const ALIGN: usize> { key: LazyKey, marker: PhantomData<Cell<T>>, } -unsafe impl<T> Sync for Storage<T> {} +unsafe impl<T, const ALIGN: usize> Sync for Storage<T, ALIGN> {} +#[repr(C)] struct Value<T: 'static> { + // This field must be first, for correctness of `#[rustc_align_static]` value: T, // INVARIANT: if this value is stored under a TLS key, `key` must be that `key`. key: Key, } -impl<T: 'static> Storage<T> { - pub const fn new() -> Storage<T> { - Storage { key: LazyKey::new(Some(destroy_value::<T>)), marker: PhantomData } +pub const fn value_align<T: 'static>() -> usize { + crate::mem::align_of::<Value<T>>() +} + +/// Equivalent to `Box<Value<T>>`, but potentially over-aligned. +struct AlignedBox<T: 'static, const ALIGN: usize> { + ptr: NonNull<Value<T>>, +} + +impl<T: 'static, const ALIGN: usize> AlignedBox<T, ALIGN> { + #[inline] + fn new(v: Value<T>) -> Self { + let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap(); + + let ptr: *mut Value<T> = (unsafe { alloc::alloc(layout) }).cast(); + let Some(ptr) = NonNull::new(ptr) else { + alloc::handle_alloc_error(layout); + }; + unsafe { ptr.write(v) }; + Self { ptr } + } + + #[inline] + fn into_raw(b: Self) -> *mut Value<T> { + let md = ManuallyDrop::new(b); + md.ptr.as_ptr() + } + + #[inline] + unsafe fn from_raw(ptr: *mut Value<T>) -> Self { + Self { ptr: unsafe { NonNull::new_unchecked(ptr) } } + } +} + +impl<T: 'static, const ALIGN: usize> Deref for AlignedBox<T, ALIGN> { + type Target = Value<T>; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { &*(self.ptr.as_ptr()) } + } +} + +impl<T: 'static, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> { + #[inline] + fn drop(&mut self) { + let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap(); + + unsafe { + let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place())); + alloc::dealloc(self.ptr.as_ptr().cast(), layout); + if let Err(payload) = unwind_result { + resume_unwind(payload); + } + } + } +} + +impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> { + pub const fn new() -> Storage<T, ALIGN> { + Storage { key: LazyKey::new(Some(destroy_value::<T, ALIGN>)), marker: PhantomData } } /// Gets a pointer to the TLS value, potentially initializing it with the @@ -95,8 +205,11 @@ impl<T: 'static> Storage<T> { return ptr::null(); } - let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); - let ptr = Box::into_raw(value); + let value = AlignedBox::<T, ALIGN>::new(Value { + value: i.and_then(Option::take).unwrap_or_else(f), + key, + }); + let ptr = AlignedBox::into_raw(value); // SAFETY: // * key came from a `LazyKey` and is thus correct. @@ -114,7 +227,7 @@ impl<T: 'static> Storage<T> { // initializer has already returned and the next scope only starts // after we return the pointer. Therefore, there can be no references // to the old value. - drop(unsafe { Box::from_raw(old) }); + drop(unsafe { AlignedBox::<T, ALIGN>::from_raw(old) }); } // SAFETY: We just created this value above. @@ -122,7 +235,7 @@ impl<T: 'static> Storage<T> { } } -unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) { +unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8) { // SAFETY: // // The OS TLS ensures that this key contains a null value when this @@ -133,7 +246,7 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) { // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. abort_on_dtor_unwind(|| { - let ptr = unsafe { Box::from_raw(ptr as *mut Value<T>) }; + let ptr = unsafe { AlignedBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) }; let key = ptr.key; // SAFETY: `key` is the TLS key `ptr` was stored under. unsafe { set(key, ptr::without_provenance_mut(1)) }; diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 0a6f2e5d508..4259a4d1f3b 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -132,6 +132,216 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { } } +#[doc(hidden)] +#[allow_internal_unstable(thread_local_internals)] +#[unstable(feature = "thread_local_internals", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +pub macro thread_local_process_attrs { + + // Parse `cfg_attr` to figure out whether it's a `rustc_align_static`. + // Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested. + + // finished parsing the `cfg_attr`, it had no `rustc_align_static` + ( + [] [$(#[$($prev_other_attrs:tt)*])*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]]; + $($rest)* + ); + ), + + // finished parsing the `cfg_attr`, it had nothing but `rustc_align_static` + ( + [$(#[$($prev_align_attrs:tt)*])+] []; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*]; + $($rest)* + ); + ), + + // finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs + ( + [$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] }; + [$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*]; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]]; + $($rest)* + ); + ), + + // it's a `rustc_align_static` + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's a nested `cfg_attr(true, ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's a nested `cfg_attr(false, ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + + // it's a nested `cfg_attr(..., ...)`; recurse into RHS + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + // it's some other attribute + ( + [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] }; + $($rest:tt)* + ) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]]; + @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; + $($rest)* + ); + ), + + + // Separate attributes into `rustc_align_static` and everything else: + + // `rustc_align_static` attribute + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(true, ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(false, ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // `cfg_attr(..., ...)` attribute; parse it + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [] []; + @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] }; + [$($prev_align_attrs)*] [$($prev_other_attrs)*]; + $($rest)* + ); + ), + + // doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+]; + $vis static $($rest)* + ); + ), + + // 8 lines of doc comment; process them all at once to avoid blowing recursion limit + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; + #[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*] + #[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*] + $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* + #[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*] + #[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]]; + $($rest)* + ); + ), + + // other attribute + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => ( + $crate::thread::local_impl::thread_local_process_attrs!( + [$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]]; + $($rest)* + ); + ), + + + // Delegate to `thread_local_inner` once attributes are fully categorized: + + // process `const` declaration and recurse + ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => ( + $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init); + + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? + ), + + // process non-`const` declaration and recurse + ([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => ( + $($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init); + + $($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)? + ), +} + /// Declare a new thread local storage key of type [`std::thread::LocalKey`]. /// /// # Syntax @@ -182,28 +392,11 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] macro_rules! thread_local { - // empty (base case for the recursion) () => {}; - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); - $crate::thread_local!($($rest)*); - ); - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); - ); - - // process multiple declarations - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init); - $crate::thread_local!($($rest)*); - ); - - // handle a single declaration - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( - $crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init); - ); + ($($tt:tt)+) => { + $crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+); + }; } /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 1768369792a..fd7cce3f97d 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -207,6 +207,7 @@ pub use self::local::{AccessError, LocalKey}; #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { + pub use super::local::thread_local_process_attrs; pub use crate::sys::thread_local::*; } diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 29f220d8a70..dc8eadd7514 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -66,6 +66,8 @@ fn thread_local_hygeiene() { type Storage = (); type LazyStorage = (); type EagerStorage = (); + #[allow(non_camel_case_types)] + type usize = (); thread_local! { static A: LocalKey = const { () }; static B: Storage = const { () }; |
