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