]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/mir/terminator.rs
Rollup merge of #81822 - Kixunil:path_try_exists, r=kennytm
[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: self.values.iter().zip(self.targets.iter()) }
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
83 pub struct SwitchTargetsIter<'a> {
84     inner: iter::Zip<slice::Iter<'a, u128>, slice::Iter<'a, BasicBlock>>,
85 }
86
87 impl<'a> Iterator for SwitchTargetsIter<'a> {
88     type Item = (u128, BasicBlock);
89
90     fn next(&mut self) -> Option<Self::Item> {
91         self.inner.next().map(|(val, bb)| (*val, *bb))
92     }
93
94     fn size_hint(&self) -> (usize, Option<usize>) {
95         self.inner.size_hint()
96     }
97 }
98
99 impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {}
100
101 #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)]
102 pub enum TerminatorKind<'tcx> {
103     /// Block should have one successor in the graph; we jump there.
104     Goto { target: BasicBlock },
105
106     /// Operand evaluates to an integer; jump depending on its value
107     /// to one of the targets, and otherwise fallback to `otherwise`.
108     SwitchInt {
109         /// The discriminant value being tested.
110         discr: Operand<'tcx>,
111
112         /// The type of value being tested.
113         /// This is always the same as the type of `discr`.
114         /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
115         switch_ty: Ty<'tcx>,
116
117         targets: SwitchTargets,
118     },
119
120     /// Indicates that the landing pad is finished and unwinding should
121     /// continue. Emitted by `build::scope::diverge_cleanup`.
122     Resume,
123
124     /// Indicates that the landing pad is finished and that the process
125     /// should abort. Used to prevent unwinding for foreign items.
126     Abort,
127
128     /// Indicates a normal return. The return place should have
129     /// been filled in before this executes. This can occur multiple times
130     /// in different basic blocks.
131     Return,
132
133     /// Indicates a terminator that can never be reached.
134     Unreachable,
135
136     /// Drop the `Place`.
137     Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
138
139     /// Drop the `Place` and assign the new value over it. This ensures
140     /// that the assignment to `P` occurs *even if* the destructor for
141     /// place unwinds. Its semantics are best explained by the
142     /// elaboration:
143     ///
144     /// ```
145     /// BB0 {
146     ///   DropAndReplace(P <- V, goto BB1, unwind BB2)
147     /// }
148     /// ```
149     ///
150     /// becomes
151     ///
152     /// ```
153     /// BB0 {
154     ///   Drop(P, goto BB1, unwind BB2)
155     /// }
156     /// BB1 {
157     ///   // P is now uninitialized
158     ///   P <- V
159     /// }
160     /// BB2 {
161     ///   // P is now uninitialized -- its dtor panicked
162     ///   P <- V
163     /// }
164     /// ```
165     ///
166     /// Note that DropAndReplace is eliminated as part of the `ElaborateDrops` pass.
167     DropAndReplace {
168         place: Place<'tcx>,
169         value: Operand<'tcx>,
170         target: BasicBlock,
171         unwind: Option<BasicBlock>,
172     },
173
174     /// Block ends with a call of a function.
175     Call {
176         /// The function that’s being called.
177         func: Operand<'tcx>,
178         /// Arguments the function is called with.
179         /// These are owned by the callee, which is free to modify them.
180         /// This allows the memory occupied by "by-value" arguments to be
181         /// reused across function calls without duplicating the contents.
182         args: Vec<Operand<'tcx>>,
183         /// Destination for the return value. If some, the call is converging.
184         destination: Option<(Place<'tcx>, BasicBlock)>,
185         /// Cleanups to be done if the call unwinds.
186         cleanup: Option<BasicBlock>,
187         /// `true` if this is from a call in HIR rather than from an overloaded
188         /// operator. True for overloaded function call.
189         from_hir_call: bool,
190         /// This `Span` is the span of the function, without the dot and receiver
191         /// (e.g. `foo(a, b)` in `x.foo(a, b)`
192         fn_span: Span,
193     },
194
195     /// Jump to the target if the condition has the expected value,
196     /// otherwise panic with a message and a cleanup target.
197     Assert {
198         cond: Operand<'tcx>,
199         expected: bool,
200         msg: AssertMessage<'tcx>,
201         target: BasicBlock,
202         cleanup: Option<BasicBlock>,
203     },
204
205     /// A suspend point.
206     Yield {
207         /// The value to return.
208         value: Operand<'tcx>,
209         /// Where to resume to.
210         resume: BasicBlock,
211         /// The place to store the resume argument in.
212         resume_arg: Place<'tcx>,
213         /// Cleanup to be done if the generator is dropped at this suspend point.
214         drop: Option<BasicBlock>,
215     },
216
217     /// Indicates the end of the dropping of a generator.
218     GeneratorDrop,
219
220     /// A block where control flow only ever takes one real path, but borrowck
221     /// needs to be more conservative.
222     FalseEdge {
223         /// The target normal control flow will take.
224         real_target: BasicBlock,
225         /// A block control flow could conceptually jump to, but won't in
226         /// practice.
227         imaginary_target: BasicBlock,
228     },
229     /// A terminator for blocks that only take one path in reality, but where we
230     /// reserve the right to unwind in borrowck, even if it won't happen in practice.
231     /// This can arise in infinite loops with no function calls for example.
232     FalseUnwind {
233         /// The target normal control flow will take.
234         real_target: BasicBlock,
235         /// The imaginary cleanup block link. This particular path will never be taken
236         /// in practice, but in order to avoid fragility we want to always
237         /// consider it in borrowck. We don't want to accept programs which
238         /// pass borrowck only when `panic=abort` or some assertions are disabled
239         /// due to release vs. debug mode builds. This needs to be an `Option` because
240         /// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
241         unwind: Option<BasicBlock>,
242     },
243
244     /// Block ends with an inline assembly block. This is a terminator since
245     /// inline assembly is allowed to diverge.
246     InlineAsm {
247         /// The template for the inline assembly, with placeholders.
248         template: &'tcx [InlineAsmTemplatePiece],
249
250         /// The operands for the inline assembly, as `Operand`s or `Place`s.
251         operands: Vec<InlineAsmOperand<'tcx>>,
252
253         /// Miscellaneous options for the inline assembly.
254         options: InlineAsmOptions,
255
256         /// Source spans for each line of the inline assembly code. These are
257         /// used to map assembler errors back to the line in the source code.
258         line_spans: &'tcx [Span],
259
260         /// Destination block after the inline assembly returns, unless it is
261         /// diverging (InlineAsmOptions::NORETURN).
262         destination: Option<BasicBlock>,
263     },
264 }
265 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
266 pub struct Terminator<'tcx> {
267     pub source_info: SourceInfo,
268     pub kind: TerminatorKind<'tcx>,
269 }
270
271 impl<'tcx> Terminator<'tcx> {
272     pub fn successors(&self) -> Successors<'_> {
273         self.kind.successors()
274     }
275
276     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
277         self.kind.successors_mut()
278     }
279
280     pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
281         self.kind.unwind()
282     }
283
284     pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
285         self.kind.unwind_mut()
286     }
287 }
288
289 impl<'tcx> TerminatorKind<'tcx> {
290     pub fn if_(
291         tcx: TyCtxt<'tcx>,
292         cond: Operand<'tcx>,
293         t: BasicBlock,
294         f: BasicBlock,
295     ) -> TerminatorKind<'tcx> {
296         TerminatorKind::SwitchInt {
297             discr: cond,
298             switch_ty: tcx.types.bool,
299             targets: SwitchTargets::static_if(0, f, t),
300         }
301     }
302
303     pub fn successors(&self) -> Successors<'_> {
304         use self::TerminatorKind::*;
305         match *self {
306             Resume
307             | Abort
308             | GeneratorDrop
309             | Return
310             | Unreachable
311             | Call { destination: None, cleanup: None, .. }
312             | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
313             Goto { target: ref t }
314             | Call { destination: None, cleanup: Some(ref t), .. }
315             | Call { destination: Some((_, ref t)), cleanup: None, .. }
316             | Yield { resume: ref t, drop: None, .. }
317             | DropAndReplace { target: ref t, unwind: None, .. }
318             | Drop { target: ref t, unwind: None, .. }
319             | Assert { target: ref t, cleanup: None, .. }
320             | FalseUnwind { real_target: ref t, unwind: None }
321             | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]),
322             Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
323             | Yield { resume: ref t, drop: Some(ref u), .. }
324             | DropAndReplace { target: ref t, unwind: Some(ref u), .. }
325             | Drop { target: ref t, unwind: Some(ref u), .. }
326             | Assert { target: ref t, cleanup: Some(ref u), .. }
327             | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => {
328                 Some(t).into_iter().chain(slice::from_ref(u))
329             }
330             SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
331             FalseEdge { ref real_target, ref imaginary_target } => {
332                 Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
333             }
334         }
335     }
336
337     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
338         use self::TerminatorKind::*;
339         match *self {
340             Resume
341             | Abort
342             | GeneratorDrop
343             | Return
344             | Unreachable
345             | Call { destination: None, cleanup: None, .. }
346             | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []),
347             Goto { target: ref mut t }
348             | Call { destination: None, cleanup: Some(ref mut t), .. }
349             | Call { destination: Some((_, ref mut t)), cleanup: None, .. }
350             | Yield { resume: ref mut t, drop: None, .. }
351             | DropAndReplace { target: ref mut t, unwind: None, .. }
352             | Drop { target: ref mut t, unwind: None, .. }
353             | Assert { target: ref mut t, cleanup: None, .. }
354             | FalseUnwind { real_target: ref mut t, unwind: None }
355             | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []),
356             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
357             | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
358             | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
359             | Drop { target: ref mut t, unwind: Some(ref mut u), .. }
360             | Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
361             | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => {
362                 Some(t).into_iter().chain(slice::from_mut(u))
363             }
364             SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
365             FalseEdge { ref mut real_target, ref mut imaginary_target } => {
366                 Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
367             }
368         }
369     }
370
371     pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
372         match *self {
373             TerminatorKind::Goto { .. }
374             | TerminatorKind::Resume
375             | TerminatorKind::Abort
376             | TerminatorKind::Return
377             | TerminatorKind::Unreachable
378             | TerminatorKind::GeneratorDrop
379             | TerminatorKind::Yield { .. }
380             | TerminatorKind::SwitchInt { .. }
381             | TerminatorKind::FalseEdge { .. }
382             | TerminatorKind::InlineAsm { .. } => None,
383             TerminatorKind::Call { cleanup: ref unwind, .. }
384             | TerminatorKind::Assert { cleanup: ref unwind, .. }
385             | TerminatorKind::DropAndReplace { ref unwind, .. }
386             | TerminatorKind::Drop { ref unwind, .. }
387             | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind),
388         }
389     }
390
391     pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
392         match *self {
393             TerminatorKind::Goto { .. }
394             | TerminatorKind::Resume
395             | TerminatorKind::Abort
396             | TerminatorKind::Return
397             | TerminatorKind::Unreachable
398             | TerminatorKind::GeneratorDrop
399             | TerminatorKind::Yield { .. }
400             | TerminatorKind::SwitchInt { .. }
401             | TerminatorKind::FalseEdge { .. }
402             | TerminatorKind::InlineAsm { .. } => None,
403             TerminatorKind::Call { cleanup: ref mut unwind, .. }
404             | TerminatorKind::Assert { cleanup: ref mut unwind, .. }
405             | TerminatorKind::DropAndReplace { ref mut unwind, .. }
406             | TerminatorKind::Drop { ref mut unwind, .. }
407             | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind),
408         }
409     }
410
411     pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> {
412         match self {
413             TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
414                 Some((discr, switch_ty, targets))
415             }
416             _ => None,
417         }
418     }
419
420     pub fn as_goto(&self) -> Option<BasicBlock> {
421         match self {
422             TerminatorKind::Goto { target } => Some(*target),
423             _ => None,
424         }
425     }
426 }
427
428 impl<'tcx> Debug for TerminatorKind<'tcx> {
429     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
430         self.fmt_head(fmt)?;
431         let successor_count = self.successors().count();
432         let labels = self.fmt_successor_labels();
433         assert_eq!(successor_count, labels.len());
434
435         match successor_count {
436             0 => Ok(()),
437
438             1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
439
440             _ => {
441                 write!(fmt, " -> [")?;
442                 for (i, target) in self.successors().enumerate() {
443                     if i > 0 {
444                         write!(fmt, ", ")?;
445                     }
446                     write!(fmt, "{}: {:?}", labels[i], target)?;
447                 }
448                 write!(fmt, "]")
449             }
450         }
451     }
452 }
453
454 impl<'tcx> TerminatorKind<'tcx> {
455     /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
456     /// successor basic block, if any. The only information not included is the list of possible
457     /// successors, which may be rendered differently between the text and the graphviz format.
458     pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
459         use self::TerminatorKind::*;
460         match self {
461             Goto { .. } => write!(fmt, "goto"),
462             SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr),
463             Return => write!(fmt, "return"),
464             GeneratorDrop => write!(fmt, "generator_drop"),
465             Resume => write!(fmt, "resume"),
466             Abort => write!(fmt, "abort"),
467             Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
468             Unreachable => write!(fmt, "unreachable"),
469             Drop { place, .. } => write!(fmt, "drop({:?})", place),
470             DropAndReplace { place, value, .. } => {
471                 write!(fmt, "replace({:?} <- {:?})", place, value)
472             }
473             Call { func, args, destination, .. } => {
474                 if let Some((destination, _)) = destination {
475                     write!(fmt, "{:?} = ", destination)?;
476                 }
477                 write!(fmt, "{:?}(", func)?;
478                 for (index, arg) in args.iter().enumerate() {
479                     if index > 0 {
480                         write!(fmt, ", ")?;
481                     }
482                     write!(fmt, "{:?}", arg)?;
483                 }
484                 write!(fmt, ")")
485             }
486             Assert { cond, expected, msg, .. } => {
487                 write!(fmt, "assert(")?;
488                 if !expected {
489                     write!(fmt, "!")?;
490                 }
491                 write!(fmt, "{:?}, ", cond)?;
492                 msg.fmt_assert_args(fmt)?;
493                 write!(fmt, ")")
494             }
495             FalseEdge { .. } => write!(fmt, "falseEdge"),
496             FalseUnwind { .. } => write!(fmt, "falseUnwind"),
497             InlineAsm { template, ref operands, options, .. } => {
498                 write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
499                 for op in operands {
500                     write!(fmt, ", ")?;
501                     let print_late = |&late| if late { "late" } else { "" };
502                     match op {
503                         InlineAsmOperand::In { reg, value } => {
504                             write!(fmt, "in({}) {:?}", reg, value)?;
505                         }
506                         InlineAsmOperand::Out { reg, late, place: Some(place) } => {
507                             write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
508                         }
509                         InlineAsmOperand::Out { reg, late, place: None } => {
510                             write!(fmt, "{}out({}) _", print_late(late), reg)?;
511                         }
512                         InlineAsmOperand::InOut {
513                             reg,
514                             late,
515                             in_value,
516                             out_place: Some(out_place),
517                         } => {
518                             write!(
519                                 fmt,
520                                 "in{}out({}) {:?} => {:?}",
521                                 print_late(late),
522                                 reg,
523                                 in_value,
524                                 out_place
525                             )?;
526                         }
527                         InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
528                             write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
529                         }
530                         InlineAsmOperand::Const { value } => {
531                             write!(fmt, "const {:?}", value)?;
532                         }
533                         InlineAsmOperand::SymFn { value } => {
534                             write!(fmt, "sym_fn {:?}", value)?;
535                         }
536                         InlineAsmOperand::SymStatic { def_id } => {
537                             write!(fmt, "sym_static {:?}", def_id)?;
538                         }
539                     }
540                 }
541                 write!(fmt, ", options({:?}))", options)
542             }
543         }
544     }
545
546     /// Returns the list of labels for the edges to the successor basic blocks.
547     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
548         use self::TerminatorKind::*;
549         match *self {
550             Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
551             Goto { .. } => vec!["".into()],
552             SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| {
553                 let param_env = ty::ParamEnv::empty();
554                 let switch_ty = tcx.lift(switch_ty).unwrap();
555                 let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
556                 targets
557                     .values
558                     .iter()
559                     .map(|&u| {
560                         ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
561                             .to_string()
562                             .into()
563                     })
564                     .chain(iter::once("otherwise".into()))
565                     .collect()
566             }),
567             Call { destination: Some(_), cleanup: Some(_), .. } => {
568                 vec!["return".into(), "unwind".into()]
569             }
570             Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
571             Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
572             Call { destination: None, cleanup: None, .. } => vec![],
573             Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
574             Yield { drop: None, .. } => vec!["resume".into()],
575             DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
576                 vec!["return".into()]
577             }
578             DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
579                 vec!["return".into(), "unwind".into()]
580             }
581             Assert { cleanup: None, .. } => vec!["".into()],
582             Assert { .. } => vec!["success".into(), "unwind".into()],
583             FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
584             FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
585             FalseUnwind { unwind: None, .. } => vec!["real".into()],
586             InlineAsm { destination: Some(_), .. } => vec!["".into()],
587             InlineAsm { destination: None, .. } => vec![],
588         }
589     }
590 }