Code change:
/// information. #[macro_export] #[allow_internal_unstable] <del> #[cfg(not(no_elf_tls))] </del> macro_rules! scoped_thread_local { (static $name:ident: $t:ty) => ( <del> #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] </del> static $name: ::std::thread::ScopedKey<$t> = <del> ::std::thread::ScopedKey::new(); </del> <ins> __scoped_thread_local_inner!($t); </ins> ); (pub static $name:ident: $t:ty) => ( <del> #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] </del> pub static $name: ::std::thread::ScopedKey<$t> = <del> ::std::thread::ScopedKey::new(); </del> <ins> __scoped_thread_local_inner!($t); </ins> ); } <ins> #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "should not be necessary")] </ins> #[macro_export] #[allow_internal_unstable] #[cfg(no_elf_tls)] <del> macro_rules! scoped_thread_local { (static $name:ident: $t:ty) => ( static $name: ::std::thread::ScopedKey<$t> = ::std::thread::ScopedKey::new(); ); (pub static $name:ident: $t:ty) => ( pub static $name: ::std::thread::ScopedKey<$t> = ::std::thread::ScopedKey::new(); ); </del> <ins> macro_rules! __scoped_thread_local_inner { ($t:ty) => {{ static _KEY: ::std::thread::__ScopedKeyInner<$t> = ::std::thread::__ScopedKeyInner::new(); fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY } ::std::thread::ScopedKey::new(_getit) }} } #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "should not be necessary")] #[macro_export] #[allow_internal_unstable] #[cfg(not(no_elf_tls))] macro_rules! __scoped_thread_local_inner { ($t:ty) => {{ #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios", target_os = "openbsd", target_arch = "aarch64")), thread_local)] static _KEY: ::std::thread::__ScopedKeyInner<$t> = ::std::thread::__ScopedKeyInner::new(); fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY } ::std::thread::ScopedKey::new(_getit) }} </ins> } #[unstable(feature = "scoped_tls",

Related discussion:
text: e.g., wouldn't that make it difficult to have a thread local point to another thread local? Maybe I just have to think about it. (BTW, I'm aware we're just talking about the low-level API :)).
in a , borrows are , I didn't even think of that. First off, a global static shouldn't be allowed to borrow a static. And in general, having borrows in a is going to be difficult to support, except for non- data, because they would be right now. We could disallow borrows or we could give some kind of scope to those borrows. Which makes me think, as a compromise between a lifetime and , there could be a pseudo-(rvalue)-scope. Combined with the ability to leave out in globals' type (or not having a type at all), the scope should bubble up into users of those statics. They would behave as if they were borrowed before everything else in the function where they're used, but still not (and they can't be returned from functions).
There's a few intertwined issues here at play, so I just want to quickly summarize the way I see things: This does not affect as it which has an , which while sound should be fixed, allowing removal of the block for . A borrow of a static should not yield something with the lifetime, as that is clearly not sound. I'm not sure I follow why we might require the types in a are not , I haven't quite followed that. Due the cross-platform requirements of std, however, I believe the best solution here is to use the same strategy as with an indirect accessor to ensure the semantics are the same across platforms that have and those that don't (where a normal is used).
triage: P-medium This is an unstable API so it's not super-high priority to fix, but we should defnitely fix!
requiring that a is not is an easy way to ensure that a borrow of it, while , cannot escape the current thread. I've changed my mind and prefer an rvalue borrow semantic for , but it still is the case that and use non- statics (ignoring the workarounds for ). If was supported everywhere, having rvalue borrow semantics wouldn't hurt the std TLS API (aside from stability issues with ), as the access patterns are already through anonymous lifetimes (references passed to closures).