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 InvalidEnumConstruction(_) => LangItem::PanicInvalidEnumConstruction,
212 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
213 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
214 LangItem::PanicAsyncFnResumedDrop
215 }
216 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
217 LangItem::PanicAsyncGenFnResumedDrop
218 }
219 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
220 LangItem::PanicGenFnNoneDrop
221 }
222
223 BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
224 bug!("Unexpected AssertKind")
225 }
226 }
227 }
228
229 pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
236 where
237 O: Debug,
238 {
239 use AssertKind::*;
240 match self {
241 BoundsCheck { len, index } => write!(
242 f,
243 "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
244 ),
245
246 OverflowNeg(op) => {
247 write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
248 }
249 DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
250 RemainderByZero(op) => write!(
251 f,
252 "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
253 ),
254 Overflow(BinOp::Add, l, r) => write!(
255 f,
256 "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
257 ),
258 Overflow(BinOp::Sub, l, r) => write!(
259 f,
260 "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
261 ),
262 Overflow(BinOp::Mul, l, r) => write!(
263 f,
264 "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
265 ),
266 Overflow(BinOp::Div, l, r) => write!(
267 f,
268 "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
269 ),
270 Overflow(BinOp::Rem, l, r) => write!(
271 f,
272 "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
273 ),
274 Overflow(BinOp::Shr, _, r) => {
275 write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
276 }
277 Overflow(BinOp::Shl, _, r) => {
278 write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
279 }
280 Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
281 MisalignedPointerDereference { required, found } => {
282 write!(
283 f,
284 "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
285 )
286 }
287 NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
288 InvalidEnumConstruction(source) => {
289 write!(f, "\"trying to construct an enum from an invalid value {{}}\", {source:?}")
290 }
291 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
292 write!(f, "\"coroutine resumed after completion\"")
293 }
294 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
295 write!(f, "\"`async fn` resumed after completion\"")
296 }
297 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
298 write!(f, "\"`async gen fn` resumed after completion\"")
299 }
300 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
301 write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
302 }
303 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
304 write!(f, "\"coroutine resumed after panicking\"")
305 }
306 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
307 write!(f, "\"`async fn` resumed after panicking\"")
308 }
309 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
310 write!(f, "\"`async gen fn` resumed after panicking\"")
311 }
312 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
313 write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
314 }
315 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
316 write!(f, "\"coroutine resumed after async drop\"")
317 }
318 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
319 write!(f, "\"`async fn` resumed after async drop\"")
320 }
321 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
322 write!(f, "\"`async gen fn` resumed after async drop\"")
323 }
324 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
325 write!(f, "\"`gen fn` resumed after drop\"")
326 }
327 }
328 }
329
330 pub fn diagnostic_message(&self) -> DiagMessage {
337 use AssertKind::*;
338
339 use crate::fluent_generated::*;
340
341 match self {
342 BoundsCheck { .. } => middle_bounds_check,
343 Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
344 Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
345 Overflow(_, _, _) => middle_assert_op_overflow,
346 OverflowNeg(_) => middle_assert_overflow_neg,
347 DivisionByZero(_) => middle_assert_divide_by_zero,
348 RemainderByZero(_) => middle_assert_remainder_by_zero,
349 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
350 middle_assert_async_resume_after_return
351 }
352 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
353 todo!()
354 }
355 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
356 bug!("gen blocks can be resumed after they return and will keep returning `None`")
357 }
358 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
359 middle_assert_coroutine_resume_after_return
360 }
361 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
362 middle_assert_async_resume_after_panic
363 }
364 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
365 todo!()
366 }
367 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
368 middle_assert_gen_resume_after_panic
369 }
370 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
371 middle_assert_coroutine_resume_after_panic
372 }
373 NullPointerDereference => middle_assert_null_ptr_deref,
374 InvalidEnumConstruction(_) => middle_assert_invalid_enum_construction,
375 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
376 middle_assert_async_resume_after_drop
377 }
378 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
379 todo!()
380 }
381 ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
382 middle_assert_gen_resume_after_drop
383 }
384 ResumedAfterDrop(CoroutineKind::Coroutine(_)) => {
385 middle_assert_coroutine_resume_after_drop
386 }
387
388 MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
389 }
390 }
391
392 pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
393 where
394 O: fmt::Debug,
395 {
396 use AssertKind::*;
397
398 macro_rules! add {
399 ($name: expr, $value: expr) => {
400 adder($name.into(), $value.into_diag_arg(&mut None));
401 };
402 }
403
404 match self {
405 BoundsCheck { len, index } => {
406 add!("len", format!("{len:?}"));
407 add!("index", format!("{index:?}"));
408 }
409 Overflow(BinOp::Shl | BinOp::Shr, _, val)
410 | DivisionByZero(val)
411 | RemainderByZero(val)
412 | OverflowNeg(val) => {
413 add!("val", format!("{val:#?}"));
414 }
415 Overflow(binop, left, right) => {
416 add!("op", binop.to_hir_binop().as_str());
417 add!("left", format!("{left:#?}"));
418 add!("right", format!("{right:#?}"));
419 }
420 ResumedAfterReturn(_)
421 | ResumedAfterPanic(_)
422 | NullPointerDereference
423 | ResumedAfterDrop(_) => {}
424 MisalignedPointerDereference { required, found } => {
425 add!("required", format!("{required:#?}"));
426 add!("found", format!("{found:#?}"));
427 }
428 InvalidEnumConstruction(source) => {
429 add!("source", format!("{source:#?}"));
430 }
431 }
432 }
433}
434
435#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
436pub struct Terminator<'tcx> {
437 pub source_info: SourceInfo,
438 pub kind: TerminatorKind<'tcx>,
439}
440
441impl<'tcx> Terminator<'tcx> {
442 #[inline]
443 pub fn successors(&self) -> Successors<'_> {
444 self.kind.successors()
445 }
446
447 #[inline]
449 pub fn identical_successor(&self) -> Option<BasicBlock> {
450 let mut successors = self.successors();
451 let first_succ = successors.next()?;
452 if successors.all(|succ| first_succ == succ) { Some(first_succ) } else { None }
453 }
454
455 #[inline]
456 pub fn successors_mut<'a>(&'a mut self, f: impl FnMut(&'a mut BasicBlock)) {
457 self.kind.successors_mut(f)
458 }
459
460 #[inline]
461 pub fn unwind(&self) -> Option<&UnwindAction> {
462 self.kind.unwind()
463 }
464
465 #[inline]
466 pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
467 self.kind.unwind_mut()
468 }
469}
470
471impl<'tcx> TerminatorKind<'tcx> {
472 pub const fn name(&self) -> &'static str {
475 match self {
476 TerminatorKind::Goto { .. } => "Goto",
477 TerminatorKind::SwitchInt { .. } => "SwitchInt",
478 TerminatorKind::UnwindResume => "UnwindResume",
479 TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
480 TerminatorKind::Return => "Return",
481 TerminatorKind::Unreachable => "Unreachable",
482 TerminatorKind::Drop { .. } => "Drop",
483 TerminatorKind::Call { .. } => "Call",
484 TerminatorKind::TailCall { .. } => "TailCall",
485 TerminatorKind::Assert { .. } => "Assert",
486 TerminatorKind::Yield { .. } => "Yield",
487 TerminatorKind::CoroutineDrop => "CoroutineDrop",
488 TerminatorKind::FalseEdge { .. } => "FalseEdge",
489 TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
490 TerminatorKind::InlineAsm { .. } => "InlineAsm",
491 }
492 }
493
494 #[inline]
495 pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
496 TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
497 }
498}
499
500pub use helper::*;
501
502mod helper {
503 use super::*;
504 pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
505
506 #[inline]
508 #[define_opaque(Successors)]
509 fn mk_successors(
510 slice: &[BasicBlock],
511 option1: Option<BasicBlock>,
512 option2: Option<BasicBlock>,
513 ) -> Successors<'_> {
514 slice.iter().copied().chain(option1.into_iter().chain(option2))
515 }
516
517 impl SwitchTargets {
518 #[inline]
521 pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
522 let target = self.target_for_value(value);
523 mk_successors(&[], Some(target), None)
524 }
525 }
526
527 impl<'tcx> TerminatorKind<'tcx> {
528 #[inline]
529 pub fn successors(&self) -> Successors<'_> {
530 use self::TerminatorKind::*;
531 match *self {
532 Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => {
534 mk_successors(slice::from_ref(t), Some(u), Some(d))
535 }
536 Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
538 | Yield { resume: ref t, drop: Some(u), .. }
539 | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. }
540 | Drop { target: ref t, unwind: _, drop: Some(u), .. }
541 | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
542 | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
543 mk_successors(slice::from_ref(t), Some(u), None)
544 }
545 Goto { target: ref t }
547 | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
548 | Call { target: Some(ref t), unwind: _, .. }
549 | Yield { resume: ref t, drop: None, .. }
550 | Drop { target: ref t, unwind: _, .. }
551 | Assert { target: ref t, unwind: _, .. }
552 | FalseUnwind { real_target: ref t, unwind: _ } => {
553 mk_successors(slice::from_ref(t), None, None)
554 }
555 UnwindResume
557 | UnwindTerminate(_)
558 | CoroutineDrop
559 | Return
560 | Unreachable
561 | TailCall { .. }
562 | Call { target: None, unwind: _, .. } => mk_successors(&[], None, None),
563 InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
565 mk_successors(targets, Some(u), None)
566 }
567 InlineAsm { ref targets, unwind: _, .. } => mk_successors(targets, None, None),
568 SwitchInt { ref targets, .. } => mk_successors(&targets.targets, None, None),
569 FalseEdge { ref real_target, imaginary_target } => {
571 mk_successors(slice::from_ref(real_target), Some(imaginary_target), None)
572 }
573 }
574 }
575
576 #[inline]
577 pub fn successors_mut<'a>(&'a mut self, mut f: impl FnMut(&'a mut BasicBlock)) {
578 use self::TerminatorKind::*;
579 match self {
580 Drop { target, unwind, drop, .. } => {
581 f(target);
582 if let UnwindAction::Cleanup(u) = unwind {
583 f(u)
584 }
585 if let Some(d) = drop {
586 f(d)
587 }
588 }
589 Call { target, unwind, .. } => {
590 if let Some(target) = target {
591 f(target);
592 }
593 if let UnwindAction::Cleanup(u) = unwind {
594 f(u)
595 }
596 }
597 Yield { resume, drop, .. } => {
598 f(resume);
599 if let Some(d) = drop {
600 f(d)
601 }
602 }
603 Assert { target, unwind, .. } | FalseUnwind { real_target: target, unwind } => {
604 f(target);
605 if let UnwindAction::Cleanup(u) = unwind {
606 f(u)
607 }
608 }
609 Goto { target } => {
610 f(target);
611 }
612 UnwindResume
613 | UnwindTerminate(_)
614 | CoroutineDrop
615 | Return
616 | Unreachable
617 | TailCall { .. } => {}
618 InlineAsm { targets, unwind, .. } => {
619 for target in targets {
620 f(target);
621 }
622 if let UnwindAction::Cleanup(u) = unwind {
623 f(u)
624 }
625 }
626 SwitchInt { targets, .. } => {
627 for target in &mut targets.targets {
628 f(target);
629 }
630 }
631 FalseEdge { real_target, imaginary_target } => {
632 f(real_target);
633 f(imaginary_target);
634 }
635 }
636 }
637 }
638}
639
640impl<'tcx> TerminatorKind<'tcx> {
641 #[inline]
642 pub fn unwind(&self) -> Option<&UnwindAction> {
643 match *self {
644 TerminatorKind::Goto { .. }
645 | TerminatorKind::UnwindResume
646 | TerminatorKind::UnwindTerminate(_)
647 | TerminatorKind::Return
648 | TerminatorKind::TailCall { .. }
649 | TerminatorKind::Unreachable
650 | TerminatorKind::CoroutineDrop
651 | TerminatorKind::Yield { .. }
652 | TerminatorKind::SwitchInt { .. }
653 | TerminatorKind::FalseEdge { .. } => None,
654 TerminatorKind::Call { ref unwind, .. }
655 | TerminatorKind::Assert { ref unwind, .. }
656 | TerminatorKind::Drop { ref unwind, .. }
657 | TerminatorKind::FalseUnwind { ref unwind, .. }
658 | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
659 }
660 }
661
662 #[inline]
663 pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
664 match *self {
665 TerminatorKind::Goto { .. }
666 | TerminatorKind::UnwindResume
667 | TerminatorKind::UnwindTerminate(_)
668 | TerminatorKind::Return
669 | TerminatorKind::TailCall { .. }
670 | TerminatorKind::Unreachable
671 | TerminatorKind::CoroutineDrop
672 | TerminatorKind::Yield { .. }
673 | TerminatorKind::SwitchInt { .. }
674 | TerminatorKind::FalseEdge { .. } => None,
675 TerminatorKind::Call { ref mut unwind, .. }
676 | TerminatorKind::Assert { ref mut unwind, .. }
677 | TerminatorKind::Drop { ref mut unwind, .. }
678 | TerminatorKind::FalseUnwind { ref mut unwind, .. }
679 | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
680 }
681 }
682
683 #[inline]
684 pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
685 match self {
686 TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
687 _ => None,
688 }
689 }
690
691 #[inline]
692 pub fn as_goto(&self) -> Option<BasicBlock> {
693 match self {
694 TerminatorKind::Goto { target } => Some(*target),
695 _ => None,
696 }
697 }
698
699 pub fn can_write_to_memory(&self) -> bool {
701 match self {
702 TerminatorKind::Goto { .. }
703 | TerminatorKind::SwitchInt { .. }
704 | TerminatorKind::UnwindResume
705 | TerminatorKind::UnwindTerminate(_)
706 | TerminatorKind::Return
707 | TerminatorKind::Assert { .. }
708 | TerminatorKind::CoroutineDrop
709 | TerminatorKind::FalseEdge { .. }
710 | TerminatorKind::FalseUnwind { .. }
711 | TerminatorKind::Unreachable => false,
712 TerminatorKind::Call { .. }
713 | TerminatorKind::Drop { .. }
714 | TerminatorKind::TailCall { .. }
715 | TerminatorKind::Yield { .. }
717 | TerminatorKind::InlineAsm { .. } => true,
718 }
719 }
720}
721
722#[derive(Copy, Clone, Debug)]
723pub enum TerminatorEdges<'mir, 'tcx> {
724 None,
726 Single(BasicBlock),
729 Double(BasicBlock, BasicBlock),
732 AssignOnReturn {
734 return_: &'mir [BasicBlock],
735 cleanup: Option<BasicBlock>,
737 place: CallReturnPlaces<'mir, 'tcx>,
738 },
739 SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
741}
742
743#[derive(Copy, Clone, Debug)]
746pub enum CallReturnPlaces<'a, 'tcx> {
747 Call(Place<'tcx>),
748 Yield(Place<'tcx>),
749 InlineAsm(&'a [InlineAsmOperand<'tcx>]),
750}
751
752impl<'tcx> CallReturnPlaces<'_, 'tcx> {
753 pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
754 match *self {
755 Self::Call(place) | Self::Yield(place) => f(place),
756 Self::InlineAsm(operands) => {
757 for op in operands {
758 match *op {
759 InlineAsmOperand::Out { place: Some(place), .. }
760 | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
761 _ => {}
762 }
763 }
764 }
765 }
766 }
767}
768
769impl<'tcx> Terminator<'tcx> {
770 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
771 self.kind.edges()
772 }
773}
774
775impl<'tcx> TerminatorKind<'tcx> {
776 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
777 use TerminatorKind::*;
778 match *self {
779 Return
780 | TailCall { .. }
781 | UnwindResume
782 | UnwindTerminate(_)
783 | CoroutineDrop
784 | Unreachable => TerminatorEdges::None,
785
786 Goto { target } => TerminatorEdges::Single(target),
787
788 Assert { target, unwind, expected: _, msg: _, cond: _ }
791 | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ }
792 | FalseUnwind { real_target: target, unwind } => match unwind {
793 UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
794 UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
795 TerminatorEdges::Single(target)
796 }
797 },
798
799 FalseEdge { real_target, imaginary_target } => {
800 TerminatorEdges::Double(real_target, imaginary_target)
801 }
802
803 Yield { resume: ref target, drop, resume_arg, value: _ } => {
804 TerminatorEdges::AssignOnReturn {
805 return_: slice::from_ref(target),
806 cleanup: drop,
807 place: CallReturnPlaces::Yield(resume_arg),
808 }
809 }
810
811 Call {
812 unwind,
813 destination,
814 ref target,
815 func: _,
816 args: _,
817 fn_span: _,
818 call_source: _,
819 } => TerminatorEdges::AssignOnReturn {
820 return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
821 cleanup: unwind.cleanup_block(),
822 place: CallReturnPlaces::Call(destination),
823 },
824
825 InlineAsm {
826 asm_macro: _,
827 template: _,
828 ref operands,
829 options: _,
830 line_spans: _,
831 ref targets,
832 unwind,
833 } => TerminatorEdges::AssignOnReturn {
834 return_: targets,
835 cleanup: unwind.cleanup_block(),
836 place: CallReturnPlaces::InlineAsm(operands),
837 },
838
839 SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
840 }
841 }
842}
843
844impl CallSource {
845 pub fn from_hir_call(self) -> bool {
846 matches!(self, CallSource::Normal)
847 }
848}
849
850impl InlineAsmMacro {
851 pub const fn diverges(self, options: InlineAsmOptions) -> bool {
852 match self {
853 InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
854 InlineAsmMacro::NakedAsm => true,
855 }
856 }
857}