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;
41 ExprKind::Scope { extent, value } => {
42 this.in_scope(extent, block, |this| this.into(destination, block, value))
44 ExprKind::Block { body: ast_block } => {
45 this.ast_block(destination, block, ast_block)
47 ExprKind::Match { discriminant, arms } => {
48 this.match_expr(destination, expr_span, block, discriminant, arms)
50 ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
51 let operand = unpack!(block = this.as_operand(block, cond_expr));
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 {
57 targets: (then_block, else_block)
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))
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);
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 });
76 ExprKind::LogicalOp { op, lhs, rhs } => {
79 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
81 // +----------false-----------+------------------> [false_block]
85 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
87 // +----------true------------+-------------------> [false_block]
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());
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),
98 this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
100 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
101 this.cfg.terminate(else_block, Terminator::If {
103 targets: (true_block, false_block)
106 this.cfg.push_assign_constant(
107 true_block, expr_span, destination,
110 ty: this.hir.bool_ty(),
111 literal: this.hir.true_literal(),
114 this.cfg.push_assign_constant(
115 false_block, expr_span, destination,
118 ty: this.hir.bool_ty(),
119 literal: this.hir.false_literal(),
122 this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
123 this.cfg.terminate(false_block, Terminator::Goto { target: join_block });
127 ExprKind::Loop { condition: opt_cond_expr, body } => {
128 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
133 // [body_block_end] <~~~ [body_block]
135 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
137 // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
140 // +--------------------------+
143 let loop_block = this.cfg.start_new_block();
144 let exit_block = this.cfg.start_new_block();
147 this.cfg.terminate(block, Terminator::Goto { target: loop_block });
149 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
150 // conduct the test, if necessary
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;
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,
162 targets: (body_block, exit_block)
165 body_block = loop_block;
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 });
178 // If the loop may reach its exit_block, we assign an empty tuple to the
179 // destination to keep the MIR well-formed.
181 this.cfg.push_assign_unit(exit_block, expr_span, destination);
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));
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)`?
205 let rhs = unpack!(block = this.as_operand(block, rhs));
206 let lhs = unpack!(block = this.as_lvalue(block, lhs));
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,
213 Operand::Consume(lhs.clone()),
218 ExprKind::Continue { label } => {
219 this.break_or_continue(expr_span, label, block,
220 |loop_scope| loop_scope.continue_block)
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
228 ExprKind::Return { value } => {
229 block = match value {
230 Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
232 this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
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()
240 ExprKind::Call { ty, fun, args } => {
241 let diverges = match ty.sty {
242 ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(),
245 let fun = unpack!(block = this.as_operand(block, fun));
248 .map(|arg| unpack!(block = this.as_operand(block, arg)))
251 let success = this.cfg.start_new_block();
252 let cleanup = this.diverge_cleanup();
253 this.cfg.terminate(block, Terminator::Call {
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(),
263 (Some(c), false) => CallKind::ConvergingCleanup {
264 destination: destination.clone(),
265 targets: (success, c)
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 { .. } |
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,
299 let rvalue = unpack!(block = this.as_rvalue(block, expr));
300 this.cfg.push_assign(block, expr_span, destination, rvalue);
306 fn break_or_continue<F>(&mut self,
308 label: Option<CodeExtent>,
312 where F: FnOnce(&mut LoopScope) -> BasicBlock
314 let (exit_block, extent) = {
315 let loop_scope = self.find_loop_scope(span, label);
316 (exit_selector(loop_scope), loop_scope.extent)
318 self.exit_scope(span, extent, block, exit_block);
319 self.cfg.start_new_block().unit()