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};
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(&mut self,
25 destination: &Place<'tcx>,
26 mut block: BasicBlock,
30 debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
31 destination, block, expr);
33 // since we frequently have to reference `self` from within a
34 // closure, where `self` would be shadowed, it's easier to
35 // just use the name `this` uniformly
37 let expr_span = expr.span;
38 let source_info = this.source_info(expr_span);
41 ExprKind::Scope { region_scope, lint_level, value } => {
42 let region_scope = (region_scope, source_info);
43 this.in_scope(region_scope, lint_level, block,
44 |this| this.into(destination, block, value))
46 ExprKind::Block { body: ast_block } => {
47 this.ast_block(destination, block, ast_block, source_info)
49 ExprKind::Match { discriminant, arms } => {
50 this.match_expr(destination, expr_span, block, discriminant, arms)
52 ExprKind::NeverToAny { source } => {
53 let source = this.hir.mirror(source);
54 let is_call = match source.kind {
55 ExprKind::Call { .. } => true,
59 unpack!(block = this.as_local_rvalue(block, source));
61 // This is an optimization. If the expression was a call then we already have an
62 // unreachable block. Don't bother to terminate it and create a new one.
66 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
67 let end_block = this.cfg.start_new_block();
71 ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
72 let operand = unpack!(block = this.as_local_operand(block, cond_expr));
74 let mut then_block = this.cfg.start_new_block();
75 let mut else_block = this.cfg.start_new_block();
76 let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
77 this.cfg.terminate(block, source_info, term);
79 unpack!(then_block = this.into(destination, then_block, then_expr));
80 else_block = if let Some(else_expr) = else_expr {
81 unpack!(this.into(destination, else_block, else_expr))
83 // Body of the `if` expression without an `else` clause must return `()`, thus
84 // we implicitly generate a `else {}` if it is not specified.
85 this.cfg.push_assign_unit(else_block, source_info, destination);
89 let join_block = this.cfg.start_new_block();
90 this.cfg.terminate(then_block, source_info,
91 TerminatorKind::Goto { target: join_block });
92 this.cfg.terminate(else_block, source_info,
93 TerminatorKind::Goto { target: join_block });
97 ExprKind::LogicalOp { op, lhs, rhs } => {
100 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
102 // +----------false-----------+------------------> [false_block]
106 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
108 // +----------true------------+-------------------> [false_block]
110 let (true_block, false_block, mut else_block, join_block) =
111 (this.cfg.start_new_block(), this.cfg.start_new_block(),
112 this.cfg.start_new_block(), this.cfg.start_new_block());
114 let lhs = unpack!(block = this.as_local_operand(block, lhs));
115 let blocks = match op {
116 LogicalOp::And => (else_block, false_block),
117 LogicalOp::Or => (true_block, else_block),
119 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
120 this.cfg.terminate(block, source_info, term);
122 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
123 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
124 this.cfg.terminate(else_block, source_info, term);
126 this.cfg.push_assign_constant(
127 true_block, source_info, destination,
130 ty: this.hir.bool_ty(),
131 literal: this.hir.true_literal(),
134 this.cfg.push_assign_constant(
135 false_block, source_info, destination,
138 ty: this.hir.bool_ty(),
139 literal: this.hir.false_literal(),
142 this.cfg.terminate(true_block, source_info,
143 TerminatorKind::Goto { target: join_block });
144 this.cfg.terminate(false_block, source_info,
145 TerminatorKind::Goto { target: join_block });
149 ExprKind::Loop { condition: opt_cond_expr, body } => {
150 // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
155 // [body_block_end] <~~~ [body_block]
157 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
159 // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
162 // +--------------------------+
165 let loop_block = this.cfg.start_new_block();
166 let exit_block = this.cfg.start_new_block();
169 this.cfg.terminate(block, source_info,
170 TerminatorKind::Goto { target: loop_block });
172 this.in_breakable_scope(
173 Some(loop_block), exit_block, destination.clone(),
175 // conduct the test, if necessary
177 if let Some(cond_expr) = opt_cond_expr {
180 loop_block_end = this.as_local_operand(loop_block, cond_expr));
181 body_block = this.cfg.start_new_block();
182 let term = TerminatorKind::if_(this.hir.tcx(), cond,
183 body_block, exit_block);
184 this.cfg.terminate(loop_block_end, source_info, term);
186 // if the test is false, there's no `break` to assign `destination`, so
187 // we have to do it; this overwrites any `break`-assigned value but it's
188 // always `()` anyway
189 this.cfg.push_assign_unit(exit_block, source_info, destination);
191 body_block = loop_block;
194 // The “return” value of the loop body must always be an unit. We therefore
195 // introduce a unit temporary as the destination for the loop body.
196 let tmp = this.get_unit_temp();
197 // Execute the body, branching back to the test.
198 let body_block_end = unpack!(this.into(&tmp, body_block, body));
199 this.cfg.terminate(body_block_end, source_info,
200 TerminatorKind::Goto { target: loop_block });
205 ExprKind::Call { ty, fun, args } => {
206 // FIXME(canndrew): This is_never should probably be an is_uninhabited
207 let diverges = expr.ty.is_never();
208 let intrinsic = match ty.sty {
209 ty::TyFnDef(def_id, _) => {
210 let f = ty.fn_sig(this.hir.tcx());
211 if f.abi() == Abi::RustIntrinsic ||
212 f.abi() == Abi::PlatformIntrinsic {
213 Some(this.hir.tcx().item_name(def_id))
220 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
221 let fun = unpack!(block = this.as_local_operand(block, fun));
222 if intrinsic == Some("move_val_init") {
223 // `move_val_init` has "magic" semantics - the second argument is
224 // always evaluated "directly" into the first one.
226 let mut args = args.into_iter();
227 let ptr = args.next().expect("0 arguments to `move_val_init`");
228 let val = args.next().expect("1 argument to `move_val_init`");
229 assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
231 let ptr = this.hir.mirror(ptr);
233 // Create an *internal* temp for the pointer, so that unsafety
234 // checking won't complain about the raw pointer assignment.
235 let ptr_temp = this.local_decls.push(LocalDecl {
236 mutability: Mutability::Mut,
240 lexical_scope: source_info.scope,
242 is_user_variable: false
244 let ptr_temp = Place::Local(ptr_temp);
245 let block = unpack!(this.into(&ptr_temp, block, ptr));
246 this.into(&ptr_temp.deref(), block, val)
250 .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
253 let success = this.cfg.start_new_block();
254 let cleanup = this.diverge_cleanup();
255 this.cfg.terminate(block, source_info, TerminatorKind::Call {
259 destination: if diverges {
262 Some ((destination.clone(), success))
269 // These cases don't actually need a destination
270 ExprKind::Assign { .. } |
271 ExprKind::AssignOp { .. } |
272 ExprKind::Continue { .. } |
273 ExprKind::Break { .. } |
274 ExprKind::InlineAsm { .. } |
275 ExprKind::Return {.. } => {
276 this.stmt_expr(block, expr)
279 // these are the cases that are more naturally handled by some other mode
280 ExprKind::Unary { .. } |
281 ExprKind::Binary { .. } |
282 ExprKind::Box { .. } |
283 ExprKind::Cast { .. } |
284 ExprKind::Use { .. } |
285 ExprKind::ReifyFnPointer { .. } |
286 ExprKind::ClosureFnPointer { .. } |
287 ExprKind::UnsafeFnPointer { .. } |
288 ExprKind::Unsize { .. } |
289 ExprKind::Repeat { .. } |
290 ExprKind::Borrow { .. } |
291 ExprKind::VarRef { .. } |
293 ExprKind::StaticRef { .. } |
294 ExprKind::Array { .. } |
295 ExprKind::Tuple { .. } |
296 ExprKind::Adt { .. } |
297 ExprKind::Closure { .. } |
298 ExprKind::Index { .. } |
299 ExprKind::Deref { .. } |
300 ExprKind::Literal { .. } |
301 ExprKind::Yield { .. } |
302 ExprKind::Field { .. } => {
303 debug_assert!(match Category::of(&expr.kind).unwrap() {
304 Category::Rvalue(RvalueFunc::Into) => false,
308 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
309 this.cfg.push_assign(block, source_info, destination, rvalue);