rustc_middle/mir/
terminator.rs

1//! Functionality for terminators and helper types that appear in terminators.
2
3use std::slice;
4
5use rustc_ast::InlineAsmOptions;
6use rustc_data_structures::packed::Pu128;
7use rustc_hir::LangItem;
8use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
9use smallvec::{SmallVec, smallvec};
10
11use super::*;
12
13impl SwitchTargets {
14    /// Creates switch targets from an iterator of values and target blocks.
15    ///
16    /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
17    /// `goto otherwise;`.
18    pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
19        let (values, mut targets): (SmallVec<_>, SmallVec<_>) =
20            targets.map(|(v, t)| (Pu128(v), t)).unzip();
21        targets.push(otherwise);
22        Self { values, targets }
23    }
24
25    /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
26    /// and to `else_` if not.
27    pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
28        Self { values: smallvec![Pu128(value)], targets: smallvec![then, else_] }
29    }
30
31    /// Inverse of `SwitchTargets::static_if`.
32    #[inline]
33    pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
34        if let &[value] = &self.values[..]
35            && let &[then, else_] = &self.targets[..]
36        {
37            Some((value.get(), then, else_))
38        } else {
39            None
40        }
41    }
42
43    /// Returns the fallback target that is jumped to when none of the values match the operand.
44    #[inline]
45    pub fn otherwise(&self) -> BasicBlock {
46        *self.targets.last().unwrap()
47    }
48
49    /// Returns an iterator over the switch targets.
50    ///
51    /// The iterator will yield tuples containing the value and corresponding target to jump to, not
52    /// including the `otherwise` fallback target.
53    ///
54    /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
55    #[inline]
56    pub fn iter(&self) -> SwitchTargetsIter<'_> {
57        SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
58    }
59
60    /// Returns a slice with all possible jump targets (including the fallback target).
61    #[inline]
62    pub fn all_targets(&self) -> &[BasicBlock] {
63        &self.targets
64    }
65
66    #[inline]
67    pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
68        &mut self.targets
69    }
70
71    /// Returns a slice with all considered values (not including the fallback).
72    #[inline]
73    pub fn all_values(&self) -> &[Pu128] {
74        &self.values
75    }
76
77    #[inline]
78    pub fn all_values_mut(&mut self) -> &mut [Pu128] {
79        &mut self.values
80    }
81
82    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
83    /// specific value. This cannot fail, as it'll return the `otherwise`
84    /// branch if there's not a specific match for the value.
85    #[inline]
86    pub fn target_for_value(&self, value: u128) -> BasicBlock {
87        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
88    }
89
90    /// Adds a new target to the switch. Panics if you add an already present value.
91    #[inline]
92    pub fn add_target(&mut self, value: u128, bb: BasicBlock) {
93        let value = Pu128(value);
94        if self.values.contains(&value) {
95            bug!("target value {:?} already present", value);
96        }
97        self.values.push(value);
98        self.targets.insert(self.targets.len() - 1, bb);
99    }
100
101    /// Returns true if all targets (including the fallback target) are distinct.
102    #[inline]
103    pub fn is_distinct(&self) -> bool {
104        self.targets.iter().collect::<FxHashSet<_>>().len() == self.targets.len()
105    }
106}
107
108pub struct SwitchTargetsIter<'a> {
109    inner: iter::Zip<slice::Iter<'a, Pu128>, slice::Iter<'a, BasicBlock>>,
110}
111
112impl<'a> Iterator for SwitchTargetsIter<'a> {
113    type Item = (u128, BasicBlock);
114
115    #[inline]
116    fn next(&mut self) -> Option<Self::Item> {
117        self.inner.next().map(|(val, bb)| (val.get(), *bb))
118    }
119
120    #[inline]
121    fn size_hint(&self) -> (usize, Option<usize>) {
122        self.inner.size_hint()
123    }
124}
125
126impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
127
128impl UnwindAction {
129    fn cleanup_block(self) -> Option<BasicBlock> {
130        match self {
131            UnwindAction::Cleanup(bb) => Some(bb),
132            UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
133        }
134    }
135}
136
137impl UnwindTerminateReason {
138    pub fn as_str(self) -> &'static str {
139        // Keep this in sync with the messages in `core/src/panicking.rs`.
140        match self {
141            UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
142            UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
143        }
144    }
145
146    /// A short representation of this used for MIR printing.
147    pub fn as_short_str(self) -> &'static str {
148        match self {
149            UnwindTerminateReason::Abi => "abi",
150            UnwindTerminateReason::InCleanup => "cleanup",
151        }
152    }
153
154    pub fn lang_item(self) -> LangItem {
155        match self {
156            UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
157            UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
158        }
159    }
160}
161
162impl<O> AssertKind<O> {
163    /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
164    pub fn is_optional_overflow_check(&self) -> bool {
165        use AssertKind::*;
166        use BinOp::*;
167        matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
168    }
169
170    /// Get the lang item that is invoked to print a static message when this assert fires.
171    ///
172    /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
173    /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
174    /// instead of printing a static message. Those have dynamic arguments that aren't present for
175    /// the rest of the messages here.
176    pub fn panic_function(&self) -> LangItem {
177        use AssertKind::*;
178        match self {
179            Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
180            Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
181            Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
182            Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
183            Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
184            OverflowNeg(_) => LangItem::PanicNegOverflow,
185            Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
186            Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
187            Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
188            DivisionByZero(_) => LangItem::PanicDivZero,
189            RemainderByZero(_) => LangItem::PanicRemZero,
190            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
191            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
192                LangItem::PanicAsyncFnResumed
193            }
194            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
195                LangItem::PanicAsyncGenFnResumed
196            }
197            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
198                LangItem::PanicGenFnNone
199            }
200            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
201            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
202                LangItem::PanicAsyncFnResumedPanic
203            }
204            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
205                LangItem::PanicAsyncGenFnResumedPanic
206            }
207            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
208                LangItem::PanicGenFnNonePanic
209            }
210            NullPointerDereference => LangItem::PanicNullPointerDereference,
211
212            BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
213                bug!("Unexpected AssertKind")
214            }
215        }
216    }
217
218    /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
219    ///
220    /// Needs to be kept in sync with the run-time behavior (which is defined by
221    /// `AssertKind::panic_function` and the lang items mentioned in its docs).
222    /// Note that we deliberately show more details here than we do at runtime, such as the actual
223    /// numbers that overflowed -- it is much easier to do so here than at runtime.
224    pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
225    where
226        O: Debug,
227    {
228        use AssertKind::*;
229        match self {
230            BoundsCheck { len, index } => write!(
231                f,
232                "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
233            ),
234
235            OverflowNeg(op) => {
236                write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
237            }
238            DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
239            RemainderByZero(op) => write!(
240                f,
241                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
242            ),
243            Overflow(BinOp::Add, l, r) => write!(
244                f,
245                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
246            ),
247            Overflow(BinOp::Sub, l, r) => write!(
248                f,
249                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
250            ),
251            Overflow(BinOp::Mul, l, r) => write!(
252                f,
253                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
254            ),
255            Overflow(BinOp::Div, l, r) => write!(
256                f,
257                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
258            ),
259            Overflow(BinOp::Rem, l, r) => write!(
260                f,
261                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
262            ),
263            Overflow(BinOp::Shr, _, r) => {
264                write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
265            }
266            Overflow(BinOp::Shl, _, r) => {
267                write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
268            }
269            Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
270            MisalignedPointerDereference { required, found } => {
271                write!(
272                    f,
273                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
274                )
275            }
276            NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
277            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
278                write!(f, "\"coroutine resumed after completion\"")
279            }
280            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
281                write!(f, "\"`async fn` resumed after completion\"")
282            }
283            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
284                write!(f, "\"`async gen fn` resumed after completion\"")
285            }
286            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
287                write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
288            }
289            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
290                write!(f, "\"coroutine resumed after panicking\"")
291            }
292            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
293                write!(f, "\"`async fn` resumed after panicking\"")
294            }
295            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
296                write!(f, "\"`async gen fn` resumed after panicking\"")
297            }
298            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
299                write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
300            }
301        }
302    }
303
304    /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
305    ///
306    /// Needs to be kept in sync with the run-time behavior (which is defined by
307    /// `AssertKind::panic_function` and the lang items mentioned in its docs).
308    /// Note that we deliberately show more details here than we do at runtime, such as the actual
309    /// numbers that overflowed -- it is much easier to do so here than at runtime.
310    pub fn diagnostic_message(&self) -> DiagMessage {
311        use AssertKind::*;
312
313        use crate::fluent_generated::*;
314
315        match self {
316            BoundsCheck { .. } => middle_bounds_check,
317            Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
318            Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
319            Overflow(_, _, _) => middle_assert_op_overflow,
320            OverflowNeg(_) => middle_assert_overflow_neg,
321            DivisionByZero(_) => middle_assert_divide_by_zero,
322            RemainderByZero(_) => middle_assert_remainder_by_zero,
323            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
324                middle_assert_async_resume_after_return
325            }
326            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
327                todo!()
328            }
329            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
330                bug!("gen blocks can be resumed after they return and will keep returning `None`")
331            }
332            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
333                middle_assert_coroutine_resume_after_return
334            }
335            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
336                middle_assert_async_resume_after_panic
337            }
338            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
339                todo!()
340            }
341            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
342                middle_assert_gen_resume_after_panic
343            }
344            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
345                middle_assert_coroutine_resume_after_panic
346            }
347            NullPointerDereference => middle_assert_null_ptr_deref,
348            MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
349        }
350    }
351
352    pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
353    where
354        O: fmt::Debug,
355    {
356        use AssertKind::*;
357
358        macro_rules! add {
359            ($name: expr, $value: expr) => {
360                adder($name.into(), $value.into_diag_arg(&mut None));
361            };
362        }
363
364        match self {
365            BoundsCheck { len, index } => {
366                add!("len", format!("{len:?}"));
367                add!("index", format!("{index:?}"));
368            }
369            Overflow(BinOp::Shl | BinOp::Shr, _, val)
370            | DivisionByZero(val)
371            | RemainderByZero(val)
372            | OverflowNeg(val) => {
373                add!("val", format!("{val:#?}"));
374            }
375            Overflow(binop, left, right) => {
376                add!("op", binop.to_hir_binop().as_str());
377                add!("left", format!("{left:#?}"));
378                add!("right", format!("{right:#?}"));
379            }
380            ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {}
381            MisalignedPointerDereference { required, found } => {
382                add!("required", format!("{required:#?}"));
383                add!("found", format!("{found:#?}"));
384            }
385        }
386    }
387}
388
389#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
390pub struct Terminator<'tcx> {
391    pub source_info: SourceInfo,
392    pub kind: TerminatorKind<'tcx>,
393}
394
395impl<'tcx> Terminator<'tcx> {
396    #[inline]
397    pub fn successors(&self) -> Successors<'_> {
398        self.kind.successors()
399    }
400
401    #[inline]
402    pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
403        self.kind.successors_mut()
404    }
405
406    #[inline]
407    pub fn unwind(&self) -> Option<&UnwindAction> {
408        self.kind.unwind()
409    }
410
411    #[inline]
412    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
413        self.kind.unwind_mut()
414    }
415}
416
417impl<'tcx> TerminatorKind<'tcx> {
418    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
419    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
420    pub const fn name(&self) -> &'static str {
421        match self {
422            TerminatorKind::Goto { .. } => "Goto",
423            TerminatorKind::SwitchInt { .. } => "SwitchInt",
424            TerminatorKind::UnwindResume => "UnwindResume",
425            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
426            TerminatorKind::Return => "Return",
427            TerminatorKind::Unreachable => "Unreachable",
428            TerminatorKind::Drop { .. } => "Drop",
429            TerminatorKind::Call { .. } => "Call",
430            TerminatorKind::TailCall { .. } => "TailCall",
431            TerminatorKind::Assert { .. } => "Assert",
432            TerminatorKind::Yield { .. } => "Yield",
433            TerminatorKind::CoroutineDrop => "CoroutineDrop",
434            TerminatorKind::FalseEdge { .. } => "FalseEdge",
435            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
436            TerminatorKind::InlineAsm { .. } => "InlineAsm",
437        }
438    }
439
440    #[inline]
441    pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
442        TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
443    }
444}
445
446pub use helper::*;
447
448mod helper {
449    use super::*;
450    pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
451    pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;
452
453    impl SwitchTargets {
454        /// Like [`SwitchTargets::target_for_value`], but returning the same type as
455        /// [`Terminator::successors`].
456        #[inline]
457        #[cfg_attr(not(bootstrap), define_opaque(Successors))]
458        pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
459            let target = self.target_for_value(value);
460            (&[]).into_iter().copied().chain(Some(target))
461        }
462    }
463
464    impl<'tcx> TerminatorKind<'tcx> {
465        #[inline]
466        #[cfg_attr(not(bootstrap), define_opaque(Successors))]
467        pub fn successors(&self) -> Successors<'_> {
468            use self::TerminatorKind::*;
469            match *self {
470                Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
471                | Yield { resume: ref t, drop: Some(u), .. }
472                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
473                | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
474                | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
475                    slice::from_ref(t).into_iter().copied().chain(Some(u))
476                }
477                Goto { target: ref t }
478                | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
479                | Call { target: Some(ref t), unwind: _, .. }
480                | Yield { resume: ref t, drop: None, .. }
481                | Drop { target: ref t, unwind: _, .. }
482                | Assert { target: ref t, unwind: _, .. }
483                | FalseUnwind { real_target: ref t, unwind: _ } => {
484                    slice::from_ref(t).into_iter().copied().chain(None)
485                }
486                UnwindResume
487                | UnwindTerminate(_)
488                | CoroutineDrop
489                | Return
490                | Unreachable
491                | TailCall { .. }
492                | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
493                InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
494                    targets.iter().copied().chain(Some(u))
495                }
496                InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
497                SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
498                FalseEdge { ref real_target, imaginary_target } => {
499                    slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
500                }
501            }
502        }
503
504        #[inline]
505        #[cfg_attr(not(bootstrap), define_opaque(SuccessorsMut))]
506        pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
507            use self::TerminatorKind::*;
508            match *self {
509                Call {
510                    target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), ..
511                }
512                | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
513                | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
514                | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
515                | FalseUnwind {
516                    real_target: ref mut t,
517                    unwind: UnwindAction::Cleanup(ref mut u),
518                } => slice::from_mut(t).into_iter().chain(Some(u)),
519                Goto { target: ref mut t }
520                | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
521                | Call { target: Some(ref mut t), unwind: _, .. }
522                | Yield { resume: ref mut t, drop: None, .. }
523                | Drop { target: ref mut t, unwind: _, .. }
524                | Assert { target: ref mut t, unwind: _, .. }
525                | FalseUnwind { real_target: ref mut t, unwind: _ } => {
526                    slice::from_mut(t).into_iter().chain(None)
527                }
528                UnwindResume
529                | UnwindTerminate(_)
530                | CoroutineDrop
531                | Return
532                | Unreachable
533                | TailCall { .. }
534                | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
535                InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
536                    targets.iter_mut().chain(Some(u))
537                }
538                InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
539                SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
540                FalseEdge { ref mut real_target, ref mut imaginary_target } => {
541                    slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
542                }
543            }
544        }
545    }
546}
547
548impl<'tcx> TerminatorKind<'tcx> {
549    #[inline]
550    pub fn unwind(&self) -> Option<&UnwindAction> {
551        match *self {
552            TerminatorKind::Goto { .. }
553            | TerminatorKind::UnwindResume
554            | TerminatorKind::UnwindTerminate(_)
555            | TerminatorKind::Return
556            | TerminatorKind::TailCall { .. }
557            | TerminatorKind::Unreachable
558            | TerminatorKind::CoroutineDrop
559            | TerminatorKind::Yield { .. }
560            | TerminatorKind::SwitchInt { .. }
561            | TerminatorKind::FalseEdge { .. } => None,
562            TerminatorKind::Call { ref unwind, .. }
563            | TerminatorKind::Assert { ref unwind, .. }
564            | TerminatorKind::Drop { ref unwind, .. }
565            | TerminatorKind::FalseUnwind { ref unwind, .. }
566            | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
567        }
568    }
569
570    #[inline]
571    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
572        match *self {
573            TerminatorKind::Goto { .. }
574            | TerminatorKind::UnwindResume
575            | TerminatorKind::UnwindTerminate(_)
576            | TerminatorKind::Return
577            | TerminatorKind::TailCall { .. }
578            | TerminatorKind::Unreachable
579            | TerminatorKind::CoroutineDrop
580            | TerminatorKind::Yield { .. }
581            | TerminatorKind::SwitchInt { .. }
582            | TerminatorKind::FalseEdge { .. } => None,
583            TerminatorKind::Call { ref mut unwind, .. }
584            | TerminatorKind::Assert { ref mut unwind, .. }
585            | TerminatorKind::Drop { ref mut unwind, .. }
586            | TerminatorKind::FalseUnwind { ref mut unwind, .. }
587            | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
588        }
589    }
590
591    #[inline]
592    pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
593        match self {
594            TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
595            _ => None,
596        }
597    }
598
599    #[inline]
600    pub fn as_goto(&self) -> Option<BasicBlock> {
601        match self {
602            TerminatorKind::Goto { target } => Some(*target),
603            _ => None,
604        }
605    }
606}
607
608#[derive(Copy, Clone, Debug)]
609pub enum TerminatorEdges<'mir, 'tcx> {
610    /// For terminators that have no successor, like `return`.
611    None,
612    /// For terminators that have a single successor, like `goto`, and `assert` without a cleanup
613    /// block.
614    Single(BasicBlock),
615    /// For terminators that have two successors, like `assert` with a cleanup block, and
616    /// `falseEdge`.
617    Double(BasicBlock, BasicBlock),
618    /// Special action for `Yield`, `Call` and `InlineAsm` terminators.
619    AssignOnReturn {
620        return_: &'mir [BasicBlock],
621        /// The cleanup block, if it exists.
622        cleanup: Option<BasicBlock>,
623        place: CallReturnPlaces<'mir, 'tcx>,
624    },
625    /// Special edge for `SwitchInt`.
626    SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
627}
628
629/// List of places that are written to after a successful (non-unwind) return
630/// from a `Call`, `Yield` or `InlineAsm`.
631#[derive(Copy, Clone, Debug)]
632pub enum CallReturnPlaces<'a, 'tcx> {
633    Call(Place<'tcx>),
634    Yield(Place<'tcx>),
635    InlineAsm(&'a [InlineAsmOperand<'tcx>]),
636}
637
638impl<'tcx> CallReturnPlaces<'_, 'tcx> {
639    pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
640        match *self {
641            Self::Call(place) | Self::Yield(place) => f(place),
642            Self::InlineAsm(operands) => {
643                for op in operands {
644                    match *op {
645                        InlineAsmOperand::Out { place: Some(place), .. }
646                        | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
647                        _ => {}
648                    }
649                }
650            }
651        }
652    }
653}
654
655impl<'tcx> Terminator<'tcx> {
656    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
657        self.kind.edges()
658    }
659}
660
661impl<'tcx> TerminatorKind<'tcx> {
662    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
663        use TerminatorKind::*;
664        match *self {
665            Return
666            | TailCall { .. }
667            | UnwindResume
668            | UnwindTerminate(_)
669            | CoroutineDrop
670            | Unreachable => TerminatorEdges::None,
671
672            Goto { target } => TerminatorEdges::Single(target),
673
674            Assert { target, unwind, expected: _, msg: _, cond: _ }
675            | Drop { target, unwind, place: _, replace: _ }
676            | FalseUnwind { real_target: target, unwind } => match unwind {
677                UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
678                UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
679                    TerminatorEdges::Single(target)
680                }
681            },
682
683            FalseEdge { real_target, imaginary_target } => {
684                TerminatorEdges::Double(real_target, imaginary_target)
685            }
686
687            Yield { resume: ref target, drop, resume_arg, value: _ } => {
688                TerminatorEdges::AssignOnReturn {
689                    return_: slice::from_ref(target),
690                    cleanup: drop,
691                    place: CallReturnPlaces::Yield(resume_arg),
692                }
693            }
694
695            Call {
696                unwind,
697                destination,
698                ref target,
699                func: _,
700                args: _,
701                fn_span: _,
702                call_source: _,
703            } => TerminatorEdges::AssignOnReturn {
704                return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
705                cleanup: unwind.cleanup_block(),
706                place: CallReturnPlaces::Call(destination),
707            },
708
709            InlineAsm {
710                asm_macro: _,
711                template: _,
712                ref operands,
713                options: _,
714                line_spans: _,
715                ref targets,
716                unwind,
717            } => TerminatorEdges::AssignOnReturn {
718                return_: targets,
719                cleanup: unwind.cleanup_block(),
720                place: CallReturnPlaces::InlineAsm(operands),
721            },
722
723            SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
724        }
725    }
726}
727
728impl CallSource {
729    pub fn from_hir_call(self) -> bool {
730        matches!(self, CallSource::Normal)
731    }
732}
733
734impl InlineAsmMacro {
735    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
736        match self {
737            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
738            InlineAsmMacro::NakedAsm => true,
739        }
740    }
741}