Avoid shadowing user provided types or type aliases in thread_local!

By using qualified imports, i.e. `$crate::...::LocalKey`.
This commit is contained in:
许杰友 Jieyou Xu (Joe) 2024-10-18 10:12:11 +08:00
parent d9c4b8d475
commit 7b2320c3df
3 changed files with 49 additions and 22 deletions

View File

@ -49,20 +49,21 @@
#[unstable(feature = "thread_local_internals", issue = "none")] #[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semitransparent"] #[rustc_macro_transparency = "semitransparent"]
pub macro thread_local_inner { pub macro thread_local_inner {
// used to generate the `LocalKey` value for const-initialized thread locals // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` 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 const-initialized thread locals.
(@key $t:ty, const $init:expr) => {{ (@key $t:ty, const $init:expr) => {{
const __INIT: $t = $init; const __INIT: $t = $init;
unsafe { unsafe {
use $crate::mem::needs_drop; $crate::thread::LocalKey::new(const {
use $crate::thread::LocalKey; if $crate::mem::needs_drop::<$t>() {
use $crate::thread::local_impl::EagerStorage;
LocalKey::new(const {
if needs_drop::<$t>() {
|_| { |_| {
#[thread_local] #[thread_local]
static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); static VAL: $crate::thread::local_impl::EagerStorage<$t>
= $crate::thread::local_impl::EagerStorage::new(__INIT);
VAL.get() VAL.get()
} }
} else { } else {
@ -84,21 +85,19 @@ fn __init() -> $t {
} }
unsafe { unsafe {
use $crate::mem::needs_drop; $crate::thread::LocalKey::new(const {
use $crate::thread::LocalKey; if $crate::mem::needs_drop::<$t>() {
use $crate::thread::local_impl::LazyStorage;
LocalKey::new(const {
if needs_drop::<$t>() {
|init| { |init| {
#[thread_local] #[thread_local]
static VAL: LazyStorage<$t, ()> = LazyStorage::new(); static VAL: $crate::thread::local_impl::LazyStorage<$t, ()>
= $crate::thread::local_impl::LazyStorage::new();
VAL.get_or_init(init, __init) VAL.get_or_init(init, __init)
} }
} else { } else {
|init| { |init| {
#[thread_local] #[thread_local]
static VAL: LazyStorage<$t, !> = LazyStorage::new(); static VAL: $crate::thread::local_impl::LazyStorage<$t, !>
= $crate::thread::local_impl::LazyStorage::new();
VAL.get_or_init(init, __init) VAL.get_or_init(init, __init)
} }
} }

View File

@ -15,19 +15,24 @@
$crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR })
}, },
// used to generate the `LocalKey` value for `thread_local!` // 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, $init:expr) => {{
#[inline] #[inline]
fn __init() -> $t { $init } fn __init() -> $t { $init }
// NOTE: this cannot import `LocalKey` or `Storage` 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.
unsafe { unsafe {
use $crate::thread::LocalKey;
use $crate::thread::local_impl::Storage;
// Inlining does not work on windows-gnu due to linking errors around // Inlining does not work on windows-gnu due to linking errors around
// dllimports. See https://github.com/rust-lang/rust/issues/109797. // dllimports. See https://github.com/rust-lang/rust/issues/109797.
LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { $crate::thread::LocalKey::new(#[cfg_attr(windows, inline(never))] |init| {
static VAL: Storage<$t> = Storage::new(); static VAL: $crate::thread::local_impl::Storage<$t>
= $crate::thread::local_impl::Storage::new();
VAL.get(init, __init) VAL.get(init, __init)
}) })
} }

View File

@ -38,6 +38,29 @@ fn thread_local_containing_const_statements() {
assert_eq!(REFCELL.take(), 1); assert_eq!(REFCELL.take(), 1);
} }
#[test]
fn thread_local_hygeiene() {
// Previously `thread_local_inner!` had use imports for `LocalKey`, `Storage`, `EagerStorage`
// and `LazyStorage`. The use imports will shadow a user-provided type or type alias if the
// user-provided type or type alias has the same name. Make sure that this does not happen. See
// <https://github.com/rust-lang/rust/issues/131863>.
//
// NOTE: if the internal implementation details change (i.e. get renamed), this test should be
// updated.
#![allow(dead_code)]
type LocalKey = ();
type Storage = ();
type LazyStorage = ();
type EagerStorage = ();
thread_local! {
static A: LocalKey = const { () };
static B: Storage = const { () };
static C: LazyStorage = const { () };
static D: EagerStorage = const { () };
}
}
#[test] #[test]
// Include an ignore list on purpose, so that new platforms don't miss it // Include an ignore list on purpose, so that new platforms don't miss it
#[cfg_attr( #[cfg_attr(