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::expr::category::{Category, RvalueFunc};
14 use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
19 use rustc_target::spec::abi::Abi;
21 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
22 /// Compile `expr`, storing the result into `destination`, which
23 /// is assumed to be uninitialized.
26 destination: &Place<'tcx>,
27 mut block: BasicBlock,
31 "into_expr(destination={:?}, block={:?}, expr={:?})",
32 destination, block, expr
35 // since we frequently have to reference `self` from within a
36 // closure, where `self` would be shadowed, it's easier to
37 // just use the name `this` uniformly
39 let expr_span = expr.span;
40 let source_info = this.source_info(expr_span);
42 let expr_is_block_or_scope = match expr.kind {
43 ExprKind::Block { .. } => true,
44 ExprKind::Scope { .. } => true,
48 if !expr_is_block_or_scope {
49 this.block_context.push(BlockFrame::SubExpr);
52 let block_and = match expr.kind {
58 let region_scope = (region_scope, source_info);
59 this.in_scope(region_scope, lint_level, block, |this| {
60 this.into(destination, block, value)
63 ExprKind::Block { body: ast_block } => {
64 this.ast_block(destination, block, ast_block, source_info)
66 ExprKind::Match { discriminant, arms } => {
67 this.match_expr(destination, expr_span, block, discriminant, arms)
69 ExprKind::NeverToAny { source } => {
70 let source = this.hir.mirror(source);
71 let is_call = match source.kind {
72 ExprKind::Call { .. } => true,
76 unpack!(block = this.as_local_rvalue(block, source));
78 // This is an optimization. If the expression was a call then we already have an
79 // unreachable block. Don't bother to terminate it and create a new one.
84 .terminate(block, source_info, TerminatorKind::Unreachable);
85 let end_block = this.cfg.start_new_block();
94 let operand = unpack!(block = this.as_local_operand(block, cond_expr));
96 let mut then_block = this.cfg.start_new_block();
97 let mut else_block = this.cfg.start_new_block();
98 let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
99 this.cfg.terminate(block, source_info, term);
101 unpack!(then_block = this.into(destination, then_block, then_expr));
102 else_block = if let Some(else_expr) = else_expr {
103 unpack!(this.into(destination, else_block, else_expr))
105 // Body of the `if` expression without an `else` clause must return `()`, thus
106 // we implicitly generate a `else {}` if it is not specified.
108 .push_assign_unit(else_block, source_info, destination);
112 let join_block = this.cfg.start_new_block();
116 TerminatorKind::Goto { target: join_block },
121 TerminatorKind::Goto { target: join_block },
126 ExprKind::LogicalOp { op, lhs, rhs } => {
129 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
131 // +----------false-----------+------------------> [false_block]
135 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
136 // | (true) | (false)
137 // [true_block] [false_block]
139 let (true_block, false_block, mut else_block, join_block) = (
140 this.cfg.start_new_block(),
141 this.cfg.start_new_block(),
142 this.cfg.start_new_block(),
143 this.cfg.start_new_block(),
146 let lhs = unpack!(block = this.as_local_operand(block, lhs));
147 let blocks = match op {
148 LogicalOp::And => (else_block, false_block),
149 LogicalOp::Or => (true_block, else_block),
151 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
152 this.cfg.terminate(block, source_info, term);
154 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
155 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
156 this.cfg.terminate(else_block, source_info, term);
158 this.cfg.push_assign_constant(
164 ty: this.hir.bool_ty(),
166 literal: this.hir.true_literal(),
170 this.cfg.push_assign_constant(
176 ty: this.hir.bool_ty(),
178 literal: this.hir.false_literal(),
185 TerminatorKind::Goto { target: join_block },
190 TerminatorKind::Goto { target: join_block },
196 condition: opt_cond_expr,
199 // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
204 // [body_block_end] <-/eval. body/-- [body_block]
206 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
210 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
213 // | +-----------------------------------------+
214 // +-> [diverge_cleanup]
215 // The false link is required to make sure borrowck considers unwinds through the
216 // body, even when the exact code in the body cannot unwind
218 let loop_block = this.cfg.start_new_block();
219 let exit_block = this.cfg.start_new_block();
225 TerminatorKind::Goto { target: loop_block },
228 this.in_breakable_scope(
233 // conduct the test, if necessary
235 if let Some(cond_expr) = opt_cond_expr {
238 loop_block_end = this.as_local_operand(loop_block, cond_expr)
240 body_block = this.cfg.start_new_block();
242 TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
243 this.cfg.terminate(loop_block_end, source_info, term);
245 // if the test is false, there's no `break` to assign `destination`, so
246 // we have to do it; this overwrites any `break`-assigned value but it's
247 // always `()` anyway
249 .push_assign_unit(exit_block, source_info, destination);
251 body_block = this.cfg.start_new_block();
252 let diverge_cleanup = this.diverge_cleanup();
256 TerminatorKind::FalseUnwind {
257 real_target: body_block,
258 unwind: Some(diverge_cleanup),
263 // The “return” value of the loop body must always be an unit. We therefore
264 // introduce a unit temporary as the destination for the loop body.
265 let tmp = this.get_unit_temp();
266 // Execute the body, branching back to the test.
267 let body_block_end = unpack!(this.into(&tmp, body_block, body));
271 TerminatorKind::Goto { target: loop_block },
277 ExprKind::Call { ty, fun, args, from_hir_call } => {
278 // FIXME(canndrew): This is_never should probably be an is_uninhabited
279 let diverges = expr.ty.is_never();
280 let intrinsic = match ty.sty {
281 ty::FnDef(def_id, _) => {
282 let f = ty.fn_sig(this.hir.tcx());
283 if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
284 Some(this.hir.tcx().item_name(def_id).as_str())
291 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
292 let fun = unpack!(block = this.as_local_operand(block, fun));
293 if intrinsic == Some("move_val_init") {
294 // `move_val_init` has "magic" semantics - the second argument is
295 // always evaluated "directly" into the first one.
297 let mut args = args.into_iter();
298 let ptr = args.next().expect("0 arguments to `move_val_init`");
299 let val = args.next().expect("1 argument to `move_val_init`");
300 assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
302 let ptr = this.hir.mirror(ptr);
304 // Create an *internal* temp for the pointer, so that unsafety
305 // checking won't complain about the raw pointer assignment.
306 let ptr_temp = this.local_decls.push(LocalDecl {
307 mutability: Mutability::Mut,
309 user_ty: UserTypeProjections::none(),
312 visibility_scope: source_info.scope,
314 is_user_variable: None,
317 let ptr_temp = Place::Local(ptr_temp);
318 let block = unpack!(this.into(&ptr_temp, block, ptr));
319 this.into(&ptr_temp.deref(), block, val)
321 let args: Vec<_> = args
323 .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
326 let success = this.cfg.start_new_block();
327 let cleanup = this.diverge_cleanup();
331 TerminatorKind::Call {
334 cleanup: Some(cleanup),
335 destination: if diverges {
338 Some((destination.clone(), success))
347 // These cases don't actually need a destination
348 ExprKind::Assign { .. }
349 | ExprKind::AssignOp { .. }
350 | ExprKind::Continue { .. }
351 | ExprKind::Break { .. }
352 | ExprKind::InlineAsm { .. }
353 | ExprKind::Return { .. } => {
354 unpack!(block = this.stmt_expr(block, expr, None));
355 this.cfg.push_assign_unit(block, source_info, destination);
359 // Avoid creating a temporary
360 ExprKind::VarRef { .. } |
362 ExprKind::StaticRef { .. } |
363 ExprKind::PlaceTypeAscription { .. } |
364 ExprKind::ValueTypeAscription { .. } => {
365 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
367 let place = unpack!(block = this.as_place(block, expr));
368 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
370 .push_assign(block, source_info, destination, rvalue);
373 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
374 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
376 // Create a "fake" temporary variable so that we check that the
377 // value is Sized. Usually, this is caught in type checking, but
378 // in the case of box expr there is no such check.
379 if let Place::Projection(..) = destination {
381 .push(LocalDecl::new_temp(expr.ty, expr.span));
384 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
386 let place = unpack!(block = this.as_place(block, expr));
387 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
389 .push_assign(block, source_info, destination, rvalue);
393 // these are the cases that are more naturally handled by some other mode
394 ExprKind::Unary { .. }
395 | ExprKind::Binary { .. }
396 | ExprKind::Box { .. }
397 | ExprKind::Cast { .. }
398 | ExprKind::Use { .. }
399 | ExprKind::ReifyFnPointer { .. }
400 | ExprKind::ClosureFnPointer { .. }
401 | ExprKind::UnsafeFnPointer { .. }
402 | ExprKind::Unsize { .. }
403 | ExprKind::Repeat { .. }
404 | ExprKind::Borrow { .. }
405 | ExprKind::Array { .. }
406 | ExprKind::Tuple { .. }
407 | ExprKind::Adt { .. }
408 | ExprKind::Closure { .. }
409 | ExprKind::Literal { .. }
410 | ExprKind::Yield { .. } => {
411 debug_assert!(match Category::of(&expr.kind).unwrap() {
412 // should be handled above
413 Category::Rvalue(RvalueFunc::Into) => false,
415 // must be handled above or else we get an
416 // infinite loop in the builder; see
417 // e.g. `ExprKind::VarRef` above
418 Category::Place => false,
423 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
425 .push_assign(block, source_info, destination, rvalue);
430 if !expr_is_block_or_scope {
431 let popped = this.block_context.pop();
432 assert!(popped.is_some());