]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/into.rs
[MIR] Reintroduce the unit temporary
[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::{BlockAnd, BlockAndExtension, Builder};
14 use build::expr::category::{Category, RvalueFunc};
15 use build::scope::LoopScope;
16 use hair::*;
17 use rustc::middle::region::CodeExtent;
18 use rustc::middle::ty;
19 use rustc::mir::repr::*;
20 use syntax::codemap::Span;
21
22 impl<'a,'tcx> Builder<'a,'tcx> {
23     /// Compile `expr`, storing the result into `destination`, which
24     /// is assumed to be uninitialized.
25     pub fn into_expr(&mut self,
26                      destination: &Lvalue<'tcx>,
27                      mut block: BasicBlock,
28                      expr: Expr<'tcx>)
29                      -> BlockAnd<()>
30     {
31         debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
32                destination, block, expr);
33
34         // since we frequently have to reference `self` from within a
35         // closure, where `self` would be shadowed, it's easier to
36         // just use the name `this` uniformly
37         let this = self;
38         let expr_span = expr.span;
39
40         match expr.kind {
41             ExprKind::Scope { extent, value } => {
42                 this.in_scope(extent, block, |this| this.into(destination, block, value))
43             }
44             ExprKind::Block { body: ast_block } => {
45                 this.ast_block(destination, block, ast_block)
46             }
47             ExprKind::Match { discriminant, arms } => {
48                 this.match_expr(destination, expr_span, block, discriminant, arms)
49             }
50             ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
51                 let operand = unpack!(block = this.as_operand(block, cond_expr));
52
53                 let mut then_block = this.cfg.start_new_block();
54                 let mut else_block = this.cfg.start_new_block();
55                 this.cfg.terminate(block, Terminator::If {
56                     cond: operand,
57                     targets: (then_block, else_block)
58                 });
59
60                 unpack!(then_block = this.into(destination, then_block, then_expr));
61                 else_block = if let Some(else_expr) = else_expr {
62                     unpack!(this.into(destination, else_block, else_expr))
63                 } else {
64                     // Body of the `if` expression without an `else` clause must return `()`, thus
65                     // we implicitly generate a `else {}` if it is not specified.
66                     this.cfg.push_assign_unit(else_block, expr_span, &Lvalue::ReturnPointer);
67                     else_block
68                 };
69
70                 let join_block = this.cfg.start_new_block();
71                 this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
72                 this.cfg.terminate(else_block, Terminator::Goto { target: join_block });
73
74                 join_block.unit()
75             }
76             ExprKind::LogicalOp { op, lhs, rhs } => {
77                 // And:
78                 //
79                 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
80                 //        |                          | (false)
81                 //        +----------false-----------+------------------> [false_block]
82                 //
83                 // Or:
84                 //
85                 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
86                 //        |                          | (false)
87                 //        +----------true------------+-------------------> [false_block]
88
89                 let (true_block, false_block, mut else_block, join_block) =
90                     (this.cfg.start_new_block(), this.cfg.start_new_block(),
91                      this.cfg.start_new_block(), this.cfg.start_new_block());
92
93                 let lhs = unpack!(block = this.as_operand(block, lhs));
94                 let blocks = match op {
95                     LogicalOp::And => (else_block, false_block),
96                     LogicalOp::Or => (true_block, else_block),
97                 };
98                 this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
99
100                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
101                 this.cfg.terminate(else_block, Terminator::If {
102                     cond: rhs,
103                     targets: (true_block, false_block)
104                 });
105
106                 this.cfg.push_assign_constant(
107                     true_block, expr_span, destination,
108                     Constant {
109                         span: expr_span,
110                         ty: this.hir.bool_ty(),
111                         literal: this.hir.true_literal(),
112                     });
113
114                 this.cfg.push_assign_constant(
115                     false_block, expr_span, destination,
116                     Constant {
117                         span: expr_span,
118                         ty: this.hir.bool_ty(),
119                         literal: this.hir.false_literal(),
120                     });
121
122                 this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
123                 this.cfg.terminate(false_block, Terminator::Goto { target: join_block });
124
125                 join_block.unit()
126             }
127             ExprKind::Loop { condition: opt_cond_expr, body } => {
128                 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
129                 //                  ^                  |
130                 //                  |                  0
131                 //                  |                  |
132                 //                  |                  v
133                 //           [body_block_end] <~~~ [body_block]
134                 //
135                 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
136                 //
137                 // [block] --> [loop_block / body_block ] ~~> [body_block_end]    [exit_block]
138                 //                         ^                          |
139                 //                         |                          |
140                 //                         +--------------------------+
141                 //
142
143                 let loop_block = this.cfg.start_new_block();
144                 let exit_block = this.cfg.start_new_block();
145
146                 // start the loop
147                 this.cfg.terminate(block, Terminator::Goto { target: loop_block });
148
149                 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
150                     // conduct the test, if necessary
151                     let body_block;
152                     if let Some(cond_expr) = opt_cond_expr {
153                         // This loop has a condition, ergo its exit_block is reachable.
154                         this.find_loop_scope(expr_span, None).might_break = true;
155
156                         let loop_block_end;
157                         let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
158                         body_block = this.cfg.start_new_block();
159                         this.cfg.terminate(loop_block_end,
160                                            Terminator::If {
161                                                cond: cond,
162                                                targets: (body_block, exit_block)
163                                            });
164                     } else {
165                         body_block = loop_block;
166                     }
167
168                     // The “return” value of the loop body must always be an unit, but we cannot
169                     // reuse that as a “return” value of the whole loop expressions, because some
170                     // loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
171                     // the destination for the loop body and assign the loop’s own “return” value
172                     // immediately after the iteration is finished.
173                     let tmp = this.get_unit_temp();
174                     // Execute the body, branching back to the test.
175                     let body_block_end = unpack!(this.into(&tmp, body_block, body));
176                     this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
177                 });
178                 // If the loop may reach its exit_block, we assign an empty tuple to the
179                 // destination to keep the MIR well-formed.
180                 if might_break {
181                     this.cfg.push_assign_unit(exit_block, expr_span, destination);
182                 }
183                 exit_block.unit()
184             }
185             ExprKind::Assign { lhs, rhs } => {
186                 // Note: we evaluate assignments right-to-left. This
187                 // is better for borrowck interaction with overloaded
188                 // operators like x[j] = x[i].
189                 let rhs = unpack!(block = this.as_operand(block, rhs));
190                 let lhs = unpack!(block = this.as_lvalue(block, lhs));
191                 this.cfg.push_drop(block, expr_span, DropKind::Deep, &lhs);
192                 this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
193                 block.unit()
194             }
195             ExprKind::AssignOp { op, lhs, rhs } => {
196                 // FIXME(#28160) there is an interesting semantics
197                 // question raised here -- should we "freeze" the
198                 // value of the lhs here?  I'm inclined to think not,
199                 // since it seems closer to the semantics of the
200                 // overloaded version, which takes `&mut self`.  This
201                 // only affects weird things like `x += {x += 1; x}`
202                 // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
203
204                 // As above, RTL.
205                 let rhs = unpack!(block = this.as_operand(block, rhs));
206                 let lhs = unpack!(block = this.as_lvalue(block, lhs));
207
208                 // we don't have to drop prior contents or anything
209                 // because AssignOp is only legal for Copy types
210                 // (overloaded ops should be desugared into a call).
211                 this.cfg.push_assign(block, expr_span, &lhs,
212                                      Rvalue::BinaryOp(op,
213                                                       Operand::Consume(lhs.clone()),
214                                                       rhs));
215
216                 block.unit()
217             }
218             ExprKind::Continue { label } => {
219                 this.break_or_continue(expr_span, label, block,
220                                        |loop_scope| loop_scope.continue_block)
221             }
222             ExprKind::Break { label } => {
223                 this.break_or_continue(expr_span, label, block, |loop_scope| {
224                     loop_scope.might_break = true;
225                     loop_scope.break_block
226                 })
227             }
228             ExprKind::Return { value } => {
229                 block = match value {
230                     Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
231                     None => {
232                         this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
233                         block
234                     }
235                 };
236                 let extent = this.extent_of_outermost_scope();
237                 this.exit_scope(expr_span, extent, block, END_BLOCK);
238                 this.cfg.start_new_block().unit()
239             }
240             ExprKind::Call { ty, fun, args } => {
241                 let diverges = match ty.sty {
242                     ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(),
243                     _ => false
244                 };
245                 let fun = unpack!(block = this.as_operand(block, fun));
246                 let args: Vec<_> =
247                     args.into_iter()
248                         .map(|arg| unpack!(block = this.as_operand(block, arg)))
249                         .collect();
250
251                 let success = this.cfg.start_new_block();
252                 let cleanup = this.diverge_cleanup();
253                 this.cfg.terminate(block, Terminator::Call {
254                     func: fun,
255                     args: args,
256                     kind: match (cleanup, diverges) {
257                         (None, true) => CallKind::Diverging,
258                         (Some(c), true) => CallKind::DivergingCleanup(c),
259                         (None, false) => CallKind::Converging {
260                             destination: destination.clone(),
261                             target: success
262                         },
263                         (Some(c), false) => CallKind::ConvergingCleanup {
264                             destination: destination.clone(),
265                             targets: (success, c)
266                         }
267                     }
268                 });
269                 success.unit()
270             }
271
272             // these are the cases that are more naturally handled by some other mode
273             ExprKind::Unary { .. } |
274             ExprKind::Binary { .. } |
275             ExprKind::Box { .. } |
276             ExprKind::Cast { .. } |
277             ExprKind::ReifyFnPointer { .. } |
278             ExprKind::UnsafeFnPointer { .. } |
279             ExprKind::Unsize { .. } |
280             ExprKind::Repeat { .. } |
281             ExprKind::Borrow { .. } |
282             ExprKind::VarRef { .. } |
283             ExprKind::SelfRef |
284             ExprKind::StaticRef { .. } |
285             ExprKind::Vec { .. } |
286             ExprKind::Tuple { .. } |
287             ExprKind::Adt { .. } |
288             ExprKind::Closure { .. } |
289             ExprKind::Index { .. } |
290             ExprKind::Deref { .. } |
291             ExprKind::Literal { .. } |
292             ExprKind::InlineAsm { .. } |
293             ExprKind::Field { .. } => {
294                 debug_assert!(match Category::of(&expr.kind).unwrap() {
295                     Category::Rvalue(RvalueFunc::Into) => false,
296                     _ => true,
297                 });
298
299                 let rvalue = unpack!(block = this.as_rvalue(block, expr));
300                 this.cfg.push_assign(block, expr_span, destination, rvalue);
301                 block.unit()
302             }
303         }
304     }
305
306     fn break_or_continue<F>(&mut self,
307                             span: Span,
308                             label: Option<CodeExtent>,
309                             block: BasicBlock,
310                             exit_selector: F)
311                             -> BlockAnd<()>
312         where F: FnOnce(&mut LoopScope) -> BasicBlock
313     {
314         let (exit_block, extent) = {
315             let loop_scope = self.find_loop_scope(span, label);
316             (exit_selector(loop_scope), loop_scope.extent)
317         };
318         self.exit_scope(span, extent, block, exit_block);
319         self.cfg.start_new_block().unit()
320     }
321 }