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            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
212            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
213                LangItem::PanicAsyncFnResumedDrop
214            }
215            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
216                LangItem::PanicAsyncGenFnResumedDrop
217            }
218            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
219                LangItem::PanicGenFnNoneDrop
220            }
221
222            BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
223                bug!("Unexpected AssertKind")
224            }
225        }
226    }
227
228    /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
229    ///
230    /// Needs to be kept in sync with the run-time behavior (which is defined by
231    /// `AssertKind::panic_function` and the lang items mentioned in its docs).
232    /// Note that we deliberately show more details here than we do at runtime, such as the actual
233    /// numbers that overflowed -- it is much easier to do so here than at runtime.
234    pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
235    where
236        O: Debug,
237    {
238        use AssertKind::*;
239        match self {
240            BoundsCheck { len, index } => write!(
241                f,
242                "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
243            ),
244
245            OverflowNeg(op) => {
246                write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
247            }
248            DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
249            RemainderByZero(op) => write!(
250                f,
251                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
252            ),
253            Overflow(BinOp::Add, l, r) => write!(
254                f,
255                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
256            ),
257            Overflow(BinOp::Sub, l, r) => write!(
258                f,
259                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
260            ),
261            Overflow(BinOp::Mul, l, r) => write!(
262                f,
263                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
264            ),
265            Overflow(BinOp::Div, l, r) => write!(
266                f,
267                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
268            ),
269            Overflow(BinOp::Rem, l, r) => write!(
270                f,
271                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
272            ),
273            Overflow(BinOp::Shr, _, r) => {
274                write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
275            }
276            Overflow(BinOp::Shl, _, r) => {
277                write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
278            }
279            Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
280            MisalignedPointerDereference { required, found } => {
281                write!(
282                    f,
283                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
284                )
285            }
286            NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
287            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
288                write!(f, "\"coroutine resumed after completion\"")
289            }
290            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
291                write!(f, "\"`async fn` resumed after completion\"")
292            }
293            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
294                write!(f, "\"`async gen fn` resumed after completion\"")
295            }
296            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
297                write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
298            }
299            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
300                write!(f, "\"coroutine resumed after panicking\"")
301            }
302            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
303                write!(f, "\"`async fn` resumed after panicking\"")
304            }
305            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
306                write!(f, "\"`async gen fn` resumed after panicking\"")
307            }
308            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
309                write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
310            }
311            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
312                write!(f, "\"coroutine resumed after async drop\"")
313            }
314            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
315                write!(f, "\"`async fn` resumed after async drop\"")
316            }
317            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
318                write!(f, "\"`async gen fn` resumed after async drop\"")
319            }
320            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
321                write!(f, "\"`gen fn` resumed after drop\"")
322            }
323        }
324    }
325
326    /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
327    ///
328    /// Needs to be kept in sync with the run-time behavior (which is defined by
329    /// `AssertKind::panic_function` and the lang items mentioned in its docs).
330    /// Note that we deliberately show more details here than we do at runtime, such as the actual
331    /// numbers that overflowed -- it is much easier to do so here than at runtime.
332    pub fn diagnostic_message(&self) -> DiagMessage {
333        use AssertKind::*;
334
335        use crate::fluent_generated::*;
336
337        match self {
338            BoundsCheck { .. } => middle_bounds_check,
339            Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
340            Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
341            Overflow(_, _, _) => middle_assert_op_overflow,
342            OverflowNeg(_) => middle_assert_overflow_neg,
343            DivisionByZero(_) => middle_assert_divide_by_zero,
344            RemainderByZero(_) => middle_assert_remainder_by_zero,
345            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
346                middle_assert_async_resume_after_return
347            }
348            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
349                todo!()
350            }
351            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
352                bug!("gen blocks can be resumed after they return and will keep returning `None`")
353            }
354            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
355                middle_assert_coroutine_resume_after_return
356            }
357            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
358                middle_assert_async_resume_after_panic
359            }
360            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
361                todo!()
362            }
363            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
364                middle_assert_gen_resume_after_panic
365            }
366            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
367                middle_assert_coroutine_resume_after_panic
368            }
369            NullPointerDereference => middle_assert_null_ptr_deref,
370            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
371                middle_assert_async_resume_after_drop
372            }
373            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
374                todo!()
375            }
376            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
377                middle_assert_gen_resume_after_drop
378            }
379            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
380                middle_assert_coroutine_resume_after_drop
381            }
382
383            MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
384        }
385    }
386
387    pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
388    where
389        O: fmt::Debug,
390    {
391        use AssertKind::*;
392
393        macro_rules! add {
394            ($name: expr, $value: expr) => {
395                adder($name.into(), $value.into_diag_arg(&mut None));
396            };
397        }
398
399        match self {
400            BoundsCheck { len, index } => {
401                add!("len", format!("{len:?}"));
402                add!("index", format!("{index:?}"));
403            }
404            Overflow(BinOp::Shl | BinOp::Shr, _, val)
405            | DivisionByZero(val)
406            | RemainderByZero(val)
407            | OverflowNeg(val) => {
408                add!("val", format!("{val:#?}"));
409            }
410            Overflow(binop, left, right) => {
411                add!("op", binop.to_hir_binop().as_str());
412                add!("left", format!("{left:#?}"));
413                add!("right", format!("{right:#?}"));
414            }
415            ResumedAfterReturn(_)
416            | ResumedAfterPanic(_)
417            | NullPointerDereference
418            | ResumedAfterDrop(_) => {}
419            MisalignedPointerDereference { required, found } => {
420                add!("required", format!("{required:#?}"));
421                add!("found", format!("{found:#?}"));
422            }
423        }
424    }
425}
426
427#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
428pub struct Terminator<'tcx> {
429    pub source_info: SourceInfo,
430    pub kind: TerminatorKind<'tcx>,
431}
432
433impl<'tcx> Terminator<'tcx> {
434    #[inline]
435    pub fn successors(&self) -> Successors<'_> {
436        self.kind.successors()
437    }
438
439    #[inline]
440    pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
441        self.kind.successors_mut(f)
442    }
443
444    #[inline]
445    pub fn unwind(&self) -> Option<&UnwindAction> {
446        self.kind.unwind()
447    }
448
449    #[inline]
450    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
451        self.kind.unwind_mut()
452    }
453}
454
455impl<'tcx> TerminatorKind<'tcx> {
456    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
457    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
458    pub const fn name(&self) -> &'static str {
459        match self {
460            TerminatorKind::Goto { .. } => "Goto",
461            TerminatorKind::SwitchInt { .. } => "SwitchInt",
462            TerminatorKind::UnwindResume => "UnwindResume",
463            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
464            TerminatorKind::Return => "Return",
465            TerminatorKind::Unreachable => "Unreachable",
466            TerminatorKind::Drop { .. } => "Drop",
467            TerminatorKind::Call { .. } => "Call",
468            TerminatorKind::TailCall { .. } => "TailCall",
469            TerminatorKind::Assert { .. } => "Assert",
470            TerminatorKind::Yield { .. } => "Yield",
471            TerminatorKind::CoroutineDrop => "CoroutineDrop",
472            TerminatorKind::FalseEdge { .. } => "FalseEdge",
473            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
474            TerminatorKind::InlineAsm { .. } => "InlineAsm",
475        }
476    }
477
478    #[inline]
479    pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
480        TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
481    }
482}
483
484pub use helper::*;
485
486mod helper {
487    use super::*;
488    pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
489
490    impl SwitchTargets {
491        /// Like [`SwitchTargets::target_for_value`], but returning the same type as
492        /// [`Terminator::successors`].
493        #[inline]
494        #[define_opaque(Successors)]
495        pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
496            let target = self.target_for_value(value);
497            (&[]).into_iter().copied().chain(Some(target).into_iter().chain(None))
498        }
499    }
500
501    impl<'tcx> TerminatorKind<'tcx> {
502        #[inline]
503        #[define_opaque(Successors)]
504        pub fn successors(&self) -> Successors<'_> {
505            use self::TerminatorKind::*;
506            match *self {
507                // 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
508                Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
509                    slice::from_ref(t)
510                        .into_iter()
511                        .copied()
512                        .chain(Some(u).into_iter().chain(Some(d)))
513                }
514                // 2-successors
515                Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
516                | Yield { resume: ref t, drop: Some(u), .. }
517                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
518                | Drop { target: ref t, unwind: _, drop: Some(u), .. }
519                | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
520                | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
521                    slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None))
522                }
523                // single successor
524                Goto { target: ref t }
525                | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
526                | Call { target: Some(ref t), unwind: _, .. }
527                | Yield { resume: ref t, drop: None, .. }
528                | Drop { target: ref t, unwind: _, .. }
529                | Assert { target: ref t, unwind: _, .. }
530                | FalseUnwind { real_target: ref t, unwind: _ } => {
531                    slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None))
532                }
533                // No successors
534                UnwindResume
535                | UnwindTerminate(_)
536                | CoroutineDrop
537                | Return
538                | Unreachable
539                | TailCall { .. }
540                | Call { target: None, unwind: _, .. } => {
541                    (&[]).into_iter().copied().chain(None.into_iter().chain(None))
542                }
543                // Multiple successors
544                InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
545                    targets.iter().copied().chain(Some(u).into_iter().chain(None))
546                }
547                InlineAsm { ref targets, unwind: _, .. } => {
548                    targets.iter().copied().chain(None.into_iter().chain(None))
549                }
550                SwitchInt { ref targets, .. } => {
551                    targets.targets.iter().copied().chain(None.into_iter().chain(None))
552                }
553                // FalseEdge
554                FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target)
555                    .into_iter()
556                    .copied()
557                    .chain(Some(imaginary_target).into_iter().chain(None)),
558            }
559        }
560
561        #[inline]
562        pub fn successors_mut<'a>(&'a mut self, mut f: impl FnMut(&'a mut BasicBlock)) {
563            use self::TerminatorKind::*;
564            match self {
565                Drop { target, unwind, drop, .. } => {
566                    f(target);
567                    if let UnwindAction::Cleanup(u) = unwind {
568                        f(u)
569                    }
570                    if let Some(d) = drop {
571                        f(d)
572                    }
573                }
574                Call { target, unwind, .. } => {
575                    if let Some(target) = target {
576                        f(target);
577                    }
578                    if let UnwindAction::Cleanup(u) = unwind {
579                        f(u)
580                    }
581                }
582                Yield { resume, drop, .. } => {
583                    f(resume);
584                    if let Some(d) = drop {
585                        f(d)
586                    }
587                }
588                Assert { target, unwind, .. } | FalseUnwind { real_target: target, unwind } => {
589                    f(target);
590                    if let UnwindAction::Cleanup(u) = unwind {
591                        f(u)
592                    }
593                }
594                Goto { target } => {
595                    f(target);
596                }
597                UnwindResume
598                | UnwindTerminate(_)
599                | CoroutineDrop
600                | Return
601                | Unreachable
602                | TailCall { .. } => {}
603                InlineAsm { targets, unwind, .. } => {
604                    for target in targets {
605                        f(target);
606                    }
607                    if let UnwindAction::Cleanup(u) = unwind {
608                        f(u)
609                    }
610                }
611                SwitchInt { targets, .. } => {
612                    for target in &mut targets.targets {
613                        f(target);
614                    }
615                }
616                FalseEdge { real_target, imaginary_target } => {
617                    f(real_target);
618                    f(imaginary_target);
619                }
620            }
621        }
622    }
623}
624
625impl<'tcx> TerminatorKind<'tcx> {
626    #[inline]
627    pub fn unwind(&self) -> Option<&UnwindAction> {
628        match *self {
629            TerminatorKind::Goto { .. }
630            | TerminatorKind::UnwindResume
631            | TerminatorKind::UnwindTerminate(_)
632            | TerminatorKind::Return
633            | TerminatorKind::TailCall { .. }
634            | TerminatorKind::Unreachable
635            | TerminatorKind::CoroutineDrop
636            | TerminatorKind::Yield { .. }
637            | TerminatorKind::SwitchInt { .. }
638            | TerminatorKind::FalseEdge { .. } => None,
639            TerminatorKind::Call { ref unwind, .. }
640            | TerminatorKind::Assert { ref unwind, .. }
641            | TerminatorKind::Drop { ref unwind, .. }
642            | TerminatorKind::FalseUnwind { ref unwind, .. }
643            | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
644        }
645    }
646
647    #[inline]
648    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
649        match *self {
650            TerminatorKind::Goto { .. }
651            | TerminatorKind::UnwindResume
652            | TerminatorKind::UnwindTerminate(_)
653            | TerminatorKind::Return
654            | TerminatorKind::TailCall { .. }
655            | TerminatorKind::Unreachable
656            | TerminatorKind::CoroutineDrop
657            | TerminatorKind::Yield { .. }
658            | TerminatorKind::SwitchInt { .. }
659            | TerminatorKind::FalseEdge { .. } => None,
660            TerminatorKind::Call { ref mut unwind, .. }
661            | TerminatorKind::Assert { ref mut unwind, .. }
662            | TerminatorKind::Drop { ref mut unwind, .. }
663            | TerminatorKind::FalseUnwind { ref mut unwind, .. }
664            | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
665        }
666    }
667
668    #[inline]
669    pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
670        match self {
671            TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
672            _ => None,
673        }
674    }
675
676    #[inline]
677    pub fn as_goto(&self) -> Option<BasicBlock> {
678        match self {
679            TerminatorKind::Goto { target } => Some(*target),
680            _ => None,
681        }
682    }
683}
684
685#[derive(Copy, Clone, Debug)]
686pub enum TerminatorEdges<'mir, 'tcx> {
687    /// For terminators that have no successor, like `return`.
688    None,
689    /// For terminators that have a single successor, like `goto`, and `assert` without a cleanup
690    /// block.
691    Single(BasicBlock),
692    /// For terminators that have two successors, like `assert` with a cleanup block, and
693    /// `falseEdge`.
694    Double(BasicBlock, BasicBlock),
695    /// Special action for `Yield`, `Call` and `InlineAsm` terminators.
696    AssignOnReturn {
697        return_: &'mir [BasicBlock],
698        /// The cleanup block, if it exists.
699        cleanup: Option<BasicBlock>,
700        place: CallReturnPlaces<'mir, 'tcx>,
701    },
702    /// Special edge for `SwitchInt`.
703    SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
704}
705
706/// List of places that are written to after a successful (non-unwind) return
707/// from a `Call`, `Yield` or `InlineAsm`.
708#[derive(Copy, Clone, Debug)]
709pub enum CallReturnPlaces<'a, 'tcx> {
710    Call(Place<'tcx>),
711    Yield(Place<'tcx>),
712    InlineAsm(&'a [InlineAsmOperand<'tcx>]),
713}
714
715impl<'tcx> CallReturnPlaces<'_, 'tcx> {
716    pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
717        match *self {
718            Self::Call(place) | Self::Yield(place) => f(place),
719            Self::InlineAsm(operands) => {
720                for op in operands {
721                    match *op {
722                        InlineAsmOperand::Out { place: Some(place), .. }
723                        | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
724                        _ => {}
725                    }
726                }
727            }
728        }
729    }
730}
731
732impl<'tcx> Terminator<'tcx> {
733    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
734        self.kind.edges()
735    }
736}
737
738impl<'tcx> TerminatorKind<'tcx> {
739    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
740        use TerminatorKind::*;
741        match *self {
742            Return
743            | TailCall { .. }
744            | UnwindResume
745            | UnwindTerminate(_)
746            | CoroutineDrop
747            | Unreachable => TerminatorEdges::None,
748
749            Goto { target } => TerminatorEdges::Single(target),
750
751            // FIXME: Maybe we need also TerminatorEdges::Trio for async drop
752            // (target + unwind + dropline)
753            Assert { target, unwind, expected: _, msg: _, cond: _ }
754            | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
755            | FalseUnwind { real_target: target, unwind } => match unwind {
756                UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
757                UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
758                    TerminatorEdges::Single(target)
759                }
760            },
761
762            FalseEdge { real_target, imaginary_target } => {
763                TerminatorEdges::Double(real_target, imaginary_target)
764            }
765
766            Yield { resume: ref target, drop, resume_arg, value: _ } => {
767                TerminatorEdges::AssignOnReturn {
768                    return_: slice::from_ref(target),
769                    cleanup: drop,
770                    place: CallReturnPlaces::Yield(resume_arg),
771                }
772            }
773
774            Call {
775                unwind,
776                destination,
777                ref target,
778                func: _,
779                args: _,
780                fn_span: _,
781                call_source: _,
782            } => TerminatorEdges::AssignOnReturn {
783                return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
784                cleanup: unwind.cleanup_block(),
785                place: CallReturnPlaces::Call(destination),
786            },
787
788            InlineAsm {
789                asm_macro: _,
790                template: _,
791                ref operands,
792                options: _,
793                line_spans: _,
794                ref targets,
795                unwind,
796            } => TerminatorEdges::AssignOnReturn {
797                return_: targets,
798                cleanup: unwind.cleanup_block(),
799                place: CallReturnPlaces::InlineAsm(operands),
800            },
801
802            SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
803        }
804    }
805}
806
807impl CallSource {
808    pub fn from_hir_call(self) -> bool {
809        matches!(self, CallSource::Normal)
810    }
811}
812
813impl InlineAsmMacro {
814    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
815        match self {
816            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
817            InlineAsmMacro::NakedAsm => true,
818        }
819    }
820}