]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/into.rs
f85e37a6ca57b6d27aea2b846693f1dd4f449dff
[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, 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         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, block, |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 { discriminant, arms } => {
57                 this.match_expr(destination, expr_span, block, discriminant, 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::If {
80                 condition: cond_expr,
81                 then: then_expr,
82                 otherwise: else_expr,
83             } => {
84                 let operand = unpack!(block = this.as_local_operand(block, cond_expr));
85
86                 let mut then_block = this.cfg.start_new_block();
87                 let mut else_block = this.cfg.start_new_block();
88                 let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
89                 this.cfg.terminate(block, source_info, term);
90
91                 unpack!(then_block = this.into(destination, then_block, then_expr));
92                 else_block = if let Some(else_expr) = else_expr {
93                     unpack!(this.into(destination, else_block, else_expr))
94                 } else {
95                     // Body of the `if` expression without an `else` clause must return `()`, thus
96                     // we implicitly generate a `else {}` if it is not specified.
97                     this.cfg
98                         .push_assign_unit(else_block, source_info, destination);
99                     else_block
100                 };
101
102                 let join_block = this.cfg.start_new_block();
103                 this.cfg.terminate(
104                     then_block,
105                     source_info,
106                     TerminatorKind::Goto { target: join_block },
107                 );
108                 this.cfg.terminate(
109                     else_block,
110                     source_info,
111                     TerminatorKind::Goto { target: join_block },
112                 );
113
114                 join_block.unit()
115             }
116             ExprKind::LogicalOp { op, lhs, rhs } => {
117                 // And:
118                 //
119                 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
120                 //        |                          | (false)
121                 //        +----------false-----------+------------------> [false_block]
122                 //
123                 // Or:
124                 //
125                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
126                 //        | (true)                   | (false)
127                 //  [true_block]               [false_block]
128
129                 let (true_block, false_block, mut else_block, join_block) = (
130                     this.cfg.start_new_block(),
131                     this.cfg.start_new_block(),
132                     this.cfg.start_new_block(),
133                     this.cfg.start_new_block(),
134                 );
135
136                 let lhs = unpack!(block = this.as_local_operand(block, lhs));
137                 let blocks = match op {
138                     LogicalOp::And => (else_block, false_block),
139                     LogicalOp::Or => (true_block, else_block),
140                 };
141                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
142                 this.cfg.terminate(block, source_info, term);
143
144                 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
145                 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
146                 this.cfg.terminate(else_block, source_info, term);
147
148                 this.cfg.push_assign_constant(
149                     true_block,
150                     source_info,
151                     destination,
152                     Constant {
153                         span: expr_span,
154                         ty: this.hir.bool_ty(),
155                         user_ty: None,
156                         literal: this.hir.true_literal(),
157                     },
158                 );
159
160                 this.cfg.push_assign_constant(
161                     false_block,
162                     source_info,
163                     destination,
164                     Constant {
165                         span: expr_span,
166                         ty: this.hir.bool_ty(),
167                         user_ty: None,
168                         literal: this.hir.false_literal(),
169                     },
170                 );
171
172                 this.cfg.terminate(
173                     true_block,
174                     source_info,
175                     TerminatorKind::Goto { target: join_block },
176                 );
177                 this.cfg.terminate(
178                     false_block,
179                     source_info,
180                     TerminatorKind::Goto { target: join_block },
181                 );
182
183                 join_block.unit()
184             }
185             ExprKind::Loop {
186                 condition: opt_cond_expr,
187                 body,
188             } => {
189                 // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
190                 //                  ^                               |
191                 //                  |                               0
192                 //                  |                               |
193                 //                  |                               v
194                 //           [body_block_end] <-/eval. body/-- [body_block]
195                 //
196                 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
197                 //
198                 // [block]
199                 //    |
200                 //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
201                 //    |        ^                                         |
202                 // false link  |                                         |
203                 //    |        +-----------------------------------------+
204                 //    +-> [diverge_cleanup]
205                 // The false link is required to make sure borrowck considers unwinds through the
206                 // body, even when the exact code in the body cannot unwind
207
208                 let loop_block = this.cfg.start_new_block();
209                 let exit_block = this.cfg.start_new_block();
210
211                 // start the loop
212                 this.cfg.terminate(
213                     block,
214                     source_info,
215                     TerminatorKind::Goto { target: loop_block },
216                 );
217
218                 this.in_breakable_scope(
219                     Some(loop_block),
220                     exit_block,
221                     destination.clone(),
222                     move |this| {
223                         // conduct the test, if necessary
224                         let body_block;
225                         if let Some(cond_expr) = opt_cond_expr {
226                             let loop_block_end;
227                             let cond = unpack!(
228                                 loop_block_end = this.as_local_operand(loop_block, cond_expr)
229                             );
230                             body_block = this.cfg.start_new_block();
231                             let term =
232                                 TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
233                             this.cfg.terminate(loop_block_end, source_info, term);
234
235                             // if the test is false, there's no `break` to assign `destination`, so
236                             // we have to do it; this overwrites any `break`-assigned value but it's
237                             // always `()` anyway
238                             this.cfg
239                                 .push_assign_unit(exit_block, source_info, destination);
240                         } else {
241                             body_block = this.cfg.start_new_block();
242                             let diverge_cleanup = this.diverge_cleanup();
243                             this.cfg.terminate(
244                                 loop_block,
245                                 source_info,
246                                 TerminatorKind::FalseUnwind {
247                                     real_target: body_block,
248                                     unwind: Some(diverge_cleanup),
249                                 },
250                             )
251                         }
252
253                         // The “return” value of the loop body must always be an unit. We therefore
254                         // introduce a unit temporary as the destination for the loop body.
255                         let tmp = this.get_unit_temp();
256                         // Execute the body, branching back to the test.
257                         let body_block_end = unpack!(this.into(&tmp, body_block, body));
258                         this.cfg.terminate(
259                             body_block_end,
260                             source_info,
261                             TerminatorKind::Goto { target: loop_block },
262                         );
263                     },
264                 );
265                 exit_block.unit()
266             }
267             ExprKind::Call { ty, fun, args, from_hir_call } => {
268                 // FIXME(canndrew): This is_never should probably be an is_uninhabited
269                 let diverges = expr.ty.is_never();
270                 let intrinsic = match ty.sty {
271                     ty::FnDef(def_id, _) => {
272                         let f = ty.fn_sig(this.hir.tcx());
273                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
274                             Some(this.hir.tcx().item_name(def_id).as_str())
275                         } else {
276                             None
277                         }
278                     }
279                     _ => None,
280                 };
281                 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
282                 let fun = unpack!(block = this.as_local_operand(block, fun));
283                 if intrinsic == Some("move_val_init") {
284                     // `move_val_init` has "magic" semantics - the second argument is
285                     // always evaluated "directly" into the first one.
286
287                     let mut args = args.into_iter();
288                     let ptr = args.next().expect("0 arguments to `move_val_init`");
289                     let val = args.next().expect("1 argument to `move_val_init`");
290                     assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
291
292                     let ptr = this.hir.mirror(ptr);
293                     let ptr_ty = ptr.ty;
294                     // Create an *internal* temp for the pointer, so that unsafety
295                     // checking won't complain about the raw pointer assignment.
296                     let ptr_temp = this.local_decls.push(LocalDecl {
297                         mutability: Mutability::Mut,
298                         ty: ptr_ty,
299                         user_ty: None,
300                         name: None,
301                         source_info,
302                         visibility_scope: source_info.scope,
303                         internal: true,
304                         is_user_variable: None,
305                     });
306                     let ptr_temp = Place::Local(ptr_temp);
307                     let block = unpack!(this.into(&ptr_temp, block, ptr));
308                     this.into(&ptr_temp.deref(), block, val)
309                 } else {
310                     let args: Vec<_> = args
311                         .into_iter()
312                         .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
313                         .collect();
314
315                     let success = this.cfg.start_new_block();
316                     let cleanup = this.diverge_cleanup();
317                     this.cfg.terminate(
318                         block,
319                         source_info,
320                         TerminatorKind::Call {
321                             func: fun,
322                             args,
323                             cleanup: Some(cleanup),
324                             destination: if diverges {
325                                 None
326                             } else {
327                                 Some((destination.clone(), success))
328                             },
329                             from_hir_call,
330                         },
331                     );
332                     success.unit()
333                 }
334             }
335
336             // These cases don't actually need a destination
337             ExprKind::Assign { .. }
338             | ExprKind::AssignOp { .. }
339             | ExprKind::Continue { .. }
340             | ExprKind::Break { .. }
341             | ExprKind::InlineAsm { .. }
342             | ExprKind::Return { .. } => {
343                 unpack!(block = this.stmt_expr(block, expr));
344                 this.cfg.push_assign_unit(block, source_info, destination);
345                 block.unit()
346             }
347
348             // Avoid creating a temporary
349             ExprKind::VarRef { .. } |
350             ExprKind::SelfRef |
351             ExprKind::StaticRef { .. } |
352             ExprKind::PlaceTypeAscription { .. } |
353             ExprKind::ValueTypeAscription { .. } => {
354                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
355
356                 let place = unpack!(block = this.as_place(block, expr));
357                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
358                 this.cfg
359                     .push_assign(block, source_info, destination, rvalue);
360                 block.unit()
361             }
362             ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
363                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
364
365                 // Create a "fake" temporary variable so that we check that the
366                 // value is Sized. Usually, this is caught in type checking, but
367                 // in the case of box expr there is no such check.
368                 if let Place::Projection(..) = destination {
369                     this.local_decls
370                         .push(LocalDecl::new_temp(expr.ty, expr.span));
371                 }
372
373                 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
374
375                 let place = unpack!(block = this.as_place(block, expr));
376                 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
377                 this.cfg
378                     .push_assign(block, source_info, destination, rvalue);
379                 block.unit()
380             }
381
382             // these are the cases that are more naturally handled by some other mode
383             ExprKind::Unary { .. }
384             | ExprKind::Binary { .. }
385             | ExprKind::Box { .. }
386             | ExprKind::Cast { .. }
387             | ExprKind::Use { .. }
388             | ExprKind::ReifyFnPointer { .. }
389             | ExprKind::ClosureFnPointer { .. }
390             | ExprKind::UnsafeFnPointer { .. }
391             | ExprKind::Unsize { .. }
392             | ExprKind::Repeat { .. }
393             | ExprKind::Borrow { .. }
394             | ExprKind::Array { .. }
395             | ExprKind::Tuple { .. }
396             | ExprKind::Adt { .. }
397             | ExprKind::Closure { .. }
398             | ExprKind::Literal { .. }
399             | ExprKind::Yield { .. } => {
400                 debug_assert!(match Category::of(&expr.kind).unwrap() {
401                     // should be handled above
402                     Category::Rvalue(RvalueFunc::Into) => false,
403
404                     // must be handled above or else we get an
405                     // infinite loop in the builder; see
406                     // e.g. `ExprKind::VarRef` above
407                     Category::Place => false,
408
409                     _ => true,
410                 });
411
412                 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
413                 this.cfg
414                     .push_assign(block, source_info, destination, rvalue);
415                 block.unit()
416             }
417         }
418     }
419 }