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