]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/mir/terminator.rs
Auto merge of #96002 - nnethercote:speed-up-Vec-clear-2, r=m-ou-se
[rust.git] / compiler / rustc_middle / src / mir / terminator.rs
1 use crate::mir::interpret::Scalar;
2 use crate::ty::{self, Ty, TyCtxt};
3 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
4 use smallvec::{smallvec, SmallVec};
5
6 use super::{
7     AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors,
8     SuccessorsMut,
9 };
10 pub use rustc_ast::Mutability;
11 use rustc_macros::HashStable;
12 use rustc_span::Span;
13 use std::borrow::Cow;
14 use std::fmt::{self, Debug, Formatter, Write};
15 use std::iter;
16 use std::slice;
17
18 pub use super::query::*;
19
20 #[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)]
21 pub struct SwitchTargets {
22     /// Possible values. The locations to branch to in each case
23     /// are found in the corresponding indices from the `targets` vector.
24     values: SmallVec<[u128; 1]>,
25
26     /// Possible branch sites. The last element of this vector is used
27     /// for the otherwise branch, so targets.len() == values.len() + 1
28     /// should hold.
29     //
30     // This invariant is quite non-obvious and also could be improved.
31     // One way to make this invariant is to have something like this instead:
32     //
33     // branches: Vec<(ConstInt, BasicBlock)>,
34     // otherwise: Option<BasicBlock> // exhaustive if None
35     //
36     // However we’ve decided to keep this as-is until we figure a case
37     // where some other approach seems to be strictly better than other.
38     targets: SmallVec<[BasicBlock; 2]>,
39 }
40
41 impl SwitchTargets {
42     /// Creates switch targets from an iterator of values and target blocks.
43     ///
44     /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
45     /// `goto otherwise;`.
46     pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
47         let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
48         targets.push(otherwise);
49         Self { values, targets }
50     }
51
52     /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
53     /// and to `else_` if not.
54     pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
55         Self { values: smallvec![value], targets: smallvec![then, else_] }
56     }
57
58     /// Returns the fallback target that is jumped to when none of the values match the operand.
59     pub fn otherwise(&self) -> BasicBlock {
60         *self.targets.last().unwrap()
61     }
62
63     /// Returns an iterator over the switch targets.
64     ///
65     /// The iterator will yield tuples containing the value and corresponding target to jump to, not
66     /// including the `otherwise` fallback target.
67     ///
68     /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
69     pub fn iter(&self) -> SwitchTargetsIter<'_> {
70         SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
71     }
72
73     /// Returns a slice with all possible jump targets (including the fallback target).
74     pub fn all_targets(&self) -> &[BasicBlock] {
75         &self.targets
76     }
77
78     pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
79         &mut self.targets
80     }
81
82     /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
83     /// specific value.  This cannot fail, as it'll return the `otherwise`
84     /// branch if there's not a specific match for the value.
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
90 pub struct SwitchTargetsIter<'a> {
91     inner: iter::Zip<slice::Iter<'a, u128>, slice::Iter<'a, BasicBlock>>,
92 }
93
94 impl<'a> Iterator for SwitchTargetsIter<'a> {
95     type Item = (u128, BasicBlock);
96
97     fn next(&mut self) -> Option<Self::Item> {
98         self.inner.next().map(|(val, bb)| (*val, *bb))
99     }
100
101     fn size_hint(&self) -> (usize, Option<usize>) {
102         self.inner.size_hint()
103     }
104 }
105
106 impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
107
108 /// A note on unwinding: Panics may occur during the execution of some terminators. Depending on the
109 /// `-C panic` flag, this may either cause the program to abort or the call stack to unwind. Such
110 /// terminators have a `cleanup: Option<BasicBlock>` field on them. If stack unwinding occurs, then
111 /// once the current function is reached, execution continues at the given basic block, if any. If
112 /// `cleanup` is `None` then no cleanup is performed, and the stack continues unwinding. This is
113 /// equivalent to the execution of a `Resume` terminator.
114 ///
115 /// The basic block pointed to by a `cleanup` field must have its `cleanup` flag set. `cleanup`
116 /// basic blocks have a couple restrictions:
117 ///  1. All `cleanup` fields in them must be `None`.
118 ///  2. `Return` terminators are not allowed in them. `Abort` and `Unwind` terminators are.
119 ///  3. All other basic blocks (in the current body) that are reachable from `cleanup` basic blocks
120 ///     must also be `cleanup`. This is a part of the type system and checked statically, so it is
121 ///     still an error to have such an edge in the CFG even if it's known that it won't be taken at
122 ///     runtime.
123 #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
124 pub enum TerminatorKind<'tcx> {
125     /// Block has one successor; we continue execution there.
126     Goto { target: BasicBlock },
127
128     /// Switches based on the computed value.
129     ///
130     /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned
131     /// integer, char, or bool, and must match the given type. Then, if the list of switch targets
132     /// contains the computed value, continues execution at the associated basic block. Otherwise,
133     /// continues execution at the "otherwise" basic block.
134     ///
135     /// Target values may not appear more than once.
136     SwitchInt {
137         /// The discriminant value being tested.
138         discr: Operand<'tcx>,
139
140         /// The type of value being tested.
141         /// This is always the same as the type of `discr`.
142         /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
143         switch_ty: Ty<'tcx>,
144
145         targets: SwitchTargets,
146     },
147
148     /// Indicates that the landing pad is finished and that the process should continue unwinding.
149     ///
150     /// Like a return, this marks the end of this invocation of the function.
151     ///
152     /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
153     /// deaggregation runs.
154     Resume,
155
156     /// Indicates that the landing pad is finished and that the process should abort.
157     ///
158     /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in
159     /// cleanup blocks.
160     Abort,
161
162     /// Returns from the function.
163     ///
164     /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very
165     /// likely at least assigns the value currently in the return place (`_0`) to the place
166     /// specified in the associated `Call` terminator in the calling function, as if assigned via
167     /// `dest = move _0`. It might additionally do other things, like have side-effects in the
168     /// aliasing model.
169     ///
170     /// If the body is a generator body, this has slightly different semantics; it instead causes a
171     /// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned
172     /// to the return place.
173     Return,
174
175     /// Indicates a terminator that can never be reached.
176     ///
177     /// Executing this terminator is UB.
178     Unreachable,
179
180     /// The behavior of this statement differs significantly before and after drop elaboration.
181     /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
182     /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
183     /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
184     /// issue tracking if drop glue has any interesting semantics in addition to those of a function
185     /// call?)
186     ///
187     /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
188     /// `Drop` will be executed if...
189     ///
190     /// **Needs clarification**: End of that sentence. This in effect should document the exact
191     /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
192     ///
193     /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
194     /// > the place or one of its "parents" occurred more recently than a move out of it. This does not
195     /// > consider indirect assignments.
196     Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
197
198     /// Drops the place and assigns a new value to it.
199     ///
200     /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator;
201     /// it then additionally assigns the `value` to the `place` as if by an assignment statement.
202     /// This assignment occurs both in the unwind and the regular code paths. The semantics are best
203     /// explained by the elaboration:
204     ///
205     /// ```
206     /// BB0 {
207     ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
208     /// }
209     /// ```
210     ///
211     /// becomes
212     ///
213     /// ```
214     /// BB0 {
215     ///   Drop(P, goto BB1, unwind BB2)
216     /// }
217     /// BB1 {
218     ///   // P is now uninitialized
219     ///   P <- V
220     /// }
221     /// BB2 {
222     ///   // P is now uninitialized -- its dtor panicked
223     ///   P <- V
224     /// }
225     /// ```
226     ///
227     /// Disallowed after drop elaboration.
228     DropAndReplace {
229         place: Place<'tcx>,
230         value: Operand<'tcx>,
231         target: BasicBlock,
232         unwind: Option<BasicBlock>,
233     },
234
235     /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
236     /// the referred to function. The operand types must match the argument types of the function.
237     /// The return place type must match the return type. The type of the `func` operand must be
238     /// callable, meaning either a function pointer, a function type, or a closure type.
239     ///
240     /// **Needs clarification**: The exact semantics of this. Current backends rely on `move`
241     /// operands not aliasing the return place. It is unclear how this is justified in MIR, see
242     /// [#71117].
243     ///
244     /// [#71117]: https://github.com/rust-lang/rust/issues/71117
245     Call {
246         /// The function that’s being called.
247         func: Operand<'tcx>,
248         /// Arguments the function is called with.
249         /// These are owned by the callee, which is free to modify them.
250         /// This allows the memory occupied by "by-value" arguments to be
251         /// reused across function calls without duplicating the contents.
252         args: Vec<Operand<'tcx>>,
253         /// Destination for the return value. If none, the call necessarily diverges.
254         destination: Option<(Place<'tcx>, BasicBlock)>,
255         /// Cleanups to be done if the call unwinds.
256         cleanup: Option<BasicBlock>,
257         /// `true` if this is from a call in HIR rather than from an overloaded
258         /// operator. True for overloaded function call.
259         from_hir_call: bool,
260         /// This `Span` is the span of the function, without the dot and receiver
261         /// (e.g. `foo(a, b)` in `x.foo(a, b)`
262         fn_span: Span,
263     },
264
265     /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
266     /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
267     /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
268     /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not
269     /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
270     /// assertion does not fail, execution continues at the specified basic block.
271     Assert {
272         cond: Operand<'tcx>,
273         expected: bool,
274         msg: AssertMessage<'tcx>,
275         target: BasicBlock,
276         cleanup: Option<BasicBlock>,
277     },
278
279     /// Marks a suspend point.
280     ///
281     /// Like `Return` terminators in generator bodies, this computes `value` and then a
282     /// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
283     /// the return place of the function calling this one, and execution continues in the calling
284     /// function. When next invoked with the same first argument, execution of this function
285     /// continues at the `resume` basic block, with the second argument written to the `resume_arg`
286     /// place. If the generator is dropped before then, the `drop` basic block is invoked.
287     ///
288     /// Not permitted in bodies that are not generator bodies, or after generator lowering.
289     ///
290     /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
291     Yield {
292         /// The value to return.
293         value: Operand<'tcx>,
294         /// Where to resume to.
295         resume: BasicBlock,
296         /// The place to store the resume argument in.
297         resume_arg: Place<'tcx>,
298         /// Cleanup to be done if the generator is dropped at this suspend point.
299         drop: Option<BasicBlock>,
300     },
301
302     /// Indicates the end of dropping a generator.
303     ///
304     /// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations
305     /// as `yield`.
306     ///
307     /// **Needs clarification**: Is that even correct? The generator drop code is always confusing
308     /// to me, because it's not even really in the current body.
309     ///
310     /// **Needs clarification**: Are there type system constraints on these terminators? Should
311     /// there be a "block type" like `cleanup` blocks for them?
312     GeneratorDrop,
313
314     /// A block where control flow only ever takes one real path, but borrowck needs to be more
315     /// conservative.
316     ///
317     /// At runtime this is semantically just a goto.
318     ///
319     /// Disallowed after drop elaboration.
320     FalseEdge {
321         /// The target normal control flow will take.
322         real_target: BasicBlock,
323         /// A block control flow could conceptually jump to, but won't in
324         /// practice.
325         imaginary_target: BasicBlock,
326     },
327
328     /// A terminator for blocks that only take one path in reality, but where we reserve the right
329     /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops
330     /// with no function calls for example.
331     ///
332     /// At runtime this is semantically just a goto.
333     ///
334     /// Disallowed after drop elaboration.
335     FalseUnwind {
336         /// The target normal control flow will take.
337         real_target: BasicBlock,
338         /// The imaginary cleanup block link. This particular path will never be taken
339         /// in practice, but in order to avoid fragility we want to always
340         /// consider it in borrowck. We don't want to accept programs which
341         /// pass borrowck only when `panic=abort` or some assertions are disabled
342         /// due to release vs. debug mode builds. This needs to be an `Option` because
343         /// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
344         unwind: Option<BasicBlock>,
345     },
346
347     /// Block ends with an inline assembly block. This is a terminator since
348     /// inline assembly is allowed to diverge.
349     InlineAsm {
350         /// The template for the inline assembly, with placeholders.
351         template: &'tcx [InlineAsmTemplatePiece],
352
353         /// The operands for the inline assembly, as `Operand`s or `Place`s.
354         operands: Vec<InlineAsmOperand<'tcx>>,
355
356         /// Miscellaneous options for the inline assembly.
357         options: InlineAsmOptions,
358
359         /// Source spans for each line of the inline assembly code. These are
360         /// used to map assembler errors back to the line in the source code.
361         line_spans: &'tcx [Span],
362
363         /// Destination block after the inline assembly returns, unless it is
364         /// diverging (InlineAsmOptions::NORETURN).
365         destination: Option<BasicBlock>,
366
367         /// Cleanup to be done if the inline assembly unwinds. This is present
368         /// if and only if InlineAsmOptions::MAY_UNWIND is set.
369         cleanup: Option<BasicBlock>,
370     },
371 }
372 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
373 pub struct Terminator<'tcx> {
374     pub source_info: SourceInfo,
375     pub kind: TerminatorKind<'tcx>,
376 }
377
378 impl<'tcx> Terminator<'tcx> {
379     pub fn successors(&self) -> Successors<'_> {
380         self.kind.successors()
381     }
382
383     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
384         self.kind.successors_mut()
385     }
386
387     pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
388         self.kind.unwind()
389     }
390
391     pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
392         self.kind.unwind_mut()
393     }
394 }
395
396 impl<'tcx> TerminatorKind<'tcx> {
397     pub fn if_(
398         tcx: TyCtxt<'tcx>,
399         cond: Operand<'tcx>,
400         t: BasicBlock,
401         f: BasicBlock,
402     ) -> TerminatorKind<'tcx> {
403         TerminatorKind::SwitchInt {
404             discr: cond,
405             switch_ty: tcx.types.bool,
406             targets: SwitchTargets::static_if(0, f, t),
407         }
408     }
409
410     pub fn successors(&self) -> Successors<'_> {
411         use self::TerminatorKind::*;
412         match *self {
413             Resume
414             | Abort
415             | GeneratorDrop
416             | Return
417             | Unreachable
418             | Call { destination: None, cleanup: None, .. }
419             | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]),
420             Goto { target: ref t }
421             | Call { destination: None, cleanup: Some(ref t), .. }
422             | Call { destination: Some((_, ref t)), cleanup: None, .. }
423             | Yield { resume: ref t, drop: None, .. }
424             | DropAndReplace { target: ref t, unwind: None, .. }
425             | Drop { target: ref t, unwind: None, .. }
426             | Assert { target: ref t, cleanup: None, .. }
427             | FalseUnwind { real_target: ref t, unwind: None }
428             | InlineAsm { destination: Some(ref t), cleanup: None, .. }
429             | InlineAsm { destination: None, cleanup: Some(ref t), .. } => {
430                 Some(t).into_iter().chain(&[])
431             }
432             Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
433             | Yield { resume: ref t, drop: Some(ref u), .. }
434             | DropAndReplace { target: ref t, unwind: Some(ref u), .. }
435             | Drop { target: ref t, unwind: Some(ref u), .. }
436             | Assert { target: ref t, cleanup: Some(ref u), .. }
437             | FalseUnwind { real_target: ref t, unwind: Some(ref u) }
438             | InlineAsm { destination: Some(ref t), cleanup: Some(ref u), .. } => {
439                 Some(t).into_iter().chain(slice::from_ref(u))
440             }
441             SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets),
442             FalseEdge { ref real_target, ref imaginary_target } => {
443                 Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
444             }
445         }
446     }
447
448     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
449         use self::TerminatorKind::*;
450         match *self {
451             Resume
452             | Abort
453             | GeneratorDrop
454             | Return
455             | Unreachable
456             | Call { destination: None, cleanup: None, .. }
457             | InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&mut []),
458             Goto { target: ref mut t }
459             | Call { destination: None, cleanup: Some(ref mut t), .. }
460             | Call { destination: Some((_, ref mut t)), cleanup: None, .. }
461             | Yield { resume: ref mut t, drop: None, .. }
462             | DropAndReplace { target: ref mut t, unwind: None, .. }
463             | Drop { target: ref mut t, unwind: None, .. }
464             | Assert { target: ref mut t, cleanup: None, .. }
465             | FalseUnwind { real_target: ref mut t, unwind: None }
466             | InlineAsm { destination: Some(ref mut t), cleanup: None, .. }
467             | InlineAsm { destination: None, cleanup: Some(ref mut t), .. } => {
468                 Some(t).into_iter().chain(&mut [])
469             }
470             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
471             | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
472             | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
473             | Drop { target: ref mut t, unwind: Some(ref mut u), .. }
474             | Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
475             | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) }
476             | InlineAsm { destination: Some(ref mut t), cleanup: Some(ref mut u), .. } => {
477                 Some(t).into_iter().chain(slice::from_mut(u))
478             }
479             SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets),
480             FalseEdge { ref mut real_target, ref mut imaginary_target } => {
481                 Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
482             }
483         }
484     }
485
486     pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
487         match *self {
488             TerminatorKind::Goto { .. }
489             | TerminatorKind::Resume
490             | TerminatorKind::Abort
491             | TerminatorKind::Return
492             | TerminatorKind::Unreachable
493             | TerminatorKind::GeneratorDrop
494             | TerminatorKind::Yield { .. }
495             | TerminatorKind::SwitchInt { .. }
496             | TerminatorKind::FalseEdge { .. } => None,
497             TerminatorKind::Call { cleanup: ref unwind, .. }
498             | TerminatorKind::Assert { cleanup: ref unwind, .. }
499             | TerminatorKind::DropAndReplace { ref unwind, .. }
500             | TerminatorKind::Drop { ref unwind, .. }
501             | TerminatorKind::FalseUnwind { ref unwind, .. }
502             | TerminatorKind::InlineAsm { cleanup: ref unwind, .. } => Some(unwind),
503         }
504     }
505
506     pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
507         match *self {
508             TerminatorKind::Goto { .. }
509             | TerminatorKind::Resume
510             | TerminatorKind::Abort
511             | TerminatorKind::Return
512             | TerminatorKind::Unreachable
513             | TerminatorKind::GeneratorDrop
514             | TerminatorKind::Yield { .. }
515             | TerminatorKind::SwitchInt { .. }
516             | TerminatorKind::FalseEdge { .. } => None,
517             TerminatorKind::Call { cleanup: ref mut unwind, .. }
518             | TerminatorKind::Assert { cleanup: ref mut unwind, .. }
519             | TerminatorKind::DropAndReplace { ref mut unwind, .. }
520             | TerminatorKind::Drop { ref mut unwind, .. }
521             | TerminatorKind::FalseUnwind { ref mut unwind, .. }
522             | TerminatorKind::InlineAsm { cleanup: ref mut unwind, .. } => Some(unwind),
523         }
524     }
525
526     pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> {
527         match self {
528             TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
529                 Some((discr, *switch_ty, targets))
530             }
531             _ => None,
532         }
533     }
534
535     pub fn as_goto(&self) -> Option<BasicBlock> {
536         match self {
537             TerminatorKind::Goto { target } => Some(*target),
538             _ => None,
539         }
540     }
541 }
542
543 impl<'tcx> Debug for TerminatorKind<'tcx> {
544     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
545         self.fmt_head(fmt)?;
546         let successor_count = self.successors().count();
547         let labels = self.fmt_successor_labels();
548         assert_eq!(successor_count, labels.len());
549
550         match successor_count {
551             0 => Ok(()),
552
553             1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
554
555             _ => {
556                 write!(fmt, " -> [")?;
557                 for (i, target) in self.successors().enumerate() {
558                     if i > 0 {
559                         write!(fmt, ", ")?;
560                     }
561                     write!(fmt, "{}: {:?}", labels[i], target)?;
562                 }
563                 write!(fmt, "]")
564             }
565         }
566     }
567 }
568
569 impl<'tcx> TerminatorKind<'tcx> {
570     /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
571     /// successor basic block, if any. The only information not included is the list of possible
572     /// successors, which may be rendered differently between the text and the graphviz format.
573     pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
574         use self::TerminatorKind::*;
575         match self {
576             Goto { .. } => write!(fmt, "goto"),
577             SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr),
578             Return => write!(fmt, "return"),
579             GeneratorDrop => write!(fmt, "generator_drop"),
580             Resume => write!(fmt, "resume"),
581             Abort => write!(fmt, "abort"),
582             Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
583             Unreachable => write!(fmt, "unreachable"),
584             Drop { place, .. } => write!(fmt, "drop({:?})", place),
585             DropAndReplace { place, value, .. } => {
586                 write!(fmt, "replace({:?} <- {:?})", place, value)
587             }
588             Call { func, args, destination, .. } => {
589                 if let Some((destination, _)) = destination {
590                     write!(fmt, "{:?} = ", destination)?;
591                 }
592                 write!(fmt, "{:?}(", func)?;
593                 for (index, arg) in args.iter().enumerate() {
594                     if index > 0 {
595                         write!(fmt, ", ")?;
596                     }
597                     write!(fmt, "{:?}", arg)?;
598                 }
599                 write!(fmt, ")")
600             }
601             Assert { cond, expected, msg, .. } => {
602                 write!(fmt, "assert(")?;
603                 if !expected {
604                     write!(fmt, "!")?;
605                 }
606                 write!(fmt, "{:?}, ", cond)?;
607                 msg.fmt_assert_args(fmt)?;
608                 write!(fmt, ")")
609             }
610             FalseEdge { .. } => write!(fmt, "falseEdge"),
611             FalseUnwind { .. } => write!(fmt, "falseUnwind"),
612             InlineAsm { template, ref operands, options, .. } => {
613                 write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
614                 for op in operands {
615                     write!(fmt, ", ")?;
616                     let print_late = |&late| if late { "late" } else { "" };
617                     match op {
618                         InlineAsmOperand::In { reg, value } => {
619                             write!(fmt, "in({}) {:?}", reg, value)?;
620                         }
621                         InlineAsmOperand::Out { reg, late, place: Some(place) } => {
622                             write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
623                         }
624                         InlineAsmOperand::Out { reg, late, place: None } => {
625                             write!(fmt, "{}out({}) _", print_late(late), reg)?;
626                         }
627                         InlineAsmOperand::InOut {
628                             reg,
629                             late,
630                             in_value,
631                             out_place: Some(out_place),
632                         } => {
633                             write!(
634                                 fmt,
635                                 "in{}out({}) {:?} => {:?}",
636                                 print_late(late),
637                                 reg,
638                                 in_value,
639                                 out_place
640                             )?;
641                         }
642                         InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
643                             write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
644                         }
645                         InlineAsmOperand::Const { value } => {
646                             write!(fmt, "const {:?}", value)?;
647                         }
648                         InlineAsmOperand::SymFn { value } => {
649                             write!(fmt, "sym_fn {:?}", value)?;
650                         }
651                         InlineAsmOperand::SymStatic { def_id } => {
652                             write!(fmt, "sym_static {:?}", def_id)?;
653                         }
654                     }
655                 }
656                 write!(fmt, ", options({:?}))", options)
657             }
658         }
659     }
660
661     /// Returns the list of labels for the edges to the successor basic blocks.
662     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
663         use self::TerminatorKind::*;
664         match *self {
665             Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
666             Goto { .. } => vec!["".into()],
667             SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
668                 let param_env = ty::ParamEnv::empty();
669                 let switch_ty = tcx.lift(switch_ty).unwrap();
670                 let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
671                 targets
672                     .values
673                     .iter()
674                     .map(|&u| {
675                         ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
676                             .to_string()
677                             .into()
678                     })
679                     .chain(iter::once("otherwise".into()))
680                     .collect()
681             }),
682             Call { destination: Some(_), cleanup: Some(_), .. } => {
683                 vec!["return".into(), "unwind".into()]
684             }
685             Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
686             Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
687             Call { destination: None, cleanup: None, .. } => vec![],
688             Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
689             Yield { drop: None, .. } => vec!["resume".into()],
690             DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
691                 vec!["return".into()]
692             }
693             DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
694                 vec!["return".into(), "unwind".into()]
695             }
696             Assert { cleanup: None, .. } => vec!["".into()],
697             Assert { .. } => vec!["success".into(), "unwind".into()],
698             FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
699             FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
700             FalseUnwind { unwind: None, .. } => vec!["real".into()],
701             InlineAsm { destination: Some(_), cleanup: Some(_), .. } => {
702                 vec!["return".into(), "unwind".into()]
703             }
704             InlineAsm { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
705             InlineAsm { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
706             InlineAsm { destination: None, cleanup: None, .. } => vec![],
707         }
708     }
709 }