MIR: Do we allow accesing a moved place? · Issue #112564 · rust-lang/rust (original) (raw)
fn14()
dereferences a pointer to a place that has been moved to another function. Currently, Miri thinks this is fine, but this causes the compiled code to produce different results under different optimisation levels.
#![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*;
pub fn dump_var(val0: u32) { println!("{val0}"); }
pub struct Adt52 { fld1: (u32, usize, u16), }
#[custom_mir(dialect = "runtime", phase = "initial")] fn fn14() { mir! { let fld1: (u32, usize, u16); let non_copy: Adt52; let p: *const u32; let i: u32; let unit: (); { fld1 = (0, 0_usize, 0); non_copy = Adt52 {fld1}; p = core::ptr::addr_of!(non_copy.fld1.0); Call(unit, bb13, fn15(Move(non_copy))) } bb13 = { i = *p; Call(unit, bb18, dump_var(i)) } bb18 = { Return() }
}
} pub fn fn15(mut x: Adt52) { x.fld1 = (1, 0, 0); } pub fn main() { fn14(); }
% rustc -Zmir-opt-level=2 -Copt-level=3 repro.rs && ./repro 0 % rustc -Zmir-opt-level=1 -Copt-level=3 repro.rs && ./repro 1 % ../miri/miri run ./repro.rs 0
At the moment, this is only reproducible with custom MIR, because when we build MIR from surface Rust we always create a temporary, so you cannot create a pointer to anything that will be directly moved into another function.
fn fn14() { let fld1 = (0, 0, 0);
let mut non_copy = Adt52 {fld1}; // _2 = Adt52 { fld1: _1 };
let p = core::ptr::addr_of!(non_copy.fld1.0); // _3 = &raw const ((_2.0: (u32, usize, u16)).0: u32);
fn15(non_copy); // _5 = move _2;
// _4 = fn15(move _5) -> bb1;
let i = unsafe { *p };
dump_var(i);
}
So either we should call this UB or codegen shouldn't rely on the assumption that a moved MIR place will never be used later.
cc @RalfJung