Change:
Feature gate: This is a tracking issue for the -initialization of thread locals in the standard library in The API here is intended to be exclusively within the syntax of the preexisting macro. The initialization expression for thread local keys can now be surrounded with to indicate that the value should be const-evaluated and initialized. Note that this also supports the multi-definition form: [x] Implementation: [x] Final commenting period (FCP) [x] Stabilization PRSyntax bikeshed - is the use of what is likely to be a future-feature, blocks, ok here? Are there other options for syntax?
I've requested this be tagged for discussion for stabilization, but I figured I'd want to paste some background here as well. My personal main motivation for this is optimizing the pieces of Wasmtime where a host calls into a WebAssembly module. Part of that is frobbing some TLS values as we enter into wasm, and this frobbing is actually marked for the poor cross-crate codegen that TLS has otherwise. This outlining plus the extra checks for whether or not TLS is initialized is what I'm trying to optimize. In benchmarks locally the call overhead of host-wasm decreases from 20ns to 18ns (10% reduction) with the usage of const-initialized-thread-locals (which are also 'd since they have good codegen). This isn't really gonna make or break anyone's day since it's a pretty small difference, but nevertheless I'd love to get it out of the way so I don't have to worry about it again. Another data point I see linked here is that for some . There was some when I originally asked to stabilize. One thing I'll clarify is that this doesn't literally use blocks in the expanded code, the macro is simply recognizing it as input syntax. I believe one gotcha of this is that if you have in a macro and in the future is something like I don't think that it will use the const-initialized version of were you to do something like since I don't think that rustc splits apart tokens to match sub-patterns in a macro. This will, however, work for literal declarations like Overall the biggest thing to decide here I think is the syntax. One alternative is a whole new macro, but I think that it'd be ideal if we could keep it in one macro to not have to worry about multiple. Otherwise though I'm happy to implement whatever syntax.
Discussed in the library api team meeting. We'd like to sign off on this.
This seems like a fine thing to stabilize; however, just to be clear, is this feature any use without blocks? I guess that one can still stabilize to other constants (e.g., simple integer values)?
This doesn't actually use blocks at the compiler level, it's basically just surface syntax to the macro itself. In that sense this should still be useful even if Rust never gets blocks because it would still trigger the specialized implementation for this macro where the initialization expression is a constant known at compile time
It seems a little funky to me to stabilize a syntax that, at a surface level, matches that of a pre-existing unstable feature (const blocks). Even though it may not require const blocks under the hood, in the long-term I think users would expect that the set of things allowed inside would match the rules for what could be present in . That is, if we decided that some surface syntax or semantics should be different inside blocks, I'd also want it to be different in these not-const-blocks. Are we confident that we'll be able to provide matching semantics in each of these contexts in the future? That is, that the not-const-block in doesn't allow anything that a block wouldn't allow?
Oh, wait, maybe I misunderstood. I didn't realize that was part of the macro, I expected it to accept "any expression". That said, I'll have to review, but I would expect that indeed it should accept the same set of code?
For reference, the is (trimmed down a bit): The actual expanded code ends up doing: I believe what is describing is indeed a risk of using the syntax in the macro without literally using it in the expanded code. If for whatever reason the code allowed inside of differed from that in then this macro expansion would not reflect that becaue the expansion doesn't actually use . Personally I'm fine implementing some other syntax if there's too many worries about using the syntax. What I would probably suggest is an alternative macro, , or something like that (although I realize there are downsides to this as well and would welcome other thoughts on alternative syntaxes)
fcp merge We . Consensus was that we should move forward with stabilization here, so I am beginning an FCP. Here are our notes: What is the change? Macro looks for and changes the code that gets generated currently this gets generated to Why this change? Codegen improvement: the initial value of a thread-local is constant and not re-evaluated per thread Expected outcome? We stabilize the macro and eventually move it to use inline constants What is the risk? The macro currently accepts syntax but desugars to a If the set of expressions accepted as initial value for a static diverged from set that can appear in this would be weird, but we consider this unlikely. Moreover, if that were to occur in the future before const is stabilied, we could add into the desugaring and use "allow internal unstable",
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [x] [x] [x] [x] [ ] [x] Concerns: resolved by Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
I don't understand how this would work. If the set of things allowed in is limited, it'd be a breaking change to move this macro over to use after this macro has already been available on stable. I'm happy to land as-is if we think the risk of these sorts of additional limitations is low, though.
If this happened I believe we could fix the inconsistency at an edition boundary the same way we did for , so I don't think this is a particularly large risk.
concern LocalKey type A variable defined with is of the type . I'm wondering if we should use a different type for -initialized thread locals. Right now, reusing the same type works fine, as it just a wrapped function pointer. But I can imagine that a thread local key without lazy initializer might have a different implementation or even api than the one with. (E.g. or .)
If this happened in a way where more things were accepted by a static than by , wouldn't changing to use internally be a breaking change? I wonder if we might indulge briefly in a thought experiment. If we knew that static items and items diverged in some way, would we still use the syntax here? If not, what would we use instead?
With a concern about reusing for the underlying type for this, if it were to switch to something like (or w/e name) then it would arguably warrant a different syntax as well along the lines of (or w/e name) which would help make the point of blocks moot.
I am concerned because this differs from the const block.
To what extent are the rules for evaluating different or the same as ? If they're the same then I think that the -semantic worries could be pretty easily solved by changing the macro expansion to look like: There is still the though about perhaps not wanting to use at all.
I've opened with my above suggestion. do you still believe that this should expand to a different type? Personally I would push back saying that with using the same macro name I would expect that the same kind of is generated. If in the future a or w/e is then I think that would probably fit best behind a new macro name such as or etc. If consensus though is that this needs a new type name I can try to implement that too.
resolve LocalKey type A or something like that would probably not only require the initialization expression to be const, but also forbid the type from needing Drop. If the type needs drop, it'll need to keep track of its initialization state anyway. The only thing to note is that if we do ever add a or anything similar to , might no longer work, since it'll need to track the 'initialization' state even when it doesn't need drop.
:bell: This is now entering its final comment period, as per the . :bell:
If this is a pure performance optimization, can the macro just "do the right thing" without any user facing syntax? It will require some compiler magic but maybe it's worth it in this case? This way also avoids any breaking changes in the future as what is optimized and what isn't is internal and all existing users will benefit without changing code or bumping MSRV.
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
I've posted a stabilization PR for this at
This has had a stabilization PR merged, but the todo list still lists the stabilization PR as missing.
I'm also not able to find docs on this other than that the macro now lists these patterns in the declaration: (that is nothing says "and by the way..." but maybe I'm missing it?) Nor can i find anything in the release notes or via Google. The only thing that seems to exist as to this being stable is that the PR merged a long time ago. What's the actual status? I haven't tried to use this yet on stable but presumably I can?
you're right, I opened
Cool! thanks. TLS doesn't come up super often but there was at least one case not that long ago where we were nightly for the attribute version of this in perf sensitive code.

Discussion:
#![feature(control_flow_enum)] #![feature(associated_type_defaults)] #![feature(iter_zip)] <del> #![feature(thread_local_const_init)] </del> #![feature(trusted_step)] #![feature(try_blocks)] #![feature(try_reserve_kind)]

#![feature(iter_zip)] #![feature(let_else)] #![feature(min_specialization)] <del> #![feature(thread_local_const_init)] </del> #![feature(extern_types)] #[macro_use]

#![feature(negative_impls)] #![feature(nll)] #![feature(min_specialization)] <del> #![feature(thread_local_const_init)] </del> #[macro_use] extern crate rustc_macros;

// std may use features in a platform-specific way #![allow(unused_features)] #![feature(rustc_allow_const_fn_unstable)] <del> #![cfg_attr( test, feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init) )] </del> <ins> #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] </ins> #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform)

(@key $t:ty, const $init:expr) => {{ #[cfg_attr(not(windows), inline)] // see comments below unsafe fn __getit() -> $crate::option::Option<&'static $t> { <del> const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local(); </del> const INIT_EXPR: $t = $init; // wasm without atomics maps directly to `static mut`, and dtors

#[doc(hidden)] pub use self::local::statik::Key as __StaticLocalKeyInner; <del> // This is only used to make thread locals with `const { .. }` initialization // expressions unstable. If and/or when that syntax is stabilized with thread // locals this will simply be removed. #[doc(hidden)] #[unstable(feature = "thread_local_const_init", issue = "84223")] pub const fn require_unstable_const_init_thread_local() {} </del> //////////////////////////////////////////////////////////////////////////////// // Builder ////////////////////////////////////////////////////////////////////////////////

#![crate_type = "lib"] <del> #![feature(thread_local_const_init)] </del> use std::cell::Cell;

// ignore-android does not use #[thread_local] #![crate_type = "lib"] <del> #![feature(thread_local_const_init)] </del> extern crate thread_local_aux as aux;

<del> thread_local!(static X: u32 = const { 0 }); //~^ ERROR: use of unstable library feature 'thread_local_const_init' fn main() {} </del>

<del> error[E0658]: use of unstable library feature 'thread_local_const_init' --> $DIR/thread-local-const-init.rs:1:1 | LL | thread_local!(static X: u32 = const { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #84223 <https://github.com/rust-lang/rust/issues/84223> for more information = help: add `#![feature(thread_local_const_init)]` to the crate attributes to enable = note: this error originates in the macro `$crate::__thread_local_inner` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error For more information about this error, try `rustc --explain E0658`. </del>