Allow references to interior mutable data behind a feature gate by oli-obk · Pull Request #80418 · rust-lang/rust (original) (raw)

Having no StorageDead can also arise because there was no StorageLive, either.

Afaict, in constants, that's not an issue, and even if it were, all that would mean is that our check forbids more things than we want. That's ok, we can be more permissive later.

And how does the referent matter, isn't if the reference for which we care if it ends up in the final value? When we see an expression let _x = &var.field.field, we care about whether the reference ends up in the final value, so how does looking at var tell us anything about that...?!? I am confused.^^

Unless you mean we are checking the _x (I haven't though that one though), but you keep saying "reference of locals that have no StorageDead", which to me means you are checking var, not _x.

Some examples could help. :)

Heh, ok, yea, I see the misunderstanding, and it is exactly the problem I had with building a static analyis for this until eddyb gave me the enlightening clue. Let's start with an example:

const FOO: &Cell = &Cell::new(42);

expands to something like the following MIR:

StorageLive(_2) _2 = 42; StorageLIve(_1) _1 = Cell::new(42) -> bb2

bb2: StorageDead(_2) _0 = &_1 return

Now, you are right, _0 has neither StorageLive nor StorageDead, as its storage is handled by the "caller" (at least in a normal function). Similarly function arguments have no storage markers. The problematic operation in the above example is the &_1, as that creates a &'static Cell<i32> within a constant. And we know it's 'static, because of the escaping scopes rule: _1's scope is 'static, because it is a temporary in the trailing expression of a constant. Thus it also has no StorageDead marker. All this PR does is forbid Rvalue::Ref if the local (_1 in our case) has no StorageDead markers and the local's type (Cell::<i32> in this case) has interior mutability. By the way MIR is built, we know that this can only happen for the escaping locals. Our dynamic checks for dangling pointers do the same thing, but dynamically. They check whether the allocation still exists at the end. The only way that can happen is if they do not have a StorageDead statement.

So with this new analysis, you either get a hard error because you tried to take a reference to a local that has interior mutability and lives forever, or you get a validation error later because you created a dangling pointer. We can talk about dangling pointers independently, they are entirely orthogonal to preventing references to interior mutable data in constants. You can create the easily without having to do anything related to (interior) mutability.