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