1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
6 use rustc_ast::InlineAsmOptions;
7 use rustc_data_structures::fx::FxHashMap;
8 use rustc_data_structures::stack::ensure_sufficient_stack;
10 use rustc_middle::mir::*;
11 use rustc_middle::ty::CanonicalUserTypeAnnotation;
13 impl<'a, 'tcx> Builder<'a, 'tcx> {
14 /// Compile `expr`, storing the result into `destination`, which
15 /// is assumed to be uninitialized.
18 destination: Place<'tcx>,
19 mut block: BasicBlock,
22 debug!("into_expr(destination={:?}, block={:?}, expr={:?})", destination, block, expr);
24 // since we frequently have to reference `self` from within a
25 // closure, where `self` would be shadowed, it's easier to
26 // just use the name `this` uniformly
28 let expr_span = expr.span;
29 let source_info = this.source_info(expr_span);
31 let expr_is_block_or_scope =
32 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
34 if !expr_is_block_or_scope {
35 this.block_context.push(BlockFrame::SubExpr);
38 let block_and = match expr.kind {
39 ExprKind::Scope { region_scope, lint_level, value } => {
40 let region_scope = (region_scope, source_info);
41 ensure_sufficient_stack(|| {
42 this.in_scope(region_scope, lint_level, |this| {
43 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::If { cond, then, else_opt } => {
55 block = this.as_temp(block, Some(this.local_scope()), cond, Mutability::Mut)
57 let operand = Operand::Move(Place::from(place));
59 let mut then_block = this.cfg.start_new_block();
60 let mut else_block = this.cfg.start_new_block();
61 let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
62 this.cfg.terminate(block, source_info, term);
64 unpack!(then_block = this.into(destination, then_block, then));
65 else_block = if let Some(else_opt) = else_opt {
66 unpack!(this.into(destination, else_block, else_opt))
68 // Body of the `if` expression without an `else` clause must return `()`, thus
69 // we implicitly generate a `else {}` if it is not specified.
70 let correct_si = this.source_info(expr_span.shrink_to_hi());
71 this.cfg.push_assign_unit(else_block, correct_si, destination, this.hir.tcx());
75 let join_block = this.cfg.start_new_block();
79 TerminatorKind::Goto { target: join_block },
84 TerminatorKind::Goto { target: join_block },
89 ExprKind::NeverToAny { source } => {
90 let source = this.hir.mirror(source);
92 matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
94 // (#66975) Source could be a const of type `!`, so has to
95 // exist in the generated MIR.
97 block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)
100 // This is an optimization. If the expression was a call then we already have an
101 // unreachable block. Don't bother to terminate it and create a new one.
105 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
106 let end_block = this.cfg.start_new_block();
110 ExprKind::LogicalOp { op, lhs, rhs } => {
113 // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
115 // +----------false-----------+------------------> [false_block]
119 // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
120 // | (true) | (false)
121 // [true_block] [false_block]
123 let (true_block, false_block, mut else_block, join_block) = (
124 this.cfg.start_new_block(),
125 this.cfg.start_new_block(),
126 this.cfg.start_new_block(),
127 this.cfg.start_new_block(),
130 let lhs = unpack!(block = this.as_local_operand(block, lhs));
131 let blocks = match op {
132 LogicalOp::And => (else_block, false_block),
133 LogicalOp::Or => (true_block, else_block),
135 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
136 this.cfg.terminate(block, source_info, term);
138 let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
139 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
140 this.cfg.terminate(else_block, source_info, term);
142 this.cfg.push_assign_constant(
146 Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
149 this.cfg.push_assign_constant(
153 Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
156 // Link up both branches:
157 this.cfg.goto(true_block, source_info, join_block);
158 this.cfg.goto(false_block, source_info, join_block);
161 ExprKind::Loop { body } => {
164 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
167 // | +-----------------------------------------+
168 // +-> [diverge_cleanup]
169 // The false link is required to make sure borrowck considers unwinds through the
170 // body, even when the exact code in the body cannot unwind
172 let loop_block = this.cfg.start_new_block();
175 this.cfg.goto(block, source_info, loop_block);
177 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
178 // conduct the test, if necessary
179 let body_block = this.cfg.start_new_block();
183 TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
185 this.diverge_from(loop_block);
187 // The “return” value of the loop body must always be an unit. We therefore
188 // introduce a unit temporary as the destination for the loop body.
189 let tmp = this.get_unit_temp();
190 // Execute the body, branching back to the test.
191 let body_block_end = unpack!(this.into(tmp, body_block, body));
192 this.cfg.goto(body_block_end, source_info, loop_block);
194 // Loops are only exited by `break` expressions.
198 ExprKind::Call { ty: _, fun, args, from_hir_call, fn_span } => {
199 let fun = unpack!(block = this.as_local_operand(block, fun));
200 let args: Vec<_> = args
202 .map(|arg| unpack!(block = this.as_local_call_operand(block, arg)))
205 let success = this.cfg.start_new_block();
207 this.record_operands_moved(&args);
209 debug!("into_expr: fn_span={:?}", fn_span);
214 TerminatorKind::Call {
218 // FIXME(varkor): replace this with an uninhabitedness-based check.
219 // This requires getting access to the current module to call
220 // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
221 destination: if expr.ty.is_never() {
224 Some((destination, success))
230 this.diverge_from(block);
233 ExprKind::Use { source } => this.into(destination, block, source),
234 ExprKind::Borrow { arg, borrow_kind } => {
235 // We don't do this in `as_rvalue` because we use `as_place`
236 // for borrow expressions, so we cannot create an `RValue` that
237 // remains valid across user code. `as_rvalue` is usually called
238 // by this method anyway, so this shouldn't cause too many
239 // unnecessary temporaries.
240 let arg_place = match borrow_kind {
241 BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
242 _ => unpack!(block = this.as_place(block, arg)),
245 Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
246 this.cfg.push_assign(block, source_info, destination, borrow);
249 ExprKind::AddressOf { mutability, arg } => {
250 let place = match mutability {
251 hir::Mutability::Not => this.as_read_only_place(block, arg),
252 hir::Mutability::Mut => this.as_place(block, arg),
254 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
255 this.cfg.push_assign(block, source_info, destination, address_of);
258 ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
259 // See the notes for `ExprKind::Array` in `as_rvalue` and for
260 // `ExprKind::Borrow` above.
261 let is_union = adt_def.is_union();
262 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
264 let scope = this.local_scope();
266 // first process the set of fields that were provided
267 // (evaluating them in order given by user)
268 let fields_map: FxHashMap<_, _> = fields
270 .map(|f| (f.name, unpack!(block = this.as_operand(block, Some(scope), f.expr))))
273 let field_names = this.hir.all_fields(adt_def, variant_index);
275 let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
276 let place_builder = unpack!(block = this.as_place_builder(block, base));
278 // MIR does not natively support FRU, so for each
279 // base-supplied field, generate an operand that
280 // reads it from the base.
283 .zip(field_types.into_iter())
284 .map(|(n, ty)| match fields_map.get(&n) {
285 Some(v) => v.clone(),
287 let place_builder = place_builder.clone();
288 this.consume_by_copy_or_move(
291 .into_place(this.hir.tcx(), this.hir.typeck_results()),
297 field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
300 let inferred_ty = expr.ty;
301 let user_ty = user_ty.map(|ty| {
302 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
303 span: source_info.span,
308 let adt = box AggregateKind::Adt(
315 this.cfg.push_assign(
319 Rvalue::Aggregate(adt, fields),
323 ExprKind::InlineAsm { template, operands, options, line_spans } => {
325 use rustc_middle::mir;
326 let operands = operands
329 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
331 value: unpack!(block = this.as_local_operand(block, expr)),
333 thir::InlineAsmOperand::Out { reg, late, expr } => {
334 mir::InlineAsmOperand::Out {
337 place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
340 thir::InlineAsmOperand::InOut { reg, late, expr } => {
341 let place = unpack!(block = this.as_place(block, expr));
342 mir::InlineAsmOperand::InOut {
345 // This works because asm operands must be Copy
346 in_value: Operand::Copy(place),
347 out_place: Some(place),
350 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
351 mir::InlineAsmOperand::InOut {
354 in_value: unpack!(block = this.as_local_operand(block, in_expr)),
355 out_place: out_expr.map(|out_expr| {
356 unpack!(block = this.as_place(block, out_expr))
360 thir::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const {
361 value: unpack!(block = this.as_local_operand(block, expr)),
363 thir::InlineAsmOperand::SymFn { expr } => {
364 mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) }
366 thir::InlineAsmOperand::SymStatic { def_id } => {
367 mir::InlineAsmOperand::SymStatic { def_id }
372 let destination = this.cfg.start_new_block();
377 TerminatorKind::InlineAsm {
382 destination: if options.contains(InlineAsmOptions::NORETURN) {
392 // These cases don't actually need a destination
393 ExprKind::Assign { .. }
394 | ExprKind::AssignOp { .. }
395 | ExprKind::LlvmInlineAsm { .. } => {
396 unpack!(block = this.stmt_expr(block, expr, None));
397 this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx());
401 ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
402 unpack!(block = this.stmt_expr(block, expr, None));
403 // No assign, as these have type `!`.
407 // Avoid creating a temporary
408 ExprKind::VarRef { .. }
409 | ExprKind::UpvarRef { .. }
410 | ExprKind::PlaceTypeAscription { .. }
411 | ExprKind::ValueTypeAscription { .. } => {
412 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
414 let place = unpack!(block = this.as_place(block, expr));
415 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
416 this.cfg.push_assign(block, source_info, destination, rvalue);
419 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
420 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
422 // Create a "fake" temporary variable so that we check that the
423 // value is Sized. Usually, this is caught in type checking, but
424 // in the case of box expr there is no such check.
425 if !destination.projection.is_empty() {
426 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
429 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
431 let place = unpack!(block = this.as_place(block, expr));
432 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
433 this.cfg.push_assign(block, source_info, destination, rvalue);
437 ExprKind::Yield { value } => {
438 let scope = this.local_scope();
439 let value = unpack!(block = this.as_operand(block, Some(scope), value));
440 let resume = this.cfg.start_new_block();
444 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
446 this.generator_drop_cleanup(block);
450 // these are the cases that are more naturally handled by some other mode
451 ExprKind::Unary { .. }
452 | ExprKind::Binary { .. }
453 | ExprKind::Box { .. }
454 | ExprKind::Cast { .. }
455 | ExprKind::Pointer { .. }
456 | ExprKind::Repeat { .. }
457 | ExprKind::Array { .. }
458 | ExprKind::Tuple { .. }
459 | ExprKind::Closure { .. }
460 | ExprKind::ConstBlock { .. }
461 | ExprKind::Literal { .. }
462 | ExprKind::ThreadLocalRef(_)
463 | ExprKind::StaticRef { .. } => {
464 debug_assert!(match Category::of(&expr.kind).unwrap() {
465 // should be handled above
466 Category::Rvalue(RvalueFunc::Into) => false,
468 // must be handled above or else we get an
469 // infinite loop in the builder; see
470 // e.g., `ExprKind::VarRef` above
471 Category::Place => false,
476 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
477 this.cfg.push_assign(block, source_info, destination, rvalue);
482 if !expr_is_block_or_scope {
483 let popped = this.block_context.pop();
484 assert!(popped.is_some());