Description
While investigating rust-lang/rust#121610, I noticed something problematic... we currently accept code like this:
let x: &'static Option<Cell<i32>> = &None;
Promotion kicks in despite interior mutability, because it sees the None
variant and considers that one variant to not have interior mutability.
But also, both Stacked Borrows and Tree Borrows consider this to be legal:
let o: Option<Cell<i32>> = None;
let x = &o;
ptr::from_ref(x).cast_mut().write(Some(Cell::new(0)));
They allow this because we avoid making "do we allow mutation" value-dependent. This is for two reasons: (a) we don't even want to require the reference to point to a valid value, but if the memory might store any sequence of bytes then the concept of value-dependency doesn't even make sense, and (b) checking the value stored behind the reference would introduce something very close to a cyclic argument into the memory model. So when creating a reference to o
, we can't know whether o
is None
or Some
, and therefore we just conservatively assume that interior mutability is allowed.
It should follow that this is also legal:
let x: &Option<Cell<i32>> = &None;
ptr::from_ref(x).cast_mut().write(Some(Cell::new(0)));
But this is UB, due to promotion putting the None
into read-only memory. That's Not Good, promotion is introducing UB!
This is related to #236 but seems worth a separate sub-issue.
See rust-lang/rust#122789 for a crater run of at attempt to fix this.
Cc @oli-obk