-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
E0492: borrow of an interior mutable value may end up in the final value
during const eval when no inner mutability is involved
#121250
Comments
This is probably a regression introduced by #120932. However, the error is right: this is a reference to #120932 fixed a bug in our interior-mutablity-checking code. I think what this actually shows is that the code never should have been accepted in the first place. But we may need a crater run to figure out how bad this regression is. Is there a way to retroactively do a crater run for #120932? Cc @oli-obk |
is there a trait bound or something similar that can be added to tell the compiler that there won't be any internal mutability? |
Unfortunately not on stable -- see #60715. |
Interestingly I think there is no soundness issue on stable here: in @sarah-ek is there a reason why you wrote Without curly braces, the code still works fine on nightly with the fixed checks: #[derive(Copy, Clone)]
pub struct S<T>(T);
#[doc(hidden)]
pub trait DynTrait {
type VTable: Copy + 'static;
const VTABLE: &'static Self::VTable;
}
impl<T: DynTrait> DynTrait for S<T> {
type VTable = T::VTable;
const VTABLE: &'static Self::VTable = &*T::VTABLE;
} |
this is a simplification of a different scenario. the actual code is here https://github.com/sarah-ek/equator/blob/444beec918bb83e6c9e400a1849dcf015795a7b0/equator/src/lib.rs#L581-L584 the reason i want to borrow is so i can force the promotion to static when used as a part of my own |
But promotion to static is exactly what we don't want to do when there might be interior mutability. So that kind of code is definitely not intended to work. I don't know how constrained you are with the types you can use here; would something like this work for you? impl<Lhs: DynDebug, Rhs: DynDebug> DynDebug for AndExpr<Lhs, Rhs> {
type VTable = (&'static Lhs::VTable, &'static Rhs::VTable);
const VTABLE: &'static Self::VTable = &(Lhs::VTABLE, Rhs::VTABLE);
} |
yeah, i ended up going with that |
Okay, glad to hear you have a work-around. We still need to decide what to do with this accidental regression though. It is IMO a bugfix in our analysis, even though the stars align to make it not obviously unsound -- though it's also possible that I missed something in my analysis above. Let's see what Oli says. |
I think the right thing would be to error out as TooGeneric if we encounter such a constant. Or just not intern generic constants at all, but error out as TooGeneric before interning and validation in order to still get the const eval coverage of the rest. Evaluating generic constants is best effort anyway, and repeated from scratch for every monomorphization, at which point we'll only error if there is an actual mutable allocation there |
This is not an interpreter error, it is a const-checking error (i.e., it is raised in |
Oh 🤦 I was wondering how we got that error wording from interning. I'll need to think about this some more. |
I just stumbled across the same issue in my weekly CI. In my nightly crate, I am at one point building a HList that will be layout-equivalent to an array. The following code now fails (this is somewhat simplified to collapse helper traits): pub trait ComputeSet {
type TyHList: 'static + Copy;
const TYS: &'static Self::TyHList;
}
impl<H2: TypeLayout, T: ComputeSet> ComputeSet for Cons<H2, T> {
type TyHList = Cons<&'static crate::TypeLayoutInfo<'static>, T::TyHList>;
const TYS: &'static Self::TyHList = &Cons {
head: &H2::TYPE_LAYOUT,
tail: *T::TYS,
};
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Cons<H, T> {
head: H,
tail: T,
}
pub unsafe trait TypeLayout: Sized {
const TYPE_LAYOUT: TypeLayoutInfo<'static>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TypeLayoutInfo<'a> {
...
} Error:
The complete version can be found here:
|
WG-prioritization assigning priority (Zulip discussion). @rustbot label -I-prioritize +P-medium |
Crater results are in. The equator crate (the crate of the OP) is the only regression across all of crates.io. So I am inclined to classify this as a bugfix -- we shouldn't have allowed such temporaries with potentially interior mutable data to be creates in the first place. We should prioritize letting people write generic code while preserving the information that a type has no interior mutability, i.e., letting people use |
@rust-lang/lang We accidentally fixed a bug (by cleaning up const checking code in a way that inherently forbade the bug). This bug has been found to have a single regression. The issue is already being worked around. The bug fix is that we now correctly forbid using associated constants that have generic parameters (either themselves whenever we get GACs, or from their trait), if those constants are used behind references. This was a hole in our checks for preventing interior mutability behind references in constants. These are not stable yet, and if the constant is generic, we cannot know whether it has interior mutability and need to reject it. |
@rustbot labels -I-lang-nominated As reflected above, we discussed this in lang triage today. It's now in FCP, so let's unnominate. |
The final comment period, with a disposition to merge, 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 will be merged soon. |
OHHH NO... I missed the comment period: this being stabilized before |
Can you elaborate Pierre?
…On Thu, May 2, 2024, at 6:57 PM, Pierre Avital wrote:
OHHH NO... I missed the comment period: this being stabilized before `core::marker::Freeze` has killed `stabby`'s ABI-stable vtables :(
—
Reply to this email directly, view it on GitHub <#121250 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AABF4ZQ6AKHBXB2626N7UILZAJWANAVCNFSM6AAAAABDNYA6XOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAOJRGA3DAMBVGQ>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
I've elaborated quite a bit on this thread. TL;DR:
|
That other use had a trivial work-around, though. That's why we felt it was fine to go ahead with the breakage. But yeah the fact that crater tested an old stabby is unfortunate. I still don't know why that happened, but that's on us and we should figure out how to avoid it.
Not just with crater. It violates the basic principle that nightly features are opt-in. See #120804, #124339. |
Riiight, I originally thought of doing vtables like that, but decided against it due to the number of indirections this would add to function calls. I guess I'll add it as an option, as right now my workaround does require allocations. I'll let users pick whatever runs best for them through a cfg flag.
I guess it was in an effort to reduce the (probably huge) resources a crater run takes by reusing the results from a previous run?
Yeah, but arguably, this detection was entirely to address a breaking change from It was very much a way to expand the set of compiler versions Since That's also why some of |
Thanks for elaborating on what stabby is doing! Helpful. If we can zoom out from the specific mechanism, the goal you are looking for is creating generic, constant vtables? That's interesting because it's the same thing that the Rust-for-Linux project would like to do. (I'm not sure if they require generics per se.)
They describe this as depending on ``const_mut_refs` or `const_refs_to_static`, is that something you could theoretically use as well?` (see Rust-for-Linux/linux#2)
|
I'm not so sure:
Support for associated/generic statics would address my issue entirely, and would probably have many cool uses, but that seems far away still. Alternatively, stabilizing Of note: I've just realized the workaround |
Generic statics are pretty much impossible, to my knowledge. We'd have to guarantee a unique address when it is independently instantiated with the same generics by different crates, it is unclear whether linkers can do that for us.
Using Freeze is the right solution for your problem. We should get that stabilized. But it needs an RFC first.
|
I don't know why we would have to guarantee that. (We could for example require Freeze and say uniqueness is not guaranteed?) but this seems off topic for this thread, so I guess I'll open the discussion elsewhere.
…On Fri, May 10, 2024, at 12:47 PM, Ralf Jung wrote:
Generic statics are pretty much impossible, to my knowledge. We'd have to guarantee a unique address when it is independently instantiated with the same generics by different crates, it is unclear whether linkers can do that for us.
Using Freeze is the right solution for your problem. We should get that stabilized. But it needs an RFC first.
—
Reply to this email directly, view it on GitHub <#121250 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AABF4ZXCA35CB7BEZDL6AN3ZBSQVRAVCNFSM6AAAAABDNYA6XOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMBUGM4TAMZVGE>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
I'd argue the same guarantees should be made as for |
I'll write an RFC then :) |
I don't know why we would have to guarantee that. (We could for example require Freeze and say uniqueness is not guaranteed?)
The fact that statics are a place and have a unique address is pretty much their defining feature, and what separates them from consts (which are values and where addresses are pretty unreliable).
If a unique address is not needed and Freeze is enough then a const should be used, not a static.
|
That was also my argument.
For functions we do not guarantee unique addresses (and same for vtables, FWIW). You can find several issues in the issue tracker of people being confused by the behavior of |
As promised, here's the stabilization RFC for |
@RalfJung I'm struggling to understand why the change to static promotion was even necessary. From what I've seen and tested myself, the only scenario where I've gotten an error in 1.78 for code that compiled in 1.77 is when dereferencing consts in struct initializers and then static promoting the result, but this should always be ok to do if I'm not mistaken. impl<Lhs: DynDebug, Rhs: DynDebug> DynDebug for AndExpr<Lhs, Rhs> {
type VTable = (&'static Lhs::VTable, &'static Rhs::VTable);
const VTABLE: &'static Self::VTable = &(*Lhs::VTABLE, *Rhs::VTABLE);
} Here, Edit: Alternatively, my question would be this: Is "if |
No, it's not that simple. We don't know how the const was defined, and it could have used all sorts of terrible unsafe code. For instance: const C: &Cell<i32> = unsafe { std::mem::transmute(&0) }; This currently gets rejected, but we could conceivably allow this one day. So we shouldn't rely on this always being a hard error. I don't know if there is a counterexample to this implicit bound, but I don't think it matters. Implicit bounds are very fragile, Rust has an entire line of soundness bugs caused by such assumptions elsewhere in the compiler. So I'm not willing to rely on implicit bounds for this. The bound needs to be made explicit. (And that is leaving aside the fact that I have no idea how one could possibly implement the analysis based on the implicit bound you suggested. Usually we can't just feed assumptions into the trait system like that, I think.) |
Hm, yeah the part about avoiding implicit bounds is understandable. I also forgot about the work towards allowing statics to be referenced in consts, so a reference to a static containing interior mutability might exist in a const in the future (i think). |
This code works on stable, but not latest nightly
Error:
rustc --version --verbose
:The text was updated successfully, but these errors were encountered: