-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
regression: encountered mutable pointer in final value when "outer scope" rule applies in const/static with interior mutability #121610
Comments
bisection seems to point to #119044 ? cc @RalfJung @oli-obk searched nightlies: from nightly-2024-01-01 to nightly-2024-02-26 bisected with cargo-bisect-rustc v0.6.8Host triple: x86_64-unknown-linux-gnu cargo bisect-rustc --start=2024-01-01 -- test |
Oh strange, this landed a while ago and so far we got no reports I think...
That seems to be a deprecated crate, the repo has moved on to a new version. Not sure how to get source code for it. The
That code I found here. (The current version of the crate on master doesn't have this any more, at least not in that file.) But so far my attempts at extracting an example that reproduces the issue failed. I assume it has to do with that |
I can reproduce the crash by compiling an old tarball of the boa crate (v0.13). But it also I have the feeling that trying to get to a reproducer would be expensive in terms of time vs. benefit 🤔 But I am unsure if behind this |
"crash"? It doesn't crash for any of them I hope, just show this error? I am certainly surprised that there is a regression at all, in particular given that an earlier version of that PR (#116745) has been cratered and didn't find anything. So in that sense it would be interesting to extract a testcase here. |
I stand corrected. Compile stops and exits with the error reported in this issues (sorry for mixing up apple and oranges). |
I copied more of the type definition into its own file, still no luck. But I think the only way this can happen is if different parts of the compiler disagree on whether a type has interior mutability, which is quite concerning. |
Aah! I finally got it: use std::cell::Cell;
pub enum JsValue {
Undefined,
Object(Cell<bool>),
}
impl ::std::ops::Drop for JsValue {
fn drop(&mut self) {}
}
const UNDEFINED: &JsValue = &JsValue::Undefined;
fn main() {
} The So, I think what happens here is that we don't do promotion because of the Honestly my preference would be to reject this in const-checking, IMO we shouldn't allow references to interior mutable types under the "outer scope" rule. Note that both Stacked Borrows and Tree Borrows actually currently allow writing through |
So... actually allowing this code again will be quite tricky. Not sure if we want to do that. What I would prefer to do is make this check entirely type-based, and thus give a nicer error for when this happens: rust/compiler/rustc_const_eval/src/transform/check_consts/check.rs Lines 487 to 491 in 010f394
But that is quite the departure from our attempts to handle interior mutability in a value-based way, i.e. with these "qualifs". EDIT: That's now implemented in #121786. |
So... what shall we do here? Retroactively ask T-lang to approve the part of #119044 that is a breaking change? That PR went through FCP but we didn't realize it would be a breaking change. |
I'm assuming changing that, specifically for promotion in consts/statics is not gonna fly? We are already trying to remove/limit the method call exception that promotion has there |
You mean making the code work by having promotion kick in regardless? That sounds very tricky, we'd have to ensure this happens only when it's the final value of the const being promoted, not some intermediate value. If we absolutely must accept this code then we can make const-eval do a type-driven traversal of the |
Yea, i discarded this option right away and was looking for alternatives |
I think that option is still better than making promotion cover this.^^ |
I guess we each hate each other's preferred solution, and neither of us love our own preferred solution. So... @rust-lang/lang We did an accidental breaking change in #119044 The following example does not compile on beta anymore, but does compile on stable: use std::cell::Cell;
pub enum JsValue {
Undefined,
Object(Cell<bool>),
}
impl ::std::ops::Drop for JsValue {
fn drop(&mut self) {}
}
const UNDEFINED: &JsValue = &JsValue::Undefined; with
The reason this worked before was that before #119044 we did a value based (not just type based) interning traversal, so we picked up on the fact that the specific value has no interior mutability. We have come up with two ways to get back the previous behaviour:
The issues with those are respectively:
So we would like to just keep rejecting this code, as it requires special cases to support instead of falling out of a set of simple rules. |
A full revert will be hard since there's a bunch of follow-on work that landed since then that either further changes the interner, or requires the new interner. I think it's possible to just turn that error ("encountered mutable pointer in final value") into a future-compat lint. Other than this bug, it can only be triggered by unsound flags/features (const_heap, miri-unleashed). Then we also get an idea of whether there is more code out there that relies on this pattern. |
I was wondering why promotion doesn't trigger this error; turns out the interner simply disables it for promoteds. I guess we have that case covered in our test suite (or in the standard library itself). I think increasingly my tendency is to fix this by changing |
…ng-ptr-in-final-to-future-incompat-lint, r=wesleywiser Downgrade const eval dangling ptr in final to future incompat lint Short term band-aid for issue rust-lang#121610, downgrading the prior hard error to a future-incompat lint (tracked in issue rust-lang#122153). Note we should not mark rust-lang#121610 as resolved until after this (or something analogous) is beta backported.
We should still track this issue, it's just not a stable-to-beta regression any more. |
This was discussed in a T-lang meeting. There was general sympathy with the t-opsem desire to not do value-based reasoning for interior mutability. But there also wasn't a clear idea for how value-based reasoning could be removed from both promotion and tail-expression checking. The one thing everyone agreed on was to at least do a crater run to see how bad it'd be. Then based on that, maybe there could be an edition-based transition plan. For this concrete issue, the goal is to monitor the tracking issue of the future compat lint (#122153) to see if anyone speaks up -- to see if there's notable code out there not covered by crater that would regress if we just made the current check a hard error. Depending on that we could either take the hit and break old versions of boa, or do value-based reasoning in const-eval when determining the mutability of shared references (at least in the "root frame" of const-eval, i.e. the initializer expression of the const itself). |
The crater experiment in #122789 which entirely removed value-based reasoning for interior mutability had almost 4000 regressions. I think we can conclude that this is not an option.^^ |
I'm proposing we fix this by removing the lint and just accepting the code, unfortunate as that is. See #128543. |
Mutating a non-internally-mutable type is UB, but that doesn't necessarily mean that mutating an internally-mutable type is not UB. Presumably there could just be a second rule that mutating read-only memory is also UB, even if the type being mutated is internally mutable. As long as safe code is prevented from mutating these values, then the only danger is that of people not realising this is UB when writing unsafe code - this could be mitigated by a compiler lint, which does a "best effort" attempt at detecting writes to read-only memory. |
Yes, we already have such a rule. I was just hoping it would not be necessary... Also see rust-lang/unsafe-code-guidelines#493, which has the same root cause but is worse because it is not at all clear to the user that there's any "read-only memory" here. |
const-eval interning: accept interior mutable pointers in final value …but keep rejecting mutable references This fixes rust-lang/rust#121610 by no longer firing the lint when there is a pointer with interior mutability in the final value of the constant. On stable, such pointers can be created with code like: ```rust pub enum JsValue { Undefined, Object(Cell<bool>), } impl Drop for JsValue { fn drop(&mut self) {} } // This does *not* get promoted since `JsValue` has a destructor. // However, the outer scope rule applies, still giving this 'static lifetime. const UNDEFINED: &JsValue = &JsValue::Undefined; ``` It's not great to accept such values since people *might* think that it is legal to mutate them with unsafe code. (This is related to how "infectious" `UnsafeCell` is, which is a [wide open question](rust-lang/unsafe-code-guidelines#236).) However, we [explicitly document](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) that things created by `const` are immutable. Furthermore, we also accept the following even more questionable code without any lint today: ```rust let x: &'static Option<Cell<i32>> = &None; ``` This is even more questionable since it does *not* involve a `const`, and yet still puts the data into immutable memory. We could view this as promotion [potentially introducing UB](rust-lang/unsafe-code-guidelines#493). However, we've accepted this since ~forever and it's [too late to reject this now](rust-lang/rust#122789); the pattern is just too useful. So basically, if you think that `UnsafeCell` should be tracked fully precisely, then you should want the lint we currently emit to be removed, which this PR does. If you think `UnsafeCell` should "infect" surrounding `enum`s, the big problem is really rust-lang/unsafe-code-guidelines#493 which does not trigger the lint -- the cases the lint triggers on are actually the "harmless" ones as there is an explicit surrounding `const` explaining why things end up being immutable. What all this goes to show is that the hard error added in rust-lang/rust#118324 (later turned into the future-compat lint that I am now suggesting we remove) was based on some wrong assumptions, at least insofar as it concerns shared references. Furthermore, that lint does not help at all for the most problematic case here where the potential UB is completely implicit. (In fact, the lint is actively in the way of [my preferred long-term strategy](rust-lang/unsafe-code-guidelines#493 (comment)) for dealing with this UB.) So I think we should go back to square one and remove that error/lint for shared references. For mutable references, it does seem to work as intended, so we can keep it. Here it serves as a safety net in case the static checks that try to contain mutable references to the inside of a const initializer are not working as intended; I therefore made the check ICE to encourage users to tell us if that safety net is triggered. Closes rust-lang/rust#122153 by removing the lint. Cc `@rust-lang/opsem` `@rust-lang/lang`
The text was updated successfully, but these errors were encountered: