Description
#90895 caused the Miri test suite to fail, and further investigation showed that this is due to strange MIR being generated for Option::map
: when I dump the MIR for this code and look at the pre-const-prop code, it looks like:
fn <impl at map.rs:11:1: 18:2>::map(_1: Option<T>, _2: F) -> Option<U> {
debug self => _1; // in scope 0 at map.rs:12:38: 12:42
debug f => _2; // in scope 0 at map.rs:12:44: 12:45
let mut _0: Option<U>; // return place in scope 0 at map.rs:12:53: 12:62
let mut _3: isize; // in scope 0 at map.rs:14:13: 14:21
let _4: T; // in scope 0 at map.rs:14:19: 14:20
let mut _5: U; // in scope 0 at map.rs:14:31: 14:35
let mut _6: F; // in scope 0 at map.rs:14:31: 14:32
let mut _7: (T,); // in scope 0 at map.rs:14:31: 14:35
let mut _8: T; // in scope 0 at map.rs:14:33: 14:34
let mut _9: bool; // in scope 0 at map.rs:17:5: 17:6
let mut _10: bool; // in scope 0 at map.rs:17:5: 17:6
let mut _11: isize; // in scope 0 at map.rs:17:5: 17:6
let mut _12: isize; // in scope 0 at map.rs:17:5: 17:6
let mut _13: isize; // in scope 0 at map.rs:17:5: 17:6
scope 1 {
debug x => _4; // in scope 1 at map.rs:14:19: 14:20
}
bb0: {
_10 = const false; // scope 0 at map.rs:13:15: 13:19
_9 = const false; // scope 0 at map.rs:13:15: 13:19
_10 = const true; // scope 0 at map.rs:13:15: 13:19
_9 = const true; // scope 0 at map.rs:13:15: 13:19
_3 = discriminant(_1); // scope 0 at map.rs:13:15: 13:19
switchInt(move _3) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at map.rs:13:9: 13:19
}
bb1: {
discriminant(_0) = 0; // scope 0 at map.rs:15:22: 15:27
goto -> bb7; // scope 0 at map.rs:15:22: 15:27
}
bb2: {
unreachable; // scope 0 at map.rs:13:15: 13:19
}
bb3: {
_4 = move ((_1 as Somex).0: T); // scope 0 at map.rs:14:19: 14:20
_9 = const false; // scope 1 at map.rs:14:31: 14:32
_6 = move _2; // scope 1 at map.rs:14:31: 14:32
_8 = move _4; // scope 1 at map.rs:14:33: 14:34
(_7.0: T) = move _8; // scope 1 at map.rs:14:31: 14:35
_5 = <F as FnOnce<(T,)>>::call_once(move _6, move _7) -> [return: bb4, unwind: bb8]; // scope 1 at map.rs:14:31: 14:35
// mir::Constant
// + span: map.rs:14:31: 14:32
// + literal: Const { ty: extern "rust-call" fn(F, (T,)) -> <F as std::ops::FnOnce<(T,)>>::Output {<F as std::ops::FnOnce<(T,)>>::call_once}, val: Value(Scalar(<ZST>)) }
}
bb4: {
((_0 as Somex).0: U) = move _5; // scope 1 at map.rs:14:25: 14:36
discriminant(_0) = 1; // scope 1 at map.rs:14:25: 14:36
goto -> bb7; // scope 0 at map.rs:17:5: 17:6
}
bb5: {
_11 = discriminant(_1); // scope 0 at map.rs:17:5: 17:6
return; // scope 0 at map.rs:17:6: 17:6
}
bb6: {
drop(_2) -> [return: bb5, unwind: bb8]; // scope 0 at map.rs:17:5: 17:6
}
bb7: {
switchInt(_9) -> [false: bb5, otherwise: bb6]; // scope 0 at map.rs:17:5: 17:6
}
bb8 (cleanup): {
_13 = discriminant(_1); // scope 0 at map.rs:17:5: 17:6
resume; // scope 0 at map.rs:12:5: 17:6
}
}
The strangeness is in bb5: this is where we end up after the closure returns, but it reads the discriminant of _1
which we have already moved out of at this point! With Miri now checking validity when the discriminant is read, it complains (in the linked-list testcase) that there is a dangling Box
here (I guess the closure deallocated that Box
). There are also proposals that would make a move de-initialize the memory it is moving from, which would certainly make looking at its discriminant again UB.
I am not quite sure where this comes from, but it does seem in conflict with the idea that only valid values can have their discriminant read. Cc #89764 @wesleywiser