1use std::slice;
4
5use rustc_data_structures::packed::Pu128;
6use rustc_hir::LangItem;
7use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
8use smallvec::{SmallVec, smallvec};
9
10use super::{TerminatorKind, *};
11
12impl SwitchTargets {
13 pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
18 let (values, mut targets): (SmallVec<_>, SmallVec<_>) =
19 targets.map(|(v, t)| (Pu128(v), t)).unzip();
20 targets.push(otherwise);
21 Self { values, targets }
22 }
23
24 pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
27 Self { values: smallvec![Pu128(value)], targets: smallvec![then, else_] }
28 }
29
30 #[inline]
32 pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
33 if let &[value] = &self.values[..]
34 && let &[then, else_] = &self.targets[..]
35 {
36 Some((value.get(), then, else_))
37 } else {
38 None
39 }
40 }
41
42 #[inline]
44 pub fn otherwise(&self) -> BasicBlock {
45 *self.targets.last().unwrap()
46 }
47
48 #[inline]
55 pub fn iter(&self) -> SwitchTargetsIter<'_> {
56 SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
57 }
58
59 #[inline]
61 pub fn all_targets(&self) -> &[BasicBlock] {
62 &self.targets
63 }
64
65 #[inline]
66 pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
67 &mut self.targets
68 }
69
70 #[inline]
72 pub fn all_values(&self) -> &[Pu128] {
73 &self.values
74 }
75
76 #[inline]
77 pub fn all_values_mut(&mut self) -> &mut [Pu128] {
78 &mut self.values
79 }
80
81 #[inline]
85 pub fn target_for_value(&self, value: u128) -> BasicBlock {
86 self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
87 }
88
89 #[inline]
91 pub fn add_target(&mut self, value: u128, bb: BasicBlock) {
92 let value = Pu128(value);
93 if self.values.contains(&value) {
94 bug!("target value {:?} already present", value);
95 }
96 self.values.push(value);
97 self.targets.insert(self.targets.len() - 1, bb);
98 }
99
100 #[inline]
102 pub fn is_distinct(&self) -> bool {
103 self.targets.iter().collect::<FxHashSet<_>>().len() == self.targets.len()
104 }
105}
106
107pub struct SwitchTargetsIter<'a> {
108 inner: iter::Zip<slice::Iter<'a, Pu128>, slice::Iter<'a, BasicBlock>>,
109}
110
111impl<'a> Iterator for SwitchTargetsIter<'a> {
112 type Item = (u128, BasicBlock);
113
114 #[inline]
115 fn next(&mut self) -> Option<Self::Item> {
116 self.inner.next().map(|(val, bb)| (val.get(), *bb))
117 }
118
119 #[inline]
120 fn size_hint(&self) -> (usize, Option<usize>) {
121 self.inner.size_hint()
122 }
123}
124
125impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
126
127impl UnwindAction {
128 fn cleanup_block(self) -> Option<BasicBlock> {
129 match self {
130 UnwindAction::Cleanup(bb) => Some(bb),
131 UnwindAction::Continue | UnwindAction::Unreachable | UnwindAction::Terminate(_) => None,
132 }
133 }
134}
135
136impl UnwindTerminateReason {
137 pub fn as_str(self) -> &'static str {
138 match self {
140 UnwindTerminateReason::Abi => "panic in a function that cannot unwind",
141 UnwindTerminateReason::InCleanup => "panic in a destructor during cleanup",
142 }
143 }
144
145 pub fn as_short_str(self) -> &'static str {
147 match self {
148 UnwindTerminateReason::Abi => "abi",
149 UnwindTerminateReason::InCleanup => "cleanup",
150 }
151 }
152
153 pub fn lang_item(self) -> LangItem {
154 match self {
155 UnwindTerminateReason::Abi => LangItem::PanicCannotUnwind,
156 UnwindTerminateReason::InCleanup => LangItem::PanicInCleanup,
157 }
158 }
159}
160
161impl<O> AssertKind<O> {
162 pub fn is_optional_overflow_check(&self) -> bool {
164 use AssertKind::*;
165 use BinOp::*;
166 matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
167 }
168
169 pub fn panic_function(&self) -> LangItem {
176 use AssertKind::*;
177 match self {
178 Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
179 Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
180 Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
181 Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
182 Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
183 OverflowNeg(_) => LangItem::PanicNegOverflow,
184 Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
185 Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
186 Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
187 DivisionByZero(_) => LangItem::PanicDivZero,
188 RemainderByZero(_) => LangItem::PanicRemZero,
189 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
190 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
191 LangItem::PanicAsyncFnResumed
192 }
193 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
194 LangItem::PanicAsyncGenFnResumed
195 }
196 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
197 LangItem::PanicGenFnNone
198 }
199 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
200 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
201 LangItem::PanicAsyncFnResumedPanic
202 }
203 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
204 LangItem::PanicAsyncGenFnResumedPanic
205 }
206 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
207 LangItem::PanicGenFnNonePanic
208 }
209 NullPointerDereference => LangItem::PanicNullPointerDereference,
210
211 BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
212 bug!("Unexpected AssertKind")
213 }
214 }
215 }
216
217 pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
224 where
225 O: Debug,
226 {
227 use AssertKind::*;
228 match self {
229 BoundsCheck { ref len, ref index } => write!(
230 f,
231 "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
232 ),
233
234 OverflowNeg(op) => {
235 write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
236 }
237 DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
238 RemainderByZero(op) => write!(
239 f,
240 "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
241 ),
242 Overflow(BinOp::Add, l, r) => write!(
243 f,
244 "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
245 ),
246 Overflow(BinOp::Sub, l, r) => write!(
247 f,
248 "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
249 ),
250 Overflow(BinOp::Mul, l, r) => write!(
251 f,
252 "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
253 ),
254 Overflow(BinOp::Div, l, r) => write!(
255 f,
256 "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
257 ),
258 Overflow(BinOp::Rem, l, r) => write!(
259 f,
260 "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
261 ),
262 Overflow(BinOp::Shr, _, r) => {
263 write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
264 }
265 Overflow(BinOp::Shl, _, r) => {
266 write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
267 }
268 Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
269 MisalignedPointerDereference { required, found } => {
270 write!(
271 f,
272 "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
273 )
274 }
275 NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
276 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
277 write!(f, "\"coroutine resumed after completion\"")
278 }
279 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
280 write!(f, "\"`async fn` resumed after completion\"")
281 }
282 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
283 write!(f, "\"`async gen fn` resumed after completion\"")
284 }
285 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
286 write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
287 }
288 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
289 write!(f, "\"coroutine resumed after panicking\"")
290 }
291 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
292 write!(f, "\"`async fn` resumed after panicking\"")
293 }
294 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
295 write!(f, "\"`async gen fn` resumed after panicking\"")
296 }
297 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
298 write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
299 }
300 }
301 }
302
303 pub fn diagnostic_message(&self) -> DiagMessage {
310 use AssertKind::*;
311
312 use crate::fluent_generated::*;
313
314 match self {
315 BoundsCheck { .. } => middle_bounds_check,
316 Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
317 Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
318 Overflow(_, _, _) => middle_assert_op_overflow,
319 OverflowNeg(_) => middle_assert_overflow_neg,
320 DivisionByZero(_) => middle_assert_divide_by_zero,
321 RemainderByZero(_) => middle_assert_remainder_by_zero,
322 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
323 middle_assert_async_resume_after_return
324 }
325 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
326 todo!()
327 }
328 ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
329 bug!("gen blocks can be resumed after they return and will keep returning `None`")
330 }
331 ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
332 middle_assert_coroutine_resume_after_return
333 }
334 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
335 middle_assert_async_resume_after_panic
336 }
337 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
338 todo!()
339 }
340 ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
341 middle_assert_gen_resume_after_panic
342 }
343 ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
344 middle_assert_coroutine_resume_after_panic
345 }
346 NullPointerDereference => middle_assert_null_ptr_deref,
347 MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
348 }
349 }
350
351 pub fn add_args(self, adder: &mut dyn FnMut(DiagArgName, DiagArgValue))
352 where
353 O: fmt::Debug,
354 {
355 use AssertKind::*;
356
357 macro_rules! add {
358 ($name: expr, $value: expr) => {
359 adder($name.into(), $value.into_diag_arg());
360 };
361 }
362
363 match self {
364 BoundsCheck { len, index } => {
365 add!("len", format!("{len:?}"));
366 add!("index", format!("{index:?}"));
367 }
368 Overflow(BinOp::Shl | BinOp::Shr, _, val)
369 | DivisionByZero(val)
370 | RemainderByZero(val)
371 | OverflowNeg(val) => {
372 add!("val", format!("{val:#?}"));
373 }
374 Overflow(binop, left, right) => {
375 add!("op", binop.to_hir_binop().as_str());
376 add!("left", format!("{left:#?}"));
377 add!("right", format!("{right:#?}"));
378 }
379 ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {}
380 MisalignedPointerDereference { required, found } => {
381 add!("required", format!("{required:#?}"));
382 add!("found", format!("{found:#?}"));
383 }
384 }
385 }
386}
387
388#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
389pub struct Terminator<'tcx> {
390 pub source_info: SourceInfo,
391 pub kind: TerminatorKind<'tcx>,
392}
393
394impl<'tcx> Terminator<'tcx> {
395 #[inline]
396 pub fn successors(&self) -> Successors<'_> {
397 self.kind.successors()
398 }
399
400 #[inline]
401 pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
402 self.kind.successors_mut()
403 }
404
405 #[inline]
406 pub fn unwind(&self) -> Option<&UnwindAction> {
407 self.kind.unwind()
408 }
409
410 #[inline]
411 pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
412 self.kind.unwind_mut()
413 }
414}
415
416impl<'tcx> TerminatorKind<'tcx> {
417 #[inline]
418 pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
419 TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
420 }
421}
422
423pub use helper::*;
424
425mod helper {
426 use super::*;
427 pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
428 pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;
429
430 impl SwitchTargets {
431 #[inline]
434 pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
435 let target = self.target_for_value(value);
436 (&[]).into_iter().copied().chain(Some(target))
437 }
438 }
439
440 impl<'tcx> TerminatorKind<'tcx> {
441 #[inline]
442 pub fn successors(&self) -> Successors<'_> {
443 use self::TerminatorKind::*;
444 match *self {
445 Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. }
446 | Yield { resume: ref t, drop: Some(u), .. }
447 | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
448 | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. }
449 | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => {
450 slice::from_ref(t).into_iter().copied().chain(Some(u))
451 }
452 Goto { target: ref t }
453 | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. }
454 | Call { target: Some(ref t), unwind: _, .. }
455 | Yield { resume: ref t, drop: None, .. }
456 | Drop { target: ref t, unwind: _, .. }
457 | Assert { target: ref t, unwind: _, .. }
458 | FalseUnwind { real_target: ref t, unwind: _ } => {
459 slice::from_ref(t).into_iter().copied().chain(None)
460 }
461 UnwindResume
462 | UnwindTerminate(_)
463 | CoroutineDrop
464 | Return
465 | Unreachable
466 | TailCall { .. }
467 | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
468 InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
469 targets.iter().copied().chain(Some(u))
470 }
471 InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None),
472 SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None),
473 FalseEdge { ref real_target, imaginary_target } => {
474 slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target))
475 }
476 }
477 }
478
479 #[inline]
480 pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
481 use self::TerminatorKind::*;
482 match *self {
483 Call {
484 target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), ..
485 }
486 | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
487 | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
488 | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. }
489 | FalseUnwind {
490 real_target: ref mut t,
491 unwind: UnwindAction::Cleanup(ref mut u),
492 } => slice::from_mut(t).into_iter().chain(Some(u)),
493 Goto { target: ref mut t }
494 | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. }
495 | Call { target: Some(ref mut t), unwind: _, .. }
496 | Yield { resume: ref mut t, drop: None, .. }
497 | Drop { target: ref mut t, unwind: _, .. }
498 | Assert { target: ref mut t, unwind: _, .. }
499 | FalseUnwind { real_target: ref mut t, unwind: _ } => {
500 slice::from_mut(t).into_iter().chain(None)
501 }
502 UnwindResume
503 | UnwindTerminate(_)
504 | CoroutineDrop
505 | Return
506 | Unreachable
507 | TailCall { .. }
508 | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
509 InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
510 targets.iter_mut().chain(Some(u))
511 }
512 InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None),
513 SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None),
514 FalseEdge { ref mut real_target, ref mut imaginary_target } => {
515 slice::from_mut(real_target).into_iter().chain(Some(imaginary_target))
516 }
517 }
518 }
519 }
520}
521
522impl<'tcx> TerminatorKind<'tcx> {
523 #[inline]
524 pub fn unwind(&self) -> Option<&UnwindAction> {
525 match *self {
526 TerminatorKind::Goto { .. }
527 | TerminatorKind::UnwindResume
528 | TerminatorKind::UnwindTerminate(_)
529 | TerminatorKind::Return
530 | TerminatorKind::TailCall { .. }
531 | TerminatorKind::Unreachable
532 | TerminatorKind::CoroutineDrop
533 | TerminatorKind::Yield { .. }
534 | TerminatorKind::SwitchInt { .. }
535 | TerminatorKind::FalseEdge { .. } => None,
536 TerminatorKind::Call { ref unwind, .. }
537 | TerminatorKind::Assert { ref unwind, .. }
538 | TerminatorKind::Drop { ref unwind, .. }
539 | TerminatorKind::FalseUnwind { ref unwind, .. }
540 | TerminatorKind::InlineAsm { ref unwind, .. } => Some(unwind),
541 }
542 }
543
544 #[inline]
545 pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
546 match *self {
547 TerminatorKind::Goto { .. }
548 | TerminatorKind::UnwindResume
549 | TerminatorKind::UnwindTerminate(_)
550 | TerminatorKind::Return
551 | TerminatorKind::TailCall { .. }
552 | TerminatorKind::Unreachable
553 | TerminatorKind::CoroutineDrop
554 | TerminatorKind::Yield { .. }
555 | TerminatorKind::SwitchInt { .. }
556 | TerminatorKind::FalseEdge { .. } => None,
557 TerminatorKind::Call { ref mut unwind, .. }
558 | TerminatorKind::Assert { ref mut unwind, .. }
559 | TerminatorKind::Drop { ref mut unwind, .. }
560 | TerminatorKind::FalseUnwind { ref mut unwind, .. }
561 | TerminatorKind::InlineAsm { ref mut unwind, .. } => Some(unwind),
562 }
563 }
564
565 #[inline]
566 pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
567 match self {
568 TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
569 _ => None,
570 }
571 }
572
573 #[inline]
574 pub fn as_goto(&self) -> Option<BasicBlock> {
575 match self {
576 TerminatorKind::Goto { target } => Some(*target),
577 _ => None,
578 }
579 }
580}
581
582#[derive(Copy, Clone, Debug)]
583pub enum TerminatorEdges<'mir, 'tcx> {
584 None,
586 Single(BasicBlock),
589 Double(BasicBlock, BasicBlock),
592 AssignOnReturn {
594 return_: &'mir [BasicBlock],
595 cleanup: Option<BasicBlock>,
597 place: CallReturnPlaces<'mir, 'tcx>,
598 },
599 SwitchInt { targets: &'mir SwitchTargets, discr: &'mir Operand<'tcx> },
601}
602
603#[derive(Copy, Clone, Debug)]
606pub enum CallReturnPlaces<'a, 'tcx> {
607 Call(Place<'tcx>),
608 Yield(Place<'tcx>),
609 InlineAsm(&'a [InlineAsmOperand<'tcx>]),
610}
611
612impl<'tcx> CallReturnPlaces<'_, 'tcx> {
613 pub fn for_each(&self, mut f: impl FnMut(Place<'tcx>)) {
614 match *self {
615 Self::Call(place) | Self::Yield(place) => f(place),
616 Self::InlineAsm(operands) => {
617 for op in operands {
618 match *op {
619 InlineAsmOperand::Out { place: Some(place), .. }
620 | InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
621 _ => {}
622 }
623 }
624 }
625 }
626 }
627}
628
629impl<'tcx> Terminator<'tcx> {
630 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
631 self.kind.edges()
632 }
633}
634
635impl<'tcx> TerminatorKind<'tcx> {
636 pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
637 use TerminatorKind::*;
638 match *self {
639 Return
640 | TailCall { .. }
641 | UnwindResume
642 | UnwindTerminate(_)
643 | CoroutineDrop
644 | Unreachable => TerminatorEdges::None,
645
646 Goto { target } => TerminatorEdges::Single(target),
647
648 Assert { target, unwind, expected: _, msg: _, cond: _ }
649 | Drop { target, unwind, place: _, replace: _ }
650 | FalseUnwind { real_target: target, unwind } => match unwind {
651 UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind),
652 UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => {
653 TerminatorEdges::Single(target)
654 }
655 },
656
657 FalseEdge { real_target, imaginary_target } => {
658 TerminatorEdges::Double(real_target, imaginary_target)
659 }
660
661 Yield { resume: ref target, drop, resume_arg, value: _ } => {
662 TerminatorEdges::AssignOnReturn {
663 return_: slice::from_ref(target),
664 cleanup: drop,
665 place: CallReturnPlaces::Yield(resume_arg),
666 }
667 }
668
669 Call {
670 unwind,
671 destination,
672 ref target,
673 func: _,
674 args: _,
675 fn_span: _,
676 call_source: _,
677 } => TerminatorEdges::AssignOnReturn {
678 return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
679 cleanup: unwind.cleanup_block(),
680 place: CallReturnPlaces::Call(destination),
681 },
682
683 InlineAsm {
684 asm_macro: _,
685 template: _,
686 ref operands,
687 options: _,
688 line_spans: _,
689 ref targets,
690 unwind,
691 } => TerminatorEdges::AssignOnReturn {
692 return_: targets,
693 cleanup: unwind.cleanup_block(),
694 place: CallReturnPlaces::InlineAsm(operands),
695 },
696
697 SwitchInt { ref targets, ref discr } => TerminatorEdges::SwitchInt { targets, discr },
698 }
699 }
700}