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