Description
Code
Playground. Needs release
struct A;
struct B;
impl Drop for A {
fn drop(&mut self) {}
}
impl Drop for B {
fn drop(&mut self) {}
}
#[inline(always)]
fn no_unwind() {}
fn weird_temporary(a: A, b: B, nothing: ((), (), ()), x: bool) -> ((), (), ()) {
'scope: {
(
{
let _z = b;
if x {
break 'scope nothing;
}
},
match { a } {
_ => (),
},
no_unwind(),
)
}
}
Meta
Version: 0d5573e and all other recent versions
Explanation
This causes an ICE in the MIR validator. The reason is an out of storage use of the local that is the temporary for { a }
. This local has a couple interesting properties. The most important of these is that MIR building thinks that it may need to be dropped while unwinding in two scenarios:
- If the drop of
_z
unwinds. - If
no_unwind()
unwinds.
Neither of these are actually possible.
First, drop elaboration comes along, sees that only the second of these is possible. It reacts to this by inserting a drop flag for the local. Then, inlining comes along and inlines no_unwind()
. The potential unwind edge is now gone.
The result is that the cleanup drop is actually completely unreachable, and this ends up causing an ICE.
Curiously, this does not reproduce if one replaces break 'scope nothing;
with return nothing;
. In the case of the return, MIR building correctly realizes that the temporary has not been created yet and hence does not need to be dropped. The best solution to this is likely to make the break
do the same thing - I'm not sure how difficult that is.