diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 4dad81685a94e..209a8790def08 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -81,8 +81,12 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } @@ -94,7 +98,7 @@ pub macro thread_local_inner { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, ()> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) } } else { |__rust_std_internal_init| { @@ -102,7 +106,7 @@ pub macro thread_local_inner { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t, !> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get_or_init(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) } } }) diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index f9d0ed384fe24..0927595280f1f 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -31,14 +31,18 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { $init } + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } unsafe { $crate::thread::LocalKey::new(|__rust_std_internal_init| { $(#[$align_attr])* static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new(); - __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) }) } }}, diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index bc044aafa983c..289ac33bb7b76 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -20,8 +20,12 @@ pub macro thread_local_inner { // used to generate the `LocalKey` value for `thread_local!`. (@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{ + // We intentionally have an argument-position `'static` lifetime so that elided lifetimes in `$t` + // become `'static` like they do for `const`s and `static`s, including in the other two + // `thread_local!` implementations. + #[allow(mismatched_lifetime_syntaxes)] #[inline] - fn __rust_std_internal_init_fn() -> $t { $init } + fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $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 @@ -43,7 +47,7 @@ pub macro thread_local_inner { final_align }> = $crate::thread::local_impl::Storage::new(); - __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn) + __RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, || __rust_std_internal_init_fn($crate::marker::PhantomData)) }) } }}, diff --git a/tests/ui/suggestions/missing-lifetime-specifier.rs b/tests/ui/suggestions/missing-lifetime-specifier.rs deleted file mode 100644 index 93416a9bc2ac7..0000000000000 --- a/tests/ui/suggestions/missing-lifetime-specifier.rs +++ /dev/null @@ -1,54 +0,0 @@ -// The specific errors produced depend the thread-local implementation. -// Run only on platforms with "fast" TLS. -//@ ignore-wasm globals are used instead of thread locals -//@ ignore-emscripten globals are used instead of thread locals -//@ ignore-android does not use #[thread_local] -//@ ignore-nto does not use #[thread_local] -//@ ignore-qnx does not use #[thread_local] -// Different number of duplicated diagnostics on different targets -//@ compile-flags: -Zdeduplicate-diagnostics=yes - -use std::cell::RefCell; -use std::collections::HashMap; - -pub union Foo<'t, 'k> { - i: &'t i64, - f: &'k f64, -} -trait Bar<'t, 'k> {} - -pub union Qux<'t, 'k, I> { - i: &'t I, - f: &'k I, -} -trait Tar<'t, 'k, I> {} - -thread_local! { - static a: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers -} -thread_local! { - static b: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers -} -thread_local! { - static c: RefCell>>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers -} -thread_local! { - static d: RefCell>>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers -} - -thread_local! { - static e: RefCell>>>> = RefCell::new(HashMap::new()); - //~^ ERROR union takes 2 lifetime arguments but 1 lifetime argument -} -thread_local! { - static f: RefCell>>>> = - RefCell::new(HashMap::new()); - //~^^ ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR missing lifetime specifier -} - -fn main() {} diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr deleted file mode 100644 index 4c50f2a43e270..0000000000000 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ /dev/null @@ -1,109 +0,0 @@ -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:27:44 - | -LL | static a: RefCell>>> = RefCell::new(HashMap::new()); - | ^^^ expected 2 lifetime parameters - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values - | -LL | static a: RefCell>>>> = RefCell::new(HashMap::new()); - | ++++++++++++++++++ - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:31:44 - | -LL | static b: RefCell>>> = RefCell::new(HashMap::new()); - | ^ ^^^ expected 2 lifetime parameters - | | - | expected named lifetime parameter - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` - | -LL | static b: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++ ++++++++++++++++++ - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:35:47 - | -LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); - | ^ expected 2 lifetime parameters - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values - | -LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++++++++++ - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:39:44 - | -LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | ^ ^ expected 2 lifetime parameters - | | - | expected named lifetime parameter - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` - | -LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++ +++++++++++++++++ - -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:48:44 - | -LL | static f: RefCell>>>> = - | ^ expected named lifetime parameter - | - = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from -help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` - | -LL | static f: RefCell>>>> = - | +++++++ -help: instead, you are more likely to want to return an owned value - | -LL - static f: RefCell>>>> = -LL + static f: RefCell>>>> = - | - -error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:44:44 - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:20:11 - | -LL | pub union Qux<'t, 'k, I> { - | ^^^ -- -- -help: add missing lifetime argument - | -LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); - | +++++++++ - -error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:48:49 - | -LL | static f: RefCell>>>> = - | ^^^ ------- supplied 1 lifetime argument - | | - | expected 2 lifetime arguments - | -note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:24:7 - | -LL | trait Tar<'t, 'k, I> {} - | ^^^ -- -- -help: add missing lifetime argument - | -LL | static f: RefCell>>>> = - | +++++++++ - -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0106, E0107. -For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/thread-local/lifetime-elision.rs b/tests/ui/thread-local/lifetime-elision.rs new file mode 100644 index 0000000000000..3b960d18764cb --- /dev/null +++ b/tests/ui/thread-local/lifetime-elision.rs @@ -0,0 +1,14 @@ +// Test that `thread_local!` defaults elided lifetimes in the type to `'static`. +// Regression test for 159358. +// Ideally this should be tested at least one target using each `thread_local!` +// implementation (no-threads, native, os), but currently there are no Tier 1 `no-threads` +// targets. `x86_64-unknown-linux-gnu` is a `native` target and `x86_64-pc-windows-gnu` +// is a `os` target, so those are covered (as of 2026-07-21). +//@ check-pass + +// Const initializer +std::thread_local!(static A: &str = const { "" }); +// Non-const initializer +std::thread_local!(static B: &str = ""); + +fn main() {}