Skip to main content

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: {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(Pu128(value));
        vec
    } else {
        ::smallvec::SmallVec::from_vec(<[_]>::into_vec(::alloc::boxed::box_new([Pu128(value)])))
    }
}smallvec![Pu128(value)], targets: {
    let count = 0usize + 1usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(then);
        vec.push(else_);
        vec
    } else {
        ::smallvec::SmallVec::from_vec(<[_]>::into_vec(::alloc::boxed::box_new([then,
                            else_])))
    }
}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            crate::util::bug::bug_fmt(format_args!("target value {0:?} already present",
        value));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        #[allow(non_exhaustive_omitted_patterns)] match self {
    OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..) => true,
    _ => false,
}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, _, _) => crate::util::bug::bug_fmt(format_args!("{0:?} cannot 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                crate::util::bug::bug_fmt(format_args!("Unexpected AssertKind"))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 } => f.write_fmt(format_args!("\"index out of bounds: the length is {{}} but the index is {{}}\", {0:?}, {1:?}",
        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                f.write_fmt(format_args!("\"attempt to negate `{{}}`, which would overflow\", {0:?}",
        op))write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
248            }
249            DivisionByZero(op) => f.write_fmt(format_args!("\"attempt to divide `{{}}` by zero\", {0:?}", op))write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
250            RemainderByZero(op) => f.write_fmt(format_args!("\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {0:?}",
        op))write!(
251                f,
252                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
253            ),
254            Overflow(BinOp::Add, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} + {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
255                f,
256                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
257            ),
258            Overflow(BinOp::Sub, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} - {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
259                f,
260                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
261            ),
262            Overflow(BinOp::Mul, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} * {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
263                f,
264                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
265            ),
266            Overflow(BinOp::Div, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} / {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
267                f,
268                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
269            ),
270            Overflow(BinOp::Rem, l, r) => f.write_fmt(format_args!("\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
271                f,
272                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
273            ),
274            Overflow(BinOp::Shr, _, r) => {
275                f.write_fmt(format_args!("\"attempt to shift right by `{{}}`, which would overflow\", {0:?}",
        r))write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
276            }
277            Overflow(BinOp::Shl, _, r) => {
278                f.write_fmt(format_args!("\"attempt to shift left by `{{}}`, which would overflow\", {0:?}",
        r))write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
279            }
280            Overflow(op, _, _) => crate::util::bug::bug_fmt(format_args!("{0:?} cannot overflow", op))bug!("{:?} cannot overflow", op),
281            MisalignedPointerDereference { required, found } => {
282                f.write_fmt(format_args!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {0:?}, {1:?}",
        required, found))write!(
283                    f,
284                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
285                )
286            }
287            NullPointerDereference => f.write_fmt(format_args!("\"null pointer dereference occurred\""))write!(f, "\"null pointer dereference occurred\""),
288            InvalidEnumConstruction(source) => {
289                f.write_fmt(format_args!("\"trying to construct an enum from an invalid value {{}}\", {0:?}",
        source))write!(f, "\"trying to construct an enum from an invalid value {{}}\", {source:?}")
290            }
291            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
292                f.write_fmt(format_args!("\"coroutine resumed after completion\""))write!(f, "\"coroutine resumed after completion\"")
293            }
294            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
295                f.write_fmt(format_args!("\"`async fn` resumed after completion\""))write!(f, "\"`async fn` resumed after completion\"")
296            }
297            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
298                f.write_fmt(format_args!("\"`async gen fn` resumed after completion\""))write!(f, "\"`async gen fn` resumed after completion\"")
299            }
300            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
301                f.write_fmt(format_args!("\"`gen fn` should just keep returning `None` after completion\""))write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
302            }
303            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
304                f.write_fmt(format_args!("\"coroutine resumed after panicking\""))write!(f, "\"coroutine resumed after panicking\"")
305            }
306            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
307                f.write_fmt(format_args!("\"`async fn` resumed after panicking\""))write!(f, "\"`async fn` resumed after panicking\"")
308            }
309            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
310                f.write_fmt(format_args!("\"`async gen fn` resumed after panicking\""))write!(f, "\"`async gen fn` resumed after panicking\"")
311            }
312            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
313                f.write_fmt(format_args!("\"`gen fn` should just keep returning `None` after panicking\""))write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
314            }
315            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
316                f.write_fmt(format_args!("\"coroutine resumed after async drop\""))write!(f, "\"coroutine resumed after async drop\"")
317            }
318            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
319                f.write_fmt(format_args!("\"`async fn` resumed after async drop\""))write!(f, "\"`async fn` resumed after async drop\"")
320            }
321            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
322                f.write_fmt(format_args!("\"`async gen fn` resumed after async drop\""))write!(f, "\"`async gen fn` resumed after async drop\"")
323            }
324            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
325                f.write_fmt(format_args!("\"`gen fn` resumed after drop\""))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        match self {
340            BoundsCheck { .. } => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("index out of bounds: the length is {$len} but the index is {$index}"))inline_fluent!(
341                "index out of bounds: the length is {$len} but the index is {$index}"
342            ),
343            Overflow(BinOp::Shl, _, _) => {
344                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to shift left by `{$val}`, which would overflow"))inline_fluent!("attempt to shift left by `{$val}`, which would overflow")
345            }
346            Overflow(BinOp::Shr, _, _) => {
347                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to shift right by `{$val}`, which would overflow"))inline_fluent!("attempt to shift right by `{$val}`, which would overflow")
348            }
349            Overflow(_, _, _) => {
350                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to compute `{$left} {$op} {$right}`, which would overflow"))inline_fluent!("attempt to compute `{$left} {$op} {$right}`, which would overflow")
351            }
352            OverflowNeg(_) => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to negate `{$val}`, which would overflow"))inline_fluent!("attempt to negate `{$val}`, which would overflow"),
353            DivisionByZero(_) => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to divide `{$val}` by zero"))inline_fluent!("attempt to divide `{$val}` by zero"),
354            RemainderByZero(_) => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("attempt to calculate the remainder of `{$val}` with a divisor of zero"))inline_fluent!(
355                "attempt to calculate the remainder of `{$val}` with a divisor of zero"
356            ),
357            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
358                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`async fn` resumed after completion"))inline_fluent!("`async fn` resumed after completion")
359            }
360            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
361                ::core::panicking::panic("not yet implemented")todo!()
362            }
363            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
364                crate::util::bug::bug_fmt(format_args!("gen blocks can be resumed after they return and will keep returning `None`"))bug!("gen blocks can be resumed after they return and will keep returning `None`")
365            }
366            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
367                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("coroutine resumed after completion"))inline_fluent!("coroutine resumed after completion")
368            }
369            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
370                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`async fn` resumed after panicking"))inline_fluent!("`async fn` resumed after panicking")
371            }
372            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
373                ::core::panicking::panic("not yet implemented")todo!()
374            }
375            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
376                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`gen` fn or block cannot be further iterated on after it panicked"))inline_fluent!("`gen` fn or block cannot be further iterated on after it panicked")
377            }
378            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
379                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("coroutine resumed after panicking"))inline_fluent!("coroutine resumed after panicking")
380            }
381            NullPointerDereference => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("null pointer dereference occurred"))inline_fluent!("null pointer dereference occurred"),
382            InvalidEnumConstruction(_) => {
383                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("trying to construct an enum from an invalid value `{$source}`"))inline_fluent!("trying to construct an enum from an invalid value `{$source}`")
384            }
385            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
386                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`async fn` resumed after async drop"))inline_fluent!("`async fn` resumed after async drop")
387            }
388            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
389                ::core::panicking::panic("not yet implemented")todo!()
390            }
391            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
392                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("`gen` fn or block cannot be further iterated on after it async dropped"))inline_fluent!(
393                    "`gen` fn or block cannot be further iterated on after it async dropped"
394                )
395            }
396            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
397                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("coroutine resumed after async drop"))inline_fluent!("coroutine resumed after async drop")
398            }
399
400            MisalignedPointerDereference { .. } => rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("misaligned pointer dereference: address must be a multiple of {$required} but is {$found}"))inline_fluent!(
401                "misaligned pointer dereference: address must be a multiple of {$required} but is {$found}"
402            ),
403        }
404    }
405
406    pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
407    where
408        O: fmt::Debug,
409    {
410        use AssertKind::*;
411
412        macro_rules! add {
413            ($name: expr, $value: expr) => {
414                adder($name.into(), $value.into_diag_arg(&mut None));
415            };
416        }
417
418        match self {
419            BoundsCheck { len, index } => {
420                adder("len".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:?}", len))
            }).into_diag_arg(&mut None));add!("len", format!("{len:?}"));
421                adder("index".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:?}", index))
            }).into_diag_arg(&mut None));add!("index", format!("{index:?}"));
422            }
423            Overflow(BinOp::Shl | BinOp::Shr, _, val)
424            | DivisionByZero(val)
425            | RemainderByZero(val)
426            | OverflowNeg(val) => {
427                adder("val".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", val))
            }).into_diag_arg(&mut None));add!("val", format!("{val:#?}"));
428            }
429            Overflow(binop, left, right) => {
430                adder("op".into(), binop.to_hir_binop().as_str().into_diag_arg(&mut None));add!("op", binop.to_hir_binop().as_str());
431                adder("left".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", left))
            }).into_diag_arg(&mut None));add!("left", format!("{left:#?}"));
432                adder("right".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", right))
            }).into_diag_arg(&mut None));add!("right", format!("{right:#?}"));
433            }
434            ResumedAfterReturn(_)
435            | ResumedAfterPanic(_)
436            | NullPointerDereference
437            | ResumedAfterDrop(_) => {}
438            MisalignedPointerDereference { required, found } => {
439                adder("required".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", required))
            }).into_diag_arg(&mut None));add!("required", format!("{required:#?}"));
440                adder("found".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", found))
            }).into_diag_arg(&mut None));add!("found", format!("{found:#?}"));
441            }
442            InvalidEnumConstruction(source) => {
443                adder("source".into(),
    ::alloc::__export::must_use({
                ::alloc::fmt::format(format_args!("{0:#?}", source))
            }).into_diag_arg(&mut None));add!("source", format!("{source:#?}"));
444            }
445        }
446    }
447}
448
449#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for Terminator<'tcx> {
    #[inline]
    fn clone(&self) -> Terminator<'tcx> {
        Terminator {
            source_info: ::core::clone::Clone::clone(&self.source_info),
            kind: ::core::clone::Clone::clone(&self.kind),
        }
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for Terminator<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Terminator",
            "source_info", &self.source_info, "kind", &&self.kind)
    }
}Debug, const _: () =
    {
        impl<'tcx, __E: ::rustc_middle::ty::codec::TyEncoder<'tcx>>
            ::rustc_serialize::Encodable<__E> for Terminator<'tcx> {
            fn encode(&self, __encoder: &mut __E) {
                match *self {
                    Terminator {
                        source_info: ref __binding_0, kind: ref __binding_1 } => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                    }
                }
            }
        }
    };TyEncodable, const _: () =
    {
        impl<'tcx, __D: ::rustc_middle::ty::codec::TyDecoder<'tcx>>
            ::rustc_serialize::Decodable<__D> for Terminator<'tcx> {
            fn decode(__decoder: &mut __D) -> Self {
                Terminator {
                    source_info: ::rustc_serialize::Decodable::decode(__decoder),
                    kind: ::rustc_serialize::Decodable::decode(__decoder),
                }
            }
        }
    };TyDecodable, const _: () =
    {
        impl<'tcx, '__ctx>
            ::rustc_data_structures::stable_hasher::HashStable<::rustc_query_system::ich::StableHashingContext<'__ctx>>
            for Terminator<'tcx> {
            #[inline]
            fn hash_stable(&self,
                __hcx:
                    &mut ::rustc_query_system::ich::StableHashingContext<'__ctx>,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                match *self {
                    Terminator {
                        source_info: ref __binding_0, kind: ref __binding_1 } => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                    }
                }
            }
        }
    };HashStable, const _: () =
    {
        impl<'tcx>
            ::rustc_middle::ty::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>
            for Terminator<'tcx> {
            fn try_fold_with<__F: ::rustc_middle::ty::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(self,
                __folder: &mut __F) -> Result<Self, __F::Error> {
                Ok(match self {
                        Terminator { source_info: __binding_0, kind: __binding_1 }
                            => {
                            Terminator {
                                source_info: ::rustc_middle::ty::TypeFoldable::try_fold_with(__binding_0,
                                        __folder)?,
                                kind: ::rustc_middle::ty::TypeFoldable::try_fold_with(__binding_1,
                                        __folder)?,
                            }
                        }
                    })
            }
            fn fold_with<__F: ::rustc_middle::ty::TypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(self,
                __folder: &mut __F) -> Self {
                match self {
                    Terminator { source_info: __binding_0, kind: __binding_1 }
                        => {
                        Terminator {
                            source_info: ::rustc_middle::ty::TypeFoldable::fold_with(__binding_0,
                                __folder),
                            kind: ::rustc_middle::ty::TypeFoldable::fold_with(__binding_1,
                                __folder),
                        }
                    }
                }
            }
        }
    };TypeFoldable, const _: () =
    {
        impl<'tcx>
            ::rustc_middle::ty::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>>
            for Terminator<'tcx> {
            fn visit_with<__V: ::rustc_middle::ty::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(&self,
                __visitor: &mut __V) -> __V::Result {
                match *self {
                    Terminator {
                        source_info: ref __binding_0, kind: ref __binding_1 } => {
                        {
                            match ::rustc_middle::ty::VisitorResult::branch(::rustc_middle::ty::TypeVisitable::visit_with(__binding_0,
                                        __visitor)) {
                                ::core::ops::ControlFlow::Continue(()) => {}
                                ::core::ops::ControlFlow::Break(r) => {
                                    return ::rustc_middle::ty::VisitorResult::from_residual(r);
                                }
                            }
                        }
                        {
                            match ::rustc_middle::ty::VisitorResult::branch(::rustc_middle::ty::TypeVisitable::visit_with(__binding_1,
                                        __visitor)) {
                                ::core::ops::ControlFlow::Continue(()) => {}
                                ::core::ops::ControlFlow::Break(r) => {
                                    return ::rustc_middle::ty::VisitorResult::from_residual(r);
                                }
                            }
                        }
                    }
                }
                <__V::Result as ::rustc_middle::ty::VisitorResult>::output()
            }
        }
    };TypeVisitable)]
450pub struct Terminator<'tcx> {
451    pub source_info: SourceInfo,
452    pub kind: TerminatorKind<'tcx>,
453}
454
455impl<'tcx> Terminator<'tcx> {
456    #[inline]
457    pub fn successors(&self) -> Successors<'_> {
458        self.kind.successors()
459    }
460
461    /// Return `Some` if all successors are identical.
462    #[inline]
463    pub fn identical_successor(&self) -> Option<BasicBlock> {
464        let mut successors = self.successors();
465        let first_succ = successors.next()?;
466        if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
467    }
468
469    #[inline]
470    pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
471        self.kind.successors_mut(f)
472    }
473
474    #[inline]
475    pub fn unwind(&self) -> Option<&UnwindAction> {
476        self.kind.unwind()
477    }
478
479    #[inline]
480    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
481        self.kind.unwind_mut()
482    }
483}
484
485impl<'tcx> TerminatorKind<'tcx> {
486    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
487    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
488    pub const fn name(&self) -> &'static str {
489        match self {
490            TerminatorKind::Goto { .. } => "Goto",
491            TerminatorKind::SwitchInt { .. } => "SwitchInt",
492            TerminatorKind::UnwindResume => "UnwindResume",
493            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
494            TerminatorKind::Return => "Return",
495            TerminatorKind::Unreachable => "Unreachable",
496            TerminatorKind::Drop { .. } => "Drop",
497            TerminatorKind::Call { .. } => "Call",
498            TerminatorKind::TailCall { .. } => "TailCall",
499            TerminatorKind::Assert { .. } => "Assert",
500            TerminatorKind::Yield { .. } => "Yield",
501            TerminatorKind::CoroutineDrop => "CoroutineDrop",
502            TerminatorKind::FalseEdge { .. } => "FalseEdge",
503            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
504            TerminatorKind::InlineAsm { .. } => "InlineAsm",
505        }
506    }
507
508    #[inline]
509    pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
510        TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
511    }
512}
513
514pub use helper::*;
515use rustc_errors::inline_fluent;
516
517mod helper {
518    use super::*;
519    pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
520
521    // Note: this method ensures all paths below produce an iterator with the same concrete type.
522    #[inline]
523    #[define_opaque(Successors)]
524    fn mk_successors(
525        slice: &[BasicBlock],
526        option1: Option<BasicBlock>,
527        option2: Option<BasicBlock>,
528    ) -> Successors<'_> {
529        slice.iter().copied().chain(option1.into_iter().chain(option2))
530    }
531
532    impl SwitchTargets {
533        /// Like [`SwitchTargets::target_for_value`], but returning the same type as
534        /// [`Terminator::successors`].
535        #[inline]
536        pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
537            let target = self.target_for_value(value);
538            mk_successors(&[], Some(target), None)
539        }
540    }
541
542    impl<'tcx> TerminatorKind<'tcx> {
543        #[inline]
544        pub fn successors(&self) -> Successors<'_> {
545            use self::TerminatorKind::*;
546            match *self {
547                // 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
548                Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
549                    mk_successors(slice::from_ref(t), Some(u), Some(d))
550                }
551                // 2-successors
552                Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
553                | Yield { resume: ref t, drop: Some(u), .. }
554                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
555                | Drop { target: ref t, unwind: _, drop: Some(u), .. }
556                | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
557                | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
558                    mk_successors(slice::from_ref(t), Some(u), None)
559                }
560                // single successor
561                Goto { target: ref t }
562                | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
563                | Call { target: Some(ref t), unwind: _, .. }
564                | Yield { resume: ref t, drop: None, .. }
565                | Drop { target: ref t, unwind: _, .. }
566                | Assert { target: ref t, unwind: _, .. }
567                | FalseUnwind { real_target: ref t, unwind: _ } => {
568                    mk_successors(slice::from_ref(t), None, None)
569                }
570                // No successors
571                UnwindResume
572                | UnwindTerminate(_)
573                | CoroutineDrop
574                | Return
575                | Unreachable
576                | TailCall { .. }
577                | Call { target: None, unwind: _, .. } => mk_successors(&[], None, None),
578                // Multiple successors
579                InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
580                    mk_successors(targets, Some(u), None)
581                }
582                InlineAsm { ref targets, unwind: _, .. } => mk_successors(targets, None, None),
583                SwitchInt { ref targets, .. } => mk_successors(&targets.targets, None, None),
584                // FalseEdge
585                FalseEdge { ref real_target, imaginary_target } => {
586                    mk_successors(slice::from_ref(real_target), Some(imaginary_target), None)
587                }
588            }
589        }
590
591        #[inline]
592        pub fn successors_mut<'a>(&'a mut self, mut f: impl FnMut(&'a mut BasicBlock)) {
593            use self::TerminatorKind::*;
594            match self {
595                Drop { target, unwind, drop, .. } => {
596                    f(target);
597                    if let UnwindAction::Cleanup(u) = unwind {
598                        f(u)
599                    }
600                    if let Some(d) = drop {
601                        f(d)
602                    }
603                }
604                Call { target, unwind, .. } => {
605                    if let Some(target) = target {
606                        f(target);
607                    }
608                    if let UnwindAction::Cleanup(u) = unwind {
609                        f(u)
610                    }
611                }
612                Yield { resume, drop, .. } => {
613                    f(resume);
614                    if let Some(d) = drop {
615                        f(d)
616                    }
617                }
618                Assert { target, unwind, .. } | FalseUnwind { real_target: target, unwind } => {
619                    f(target);
620                    if let UnwindAction::Cleanup(u) = unwind {
621                        f(u)
622                    }
623                }
624                Goto { target } => {
625                    f(target);
626                }
627                UnwindResume
628                | UnwindTerminate(_)
629                | CoroutineDrop
630                | Return
631                | Unreachable
632                | TailCall { .. } => {}
633                InlineAsm { targets, unwind, .. } => {
634                    for target in targets {
635                        f(target);
636                    }
637                    if let UnwindAction::Cleanup(u) = unwind {
638                        f(u)
639                    }
640                }
641                SwitchInt { targets, .. } => {
642                    for target in &mut targets.targets {
643                        f(target);
644                    }
645                }
646                FalseEdge { real_target, imaginary_target } => {
647                    f(real_target);
648                    f(imaginary_target);
649                }
650            }
651        }
652    }
653}
654
655impl<'tcx> TerminatorKind<'tcx> {
656    #[inline]
657    pub fn unwind(&self) -> Option<&UnwindAction> {
658        match *self {
659            TerminatorKind::Goto { .. }
660            | TerminatorKind::UnwindResume
661            | TerminatorKind::UnwindTerminate(_)
662            | TerminatorKind::Return
663            | TerminatorKind::TailCall { .. }
664            | TerminatorKind::Unreachable
665            | TerminatorKind::CoroutineDrop
666            | TerminatorKind::Yield { .. }
667            | TerminatorKind::SwitchInt { .. }
668            | TerminatorKind::FalseEdge { .. } => None,
669            TerminatorKind::Call { ref unwind, .. }
670            | TerminatorKind::Assert { ref unwind, .. }
671            | TerminatorKind::Drop { ref unwind, .. }
672            | TerminatorKind::FalseUnwind { ref unwind, .. }
673            | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
674        }
675    }
676
677    #[inline]
678    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
679        match *self {
680            TerminatorKind::Goto { .. }
681            | TerminatorKind::UnwindResume
682            | TerminatorKind::UnwindTerminate(_)
683            | TerminatorKind::Return
684            | TerminatorKind::TailCall { .. }
685            | TerminatorKind::Unreachable
686            | TerminatorKind::CoroutineDrop
687            | TerminatorKind::Yield { .. }
688            | TerminatorKind::SwitchInt { .. }
689            | TerminatorKind::FalseEdge { .. } => None,
690            TerminatorKind::Call { ref mut unwind, .. }
691            | TerminatorKind::Assert { ref mut unwind, .. }
692            | TerminatorKind::Drop { ref mut unwind, .. }
693            | TerminatorKind::FalseUnwind { ref mut unwind, .. }
694            | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
695        }
696    }
697
698    #[inline]
699    pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
700        match self {
701            TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
702            _ => None,
703        }
704    }
705
706    #[inline]
707    pub fn as_goto(&self) -> Option<BasicBlock> {
708        match self {
709            TerminatorKind::Goto { target } => Some(*target),
710            _ => None,
711        }
712    }
713
714    /// Returns true if the terminator can write to memory.
715    pub fn can_write_to_memory(&self) -> bool {
716        match self {
717            TerminatorKind::Goto { .. }
718            | TerminatorKind::SwitchInt { .. }
719            | TerminatorKind::UnwindResume
720            | TerminatorKind::UnwindTerminate(_)
721            | TerminatorKind::Return
722            | TerminatorKind::Assert { .. }
723            | TerminatorKind::CoroutineDrop
724            | TerminatorKind::FalseEdge { .. }
725            | TerminatorKind::FalseUnwind { .. }
726            | TerminatorKind::Unreachable => false,
727            TerminatorKind::Call { .. }
728            | TerminatorKind::Drop { .. }
729            | TerminatorKind::TailCall { .. }
730            // Yield writes to the resume_arg place.
731            | TerminatorKind::Yield { .. }
732            | TerminatorKind::InlineAsm { .. } => true,
733        }
734    }
735}
736
737#[derive(#[automatically_derived]
impl<'mir, 'tcx> ::core::marker::Copy for TerminatorEdges<'mir, 'tcx> { }Copy, #[automatically_derived]
impl<'mir, 'tcx> ::core::clone::Clone for TerminatorEdges<'mir, 'tcx> {
    #[inline]
    fn clone(&self) -> TerminatorEdges<'mir, 'tcx> {
        let _: ::core::clone::AssertParamIsClone<BasicBlock>;
        let _: ::core::clone::AssertParamIsClone<&'mir [BasicBlock]>;
        let _: ::core::clone::AssertParamIsClone<Option<BasicBlock>>;
        let _:
                ::core::clone::AssertParamIsClone<CallReturnPlaces<'mir,
                'tcx>>;
        let _: ::core::clone::AssertParamIsClone<&'mir SwitchTargets>;
        let _: ::core::clone::AssertParamIsClone<&'mir Operand<'tcx>>;
        *self
    }
}Clone, #[automatically_derived]
impl<'mir, 'tcx> ::core::fmt::Debug for TerminatorEdges<'mir, 'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            TerminatorEdges::None =>
                ::core::fmt::Formatter::write_str(f, "None"),
            TerminatorEdges::Single(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Single",
                    &__self_0),
            TerminatorEdges::Double(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f, "Double",
                    __self_0, &__self_1),
            TerminatorEdges::AssignOnReturn {
                return_: __self_0, cleanup: __self_1, place: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "AssignOnReturn", "return_", __self_0, "cleanup", __self_1,
                    "place", &__self_2),
            TerminatorEdges::SwitchInt { targets: __self_0, discr: __self_1 }
                =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "SwitchInt", "targets", __self_0, "discr", &__self_1),
        }
    }
}Debug)]
738pub enum TerminatorEdges<'mir, 'tcx> {
739    /// For terminators that have no successor, like `return`.
740    None,
741    /// For terminators that have a single successor, like `goto`, and `assert` without a cleanup
742    /// block.
743    Single(BasicBlock),
744    /// For terminators that have two successors, like `assert` with a cleanup block, and
745    /// `falseEdge`.
746    Double(BasicBlock, BasicBlock),
747    /// Special action for `Yield`, `Call` and `InlineAsm` terminators.
748    AssignOnReturn {
749        return_: &'mir [BasicBlock],
750        /// The cleanup block, if it exists.
751        cleanup: Option<BasicBlock>,
752        place: CallReturnPlaces<'mir, 'tcx>,
753    },
754    /// Special edge for `SwitchInt`.
755    SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
756}
757
758/// List of places that are written to after a successful (non-unwind) return
759/// from a `Call`, `Yield` or `InlineAsm`.
760#[derive(#[automatically_derived]
impl<'a, 'tcx> ::core::marker::Copy for CallReturnPlaces<'a, 'tcx> { }Copy, #[automatically_derived]
impl<'a, 'tcx> ::core::clone::Clone for CallReturnPlaces<'a, 'tcx> {
    #[inline]
    fn clone(&self) -> CallReturnPlaces<'a, 'tcx> {
        let _: ::core::clone::AssertParamIsClone<Place<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<Place<'tcx>>;
        let _:
                ::core::clone::AssertParamIsClone<&'a [InlineAsmOperand<'tcx>]>;
        *self
    }
}Clone, #[automatically_derived]
impl<'a, 'tcx> ::core::fmt::Debug for CallReturnPlaces<'a, 'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            CallReturnPlaces::Call(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Call",
                    &__self_0),
            CallReturnPlaces::Yield(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Yield",
                    &__self_0),
            CallReturnPlaces::InlineAsm(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "InlineAsm", &__self_0),
        }
    }
}Debug)]
761pub enum CallReturnPlaces<'a, 'tcx> {
762    Call(Place<'tcx>),
763    Yield(Place<'tcx>),
764    InlineAsm(&'a [InlineAsmOperand<'tcx>]),
765}
766
767impl<'tcx> CallReturnPlaces<'_, 'tcx> {
768    pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
769        match *self {
770            Self::Call(place) | Self::Yield(place) => f(place),
771            Self::InlineAsm(operands) => {
772                for op in operands {
773                    match *op {
774                        InlineAsmOperand::Out { place: Some(place), .. }
775                        | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
776                        _ => {}
777                    }
778                }
779            }
780        }
781    }
782}
783
784impl<'tcx> Terminator<'tcx> {
785    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
786        self.kind.edges()
787    }
788}
789
790impl<'tcx> TerminatorKind<'tcx> {
791    pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
792        use TerminatorKind::*;
793        match *self {
794            Return
795            | TailCall { .. }
796            | UnwindResume
797            | UnwindTerminate(_)
798            | CoroutineDrop
799            | Unreachable => TerminatorEdges::None,
800
801            Goto { target } => TerminatorEdges::Single(target),
802
803            // FIXME: Maybe we need also TerminatorEdges::Trio for async drop
804            // (target + unwind + dropline)
805            Assert { target, unwind, expected: _, msg: _, cond: _ }
806            | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
807            | FalseUnwind { real_target: target, unwind } => match unwind {
808                UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
809                UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
810                    TerminatorEdges::Single(target)
811                }
812            },
813
814            FalseEdge { real_target, imaginary_target } => {
815                TerminatorEdges::Double(real_target, imaginary_target)
816            }
817
818            Yield { resume: ref target, drop, resume_arg, value: _ } => {
819                TerminatorEdges::AssignOnReturn {
820                    return_: slice::from_ref(target),
821                    cleanup: drop,
822                    place: CallReturnPlaces::Yield(resume_arg),
823                }
824            }
825
826            Call {
827                unwind,
828                destination,
829                ref target,
830                func: _,
831                args: _,
832                fn_span: _,
833                call_source: _,
834            } => TerminatorEdges::AssignOnReturn {
835                return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
836                cleanup: unwind.cleanup_block(),
837                place: CallReturnPlaces::Call(destination),
838            },
839
840            InlineAsm {
841                asm_macro: _,
842                template: _,
843                ref operands,
844                options: _,
845                line_spans: _,
846                ref targets,
847                unwind,
848            } => TerminatorEdges::AssignOnReturn {
849                return_: targets,
850                cleanup: unwind.cleanup_block(),
851                place: CallReturnPlaces::InlineAsm(operands),
852            },
853
854            SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
855        }
856    }
857}
858
859impl CallSource {
860    pub fn from_hir_call(self) -> bool {
861        #[allow(non_exhaustive_omitted_patterns)] match self {
    CallSource::Normal => true,
    _ => false,
}matches!(self, CallSource::Normal)
862    }
863}
864
865impl InlineAsmMacro {
866    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
867        match self {
868            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
869            InlineAsmMacro::NakedAsm => true,
870        }
871    }
872}