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_attr_ir::lang_items::LangItem;
7use rustc_data_structures::packed::Pu128;
8use rustc_macros::{StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
9use rustc_span::bug;
10use smallvec::{SmallVec, smallvec};
11use thin_vec::ThinVec;
12
13use super::*;
14
15impl SwitchTargets {
16    /// Creates switch targets from an iterator of values and target blocks.
17    ///
18    /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
19    /// `goto otherwise;`.
20    pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
21        let (values, mut targets): (SmallVec<_>, SmallVec<_>) =
22            targets.map(|(v, t)| (Pu128(v), t)).unzip();
23        targets.push(otherwise);
24        Self { values, targets }
25    }
26
27    /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
28    /// and to `else_` if not.
29    pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
30        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(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [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(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [then, else_])))
    }
}smallvec![then, else_] }
31    }
32
33    /// Inverse of `SwitchTargets::static_if`.
34    #[inline]
35    pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
36        if let &[value] = &self.values[..]
37            && let &[then, else_] = &self.targets[..]
38        {
39            Some((value.get(), then, else_))
40        } else {
41            None
42        }
43    }
44
45    /// Returns the fallback target that is jumped to when none of the values match the operand.
46    #[inline]
47    pub fn otherwise(&self) -> BasicBlock {
48        *self.targets.last().unwrap()
49    }
50
51    /// Returns an iterator over the switch targets.
52    ///
53    /// The iterator will yield tuples containing the value and corresponding target to jump to, not
54    /// including the `otherwise` fallback target.
55    ///
56    /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
57    #[inline]
58    pub fn iter(&self) -> SwitchTargetsIter<'_> {
59        SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
60    }
61
62    /// Returns a slice with all possible jump targets (including the fallback target).
63    #[inline]
64    pub fn all_targets(&self) -> &[BasicBlock] {
65        &self.targets
66    }
67
68    #[inline]
69    pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
70        &mut self.targets
71    }
72
73    /// Returns a slice with all considered values (not including the fallback).
74    #[inline]
75    pub fn all_values(&self) -> &[Pu128] {
76        &self.values
77    }
78
79    #[inline]
80    pub fn all_values_mut(&mut self) -> &mut [Pu128] {
81        &mut self.values
82    }
83
84    /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
85    /// specific value. This cannot fail, as it'll return the `otherwise`
86    /// branch if there's not a specific match for the value.
87    #[inline]
88    pub fn target_for_value(&self, value: u128) -> BasicBlock {
89        self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
90    }
91
92    /// Adds a new target to the switch. Panics if you add an already present value.
93    #[inline]
94    pub fn add_target(&mut self, value: u128, bb: BasicBlock) {
95        let value = Pu128(value);
96        if self.values.contains(&value) {
97            ::rustc_span::macros::bug_impl(None,
    format_args!("target value {0:?} already present", value),
    Location::caller());bug!("target value {:?} already present", value);
98        }
99        self.values.push(value);
100        self.targets.insert(self.targets.len() - 1, bb);
101    }
102
103    /// Returns true if all targets (including the fallback target) are distinct.
104    #[inline]
105    pub fn is_distinct(&self) -> bool {
106        match self.targets.as_slice() {
107            // ~95% of cases in our benchmark suite are 2 or 3 targets,
108            // Avoid allocating the HashSet for those.
109            &[a, b] => a != b,
110            &[a, b, c] => a != b && a != c && b != c,
111            targets => targets.iter().collect::<FxHashSet<_>>().len() == targets.len(),
112        }
113    }
114}
115
116pub struct SwitchTargetsIter<'a> {
117    inner: iter::Zip<slice::Iter<'a, Pu128>, slice::Iter<'a, BasicBlock>>,
118}
119
120impl<'a> Iterator for SwitchTargetsIter<'a> {
121    type Item = (u128, BasicBlock);
122
123    #[inline]
124    fn next(&mut self) -> Option<Self::Item> {
125        self.inner.next().map(|(val, bb)| (val.get(), *bb))
126    }
127
128    #[inline]
129    fn size_hint(&self) -> (usize, Option<usize>) {
130        self.inner.size_hint()
131    }
132}
133
134impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
135
136impl UnwindAction {
137    fn cleanup_block(self) -> Option<BasicBlock> {
138        match self {
139            UnwindAction::Cleanup(bb) => Some(bb),
140            UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
141        }
142    }
143}
144
145impl UnwindTerminateReason {
146    pub fn as_str(self) -> &'static str {
147        // Keep this in sync with the messages in `core/src/panicking.rs`.
148        match self {
149            UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
150            UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
151        }
152    }
153
154    /// A short representation of this used for MIR printing.
155    pub fn as_short_str(self) -> &'static str {
156        match self {
157            UnwindTerminateReason::Abi => "abi",
158            UnwindTerminateReason::InCleanup => "cleanup",
159        }
160    }
161
162    pub fn lang_item(self) -> LangItem {
163        match self {
164            UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
165            UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
166        }
167    }
168}
169
170impl<O> AssertKind<O> {
171    /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
172    pub fn is_optional_overflow_check(&self) -> bool {
173        use AssertKind::*;
174        use BinOp::*;
175        #[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, ..))
176    }
177
178    /// Get the lang item that is invoked to print a static message when this assert fires.
179    ///
180    /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
181    /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
182    /// instead of printing a static message. Those have dynamic arguments that aren't present for
183    /// the rest of the messages here.
184    pub fn panic_function(&self) -> LangItem {
185        use AssertKind::*;
186        match self {
187            Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
188            Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
189            Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
190            Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
191            Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
192            OverflowNeg(_) => LangItem::PanicNegOverflow,
193            Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
194            Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
195            Overflow(op, _, _) => ::rustc_span::macros::bug_impl(None,
    format_args!("{0:?} cannot overflow", op), Location::caller())bug!("{:?} cannot overflow", op),
196            DivisionByZero(_) => LangItem::PanicDivZero,
197            RemainderByZero(_) => LangItem::PanicRemZero,
198            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
199            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
200                LangItem::PanicAsyncFnResumed
201            }
202            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
203                LangItem::PanicAsyncGenFnResumed
204            }
205            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
206                LangItem::PanicGenFnNone
207            }
208            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
209            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
210                LangItem::PanicAsyncFnResumedPanic
211            }
212            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
213                LangItem::PanicAsyncGenFnResumedPanic
214            }
215            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
216                LangItem::PanicGenFnNonePanic
217            }
218            NullPointerDereference => LangItem::PanicNullPointerDereference,
219            NullReferenceConstructed => LangItem::PanicNullReferenceConstructed,
220            InvalidEnumConstruction(_) => LangItem::PanicInvalidEnumConstruction,
221            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
222            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
223                LangItem::PanicAsyncFnResumedDrop
224            }
225            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
226                LangItem::PanicAsyncGenFnResumedDrop
227            }
228            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
229                LangItem::PanicGenFnNoneDrop
230            }
231
232            BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
233                ::rustc_span::macros::bug_impl(None, format_args!("Unexpected AssertKind"),
    Location::caller())bug!("Unexpected AssertKind")
234            }
235        }
236    }
237
238    /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
239    ///
240    /// Needs to be kept in sync with the run-time behavior (which is defined by
241    /// `AssertKind::panic_function` and the lang items mentioned in its docs).
242    /// Note that we deliberately show more details here than we do at runtime, such as the actual
243    /// numbers that overflowed -- it is much easier to do so here than at runtime.
244    pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
245    where
246        O: Debug,
247    {
248        use AssertKind::*;
249        match self {
250            BoundsCheck { len, index } => f.write_fmt(format_args!("\"index out of bounds: the length is {{}} but the index is {{}}\", {0:?}, {1:?}",
        len, index))write!(
251                f,
252                "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
253            ),
254
255            OverflowNeg(op) => {
256                f.write_fmt(format_args!("\"attempt to negate `{{}}`, which would overflow\", {0:?}",
        op))write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
257            }
258            DivisionByZero(op) => f.write_fmt(format_args!("\"attempt to divide `{{}}` by zero\", {0:?}", op))write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
259            RemainderByZero(op) => f.write_fmt(format_args!("\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {0:?}",
        op))write!(
260                f,
261                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
262            ),
263            Overflow(BinOp::Add, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} + {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
264                f,
265                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
266            ),
267            Overflow(BinOp::Sub, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} - {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
268                f,
269                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
270            ),
271            Overflow(BinOp::Mul, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} * {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
272                f,
273                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
274            ),
275            Overflow(BinOp::Div, l, r) => f.write_fmt(format_args!("\"attempt to compute `{{}} / {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
276                f,
277                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
278            ),
279            Overflow(BinOp::Rem, l, r) => f.write_fmt(format_args!("\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {0:?}, {1:?}",
        l, r))write!(
280                f,
281                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
282            ),
283            Overflow(BinOp::Shr, _, r) => {
284                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:?}")
285            }
286            Overflow(BinOp::Shl, _, r) => {
287                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:?}")
288            }
289            Overflow(op, _, _) => ::rustc_span::macros::bug_impl(None,
    format_args!("{0:?} cannot overflow", op), Location::caller())bug!("{:?} cannot overflow", op),
290            MisalignedPointerDereference { required, found } => {
291                f.write_fmt(format_args!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {0:?}, {1:?}",
        required, found))write!(
292                    f,
293                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
294                )
295            }
296            NullPointerDereference => f.write_fmt(format_args!("\"null pointer dereference occurred\""))write!(f, "\"null pointer dereference occurred\""),
297            NullReferenceConstructed => f.write_fmt(format_args!("\"null reference produced\""))write!(f, "\"null reference produced\""),
298            InvalidEnumConstruction(source) => {
299                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:?}")
300            }
301            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
302                f.write_fmt(format_args!("\"coroutine resumed after completion\""))write!(f, "\"coroutine resumed after completion\"")
303            }
304            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
305                f.write_fmt(format_args!("\"`async fn` resumed after completion\""))write!(f, "\"`async fn` resumed after completion\"")
306            }
307            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
308                f.write_fmt(format_args!("\"`async gen fn` resumed after completion\""))write!(f, "\"`async gen fn` resumed after completion\"")
309            }
310            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
311                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\"")
312            }
313            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
314                f.write_fmt(format_args!("\"coroutine resumed after panicking\""))write!(f, "\"coroutine resumed after panicking\"")
315            }
316            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
317                f.write_fmt(format_args!("\"`async fn` resumed after panicking\""))write!(f, "\"`async fn` resumed after panicking\"")
318            }
319            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
320                f.write_fmt(format_args!("\"`async gen fn` resumed after panicking\""))write!(f, "\"`async gen fn` resumed after panicking\"")
321            }
322            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
323                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\"")
324            }
325            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
326                f.write_fmt(format_args!("\"coroutine resumed after async drop\""))write!(f, "\"coroutine resumed after async drop\"")
327            }
328            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
329                f.write_fmt(format_args!("\"`async fn` resumed after async drop\""))write!(f, "\"`async fn` resumed after async drop\"")
330            }
331            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
332                f.write_fmt(format_args!("\"`async gen fn` resumed after async drop\""))write!(f, "\"`async gen fn` resumed after async drop\"")
333            }
334            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
335                f.write_fmt(format_args!("\"`gen fn` resumed after drop\""))write!(f, "\"`gen fn` resumed after drop\"")
336            }
337        }
338    }
339}
340
341/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
342///
343/// Needs to be kept in sync with the run-time behavior (which is defined by
344/// `AssertKind::panic_function` and the lang items mentioned in its docs).
345/// Note that we deliberately show more details here than we do at runtime, such as the actual
346/// numbers that overflowed -- it is much easier to do so here than at runtime.
347impl<O: fmt::Debug> fmt::Display for AssertKind<O> {
348    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
349        use AssertKind::*;
350
351        match self {
352            BoundsCheck { len, index } => {
353                f.write_fmt(format_args!("index out of bounds: the length is {0:?} but the index is {1:?}",
        len, index))write!(f, "index out of bounds: the length is {len:?} but the index is {index:?}")
354            }
355            Overflow(BinOp::Shl, _, val) => {
356                f.write_fmt(format_args!("attempt to shift left by `{0:#?}`, which would overflow",
        val))write!(f, "attempt to shift left by `{val:#?}`, which would overflow")
357            }
358            Overflow(BinOp::Shr, _, val) => {
359                f.write_fmt(format_args!("attempt to shift right by `{0:#?}`, which would overflow",
        val))write!(f, "attempt to shift right by `{val:#?}`, which would overflow")
360            }
361            Overflow(binop, left, right) => {
362                f.write_fmt(format_args!("attempt to compute `{1:#?} {0} {2:#?}`, which would overflow",
        binop.to_hir_binop().as_str(), left, right))write!(
363                    f,
364                    "attempt to compute `{left:#?} {op} {right:#?}`, which would overflow",
365                    op = binop.to_hir_binop().as_str()
366                )
367            }
368            OverflowNeg(val) => f.write_fmt(format_args!("attempt to negate `{0:#?}`, which would overflow",
        val))write!(f, "attempt to negate `{val:#?}`, which would overflow"),
369            DivisionByZero(val) => f.write_fmt(format_args!("attempt to divide `{0:#?}` by zero", val))write!(f, "attempt to divide `{val:#?}` by zero"),
370            RemainderByZero(val) => {
371                f.write_fmt(format_args!("attempt to calculate the remainder of `{0:#?}` with a divisor of zero",
        val))write!(f, "attempt to calculate the remainder of `{val:#?}` with a divisor of zero")
372            }
373            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
374                f.write_fmt(format_args!("`async fn` resumed after completion"))write!(f, "`async fn` resumed after completion")
375            }
376            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
377                ::core::panicking::unimplemented_default_msg()unimplemented!()
378            }
379            ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
380                ::rustc_span::macros::bug_impl(None,
    format_args!("gen blocks can be resumed after they return and will keep returning `None`"),
    Location::caller())bug!("gen blocks can be resumed after they return and will keep returning `None`")
381            }
382            ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
383                f.write_fmt(format_args!("coroutine resumed after completion"))write!(f, "coroutine resumed after completion")
384            }
385            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
386                f.write_fmt(format_args!("`async fn` resumed after panicking"))write!(f, "`async fn` resumed after panicking")
387            }
388            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
389                ::core::panicking::unimplemented_default_msg()unimplemented!()
390            }
391            ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
392                f.write_fmt(format_args!("`gen` fn or block cannot be further iterated on after it panicked"))write!(f, "`gen` fn or block cannot be further iterated on after it panicked")
393            }
394            ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
395                f.write_fmt(format_args!("coroutine resumed after panicking"))write!(f, "coroutine resumed after panicking")
396            }
397            NullPointerDereference => f.write_fmt(format_args!("null pointer dereference occurred"))write!(f, "null pointer dereference occurred"),
398            NullReferenceConstructed => f.write_fmt(format_args!("null reference produced"))write!(f, "null reference produced"),
399            InvalidEnumConstruction(source) => {
400                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:#?}`")
401            }
402            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
403                f.write_fmt(format_args!("`async fn` resumed after async drop"))write!(f, "`async fn` resumed after async drop")
404            }
405            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
406                ::core::panicking::unimplemented_default_msg()unimplemented!()
407            }
408            ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
409                f.write_fmt(format_args!("`gen` fn or block cannot be further iterated on after it async dropped"))write!(f, "`gen` fn or block cannot be further iterated on after it async dropped")
410            }
411            ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
412                f.write_fmt(format_args!("coroutine resumed after async drop"))write!(f, "coroutine resumed after async drop")
413            }
414
415            MisalignedPointerDereference { required, found } => f.write_fmt(format_args!("misaligned pointer dereference: address must be a multiple of {0:#?} but is {1:#?}",
        required, found))write!(
416                f,
417                "misaligned pointer dereference: address must be a multiple of {required:#?} but is {found:#?}"
418            ),
419        }
420    }
421}
422
423#[derive(#[automatically_derived]impl<'tcx> ::core::clone::Clone for Terminator<'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        Terminator {
            source_info: ::core::clone::Clone::clone(&self.source_info),
            kind: ::core::clone::Clone::clone(&self.kind),
        }
    }
}Clone, const _: () =
    {
        impl<'tcx, __E: ::rustc_middle::ty::codec::TyEncoder<'tcx>>
            ::rustc_serialize::Encodable<__E> for Terminator<'tcx> {
            fn encode(&self, __encoder: &mut __E) {
                let Terminator {
                        source_info: ref __binding_0, kind: ref __binding_1 } =
                    *self;
                ::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> ::rustc_data_structures::stable_hash::StableHash for
            Terminator<'tcx> {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hash::StableHasher) {
                match *self {
                    Terminator {
                        source_info: ref __binding_0, kind: ref __binding_1 } => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash, 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)]
424pub struct Terminator<'tcx> {
425    pub source_info: SourceInfo,
426    pub kind: TerminatorKind<'tcx>,
427}
428
429impl<'tcx> Terminator<'tcx> {
430    #[inline]
431    pub fn successors(&self) -> Successors<'_> {
432        self.kind.successors()
433    }
434
435    /// Return `Some` if all successors are identical.
436    #[inline]
437    pub fn identical_successor(&self) -> Option<BasicBlock> {
438        let mut successors = self.successors();
439        let first_succ = successors.next()?;
440        if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
441    }
442
443    #[inline]
444    pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
445        self.kind.successors_mut(f)
446    }
447
448    #[inline]
449    pub fn unwind(&self) -> Option<&UnwindAction> {
450        self.kind.unwind()
451    }
452
453    #[inline]
454    pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
455        self.kind.unwind_mut()
456    }
457}
458
459impl<'tcx> TerminatorKind<'tcx> {
460    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
461    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
462    pub const fn name(&self) -> &'static str {
463        match self {
464            TerminatorKind::Goto { .. } => "Goto",
465            TerminatorKind::SwitchInt { .. } => "SwitchInt",
466            TerminatorKind::UnwindResume => "UnwindResume",
467            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
468            TerminatorKind::Return => "Return",
469            TerminatorKind::Unreachable => "Unreachable",
470            TerminatorKind::Drop { .. } => "Drop",
471            TerminatorKind::Call { .. } => "Call",
472            TerminatorKind::TailCall { .. } => "TailCall",
473            TerminatorKind::Assert { .. } => "Assert",
474            TerminatorKind::Yield { .. } => "Yield",
475            TerminatorKind::CoroutineDrop => "CoroutineDrop",
476            TerminatorKind::FalseEdge { .. } => "FalseEdge",
477            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
478            TerminatorKind::InlineAsm { .. } => "InlineAsm",
479        }
480    }
481
482    #[inline]
483    pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
484        TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
485    }
486
487    #[inline]
488    pub fn goto(target: BasicBlock) -> TerminatorKind<'tcx> {
489        TerminatorKind::Goto { target, loop_hint_attrs: ThinVec::new() }
490    }
491}
492
493pub use helper::*;
494
495mod helper {
496    use super::*;
497    pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
498
499    // Note: this method ensures all paths below produce an iterator with the same concrete type.
500    #[inline]
501    #[define_opaque(Successors)]
502    fn mk_successors(
503        slice: &[BasicBlock],
504        option1: Option<BasicBlock>,
505        option2: Option<BasicBlock>,
506    ) -> Successors<'_> {
507        slice.iter().copied().chain(option1.into_iter().chain(option2))
508    }
509
510    impl SwitchTargets {
511        /// Like [`SwitchTargets::target_for_value`], but returning the same type as
512        /// [`Terminator::successors`].
513        #[inline]
514        pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
515            let target = self.target_for_value(value);
516            mk_successors(&[], Some(target), None)
517        }
518    }
519
520    impl<'tcx> TerminatorKind<'tcx> {
521        #[inline]
522        pub fn successors(&self) -> Successors<'_> {
523            use self::TerminatorKind::*;
524            match *self {
525                // 3-successors for async drop: target, unwind, dropline (parent coroutine drop)
526                Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
527                    mk_successors(slice::from_ref(t), Some(u), Some(d))
528                }
529                // 2-successors
530                Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
531                | Yield { resume: ref t, drop: Some(u), .. }
532                | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
533                | Drop { target: ref t, unwind: _, drop: Some(u), .. }
534                | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
535                | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
536                    mk_successors(slice::from_ref(t), Some(u), None)
537                }
538                // single successor
539                Goto { target: ref t, .. }
540                | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
541                | Call { target: Some(ref t), unwind: _, .. }
542                | Yield { resume: ref t, drop: None, .. }
543                | Drop { target: ref t, unwind: _, .. }
544                | Assert { target: ref t, unwind: _, .. }
545                | FalseUnwind { real_target: ref t, unwind: _ } => {
546                    mk_successors(slice::from_ref(t), None, None)
547                }
548                // No successors
549                UnwindResume
550                | UnwindTerminate(_)
551                | CoroutineDrop
552                | Return
553                | Unreachable
554                | TailCall { .. }
555                | Call { target: None, unwind: _, .. } => mk_successors(&[], None, None),
556                // Multiple successors
557                InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
558                    mk_successors(targets, Some(u), None)
559                }
560                InlineAsm { ref targets, unwind: _, .. } => mk_successors(targets, None, None),
561                SwitchInt { ref targets, .. } => mk_successors(&targets.targets, None, None),
562                // FalseEdge
563                FalseEdge { ref real_target, imaginary_target } => {
564                    mk_successors(slice::from_ref(real_target), Some(imaginary_target), None)
565                }
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, loop_hint_attrs: _ } => Some(*target),
688            _ => None,
689        }
690    }
691}
692
693#[derive(#[automatically_derived]impl<'mir, 'tcx> ::core::fmt::Debug for
    TerminatorEdges<'mir, 'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::None => ::core::fmt::Formatter::write_str(f, "None"),
            Self::Single(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "Single", &__self_0),
            Self::Double(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Double", __self_0, &__self_1),
            Self::AssignOnReturn {
                return_: __self_0, cleanup: __self_1, place: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "AssignOnReturn return_ cleanup place", __self_0, __self_1,
                    &__self_2),
            Self::SwitchInt { targets: __self_0, discr: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "SwitchInt targets discr", __self_0, &__self_1),
        }
    }
}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_: SmallVec<[BasicBlock; 1]>,
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(#[automatically_derived]impl<'a, 'tcx> ::core::marker::Copy for
    CallReturnPlaces<'a, 'tcx> {
}Copy, #[automatically_derived]#[doc(hidden)]unsafe impl<'a, 'tcx>
    ::core::clone::TrivialClone for CallReturnPlaces<'a, 'tcx> {
}
#[automatically_derived]impl<'a, 'tcx> ::core::clone::Clone for
    CallReturnPlaces<'a, 'tcx> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        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]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Call(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Call",
                    &__self_0),
            Self::Yield(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Yield",
                    &__self_0),
            Self::InlineAsm(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "InlineAsm", &__self_0),
        }
    }
}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, loop_hint_attrs: _ } => 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: _ }
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: target, drop, resume_arg, value: _ } => {
775                TerminatorEdges::AssignOnReturn {
776                    return_: [target].into_iter().chain(drop.into_iter()).collect(),
777                    cleanup: None,
778                    place: CallReturnPlaces::Yield(resume_arg),
779                }
780            }
781
782            Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
783                TerminatorEdges::AssignOnReturn {
784                    return_: target.into_iter().collect(),
785                    cleanup: unwind.cleanup_block(),
786                    place: CallReturnPlaces::Call(destination),
787                }
788            }
789
790            InlineAsm {
791                asm_macro: _,
792                template: _,
793                ref operands,
794                options: _,
795                line_spans: _,
796                ref targets,
797                unwind,
798            } => TerminatorEdges::AssignOnReturn {
799                return_: targets.iter().copied().collect(),
800                cleanup: unwind.cleanup_block(),
801                place: CallReturnPlaces::InlineAsm(operands),
802            },
803
804            SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
805        }
806    }
807}
808
809impl CallSource {
810    pub fn from_hir_call(self) -> bool {
811        #[allow(non_exhaustive_omitted_patterns)] match self {
    CallSource::Normal => true,
    _ => false,
}matches!(self, CallSource::Normal)
812    }
813}
814
815impl InlineAsmMacro {
816    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
817        match self {
818            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
819            InlineAsmMacro::NakedAsm => true,
820        }
821    }
822}