-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Statics in patterns #3305
RFC: Statics in patterns #3305
Conversation
b045322
to
48a7e68
Compare
48a7e68
to
e46df7f
Compare
} | ||
``` | ||
|
||
Mutable statics are not allowed, however. Patterns can't reference information that can change at runtime, and also can't be `unsafe`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignoring the unsafe
issue, is there a reason that the pattern can't reference information that can change at runtime? My understanding of how cosmopolitan is going to use this is that it references information that changes at runtime (but only once, before main
), the values of the statics are not available at link time like they normally are. Given that why would it not be possible for a developer to do the same thing using static mut
where they set the value once early in main
before the pattern is ever tested against.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason that the pattern can't reference information that can change at runtime?
The "patterns can't reference information that can change at runtime" rule is not something that I am proposing, it's a rule that Rust already has and that I see no reason to change. Tearing down this Chesterton's Fence may or may not be a good idea, but is out of scope for this RFC.
My understanding of how cosmopolitan is going to use this is that it references information that changes at runtime (but only once, before main)
This is where the exact definition of "at runtime" comes into play. If you define "runtime" as "the time when Rust code runs", then the values of the statics don't change at runtime. I argue that for the purposes of this feature, it doesn't really matter whether the values of the statics are set by the linker, the operating system, the C runtime, etc; all of these things fall into the common category of "rustc
doesn't know what the value is, but Rust code can rely on the value being constant." Note that Cosmopolitan sets the values of the statics in assembly (that I don't presume to fully understand); C code written against Cosmopolitan sees them as standard C extern const
s, and doesn't need to worry about their value changing, either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fundamentally this is how all rust static values work. Officially Rust "doesn't have life before main
", but it still requires someone (an OS or an assembly boot script) to initialize all the pre-conditions before Rust code actually begins executing.
So this part of the RFC is basically nothing new.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel a good analogy is dynamic linking. When you call a dynamically linked function in Rust, the compiler doesn't know the address of that function; that only gets determined when the program runs. But you can still write a safe wrapper for the dynamically linked function, and then treat it pretty much like any other Rust function (though not like a const fn
, of course).
Extern statics are the same. Unlike const
s, rustc
doesn't know their value. But, once the necessary #[trusted_static]
ceremonies are performed, it should still be possible (IMO) to treat them"pretty much" like any other constant value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fundamentally this is how all rust static values work.
Not normally, the vast majority of Rust code is statically linked which means the values are known at link time (and LTO would then be able to make const
and static
patterns generate the same code).
Extern statics are the same. Unlike
const
s,rustc
doesn't know their value. But, once the necessary#[trusted_static]
ceremonies are performed, it should still be possible (IMO) to treat them"pretty much" like any other constant value.
Would it be unsound to use #[trusted_static]
on a dynamically linked extern static that gets initialized early-in-main
before any code that reads the static runs? It just seems like it should be possible to unsafely promise that a value is constant once it starts being used in patterns, given that is essentially the same behavior you get with dynamically linked statics (where the dynamic loader has unsafely promised to the executable that all statics have been initialized and will no longer be written to).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh whoops, I'm the one in charge of trusted statics? That kinda stalled out because of like, long term correctness discussion XD
uhm, don't expect that one soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That kinda stalled out because of like, long term correctness discussion
Is there a list of the unresolved questions somewhere? (Or does one need to be compiled?) Might it be possible to ship an obviously correct MVP sooner, and consider loosening safety requirements later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just the zulip thread, which the project tracking issue should link to.
The problem is really simple: It's hard to tell people that the unsafe-adjacent code they've been writing the past 6 years is just barely wrong, and that they need to start doing everything the opposite way because of small edge cases. That's a hard sell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's hard to tell people that the unsafe-adjacent code they've been writing the past 6 years is just barely wrong, and that they need to start doing everything the opposite way because of small edge cases.
Could we figure out the shiny new Right Way of Doing Things ® now, worry about migrating/accommodating old code later once people are used to the new syntax? The longer we wait before offering better tools, the more technically-bad code gets written, and the more painful any necessary changes will be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's easy if migration of code and re-training of programmers isn't a concern ;3
Literally we just would have to say one day "hey declaring extern static/fn is unsafe now". And like, a compiler patch to enforce that. That's the entire end goal for Rust 3015. It's just the migration that needs to be designed really.
I was skeptical in the internals thread on the basis of complexity of implementation, but the inclusion of a full implementation appears to have proven me wrong. Good job! I guess there's only a "conceptual burden" now in that a refutable pattern can also be opaque. Correct me if I'm wrong, but I took a peek at the test cases and didn't notice any where the same static is used twice, e.g. match x {
A => 1,
A => 2,
_ => 3,
} (I was looking for this because I was wondering if it would have a lint) |
I had overlooked that case. Rectified in Jules-Bertholet/rust@46bff83 |
100% in favour of this change, although I think there's one extra detail that should be addressed: to what extent are nested patterns allowed? I think we'd agree that I personally think that all of these are fine, but we probably want to be explicit about it. I'm not sure if the provided implementation allows for all these kinds of patterns, or just plain static patterns. |
@clarfonthey Statics in range patterns ( |
Wasn't aware that empty range patterns was a hard requirement for the compiler; I would have thought that it would be marked as unused like uninhabited types are, but not be a hard error. Perhaps statics should have a similar treatment to uninhabited types, where empty ranges simply never match, but are still allowed. But it's fair to leave as an open question at the moment. I can definitely imagine ranges of the form |
The summary kind of buries the lede by talking about regular That's not an objection to supporting them, but the RFC title and summary should state this to make the scope of the proposal clear from the beginning. |
@RalfJung Could you elaborate a little more on why they're so different in your mind? |
The values of (immutable) |
- not come from an extern block, unless it is marked as safe to use with the [trusted external statics](https://github.com/rust-lang/lang-team/issues/149) feature | ||
- have a type that satisfies the structural match rules, as described in [RFC 1445](1445-restrict-constants-in-patterns.md), but without any allowances for backward compatibility like there are for consts (e.g., floating point numbers in patterns) . These rules exclude all statics with interior mutability. | ||
|
||
Static patterns match exactly when a const pattern with a const of the same type and value would match. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very subtle guarantee, since the way these patterns are treated will have to be completely different. Matching const
s relies heavily on knowing their value. const
will be taken apart to a 'valtree' that is matched on field-by-field, as least if the type permits, whereas extern static
will necessarily be compared via PartialEq
. This may or may not produce the same result, depending on how PartialEq
is implemented.
Above you reference the structural match RFC, but the rules in that RFC have little to do with what the compiler actually does. The real rules are quite the mess and need to be cleaned up, see rust-lang/rust#74446. But it seems inevitable that at least in unsafe code it will be possible to have "structural match" types that actually violate the structural match contract, and we need to say what happens then. This is not an operational requirement, so it probably cannot become UB.
So, I think guaranteeing they behave exactly the same is not feasible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If future relaxations of the structural match requirements are somehow impossible to port to static matches, we can always require the stricter rules for static patterns while loosening them everywhere else. It's impossible to say more without knowing what such relaxations might look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Future relaxation" makes it sound like we currently have some rules that we understand, and we want to change them. That is not the case. As I said, what the compiler does and what the RFC says are already different, and last time this came up, there was little interest in adjusting the compiler to the spec -- rather, the spec will be adjusted to the compiler.
So, you simply cannot use that RFC as a basis for further language evolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I referred to the structural match RFC, I was mostly concerned about the guarantee (as of right now) that, for any local x
and const
Y
of the same type, match x { Y => true, _ => false }
evaluates to true
iff PartialEq::eq(&x, &Y)
evaluates to true
. Do you know of a counter-example to this that works in stable Rust?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have patterns where PartialEq::eq(&x, &Y)
doesn't even compile, see e.g. this example from the issue that I linked twice already (rust-lang/rust#74446).
Also your stated equivalence is insufficient to substantiate the claim in the RFC since it fails to take into account UB. There are some fundamental reasoning principles for patterns that are currently true, that are no longer true with this RFC -- as explained here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have patterns where
PartialEq::eq(&x, &Y)
doesn't even compile
OK, I definitely need to account for that then. But are there cases where it does compile, but gives a different answer?
(Also, is "comparing function pointers is weird" just a potentially-fixable LLVM quirk, or is there some deep reason behind it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I definitely need to account for that then. But are there cases where it does compile, but gives a different answer?
No, I don't think so. But I already explained why that is not sufficient grounds to claim that static and const matching are equivalent.
(Also, is "comparing function pointers is wierd" just a potentially-fixable LLVM quirk, or is there some deep reason behind it?)
It's because of unnamed_addr
, see e.g. rust-lang/rust#70861
text/3305-static-in-pattern.md
Outdated
} | ||
``` | ||
|
||
As an exception, when all safe values of a type are structurally equal, the compiler is allowed to see that the match will always succeed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"all safe values" is definitely impossible to implement. I could define
// Safety invariant: the field is always 0.
struct MyWeirdUnit(i32);
MyWeirdUnit
has only one safe value, but the compiler has no way of knowing that.
Maybe you meant "all valid values", but even that seems way too subtle to include here. Do we have any precedence for a rule like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, "is allowed to see" is too weak; I assume you mean that the compiler must accept the code below, not just that it is allowed to accept the code below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And finally, given that this is a large fraction of the RFC, there also needs to be motivation for all this complexity caused by "1-valued types". The motivating example certainly doesn't need any of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I basically interpreted this as exclusively applying to ZSTs, but wanting to be more general in case we somehow added the ability to increase the number of cases we could detect this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, is isn't possible now, but it eventually could be possible to mark fields as ignored when deriving Eq
, which would allow such a type to be non-zero-sized while still having a zero-sized equivalence class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this should be "valid". I had a reason in my head for why this should be "safe", but upon writing it out I realized that my logic made no sense.
As stated in the RFC, there's no practical use-case (AFAIK) for the "one valid value" rule. It's just that, on general principle, I feel the compiler should never force you to write useless code that it statically knows to be unreachable, unless there's a good justification for it.
I guess the concern is that the match using a static pattern would look like a similar match using a const pattern while acting like something very different. Could some sort of macro situation solve this within libc and the select other places that need to deal with this? Then it will "look like" a match inside a macro (and we all know "macros can do weird stuff"), and then on select systems that macro can expand to the if-else form instead? And we don't have to disrupt the language for the rest of everyone else. |
|
||
[reference-level-explanation]: #reference-level-explanation | ||
|
||
For a static to be eligible for use in a pattern, it must: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if this is enough for soundness. The reason const
are not allowed to even point to immutable static
is pattern matching soundness: if you have a const
of type &i32
, we better make sure that the integer it points to is truly immutable. But if you define a static X: &i32
by doing &mut MUTABLE_STATIC
and then transmuting that, and then you do const C: &i32 = X
, now you have yourself a potentially unsound pattern.
But for extern static
we don't know their values, so we can't know if they do shenanigans like that. Also for all we know their type may be a lie, so maybe they actually do get mutated? There is an entire lion's den of issues here that we have so far worked very hard to avoid (e.g. grep the compiler source code for can_access_statics
and read all the comments associated with it), and all of this work is for naught if we allow extern static
in patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I hope we can fix this in a future edition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this needs a new edition, we just need to be okay with semver hazards and post-monomorphization errors.
But anyway, that won't help for extern static
. I can see some ways of resolving the structural match situation that would probably also make other kinds of "opaque consts" like extern static
be fine (*), but as of now I don't know where the structural match stuff will go so I'd rather not rely on it going any particular way.
(*) Well, there are also the issues around "evading match arms" by changing the pattern mid-evaluation, but that can probably be avoided by not doing any exhaustiveness checking and clearly documenting when opaque patterns vs valtree patterns are used (because they will probably not always be equivalent).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant specifically that with extern static
(non-mut) I would like declaring them to be the unsafe part, and it would require precise type and truly immutable memory and such, then the accesses themselves can be the safe part and people can worry a lot less.
Doesn't help the actual valtree matching (or not) part of it though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition we need here is that any pointer we reach through this memory points to immutable memory, recursively. This is required for matching on references. It is why I keep saying this is a bigger soundness issue than the RFC lets on.
I doubt trusted statics want to require that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but if that value can be valtree'd, then those two things are not UB-equivalent.
How? The current stuctural match rules oly allow constants (and, with this feature, statics) if PartialEq::eq
is guaranteed to give the same results as a valtree-based match.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mutating the value referenced by a &&&i32 is itself UB imho,
That's probably not correct. You certainly shouldn't rely on it. The rules for UB on references will probably not recursively follow references to arbitrary depth, but instead work on a 'shallow' level -- mutation is only UB one level down.
As a concrete example, Miri sees no UB here.
How? The current stuctural match rules oly allow constants (and, with this feature, statics) if PartialEq::eq is guaranteed to give the same results as a valtree-based match.
The current rules are a mess and before anyone relies on them for anything, we need to re-do those rules. See rust-lang/rust#74446.
The future rules will, hopefully, give users the ability to unsafe impl StructuralEq for MyType
. (And in fact they can already do that, even without unsafe
, by using unstable features.)
Users of course cannot expect things to behave completely sanely when they do that, but we still need to specify precisely what happens. Note in particular that saying "it is UB to have such an instance" does not work, that's simply not how we are using UB in Rust -- UB can only arise as a consequence of the program executing a statement in the wrong way, and only under conditions that can actually be decided automatically. "This type has a PartialEq that behavesd equivalently to valtrees" is not a decidable question (halting problem, yada yada), and hence cannot be grounds for UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"This type has a PartialEq that behaves equivalently to valtrees" is not a decidable question
But "at the point this specific match statement was executed, this specific call to PartialEq::eq
completed and returned a value without panicking, and that return value was different from what a valtree-based match would have returned" is decidable, no?
(This would mean loop {}
is a valid PartialEq::eq
impl, but I don't think that's a problem).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mutating the value referenced by a &&&i32 is itself UB imho,
That's probably not correct. You certainly shouldn't rely on it.
All the issues that arise with "a static of type &&&i32
could change during a pattern match" already occur with "a scrutinee of type &&&i32
could change during a pattern match", no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But "at the point this specific match statement was executed, this specific call to PartialEq::eq completed and returned a value without panicking, and that return value was different from what a valtree-based match would have returned" is decidable, no?
So now we need a way to express that in MIR, and carefully specify exactly what that does and when.
That might be possible, but it'll be a lot of work, and it'll also make the core language spec more complicated for a niche feature.
Instead I think we should just specify that statics are treated as opaque for pattern matching and always use PartialEq
no matter their type. We don't have any other "UB due to valtree inconsistencies", and IMO there is no good reason to introduce such UB here.
All the issues that arise with "a static of type &&&i32 could change during a pattern match" already occur with "a scrutinee of type &&&i32 could change during a pattern match", no?
No, patterns and scrutinees are treated very differently in pattern matching, because the pattern is inherently known at compile-time and the scrutinee isn't. This is not a symmetric situation. This RFC is proposing to make it more symmetric, but that is exactly why this is a bug fundamental change to pattern matching.
See here for a long discussion about how to ensure that we have UB when the scrutinee changes during pattern matching. The same approach does not obviously work for patterns, since it relies heavily on the fact that we compare the same value X against a number of things -- Rust has some ingredients that make it possible to ensure that X doesn't change when it is used multiple times. But with patterns that just happen to use the same extern static in several places across several patterns, we have none of that structure, so it'd be even more complicated.
So, again, we should just specify that extern statics are treated as opaque, and we should make no claims that this is equivalent to a const of the same type and value.
The intention is that, while under the hood the implementation may be different, the two types of patterns should behave exactly the same (except for exhaustiveness/reachability checking and usage in |
This RFC takes it as a given that regular and trusted extern statics should not be different; in fact, they should be 100% interchangeable in all cases. Maybe I should be more explicit about justifying this. |
That assumption is just wrong though. For regular statics we know their value at compile-time, for trusted extern statics we don't. |
Yeah but that's already not the case in your implementation. A So this RFC attempts to put a constraint on the pattern matching machinery that at least I explicitly intended not to have. Not having this constraint means users can unsafely declare their own types to be structural-match, which is broadly useful. (And we can do so without having super complicated machinery whose only purpose it is to declare code that violates certain expectations to be UB. And to what benefit? UB should be avoided whenever possible, basically the only acceptable justification is that we can get optimizations out of it, or that allowing such code would make the spec enormously complicated and hard to understand. Neither of these apply here.) Having this constraint enables a niche RFC to have a slightly simpler spec. I don't think that's a hard choice to make... Also note that I am not saying we should encourage anyone to have strange sturctural eq types like that! We should certainly establish a general expectation that structural eq types have a So it's a trivial change for this RFC -- just say that statics are always treated opaquely and compared with |
We may know the value, but currently we don't take advantage of that fact to allow non- Edit: I was wrong. The following works today: static IN: i32 = 5;
static OUT: i32 = IN + 3;
fn main() {
println!("{OUT}")
} |
As of right now, these two comparison methods are always 100% equivalent for all eligible types. Maybe some future hypothetical might-never-happen still-completely-unspecified language extension will change that. But if I have to fully specify how this feature might interact with every imaginable hypothetical future language extension, this RFC will end up with infinite length. So I would rather say "this feature does not prevent future extensions to structural match/match in patterns" and leave it at that, rather than slide down endless rabbit holes. |
7c0ca3d
to
81232f7
Compare
I skimmed the RFC and the discussion. As it stands, my inclination is to close it (though I thank the author for the submission). To start, the motivation as presented feels a bit weak. Matching against constants feels like it could be well-solved with a macro as well, where the same code is compiled to a match on one operating system but a if-else-if chain on others. I'd be more convinced if there were a lot of examples of where this would be useful that included more mainstream scenarios. Secondly, I don't think we're ready to make this decision yet. As @RalfJung noted, this question feels highly entangled with valtree and how match semantics shake out. I don't feel we can accept this RFC until we've settled that story definitively (side note that I would really love to see progress there). Also, I may be misremembering, but my sense is that we are moving in the direction of more compilation time reasoning in matches and not less, and thus I think this RFC would wind up being a bad fit overall. |
That's fair. I agree that the use-cases for this feature are relatively niche. With the current compiler, the spec and implementation are also relatively simple, which is why I felt this RFC to still be worth the trouble. But if future changes to the match code are going to potentially affect that, then the motivation for this RFC is no longer as compelling. Maybe once those issues are settled, this can be revistied? |
I do wonder why Cosmopolitan doesn't just pick arbitrary error numbers and translate from the native ones in its system call wrappers. It's not as if it needs to interoperate with native userspace libraries that may have |
based on comments so far, and discussion at the lang team meeting, I'm opting to close this proposed RFC. Like @oli-obk above, I do not think the proposed features fits the character of the Rust language. Separate from that personal opinion, I also think @nikomatsakis and @RalfJung have established that even if it did fit the character of the language, the feature is premature. (With respect to the question of whether the topic could be revisited in the future, my advice would be to identify a champion on the language design team, or the libs team, who does think this feature is worth fighting for. But I do not think there is such a champion amongst the current members of the language design team.) @rfcbot fcp close |
Team member @pnkfelix has proposed to close this. The next step is review by the rest of the tagged team members: No concerns currently listed. 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 this document for info about what commands tagged team members can give me. |
@comex Cosmopolitan uses symbolic system numbers because |
As far as I can tell, this solution would only ever work for non-exhaustive matches; it could never support exhaustive matches. Given that, it seems like I appreciate the value of trying to reuse existing code without modifying it, but I think there's a meaningful difference between "compile-time constant" and "runtime value", and I don't think we should support match or other patterns on the latter. |
@rfcbot reviewed |
I would argue that the distinction between "compile time constant" and "runtime value" has a different meaning from the compiler's perspective versus the programmer's perspective.
|
If I may nitpick this a little -- using symbols for magic numbers is still a table lookup (by string!), but either a static linker or the RTLD (
Does the programmer care that such values may change from one run to the next, especially on different machines? Maybe, maybe not, but they are only constant for a particular combination of executable+libraries. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to close, as per the review above, 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 is now closed. |
Rendered
Proposes allowing patterns to refer to statics, including trusted extern statics.
Internals thread
Full implementation