1use std::slice;
4
5use rustc_ast::InlineAsmOptions;
6use rustc_data_structures::packed::Pu128;
7use rustc_hir::LangItem;
8use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
9use smallvec::{SmallVec, smallvec};
10
11use super::*;
12
13impl SwitchTargets {
14 pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
19 let (values, mut targets): (SmallVec<_>, SmallVec<_>) =
20 targets.map(|(v, t)| (Pu128(v), t)).unzip();
21 targets.push(otherwise);
22 Self { values, targets }
23 }
24
25 pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
28 Self { values: smallvec![Pu128(value)], targets: smallvec![then, else_] }
29 }
30
31 #[inline]
33 pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
34 if let &[value] = &self.values[..]
35 && let &[then, else_] = &self.targets[..]
36 {
37 Some((value.get(), then, else_))
38 } else {
39 None
40 }
41 }
42
43 #[inline]
45 pub fn otherwise(&self) -> BasicBlock {
46 *self.targets.last().unwrap()
47 }
48
49 #[inline]
56 pub fn iter(&self) -> SwitchTargetsIter<'_> {
57 SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
58 }
59
60 #[inline]
62 pub fn all_targets(&self) -> &[BasicBlock] {
63 &self.targets
64 }
65
66 #[inline]
67 pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
68 &mut self.targets
69 }
70
71 #[inline]
73 pub fn all_values(&self) -> &[Pu128] {
74 &self.values
75 }
76
77 #[inline]
78 pub fn all_values_mut(&mut self) -> &mut [Pu128] {
79 &mut self.values
80 }
81
82 #[inline]
86 pub fn target_for_value(&self, value: u128) -> BasicBlock {
87 self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
88 }
89
90 #[inline]
92 pub fn add_target(&mut self, value: u128, bb: BasicBlock) {
93 let value = Pu128(value);
94 if self.values.contains(&value) {
95 bug!("target value {:?} already present", value);
96 }
97 self.values.push(value);
98 self.targets.insert(self.targets.len() - 1, bb);
99 }
100
101 #[inline]
103 pub fn is_distinct(&self) -> bool {
104 self.targets.iter().collect::<FxHashSet<_>>().len() == self.targets.len()
105 }
106}
107
108pub struct SwitchTargetsIter<'a> {
109 inner: iter::Zip<slice::Iter<'a, Pu128>, slice::Iter<'a, BasicBlock>>,
110}
111
112impl<'a> Iterator for SwitchTargetsIter<'a> {
113 type Item = (u128, BasicBlock);
114
115 #[inline]
116 fn next(&mut self) -> Option<Self::Item> {
117 self.inner.next().map(|(val, bb)| (val.get(), *bb))
118 }
119
120 #[inline]
121 fn size_hint(&self) -> (usize, Option<usize>) {
122 self.inner.size_hint()
123 }
124}
125
126impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
127
128impl UnwindAction {
129 fn cleanup_block(self) -> Option<BasicBlock> {
130 match self {
131 UnwindAction::Cleanup(bb) => Some(bb),
132 UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
133 }
134 }
135}
136
137impl UnwindTerminateReason {
138 pub fn as_str(self) -> &'static str {
139 match self {
141 UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
142 UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
143 }
144 }
145
146 pub fn as_short_str(self) -> &'static str {
148 match self {
149 UnwindTerminateReason::Abi => "abi",
150 UnwindTerminateReason::InCleanup => "cleanup",
151 }
152 }
153
154 pub fn lang_item(self) -> LangItem {
155 match self {
156 UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
157 UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
158 }
159 }
160}
161
162impl<O> AssertKind<O> {
163 pub fn is_optional_overflow_check(&self) -> bool {
165 use AssertKind::*;
166 use BinOp::*;
167 matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
168 }
169
170 pub fn panic_function(&self) -> LangItem {
177 use AssertKind::*;
178 match self {
179 Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
180 Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
181 Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
182 Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
183 Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
184 OverflowNeg(_) => LangItem::PanicNegOverflow,
185 Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
186 Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
187 Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
188 DivisionByZero(_) => LangItem::PanicDivZero,
189 RemainderByZero(_) => LangItem::PanicRemZero,
190 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
191 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
192 LangItem::PanicAsyncFnResumed
193 }
194 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
195 LangItem::PanicAsyncGenFnResumed
196 }
197 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
198 LangItem::PanicGenFnNone
199 }
200 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
201 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
202 LangItem::PanicAsyncFnResumedPanic
203 }
204 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
205 LangItem::PanicAsyncGenFnResumedPanic
206 }
207 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
208 LangItem::PanicGenFnNonePanic
209 }
210 NullPointerDereference => LangItem::PanicNullPointerDereference,
211 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
212 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
213 LangItem::PanicAsyncFnResumedDrop
214 }
215 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
216 LangItem::PanicAsyncGenFnResumedDrop
217 }
218 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
219 LangItem::PanicGenFnNoneDrop
220 }
221
222 BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
223 bug!("Unexpected AssertKind")
224 }
225 }
226 }
227
228 pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
235 where
236 O: Debug,
237 {
238 use AssertKind::*;
239 match self {
240 BoundsCheck { len, index } => write!(
241 f,
242 "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
243 ),
244
245 OverflowNeg(op) => {
246 write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
247 }
248 DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
249 RemainderByZero(op) => write!(
250 f,
251 "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
252 ),
253 Overflow(BinOp::Add, l, r) => write!(
254 f,
255 "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
256 ),
257 Overflow(BinOp::Sub, l, r) => write!(
258 f,
259 "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
260 ),
261 Overflow(BinOp::Mul, l, r) => write!(
262 f,
263 "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
264 ),
265 Overflow(BinOp::Div, l, r) => write!(
266 f,
267 "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
268 ),
269 Overflow(BinOp::Rem, l, r) => write!(
270 f,
271 "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
272 ),
273 Overflow(BinOp::Shr, _, r) => {
274 write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
275 }
276 Overflow(BinOp::Shl, _, r) => {
277 write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
278 }
279 Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
280 MisalignedPointerDereference { required, found } => {
281 write!(
282 f,
283 "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
284 )
285 }
286 NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
287 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
288 write!(f, "\"coroutine resumed after completion\"")
289 }
290 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
291 write!(f, "\"`async fn` resumed after completion\"")
292 }
293 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
294 write!(f, "\"`async gen fn` resumed after completion\"")
295 }
296 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
297 write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
298 }
299 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
300 write!(f, "\"coroutine resumed after panicking\"")
301 }
302 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
303 write!(f, "\"`async fn` resumed after panicking\"")
304 }
305 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
306 write!(f, "\"`async gen fn` resumed after panicking\"")
307 }
308 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
309 write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
310 }
311 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
312 write!(f, "\"coroutine resumed after async drop\"")
313 }
314 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
315 write!(f, "\"`async fn` resumed after async drop\"")
316 }
317 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
318 write!(f, "\"`async gen fn` resumed after async drop\"")
319 }
320 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
321 write!(f, "\"`gen fn` resumed after drop\"")
322 }
323 }
324 }
325
326 pub fn diagnostic_message(&self) -> DiagMessage {
333 use AssertKind::*;
334
335 use crate::fluent_generated::*;
336
337 match self {
338 BoundsCheck { .. } => middle_bounds_check,
339 Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
340 Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
341 Overflow(_, _, _) => middle_assert_op_overflow,
342 OverflowNeg(_) => middle_assert_overflow_neg,
343 DivisionByZero(_) => middle_assert_divide_by_zero,
344 RemainderByZero(_) => middle_assert_remainder_by_zero,
345 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
346 middle_assert_async_resume_after_return
347 }
348 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
349 todo!()
350 }
351 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
352 bug!("gen blocks can be resumed after they return and will keep returning `None`")
353 }
354 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
355 middle_assert_coroutine_resume_after_return
356 }
357 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
358 middle_assert_async_resume_after_panic
359 }
360 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
361 todo!()
362 }
363 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
364 middle_assert_gen_resume_after_panic
365 }
366 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
367 middle_assert_coroutine_resume_after_panic
368 }
369 NullPointerDereference => middle_assert_null_ptr_deref,
370 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
371 middle_assert_async_resume_after_drop
372 }
373 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
374 todo!()
375 }
376 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
377 middle_assert_gen_resume_after_drop
378 }
379 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
380 middle_assert_coroutine_resume_after_drop
381 }
382
383 MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
384 }
385 }
386
387 pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
388 where
389 O: fmt::Debug,
390 {
391 use AssertKind::*;
392
393 macro_rules! add {
394 ($name: expr, $value: expr) => {
395 adder($name.into(), $value.into_diag_arg(&mut None));
396 };
397 }
398
399 match self {
400 BoundsCheck { len, index } => {
401 add!("len", format!("{len:?}"));
402 add!("index", format!("{index:?}"));
403 }
404 Overflow(BinOp::Shl | BinOp::Shr, _, val)
405 | DivisionByZero(val)
406 | RemainderByZero(val)
407 | OverflowNeg(val) => {
408 add!("val", format!("{val:#?}"));
409 }
410 Overflow(binop, left, right) => {
411 add!("op", binop.to_hir_binop().as_str());
412 add!("left", format!("{left:#?}"));
413 add!("right", format!("{right:#?}"));
414 }
415 ResumedAfterReturn(_)
416 | ResumedAfterPanic(_)
417 | NullPointerDereference
418 | ResumedAfterDrop(_) => {}
419 MisalignedPointerDereference { required, found } => {
420 add!("required", format!("{required:#?}"));
421 add!("found", format!("{found:#?}"));
422 }
423 }
424 }
425}
426
427#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
428pub struct Terminator<'tcx> {
429 pub source_info: SourceInfo,
430 pub kind: TerminatorKind<'tcx>,
431}
432
433impl<'tcx> Terminator<'tcx> {
434 #[inline]
435 pub fn successors(&self) -> Successors<'_> {
436 self.kind.successors()
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 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 impl SwitchTargets {
491 #[inline]
494 #[define_opaque(Successors)]
495 pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
496 let target = self.target_for_value(value);
497 (&[]).into_iter().copied().chain(Some(target).into_iter().chain(None))
498 }
499 }
500
501 impl<'tcx> TerminatorKind<'tcx> {
502 #[inline]
503 #[define_opaque(Successors)]
504 pub fn successors(&self) -> Successors<'_> {
505 use self::TerminatorKind::*;
506 match *self {
507 Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
509 slice::from_ref(t)
510 .into_iter()
511 .copied()
512 .chain(Some(u).into_iter().chain(Some(d)))
513 }
514 Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
516 | Yield { resume: ref t, drop: Some(u), .. }
517 | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
518 | Drop { target: ref t, unwind: _, drop: Some(u), .. }
519 | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
520 | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
521 slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None))
522 }
523 Goto { target: ref t }
525 | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
526 | Call { target: Some(ref t), unwind: _, .. }
527 | Yield { resume: ref t, drop: None, .. }
528 | Drop { target: ref t, unwind: _, .. }
529 | Assert { target: ref t, unwind: _, .. }
530 | FalseUnwind { real_target: ref t, unwind: _ } => {
531 slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None))
532 }
533 UnwindResume
535 | UnwindTerminate(_)
536 | CoroutineDrop
537 | Return
538 | Unreachable
539 | TailCall { .. }
540 | Call { target: None, unwind: _, .. } => {
541 (&[]).into_iter().copied().chain(None.into_iter().chain(None))
542 }
543 InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
545 targets.iter().copied().chain(Some(u).into_iter().chain(None))
546 }
547 InlineAsm { ref targets, unwind: _, .. } => {
548 targets.iter().copied().chain(None.into_iter().chain(None))
549 }
550 SwitchInt { ref targets, .. } => {
551 targets.targets.iter().copied().chain(None.into_iter().chain(None))
552 }
553 FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target)
555 .into_iter()
556 .copied()
557 .chain(Some(imaginary_target).into_iter().chain(None)),
558 }
559 }
560
561 #[inline]
562 pub fn successors_mut<'a>(&'a mut self, mut f: impl FnMut(&'a mut BasicBlock)) {
563 use self::TerminatorKind::*;
564 match self {
565 Drop { target, unwind, drop, .. } => {
566 f(target);
567 if let UnwindAction::Cleanup(u) = unwind {
568 f(u)
569 }
570 if let Some(d) = drop {
571 f(d)
572 }
573 }
574 Call { target, unwind, .. } => {
575 if let Some(target) = target {
576 f(target);
577 }
578 if let UnwindAction::Cleanup(u) = unwind {
579 f(u)
580 }
581 }
582 Yield { resume, drop, .. } => {
583 f(resume);
584 if let Some(d) = drop {
585 f(d)
586 }
587 }
588 Assert { target, unwind, .. } | FalseUnwind { real_target: target, unwind } => {
589 f(target);
590 if let UnwindAction::Cleanup(u) = unwind {
591 f(u)
592 }
593 }
594 Goto { target } => {
595 f(target);
596 }
597 UnwindResume
598 | UnwindTerminate(_)
599 | CoroutineDrop
600 | Return
601 | Unreachable
602 | TailCall { .. } => {}
603 InlineAsm { targets, unwind, .. } => {
604 for target in targets {
605 f(target);
606 }
607 if let UnwindAction::Cleanup(u) = unwind {
608 f(u)
609 }
610 }
611 SwitchInt { targets, .. } => {
612 for target in &mut targets.targets {
613 f(target);
614 }
615 }
616 FalseEdge { real_target, imaginary_target } => {
617 f(real_target);
618 f(imaginary_target);
619 }
620 }
621 }
622 }
623}
624
625impl<'tcx> TerminatorKind<'tcx> {
626 #[inline]
627 pub fn unwind(&self) -> Option<&UnwindAction> {
628 match *self {
629 TerminatorKind::Goto { .. }
630 | TerminatorKind::UnwindResume
631 | TerminatorKind::UnwindTerminate(_)
632 | TerminatorKind::Return
633 | TerminatorKind::TailCall { .. }
634 | TerminatorKind::Unreachable
635 | TerminatorKind::CoroutineDrop
636 | TerminatorKind::Yield { .. }
637 | TerminatorKind::SwitchInt { .. }
638 | TerminatorKind::FalseEdge { .. } => None,
639 TerminatorKind::Call { ref unwind, .. }
640 | TerminatorKind::Assert { ref unwind, .. }
641 | TerminatorKind::Drop { ref unwind, .. }
642 | TerminatorKind::FalseUnwind { ref unwind, .. }
643 | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
644 }
645 }
646
647 #[inline]
648 pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
649 match *self {
650 TerminatorKind::Goto { .. }
651 | TerminatorKind::UnwindResume
652 | TerminatorKind::UnwindTerminate(_)
653 | TerminatorKind::Return
654 | TerminatorKind::TailCall { .. }
655 | TerminatorKind::Unreachable
656 | TerminatorKind::CoroutineDrop
657 | TerminatorKind::Yield { .. }
658 | TerminatorKind::SwitchInt { .. }
659 | TerminatorKind::FalseEdge { .. } => None,
660 TerminatorKind::Call { ref mut unwind, .. }
661 | TerminatorKind::Assert { ref mut unwind, .. }
662 | TerminatorKind::Drop { ref mut unwind, .. }
663 | TerminatorKind::FalseUnwind { ref mut unwind, .. }
664 | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
665 }
666 }
667
668 #[inline]
669 pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
670 match self {
671 TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
672 _ => None,
673 }
674 }
675
676 #[inline]
677 pub fn as_goto(&self) -> Option<BasicBlock> {
678 match self {
679 TerminatorKind::Goto { target } => Some(*target),
680 _ => None,
681 }
682 }
683}
684
685#[derive(Copy, Clone, Debug)]
686pub enum TerminatorEdges<'mir, 'tcx> {
687 None,
689 Single(BasicBlock),
692 Double(BasicBlock, BasicBlock),
695 AssignOnReturn {
697 return_: &'mir [BasicBlock],
698 cleanup: Option<BasicBlock>,
700 place: CallReturnPlaces<'mir, 'tcx>,
701 },
702 SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
704}
705
706#[derive(Copy, Clone, Debug)]
709pub enum CallReturnPlaces<'a, 'tcx> {
710 Call(Place<'tcx>),
711 Yield(Place<'tcx>),
712 InlineAsm(&'a [InlineAsmOperand<'tcx>]),
713}
714
715impl<'tcx> CallReturnPlaces<'_, 'tcx> {
716 pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
717 match *self {
718 Self::Call(place) | Self::Yield(place) => f(place),
719 Self::InlineAsm(operands) => {
720 for op in operands {
721 match *op {
722 InlineAsmOperand::Out { place: Some(place), .. }
723 | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
724 _ => {}
725 }
726 }
727 }
728 }
729 }
730}
731
732impl<'tcx> Terminator<'tcx> {
733 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
734 self.kind.edges()
735 }
736}
737
738impl<'tcx> TerminatorKind<'tcx> {
739 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
740 use TerminatorKind::*;
741 match *self {
742 Return
743 | TailCall { .. }
744 | UnwindResume
745 | UnwindTerminate(_)
746 | CoroutineDrop
747 | Unreachable => TerminatorEdges::None,
748
749 Goto { target } => TerminatorEdges::Single(target),
750
751 Assert { target, unwind, expected: _, msg: _, cond: _ }
754 | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
755 | FalseUnwind { real_target: target, unwind } => match unwind {
756 UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
757 UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
758 TerminatorEdges::Single(target)
759 }
760 },
761
762 FalseEdge { real_target, imaginary_target } => {
763 TerminatorEdges::Double(real_target, imaginary_target)
764 }
765
766 Yield { resume: ref target, drop, resume_arg, value: _ } => {
767 TerminatorEdges::AssignOnReturn {
768 return_: slice::from_ref(target),
769 cleanup: drop,
770 place: CallReturnPlaces::Yield(resume_arg),
771 }
772 }
773
774 Call {
775 unwind,
776 destination,
777 ref target,
778 func: _,
779 args: _,
780 fn_span: _,
781 call_source: _,
782 } => TerminatorEdges::AssignOnReturn {
783 return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
784 cleanup: unwind.cleanup_block(),
785 place: CallReturnPlaces::Call(destination),
786 },
787
788 InlineAsm {
789 asm_macro: _,
790 template: _,
791 ref operands,
792 options: _,
793 line_spans: _,
794 ref targets,
795 unwind,
796 } => TerminatorEdges::AssignOnReturn {
797 return_: targets,
798 cleanup: unwind.cleanup_block(),
799 place: CallReturnPlaces::InlineAsm(operands),
800 },
801
802 SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
803 }
804 }
805}
806
807impl CallSource {
808 pub fn from_hir_call(self) -> bool {
809 matches!(self, CallSource::Normal)
810 }
811}
812
813impl InlineAsmMacro {
814 pub const fn diverges(self, options: InlineAsmOptions) -> bool {
815 match self {
816 InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
817 InlineAsmMacro::NakedAsm => true,
818 }
819 }
820}