Description
We had earlier decided to disallow references to static mut
in:
Later, we decided to disallow hidden references to mutable statics also:
Since then, some interesting cases have come up in:
These interesting cases revolve around how we desugar syntax for equality, comparison, compound assignment, and indexing. There are two aspects.
One, though PartialEq::eq
, PartialOrd::lt
, AddAssign::add_assign
, Index::index
, and IndexMut::index_mut
all take references, for primitive types, as implemented today in lowering to MIR, these operations don't generate references at all. E.g., no references at all are created here:
fn f(x: u8) {
static mut S: u8 = 1;
static mut T: [u8; 1] = [0; 1];
unsafe {
_ = S == S;
_ = S < S;
S += x;
S += T[0];
T[0] = x;
}
}
This behavior is documented for compound assignment expressions, but not otherwise (as far as I can tell).
Two, even if acting on wrapper types where a reference would be generated, the signatures of these methods guarantee that the reference is not leaked and is short-lived. That is, there's no possible sound implementation of AddAssign::add_assign
, for any type, that would cause this:
static mut S: W<u8> = W(1);
unsafe { S += W(x); };
...to have any additional UB as compared with:
static mut S: W<u8> = W(1);
unsafe { S = S + W(x); }
...which raises the question of why we would want to treat these differently, and why we would want to encourage people to write the latter rather than the former.
In the 2024-08-07 triage meeting, we discussed whether making references to mutable statics a hard error actually met our bar for hard errors. In other cases, we've chosen that only when doing so would make the language definition in a future edition simpler. Otherwise, we've stopped at a deny-by-default lint.
We discussed how, if we made this a lint rather than a hard error, we might have more room to maneuver.
This is what the comment by @scottmcm below is in reply to. However, we did not reach an actual consensus in the meeting.
Also, we did not discuss in the meeting the second aspect described above about how short-lived references are UB-equivalent to accesses.
Proposal
On the table in this issue is the following proposal:
- Upgrade the
static_mut_ref
lint todeny-by-default
in Rust 2024.- That would replace and remove the current hard error for references to mutable statics in Rust 2024.
- Doing this as a lint would give us more room to maneuver.
- This case does not meet our bar for doing a hard error since it does not make the language definition any simpler in the new edition, and we've stopped at
deny-by-default
in other cases like this.
- Extend, in all editions,
static_mut_ref
to lint against "hidden references" (that is, references created by virtue of autoref).- This follows the decision in #123060 except that a
deny-by-default
lint rather than a hard error would be used in Rust 2024. - This extension is justified because a reference that is created by autoref and then leaked is just as dangerous as any other reference to a
static mut
.
- This follows the decision in #123060 except that a
- As feasible, do not lint in cases (whether explicit or due to autoref) where the compiler can prove that the reference is not created or does not leak.
- This resolves how to handle the open questions in #124895.
- An operation that does not create a reference, or a use where we can prove that the reference does not leak, has no additional UB as compared with any other access to the
static mut
. There's no reason for us to push people away from these patterns and into other patterns that have no more or less potential for UB. - We'd start by not linting in the cases where we can tell from the MIR that no reference is created, then later work could do more to avoid linting against other cases where such a proof is feasible. This later work would not be required to ship this lint or the edition.
cc @obeis @RalfJung @rust-lang/lang
Tracking: