Closed
Description
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