1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
9 use rustc_target::spec::abi::Abi;
11 impl<'a, 'tcx> Builder<'a, 'tcx> {
12 /// Compile `expr`, storing the result into `destination`, which
13 /// is assumed to be uninitialized.
16 destination: &Place<'tcx>,
17 mut block: BasicBlock,
21 "into_expr(destination={:?}, block={:?}, expr={:?})",
22 destination, block, expr
25 // since we frequently have to reference `self` from within a
26 // closure, where `self` would be shadowed, it's easier to
27 // just use the name `this` uniformly
29 let expr_span = expr.span;
30 let source_info = this.source_info(expr_span);
32 let expr_is_block_or_scope = match expr.kind {
33 ExprKind::Block { .. } => true,
34 ExprKind::Scope { .. } => true,
38 if !expr_is_block_or_scope {
39 this.block_context.push(BlockFrame::SubExpr);
42 let block_and = match expr.kind {
48 let region_scope = (region_scope, source_info);
49 this.in_scope(region_scope, lint_level, |this| {
50 this.into(destination, block, value)
53 ExprKind::Block { body: ast_block } => {
54 this.ast_block(destination, block, ast_block, source_info)
56 ExprKind::Match { scrutinee, arms } => {
57 this.match_expr(destination, expr_span, block, scrutinee, arms)
59 ExprKind::NeverToAny { source } => {
60 let source = this.hir.mirror(source);
61 let is_call = match source.kind {
62 ExprKind::Call { .. } => true,
66 unpack!(block = this.as_local_rvalue(block, source));
68 // This is an optimization. If the expression was a call then we already have an
69 // unreachable block. Don't bother to terminate it and create a new one.
74 .terminate(block, source_info, TerminatorKind::Unreachable);
75 let end_block = this.cfg.start_new_block();
79 ExprKind::LogicalOp { op, lhs, rhs } => {
82 // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
84 // [shortcurcuit_block: dest = false]
88 // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
90 // [shortcurcuit_block: dest = true]
92 let (shortcircuit_block, mut else_block, join_block) = (
93 this.cfg.start_new_block(),
94 this.cfg.start_new_block(),
95 this.cfg.start_new_block(),
98 let lhs = unpack!(block = this.as_local_operand(block, lhs));
99 let blocks = match op {
100 LogicalOp::And => (else_block, shortcircuit_block),
101 LogicalOp::Or => (shortcircuit_block, else_block),
103 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
104 this.cfg.terminate(block, source_info, term);
106 this.cfg.push_assign_constant(
112 ty: this.hir.bool_ty(),
115 LogicalOp::And => this.hir.false_literal(),
116 LogicalOp::Or => this.hir.true_literal(),
123 TerminatorKind::Goto { target: join_block },
126 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
127 this.cfg.push_assign(
136 TerminatorKind::Goto { target: join_block },
142 condition: opt_cond_expr,
145 // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
150 // [body_block_end] <-/eval. body/-- [body_block]
152 // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
156 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
159 // | +-----------------------------------------+
160 // +-> [diverge_cleanup]
161 // The false link is required to make sure borrowck considers unwinds through the
162 // body, even when the exact code in the body cannot unwind
164 let loop_block = this.cfg.start_new_block();
165 let exit_block = this.cfg.start_new_block();
171 TerminatorKind::Goto { target: loop_block },
174 this.in_breakable_scope(
179 // conduct the test, if necessary
181 if let Some(cond_expr) = opt_cond_expr {
184 loop_block_end = this.as_local_operand(loop_block, cond_expr)
186 body_block = this.cfg.start_new_block();
188 TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
189 this.cfg.terminate(loop_block_end, source_info, term);
191 // if the test is false, there's no `break` to assign `destination`, so
192 // we have to do it; this overwrites any `break`-assigned value but it's
193 // always `()` anyway
195 .push_assign_unit(exit_block, source_info, destination);
197 body_block = this.cfg.start_new_block();
198 let diverge_cleanup = this.diverge_cleanup();
202 TerminatorKind::FalseUnwind {
203 real_target: body_block,
204 unwind: Some(diverge_cleanup),
209 // The “return” value of the loop body must always be an unit. We therefore
210 // introduce a unit temporary as the destination for the loop body.
211 let tmp = this.get_unit_temp();
212 // Execute the body, branching back to the test.
213 let body_block_end = unpack!(this.into(&tmp, body_block, body));
217 TerminatorKind::Goto { target: loop_block },
223 ExprKind::Call { ty, fun, args, from_hir_call } => {
224 let intrinsic = match ty.sty {
225 ty::FnDef(def_id, _) => {
226 let f = ty.fn_sig(this.hir.tcx());
227 if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
228 Some(this.hir.tcx().item_name(def_id).as_str())
235 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
236 let fun = unpack!(block = this.as_local_operand(block, fun));
237 if intrinsic == Some("move_val_init") {
238 // `move_val_init` has "magic" semantics - the second argument is
239 // always evaluated "directly" into the first one.
241 let mut args = args.into_iter();
242 let ptr = args.next().expect("0 arguments to `move_val_init`");
243 let val = args.next().expect("1 argument to `move_val_init`");
244 assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
246 let ptr = this.hir.mirror(ptr);
248 // Create an *internal* temp for the pointer, so that unsafety
249 // checking won't complain about the raw pointer assignment.
250 let ptr_temp = this.local_decls.push(LocalDecl {
251 mutability: Mutability::Mut,
253 user_ty: UserTypeProjections::none(),
256 visibility_scope: source_info.scope,
258 is_user_variable: None,
261 let ptr_temp = Place::from(ptr_temp);
262 let block = unpack!(this.into(&ptr_temp, block, ptr));
263 this.into(&ptr_temp.deref(), block, val)
265 let args: Vec<_> = args
267 .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
270 let success = this.cfg.start_new_block();
271 let cleanup = this.diverge_cleanup();
275 TerminatorKind::Call {
278 cleanup: Some(cleanup),
279 // FIXME(varkor): replace this with an uninhabitedness-based check.
280 // This requires getting access to the current module to call
281 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
282 destination: if expr.ty.is_never() {
285 Some((destination.clone(), success))
293 ExprKind::Use { source } => {
294 this.into(destination, block, source)
297 // These cases don't actually need a destination
298 ExprKind::Assign { .. }
299 | ExprKind::AssignOp { .. }
300 | ExprKind::Continue { .. }
301 | ExprKind::Break { .. }
302 | ExprKind::InlineAsm { .. }
303 | ExprKind::Return { .. } => {
304 unpack!(block = this.stmt_expr(block, expr, None));
305 this.cfg.push_assign_unit(block, source_info, destination);
309 // Avoid creating a temporary
310 ExprKind::VarRef { .. } |
312 ExprKind::StaticRef { .. } |
313 ExprKind::PlaceTypeAscription { .. } |
314 ExprKind::ValueTypeAscription { .. } => {
315 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
317 let place = unpack!(block = this.as_place(block, expr));
318 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
320 .push_assign(block, source_info, destination, rvalue);
323 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
324 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
326 // Create a "fake" temporary variable so that we check that the
327 // value is Sized. Usually, this is caught in type checking, but
328 // in the case of box expr there is no such check.
329 if let Place::Projection(..) = destination {
331 .push(LocalDecl::new_temp(expr.ty, expr.span));
334 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
336 let place = unpack!(block = this.as_place(block, expr));
337 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
339 .push_assign(block, source_info, destination, rvalue);
343 // these are the cases that are more naturally handled by some other mode
344 ExprKind::Unary { .. }
345 | ExprKind::Binary { .. }
346 | ExprKind::Box { .. }
347 | ExprKind::Cast { .. }
348 | ExprKind::Pointer { .. }
349 | ExprKind::Repeat { .. }
350 | ExprKind::Borrow { .. }
351 | ExprKind::Array { .. }
352 | ExprKind::Tuple { .. }
353 | ExprKind::Adt { .. }
354 | ExprKind::Closure { .. }
355 | ExprKind::Literal { .. }
356 | ExprKind::Yield { .. } => {
357 debug_assert!(match Category::of(&expr.kind).unwrap() {
358 // should be handled above
359 Category::Rvalue(RvalueFunc::Into) => false,
361 // must be handled above or else we get an
362 // infinite loop in the builder; see
363 // e.g., `ExprKind::VarRef` above
364 Category::Place => false,
369 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
370 this.cfg.push_assign(block, source_info, destination, rvalue);
375 if !expr_is_block_or_scope {
376 let popped = this.block_context.pop();
377 assert!(popped.is_some());