diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 209a8790def08..ce039242b8983 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -81,9 +81,10 @@ 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. + // NOTE: The `PhantomData` here is a nice trick to ensure that *all* usages of `$t` can be + // inferred to have static lifetimes. This function definition would normally *not* be such + // a definition, but because lifetime elision tends to prefer lifetimes matching arguments + // to a function, the `'static` lifetime inside the `PhantomData` qualifies here. #[allow(mismatched_lifetime_syntaxes)] #[inline] fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { @@ -92,10 +93,12 @@ pub macro thread_local_inner { unsafe { $crate::thread::LocalKey::new(const { + // NOTE: `$t`'s lifetimes can always be inferred in turbofish position if $crate::mem::needs_drop::<$t>() { |__rust_std_internal_init| { #[thread_local] $(#[$align_attr])* + // NOTE: `$t`'s lifetimes can always be inferred `'static` for `static`s 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($crate::marker::PhantomData)) @@ -104,6 +107,7 @@ pub macro thread_local_inner { |__rust_std_internal_init| { #[thread_local] $(#[$align_attr])* + // NOTE: `$t`'s lifetimes can always be inferred `'static` for `static`s 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($crate::marker::PhantomData)) diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 8588fc994350b..48526169e3d5a 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -20,9 +20,10 @@ 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. + // NOTE: The `PhantomData` here is a nice trick to ensure that *all* usages of `$t` can be + // inferred to have static lifetimes. This function definition would normally *not* be such + // a definition, but because lifetime elision tends to prefer lifetimes matching arguments + // to a function, the `'static` lifetime inside the `PhantomData` qualifies here. #[allow(mismatched_lifetime_syntaxes)] #[inline] fn __rust_std_internal_init_fn(_lifetime_elision: $crate::marker::PhantomData<&'static ()>) -> $t { $init } @@ -32,6 +33,7 @@ pub macro thread_local_inner { // in `tests/thread.rs` if these types are renamed. unsafe { $crate::thread::LocalKey::new(|__rust_std_internal_init| { + // NOTE: `$t`'s lifetimes can always be inferred `'static` for `static`s static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::Storage<$t, { $({ // Ensure that attributes have valid syntax @@ -42,6 +44,7 @@ pub macro thread_local_inner { })? #[allow(unused_mut)] + // NOTE: `$t`'s lifetimes can always be inferred in turbofish position 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 diff --git a/tests/ui/thread-local/non-static-lifetimes.rs b/tests/ui/thread-local/non-static-lifetimes.rs new file mode 100644 index 0000000000000..bd87acb6d49d2 --- /dev/null +++ b/tests/ui/thread-local/non-static-lifetimes.rs @@ -0,0 +1,18 @@ +use std::borrow::Cow; + +fn main() { + let x = ""; + std::thread_local!(static A: &str = x); + //~^ ERROR attempt to use a non-constant value in a constant [E0435] +} + +fn test<'a>(x: &'a str) { + std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + //~^ ERROR can't use generic parameters from outer item [E0401] + //~| ERROR can't use generic parameters from outer item [E0401] + //~| ERROR can't use generic parameters from outer item [E0401] + //~| ERROR can't use generic parameters from outer item [E0401] + //~| ERROR can't use generic parameters from outer item [E0401] + + // wow, aren't macros great? +} diff --git a/tests/ui/thread-local/non-static-lifetimes.stderr b/tests/ui/thread-local/non-static-lifetimes.stderr new file mode 100644 index 0000000000000..5f9a0d6889fda --- /dev/null +++ b/tests/ui/thread-local/non-static-lifetimes.stderr @@ -0,0 +1,61 @@ +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/non-static-lifetimes.rs:5:41 + | +LL | std::thread_local!(static A: &str = x); + | - ^ + | | + | this would need to be a `let` + +error[E0401]: can't use generic parameters from outer item + --> $DIR/non-static-lifetimes.rs:10:38 + | +LL | fn test<'a>(x: &'a str) { + | -- lifetime parameter from outer item +LL | std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + | ^^ use of generic parameter from outer item + +error[E0401]: can't use generic parameters from outer item + --> $DIR/non-static-lifetimes.rs:10:38 + | +LL | fn test<'a>(x: &'a str) { + | -- lifetime parameter from outer item +LL | std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + | ---------------------------------^^--------------------------- + | | | + | | use of generic parameter from outer item + | lifetime `'a` is missing in item created through this procedural macro + +error[E0401]: can't use generic parameters from outer item + --> $DIR/non-static-lifetimes.rs:10:38 + | +LL | fn test<'a>(x: &'a str) { + | -- lifetime parameter from outer item +LL | std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + | ^^ use of generic parameter from outer item + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0401]: can't use generic parameters from outer item + --> $DIR/non-static-lifetimes.rs:10:38 + | +LL | fn test<'a>(x: &'a str) { + | -- lifetime parameter from outer item +LL | std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + | ^^ use of generic parameter from outer item + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0401]: can't use generic parameters from outer item + --> $DIR/non-static-lifetimes.rs:10:38 + | +LL | fn test<'a>(x: &'a str) { + | -- lifetime parameter from outer item +LL | std::thread_local!(static A: Cow<'a, str> = Cow::Borrowed("")); + | ^^ use of generic parameter from outer item + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0401, E0435. +For more information about an error, try `rustc --explain E0401`.