Skip to content

Commit 0f2f310

Browse files
committed
Auto merge of rust-lang#117330 - tmiasko:custom-mir-cleanup-blocks, r=cjgillot
Custom MIR: Support cleanup blocks Cleanup blocks are declared with `bb (cleanup) = { ... }`. `Call` and `Drop` terminators take an additional argument describing the unwind action, which is one of the following: * `UnwindContinue()` * `UnwindUnreachable()` * `UnwindTerminate(reason)`, where reason is `ReasonAbi` or `ReasonInCleanup` * `UnwindCleanup(block)` Also support unwind resume and unwind terminate terminators: * `UnwindResume()` * `UnwindTerminate(reason)`
2 parents 63b9041 + bca8e5a commit 0f2f310

File tree

1 file changed

+73
-15
lines changed

1 file changed

+73
-15
lines changed

core/src/intrinsics/mir.rs

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@
110110
//! let popped;
111111
//!
112112
//! {
113-
//! Call(_unused = Vec::push(v, value), pop)
113+
//! Call(_unused = Vec::push(v, value), pop, UnwindContinue())
114114
//! }
115115
//!
116116
//! pop = {
117-
//! Call(popped = Vec::pop(v), drop)
117+
//! Call(popped = Vec::pop(v), drop, UnwindContinue())
118118
//! }
119119
//!
120120
//! drop = {
121-
//! Drop(popped, ret)
121+
//! Drop(popped, ret, UnwindContinue())
122122
//! }
123123
//!
124124
//! ret = {
@@ -238,10 +238,6 @@
238238
//!
239239
//! #### Terminators
240240
//!
241-
//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
242-
//! are no resume and abort terminators, and terminators that might unwind do not have any way to
243-
//! indicate the unwind block.
244-
//!
245241
//! - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions.
246242
//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
247243
//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
@@ -260,7 +256,26 @@
260256
/// Type representing basic blocks.
261257
///
262258
/// All terminators will have this type as a return type. It helps achieve some type safety.
263-
pub struct BasicBlock;
259+
#[rustc_diagnostic_item = "mir_basic_block"]
260+
pub enum BasicBlock {
261+
/// A non-cleanup basic block.
262+
Normal,
263+
/// A basic block that lies on an unwind path.
264+
Cleanup,
265+
}
266+
267+
/// The reason we are terminating the process during unwinding.
268+
#[rustc_diagnostic_item = "mir_unwind_terminate_reason"]
269+
pub enum UnwindTerminateReason {
270+
/// Unwinding is just not possible given the ABI of this function.
271+
Abi,
272+
/// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was
273+
/// triggered by the drop glue.
274+
InCleanup,
275+
}
276+
277+
pub use UnwindTerminateReason::Abi as ReasonAbi;
278+
pub use UnwindTerminateReason::InCleanup as ReasonInCleanup;
264279

265280
macro_rules! define {
266281
($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => {
@@ -271,11 +286,41 @@ macro_rules! define {
271286
}
272287
}
273288

289+
// Unwind actions
290+
define!(
291+
"mir_unwind_continue",
292+
/// An unwind action that continues unwinding.
293+
fn UnwindContinue()
294+
);
295+
define!(
296+
"mir_unwind_unreachable",
297+
/// An unwind action that triggers undefined behaviour.
298+
fn UnwindUnreachable() -> BasicBlock
299+
);
300+
define!(
301+
"mir_unwind_terminate",
302+
/// An unwind action that terminates the execution.
303+
///
304+
/// `UnwindTerminate` can also be used as a terminator.
305+
fn UnwindTerminate(reason: UnwindTerminateReason)
306+
);
307+
define!(
308+
"mir_unwind_cleanup",
309+
/// An unwind action that continues execution in a given basic blok.
310+
fn UnwindCleanup(goto: BasicBlock)
311+
);
312+
313+
// Terminators
274314
define!("mir_return", fn Return() -> BasicBlock);
275315
define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
276316
define!("mir_unreachable", fn Unreachable() -> BasicBlock);
277-
define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
278-
define!("mir_call", fn Call(call: (), goto: BasicBlock));
317+
define!("mir_drop", fn Drop<T, U>(place: T, goto: BasicBlock, unwind_action: U));
318+
define!("mir_call", fn Call<U>(call: (), goto: BasicBlock, unwind_action: U));
319+
define!("mir_unwind_resume",
320+
/// A terminator that resumes the unwinding.
321+
fn UnwindResume()
322+
);
323+
279324
define!("mir_storage_live", fn StorageLive<T>(local: T));
280325
define!("mir_storage_dead", fn StorageDead<T>(local: T));
281326
define!("mir_deinit", fn Deinit<T>(place: T));
@@ -382,16 +427,15 @@ pub macro mir {
382427
}
383428

384429
$(
385-
$block_name:ident = {
430+
$block_name:ident $(($block_cleanup:ident))? = {
386431
$($block:tt)*
387432
}
388433
)*
389434
) => {{
390435
// First, we declare all basic blocks.
391-
$(
392-
let $block_name: ::core::intrinsics::mir::BasicBlock;
393-
)*
394-
436+
__internal_declare_basic_blocks!($(
437+
$block_name $(($block_cleanup))?
438+
)*);
395439
{
396440
// Now all locals
397441
#[allow(non_snake_case)]
@@ -585,3 +629,17 @@ pub macro __internal_remove_let {
585629
}
586630
},
587631
}
632+
633+
/// Helper macro that declares the basic blocks.
634+
#[doc(hidden)]
635+
pub macro __internal_declare_basic_blocks {
636+
() => {},
637+
($name:ident (cleanup) $($rest:tt)*) => {
638+
let $name = ::core::intrinsics::mir::BasicBlock::Cleanup;
639+
__internal_declare_basic_blocks!($($rest)*)
640+
},
641+
($name:ident $($rest:tt)*) => {
642+
let $name = ::core::intrinsics::mir::BasicBlock::Normal;
643+
__internal_declare_basic_blocks!($($rest)*)
644+
},
645+
}

0 commit comments

Comments
 (0)