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.
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.
11 //! See docs in build/expr/mod.rs
13 use build::{BlockAnd, BlockAndExtension, Builder};
14 use build::expr::category::{Category, RvalueFunc};
15 use build::scope::LoopScope;
17 use rustc::middle::region::CodeExtent;
18 use rustc::middle::ty;
19 use rustc::mir::repr::*;
20 use syntax::codemap::Span;
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,
31 debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
32 destination, block, expr);
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
38 let expr_span = expr.span;
39 let scope_id = this.innermost_scope_id();
42 ExprKind::Scope { extent, value } => {
43 this.in_scope(extent, block, |this, _| this.into(destination, block, value))
45 ExprKind::Block { body: ast_block } => {
46 this.ast_block(destination, block, ast_block)
48 ExprKind::Match { discriminant, arms } => {
49 this.match_expr(destination, expr_span, block, discriminant, arms)
51 ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
52 let operand = unpack!(block = this.as_operand(block, cond_expr));
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 {
58 targets: (then_block, else_block)
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))
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);
72 let join_block = this.cfg.start_new_block();
73 this.cfg.terminate(then_block,
76 TerminatorKind::Goto { target: join_block });
77 this.cfg.terminate(else_block,
80 TerminatorKind::Goto { target: join_block });
84 ExprKind::LogicalOp { op, lhs, rhs } => {
87 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
89 // +----------false-----------+------------------> [false_block]
93 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
95 // +----------true------------+-------------------> [false_block]
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());
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),
106 this.cfg.terminate(block,
109 TerminatorKind::If { cond: lhs, targets: blocks });
111 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
112 this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
114 targets: (true_block, false_block)
117 this.cfg.push_assign_constant(
118 true_block, scope_id, expr_span, destination,
121 ty: this.hir.bool_ty(),
122 literal: this.hir.true_literal(),
125 this.cfg.push_assign_constant(
126 false_block, scope_id, expr_span, destination,
129 ty: this.hir.bool_ty(),
130 literal: this.hir.false_literal(),
133 this.cfg.terminate(true_block,
136 TerminatorKind::Goto { target: join_block });
137 this.cfg.terminate(false_block,
140 TerminatorKind::Goto { target: join_block });
144 ExprKind::Loop { condition: opt_cond_expr, body } => {
145 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
150 // [body_block_end] <~~~ [body_block]
152 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
154 // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
157 // +--------------------------+
160 let loop_block = this.cfg.start_new_block();
161 let exit_block = this.cfg.start_new_block();
164 this.cfg.terminate(block,
167 TerminatorKind::Goto { target: loop_block });
169 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
170 // conduct the test, if necessary
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;
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,
184 targets: (body_block, exit_block)
187 body_block = loop_block;
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,
201 TerminatorKind::Goto { target: loop_block });
203 // If the loop may reach its exit_block, we assign an empty tuple to the
204 // destination to keep the MIR well-formed.
206 this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination);
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));
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)`?
232 let rhs = unpack!(block = this.as_operand(block, rhs));
233 let lhs = unpack!(block = this.as_lvalue(block, lhs));
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,
240 Operand::Consume(lhs.clone()),
245 ExprKind::Continue { label } => {
246 this.break_or_continue(expr_span, label, block,
247 |loop_scope| loop_scope.continue_block)
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
255 ExprKind::Return { value } => {
256 block = match value {
257 Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
259 this.cfg.push_assign_unit(block, scope_id,
260 expr_span, &Lvalue::ReturnPointer);
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()
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()
275 let fun = unpack!(block = this.as_operand(block, fun));
278 .map(|arg| unpack!(block = this.as_operand(block, arg)))
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 {
287 destination: if diverges {
290 Some ((destination.clone(), success))
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 { .. } |
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,
323 let rvalue = unpack!(block = this.as_rvalue(block, expr));
324 this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue);
330 fn break_or_continue<F>(&mut self,
332 label: Option<CodeExtent>,
336 where F: FnOnce(&mut LoopScope) -> BasicBlock
338 let (exit_block, extent) = {
339 let loop_scope = self.find_loop_scope(span, label);
340 (exit_selector(loop_scope), loop_scope.extent)
342 self.exit_scope(span, extent, block, exit_block);
343 self.cfg.start_new_block().unit()