1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
7 use rustc::ty::{self, CanonicalUserTypeAnnotation};
8 use rustc_data_structures::fx::FxHashMap;
10 use rustc_span::symbol::sym;
12 use rustc_target::spec::abi::Abi;
14 impl<'a, 'tcx> Builder<'a, 'tcx> {
15 /// Compile `expr`, storing the result into `destination`, which
16 /// is assumed to be uninitialized.
19 destination: &Place<'tcx>,
20 mut block: BasicBlock,
23 debug!("into_expr(destination={:?}, block={:?}, expr={:?})", 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 {
43 ExprKind::Scope { region_scope, lint_level, value } => {
44 let region_scope = (region_scope, source_info);
45 this.in_scope(region_scope, lint_level, |this| this.into(destination, block, value))
47 ExprKind::Block { body: ast_block } => {
48 this.ast_block(destination, block, ast_block, source_info)
50 ExprKind::Match { scrutinee, arms } => {
51 this.match_expr(destination, expr_span, block, scrutinee, arms)
53 ExprKind::NeverToAny { source } => {
54 let source = this.hir.mirror(source);
55 let is_call = match source.kind {
56 ExprKind::Call { .. } => true,
60 // (#66975) Source could be a const of type `!`, so has to
61 // exist in the generated MIR.
62 unpack!(block = this.as_temp(block, this.local_scope(), source, Mutability::Mut,));
64 // This is an optimization. If the expression was a call then we already have an
65 // unreachable block. Don't bother to terminate it and create a new one.
69 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
70 let end_block = this.cfg.start_new_block();
74 ExprKind::LogicalOp { op, lhs, rhs } => {
77 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
79 // +----------false-----------+------------------> [false_block]
83 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
85 // [true_block] [false_block]
87 let (true_block, false_block, mut else_block, join_block) = (
88 this.cfg.start_new_block(),
89 this.cfg.start_new_block(),
90 this.cfg.start_new_block(),
91 this.cfg.start_new_block(),
94 let lhs = unpack!(block = this.as_local_operand(block, lhs));
95 let blocks = match op {
96 LogicalOp::And => (else_block, false_block),
97 LogicalOp::Or => (true_block, else_block),
99 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
100 this.cfg.terminate(block, source_info, term);
102 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
103 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
104 this.cfg.terminate(else_block, source_info, term);
106 this.cfg.push_assign_constant(
110 Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
113 this.cfg.push_assign_constant(
117 Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
120 // Link up both branches:
121 this.cfg.goto(true_block, source_info, join_block);
122 this.cfg.goto(false_block, source_info, join_block);
125 ExprKind::Loop { body } => {
128 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
131 // | +-----------------------------------------+
132 // +-> [diverge_cleanup]
133 // The false link is required to make sure borrowck considers unwinds through the
134 // body, even when the exact code in the body cannot unwind
136 let loop_block = this.cfg.start_new_block();
137 let exit_block = this.cfg.start_new_block();
140 this.cfg.goto(block, source_info, loop_block);
142 this.in_breakable_scope(
147 // conduct the test, if necessary
148 let body_block = this.cfg.start_new_block();
149 let diverge_cleanup = this.diverge_cleanup();
153 TerminatorKind::FalseUnwind {
154 real_target: body_block,
155 unwind: Some(diverge_cleanup),
159 // The “return” value of the loop body must always be an unit. We therefore
160 // introduce a unit temporary as the destination for the loop body.
161 let tmp = this.get_unit_temp();
162 // Execute the body, branching back to the test.
163 let body_block_end = unpack!(this.into(&tmp, body_block, body));
164 this.cfg.goto(body_block_end, source_info, loop_block);
169 ExprKind::Call { ty, fun, args, from_hir_call } => {
170 let intrinsic = match ty.kind {
171 ty::FnDef(def_id, _) => {
172 let f = ty.fn_sig(this.hir.tcx());
173 if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
174 Some(this.hir.tcx().item_name(def_id))
181 let fun = unpack!(block = this.as_local_operand(block, fun));
182 if let Some(sym::move_val_init) = intrinsic {
183 // `move_val_init` has "magic" semantics - the second argument is
184 // always evaluated "directly" into the first one.
186 let mut args = args.into_iter();
187 let ptr = args.next().expect("0 arguments to `move_val_init`");
188 let val = args.next().expect("1 argument to `move_val_init`");
189 assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
191 let ptr = this.hir.mirror(ptr);
193 // Create an *internal* temp for the pointer, so that unsafety
194 // checking won't complain about the raw pointer assignment.
195 let ptr_temp = this.local_decls.push(LocalDecl {
196 mutability: Mutability::Mut,
198 user_ty: UserTypeProjections::none(),
201 local_info: LocalInfo::Other,
204 let ptr_temp = Place::from(ptr_temp);
205 let block = unpack!(this.into(&ptr_temp, block, ptr));
206 this.into(&this.hir.tcx().mk_place_deref(ptr_temp), block, val)
208 let args: Vec<_> = args
210 .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
213 let success = this.cfg.start_new_block();
214 let cleanup = this.diverge_cleanup();
216 this.record_operands_moved(&args);
221 TerminatorKind::Call {
224 cleanup: Some(cleanup),
225 // FIXME(varkor): replace this with an uninhabitedness-based check.
226 // This requires getting access to the current module to call
227 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
228 destination: if expr.ty.is_never() {
231 Some((*destination, success))
239 ExprKind::Use { source } => this.into(destination, block, source),
240 ExprKind::Borrow { arg, borrow_kind } => {
241 // We don't do this in `as_rvalue` because we use `as_place`
242 // for borrow expressions, so we cannot create an `RValue` that
243 // remains valid across user code. `as_rvalue` is usually called
244 // by this method anyway, so this shouldn't cause too many
245 // unnecessary temporaries.
246 let arg_place = match borrow_kind {
247 BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
248 _ => unpack!(block = this.as_place(block, arg)),
251 Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
252 this.cfg.push_assign(block, source_info, destination, borrow);
255 ExprKind::AddressOf { mutability, arg } => {
256 let place = match mutability {
257 hir::Mutability::Not => this.as_read_only_place(block, arg),
258 hir::Mutability::Mut => this.as_place(block, arg),
260 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
261 this.cfg.push_assign(block, source_info, destination, address_of);
264 ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
265 // See the notes for `ExprKind::Array` in `as_rvalue` and for
266 // `ExprKind::Borrow` above.
267 let is_union = adt_def.is_union();
268 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
270 let scope = this.local_scope();
272 // first process the set of fields that were provided
273 // (evaluating them in order given by user)
274 let fields_map: FxHashMap<_, _> = fields
276 .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
279 let field_names = this.hir.all_fields(adt_def, variant_index);
282 if let Some(FruInfo { base, field_types }) = base {
283 let base = unpack!(block = this.as_place(block, base));
285 // MIR does not natively support FRU, so for each
286 // base-supplied field, generate an operand that
287 // reads it from the base.
290 .zip(field_types.into_iter())
291 .map(|(n, ty)| match fields_map.get(&n) {
292 Some(v) => v.clone(),
293 None => this.consume_by_copy_or_move(
294 this.hir.tcx().mk_place_field(base.clone(), n, ty),
299 field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
302 let inferred_ty = expr.ty;
303 let user_ty = user_ty.map(|ty| {
304 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
305 span: source_info.span,
310 let adt = box AggregateKind::Adt(
317 this.cfg.push_assign(
321 Rvalue::Aggregate(adt, fields),
326 // These cases don't actually need a destination
327 ExprKind::Assign { .. }
328 | ExprKind::AssignOp { .. }
329 | ExprKind::Continue { .. }
330 | ExprKind::Break { .. }
331 | ExprKind::InlineAsm { .. }
332 | ExprKind::Return { .. } => {
333 unpack!(block = this.stmt_expr(block, expr, None));
334 this.cfg.push_assign_unit(block, source_info, destination);
338 // Avoid creating a temporary
339 ExprKind::VarRef { .. }
341 | ExprKind::PlaceTypeAscription { .. }
342 | ExprKind::ValueTypeAscription { .. } => {
343 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
345 let place = unpack!(block = this.as_place(block, expr));
346 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
347 this.cfg.push_assign(block, source_info, destination, rvalue);
350 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
351 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
353 // Create a "fake" temporary variable so that we check that the
354 // value is Sized. Usually, this is caught in type checking, but
355 // in the case of box expr there is no such check.
356 if !destination.projection.is_empty() {
357 this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span));
360 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
362 let place = unpack!(block = this.as_place(block, expr));
363 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
364 this.cfg.push_assign(block, source_info, destination, rvalue);
368 // these are the cases that are more naturally handled by some other mode
369 ExprKind::Unary { .. }
370 | ExprKind::Binary { .. }
371 | ExprKind::Box { .. }
372 | ExprKind::Cast { .. }
373 | ExprKind::Pointer { .. }
374 | ExprKind::Repeat { .. }
375 | ExprKind::Array { .. }
376 | ExprKind::Tuple { .. }
377 | ExprKind::Closure { .. }
378 | ExprKind::Literal { .. }
379 | ExprKind::StaticRef { .. }
380 | ExprKind::Yield { .. } => {
381 debug_assert!(match Category::of(&expr.kind).unwrap() {
382 // should be handled above
383 Category::Rvalue(RvalueFunc::Into) => false,
385 // must be handled above or else we get an
386 // infinite loop in the builder; see
387 // e.g., `ExprKind::VarRef` above
388 Category::Place => false,
393 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
394 this.cfg.push_assign(block, source_info, destination, rvalue);
399 if !expr_is_block_or_scope {
400 let popped = this.block_context.pop();
401 assert!(popped.is_some());