]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/into.rs
Fix incorrect double assignment in MIR for while loops
[rust.git] / src / librustc_mir / build / expr / into.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
5 use crate::hair::*;
6 use rustc::mir::*;
7 use rustc::ty;
8
9 use rustc_target::spec::abi::Abi;
10
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12     /// Compile `expr`, storing the result into `destination`, which
13     /// is assumed to be uninitialized.
14     pub fn into_expr(
15         &mut self,
16         destination: &Place<'tcx>,
17         mut block: BasicBlock,
18         expr: Expr<'tcx>,
19     ) -> BlockAnd<()> {
20         debug!(
21             "into_expr(destination={:?}, block={:?}, expr={:?})",
22             destination, block, expr
23         );
24
25         // since we frequently have to reference `self` from within a
26         // closure, where `self` would be shadowed, it's easier to
27         // just use the name `this` uniformly
28         let this = self;
29         let expr_span = expr.span;
30         let source_info = this.source_info(expr_span);
31
32         let expr_is_block_or_scope = match expr.kind {
33             ExprKind::Block { .. } => true,
34             ExprKind::Scope { .. } => true,
35             _ => false,
36         };
37
38         if !expr_is_block_or_scope {
39             this.block_context.push(BlockFrame::SubExpr);
40         }
41
42         let block_and = match expr.kind {
43             ExprKind::Scope {
44                 region_scope,
45                 lint_level,
46                 value,
47             } => {
48                 let region_scope = (region_scope, source_info);
49                 this.in_scope(region_scope, lint_level, |this| {
50                     this.into(destination, block, value)
51                 })
52             }
53             ExprKind::Block { body: ast_block } => {
54                 this.ast_block(destination, block, ast_block, source_info)
55             }
56             ExprKind::Match { scrutinee, arms } => {
57                 this.match_expr(destination, expr_span, block, scrutinee, arms)
58             }
59             ExprKind::NeverToAny { source } => {
60                 let source = this.hir.mirror(source);
61                 let is_call = match source.kind {
62                     ExprKind::Call { .. } => true,
63                     _ => false,
64                 };
65
66                 unpack!(block = this.as_local_rvalue(block, source));
67
68                 // This is an optimization. If the expression was a call then we already have an
69                 // unreachable block. Don't bother to terminate it and create a new one.
70                 if is_call {
71                     block.unit()
72                 } else {
73                     this.cfg
74                         .terminate(block, source_info, TerminatorKind::Unreachable);
75                     let end_block = this.cfg.start_new_block();
76                     end_block.unit()
77                 }
78             }
79             ExprKind::LogicalOp { op, lhs, rhs } => {
80                 // And:
81                 //
82                 // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
83                 //        | (false)
84                 //  [shortcurcuit_block: dest = false]
85                 //
86                 // Or:
87                 //
88                 // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
89                 //        | (true)
90                 //  [shortcurcuit_block: dest = true]
91
92                 let (shortcircuit_block, mut else_block, join_block) = (
93                     this.cfg.start_new_block(),
94                     this.cfg.start_new_block(),
95                     this.cfg.start_new_block(),
96                 );
97
98                 let lhs = unpack!(block = this.as_local_operand(block, lhs));
99                 let blocks = match op {
100                     LogicalOp::And => (else_block, shortcircuit_block),
101                     LogicalOp::Or => (shortcircuit_block, else_block),
102                 };
103                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
104                 this.cfg.terminate(block, source_info, term);
105
106                 this.cfg.push_assign_constant(
107                     shortcircuit_block,
108                     source_info,
109                     destination,
110                     Constant {
111                         span: expr_span,
112                         ty: this.hir.bool_ty(),
113                         user_ty: None,
114                         literal: match op {
115                             LogicalOp::And => this.hir.false_literal(),
116                             LogicalOp::Or => this.hir.true_literal(),
117                         },
118                     },
119                 );
120                 this.cfg.terminate(
121                     shortcircuit_block,
122                     source_info,
123                     TerminatorKind::Goto { target: join_block },
124                 );
125
126                 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
127                 this.cfg.push_assign(
128                     else_block,
129                     source_info,
130                     destination,
131                     Rvalue::Use(rhs),
132                 );
133                 this.cfg.terminate(
134                     else_block,
135                     source_info,
136                     TerminatorKind::Goto { target: join_block },
137                 );
138
139                 join_block.unit()
140             }
141             ExprKind::Loop {
142                 condition: opt_cond_expr,
143                 body,
144             } => {
145                 // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
146                 //                  ^                               |
147                 //                  |                               0
148                 //                  |                               |
149                 //                  |                               v
150                 //           [body_block_end] <-/eval. body/-- [body_block]
151                 //
152                 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
153                 //
154                 // [block]
155                 //    |
156                 //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
157                 //    |        ^                                         |
158                 // false link  |                                         |
159                 //    |        +-----------------------------------------+
160                 //    +-> [diverge_cleanup]
161                 // The false link is required to make sure borrowck considers unwinds through the
162                 // body, even when the exact code in the body cannot unwind
163
164                 let loop_block = this.cfg.start_new_block();
165                 let exit_block = this.cfg.start_new_block();
166
167                 // start the loop
168                 this.cfg.terminate(
169                     block,
170                     source_info,
171                     TerminatorKind::Goto { target: loop_block },
172                 );
173
174                 this.in_breakable_scope(
175                     Some(loop_block),
176                     exit_block,
177                     destination.clone(),
178                     move |this| {
179                         // conduct the test, if necessary
180                         let body_block;
181                         if let Some(cond_expr) = opt_cond_expr {
182                             let loop_block_end;
183                             let cond = unpack!(
184                                 loop_block_end = this.as_local_operand(loop_block, cond_expr)
185                             );
186                             body_block = this.cfg.start_new_block();
187                             let false_block = this.cfg.start_new_block();
188                             let term = TerminatorKind::if_(
189                                 this.hir.tcx(),
190                                 cond,
191                                 body_block,
192                                 false_block,
193                             );
194                             this.cfg.terminate(loop_block_end, source_info, term);
195
196                             // if the test is false, there's no `break` to assign `destination`, so
197                             // we have to do it
198                             this.cfg.push_assign_unit(false_block, source_info, destination);
199                             this.cfg.terminate(
200                                 false_block,
201                                 source_info,
202                                 TerminatorKind::Goto { target: exit_block },
203                             );
204                         } else {
205                             body_block = this.cfg.start_new_block();
206                             let diverge_cleanup = this.diverge_cleanup();
207                             this.cfg.terminate(
208                                 loop_block,
209                                 source_info,
210                                 TerminatorKind::FalseUnwind {
211                                     real_target: body_block,
212                                     unwind: Some(diverge_cleanup),
213                                 },
214                             )
215                         }
216
217                         // The “return” value of the loop body must always be an unit. We therefore
218                         // introduce a unit temporary as the destination for the loop body.
219                         let tmp = this.get_unit_temp();
220                         // Execute the body, branching back to the test.
221                         let body_block_end = unpack!(this.into(&tmp, body_block, body));
222                         this.cfg.terminate(
223                             body_block_end,
224                             source_info,
225                             TerminatorKind::Goto { target: loop_block },
226                         );
227                     },
228                 );
229                 exit_block.unit()
230             }
231             ExprKind::Call { ty, fun, args, from_hir_call } => {
232                 let intrinsic = match ty.sty {
233                     ty::FnDef(def_id, _) => {
234                         let f = ty.fn_sig(this.hir.tcx());
235                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
236                             Some(this.hir.tcx().item_name(def_id).as_str())
237                         } else {
238                             None
239                         }
240                     }
241                     _ => None,
242                 };
243                 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
244                 let fun = unpack!(block = this.as_local_operand(block, fun));
245                 if intrinsic == Some("move_val_init") {
246                     // `move_val_init` has "magic" semantics - the second argument is
247                     // always evaluated "directly" into the first one.
248
249                     let mut args = args.into_iter();
250                     let ptr = args.next().expect("0 arguments to `move_val_init`");
251                     let val = args.next().expect("1 argument to `move_val_init`");
252                     assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
253
254                     let ptr = this.hir.mirror(ptr);
255                     let ptr_ty = ptr.ty;
256                     // Create an *internal* temp for the pointer, so that unsafety
257                     // checking won't complain about the raw pointer assignment.
258                     let ptr_temp = this.local_decls.push(LocalDecl {
259                         mutability: Mutability::Mut,
260                         ty: ptr_ty,
261                         user_ty: UserTypeProjections::none(),
262                         name: None,
263                         source_info,
264                         visibility_scope: source_info.scope,
265                         internal: true,
266                         is_user_variable: None,
267                         is_block_tail: None,
268                     });
269                     let ptr_temp = Place::from(ptr_temp);
270                     let block = unpack!(this.into(&ptr_temp, block, ptr));
271                     this.into(&ptr_temp.deref(), block, val)
272                 } else {
273                     let args: Vec<_> = args
274                         .into_iter()
275                         .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
276                         .collect();
277
278                     let success = this.cfg.start_new_block();
279                     let cleanup = this.diverge_cleanup();
280                     this.cfg.terminate(
281                         block,
282                         source_info,
283                         TerminatorKind::Call {
284                             func: fun,
285                             args,
286                             cleanup: Some(cleanup),
287                             // FIXME(varkor): replace this with an uninhabitedness-based check.
288                             // This requires getting access to the current module to call
289                             // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
290                             destination: if expr.ty.is_never() {
291                                 None
292                             } else {
293                                 Some((destination.clone(), success))
294                             },
295                             from_hir_call,
296                         },
297                     );
298                     success.unit()
299                 }
300             }
301             ExprKind::Use { source } => {
302                 this.into(destination, block, source)
303             }
304
305             // These cases don't actually need a destination
306             ExprKind::Assign { .. }
307             | ExprKind::AssignOp { .. }
308             | ExprKind::Continue { .. }
309             | ExprKind::Break { .. }
310             | ExprKind::InlineAsm { .. }
311             | ExprKind::Return { .. } => {
312                 unpack!(block = this.stmt_expr(block, expr, None));
313                 this.cfg.push_assign_unit(block, source_info, destination);
314                 block.unit()
315             }
316
317             // Avoid creating a temporary
318             ExprKind::VarRef { .. } |
319             ExprKind::SelfRef |
320             ExprKind::StaticRef { .. } |
321             ExprKind::PlaceTypeAscription { .. } |
322             ExprKind::ValueTypeAscription { .. } => {
323                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
324
325                 let place = unpack!(block = this.as_place(block, expr));
326                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
327                 this.cfg
328                     .push_assign(block, source_info, destination, rvalue);
329                 block.unit()
330             }
331             ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
332                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
333
334                 // Create a "fake" temporary variable so that we check that the
335                 // value is Sized. Usually, this is caught in type checking, but
336                 // in the case of box expr there is no such check.
337                 if let Place::Projection(..) = destination {
338                     this.local_decls
339                         .push(LocalDecl::new_temp(expr.ty, expr.span));
340                 }
341
342                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
343
344                 let place = unpack!(block = this.as_place(block, expr));
345                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
346                 this.cfg
347                     .push_assign(block, source_info, destination, rvalue);
348                 block.unit()
349             }
350
351             // these are the cases that are more naturally handled by some other mode
352             ExprKind::Unary { .. }
353             | ExprKind::Binary { .. }
354             | ExprKind::Box { .. }
355             | ExprKind::Cast { .. }
356             | ExprKind::Pointer { .. }
357             | ExprKind::Repeat { .. }
358             | ExprKind::Borrow { .. }
359             | ExprKind::Array { .. }
360             | ExprKind::Tuple { .. }
361             | ExprKind::Adt { .. }
362             | ExprKind::Closure { .. }
363             | ExprKind::Literal { .. }
364             | ExprKind::Yield { .. } => {
365                 debug_assert!(match Category::of(&expr.kind).unwrap() {
366                     // should be handled above
367                     Category::Rvalue(RvalueFunc::Into) => false,
368
369                     // must be handled above or else we get an
370                     // infinite loop in the builder; see
371                     // e.g., `ExprKind::VarRef` above
372                     Category::Place => false,
373
374                     _ => true,
375                 });
376
377                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
378                 this.cfg.push_assign(block, source_info, destination, rvalue);
379                 block.unit()
380             }
381         };
382
383         if !expr_is_block_or_scope {
384             let popped = this.block_context.pop();
385             assert!(popped.is_some());
386         }
387
388         block_and
389     }
390 }