rustc_middle/mir/
terminator.rs

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