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 = {
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
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
265280macro_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
274314define ! ( "mir_return" , fn Return ( ) -> BasicBlock ) ;
275315define ! ( "mir_goto" , fn Goto ( destination: BasicBlock ) -> BasicBlock ) ;
276316define ! ( "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+
279324define ! ( "mir_storage_live" , fn StorageLive <T >( local: T ) ) ;
280325define ! ( "mir_storage_dead" , fn StorageDead <T >( local: T ) ) ;
281326define ! ( "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