1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::{Category, RvalueFunc};
4 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
5 use rustc_ast::InlineAsmOptions;
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_data_structures::stack::ensure_sufficient_stack;
9 use rustc_index::vec::Idx;
10 use rustc_middle::mir::*;
11 use rustc_middle::thir::*;
12 use rustc_middle::ty::CanonicalUserTypeAnnotation;
15 impl<'a, 'tcx> Builder<'a, 'tcx> {
16 /// Compile `expr`, storing the result into `destination`, which
17 /// is assumed to be uninitialized.
18 #[instrument(level = "debug", skip(self))]
19 pub(crate) fn expr_into_dest(
21 destination: Place<'tcx>,
22 mut block: BasicBlock,
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 =
33 matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
35 if !expr_is_block_or_scope {
36 this.block_context.push(BlockFrame::SubExpr);
39 let block_and = match expr.kind {
40 ExprKind::Scope { region_scope, lint_level, value } => {
41 let region_scope = (region_scope, source_info);
42 ensure_sufficient_stack(|| {
43 this.in_scope(region_scope, lint_level, |this| {
44 this.expr_into_dest(destination, block, &this.thir[value])
48 ExprKind::Block { block: ast_block } => {
49 this.ast_block(destination, block, ast_block, source_info)
51 ExprKind::Match { scrutinee, ref arms } => {
52 this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
54 ExprKind::If { cond, then, else_opt, if_then_scope } => {
56 let then_expr = &this.thir[then];
57 let then_source_info = this.source_info(then_expr.span);
58 let condition_scope = this.local_scope();
60 let mut else_blk = unpack!(
61 then_blk = this.in_scope(
62 (if_then_scope, then_source_info),
65 let source_info = if this.is_let(cond) {
66 let variable_scope = this.new_source_scope(
71 this.source_scope = variable_scope;
72 SourceInfo { span: then_expr.span, scope: variable_scope }
74 this.source_info(then_expr.span)
76 let (then_block, else_block) =
77 this.in_if_then_scope(condition_scope, then_expr.span, |this| {
78 let then_blk = unpack!(this.then_else_break(
81 Some(condition_scope),
86 this.expr_into_dest(destination, then_blk, then_expr)
88 then_block.and(else_block)
93 else_blk = if let Some(else_opt) = else_opt {
94 unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
96 // Body of the `if` expression without an `else` clause must return `()`, thus
97 // we implicitly generate an `else {}` if it is not specified.
98 let correct_si = this.source_info(expr_span.shrink_to_hi());
99 this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
103 let join_block = this.cfg.start_new_block();
104 this.cfg.goto(then_blk, source_info, join_block);
105 this.cfg.goto(else_blk, source_info, join_block);
108 ExprKind::Let { expr, ref pat } => {
109 let scope = this.local_scope();
110 let (true_block, false_block) = this.in_if_then_scope(scope, expr_span, |this| {
111 this.lower_let_expr(block, &this.thir[expr], pat, scope, None, expr_span)
114 this.cfg.push_assign_constant(
121 literal: ConstantKind::from_bool(this.tcx, true),
125 this.cfg.push_assign_constant(
132 literal: ConstantKind::from_bool(this.tcx, false),
136 let join_block = this.cfg.start_new_block();
137 this.cfg.goto(true_block, source_info, join_block);
138 this.cfg.goto(false_block, source_info, join_block);
141 ExprKind::NeverToAny { source } => {
142 let source = &this.thir[source];
144 matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
146 // (#66975) Source could be a const of type `!`, so has to
147 // exist in the generated MIR.
149 block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)
152 // This is an optimization. If the expression was a call then we already have an
153 // unreachable block. Don't bother to terminate it and create a new one.
157 this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
158 let end_block = this.cfg.start_new_block();
162 ExprKind::LogicalOp { op, lhs, rhs } => {
165 // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
167 // [shortcurcuit_block: dest = false]
171 // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
173 // [shortcurcuit_block: dest = true]
175 let (shortcircuit_block, mut else_block, join_block) = (
176 this.cfg.start_new_block(),
177 this.cfg.start_new_block(),
178 this.cfg.start_new_block(),
181 let lhs = unpack!(block = this.as_local_operand(block, &this.thir[lhs]));
182 let blocks = match op {
183 LogicalOp::And => (else_block, shortcircuit_block),
184 LogicalOp::Or => (shortcircuit_block, else_block),
186 let term = TerminatorKind::if_(this.tcx, lhs, blocks.0, blocks.1);
187 this.cfg.terminate(block, source_info, term);
189 this.cfg.push_assign_constant(
197 LogicalOp::And => ConstantKind::from_bool(this.tcx, false),
198 LogicalOp::Or => ConstantKind::from_bool(this.tcx, true),
202 this.cfg.goto(shortcircuit_block, source_info, join_block);
204 let rhs = unpack!(else_block = this.as_local_operand(else_block, &this.thir[rhs]));
205 this.cfg.push_assign(else_block, source_info, destination, Rvalue::Use(rhs));
206 this.cfg.goto(else_block, source_info, join_block);
210 ExprKind::Loop { body } => {
213 // [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
216 // | +-----------------------------------------+
217 // +-> [diverge_cleanup]
218 // The false link is required to make sure borrowck considers unwinds through the
219 // body, even when the exact code in the body cannot unwind
221 let loop_block = this.cfg.start_new_block();
224 this.cfg.goto(block, source_info, loop_block);
226 this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
227 // conduct the test, if necessary
228 let body_block = this.cfg.start_new_block();
232 TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
234 this.diverge_from(loop_block);
236 // The “return” value of the loop body must always be a unit. We therefore
237 // introduce a unit temporary as the destination for the loop body.
238 let tmp = this.get_unit_temp();
239 // Execute the body, branching back to the test.
241 unpack!(this.expr_into_dest(tmp, body_block, &this.thir[body]));
242 this.cfg.goto(body_block_end, source_info, loop_block);
244 // Loops are only exited by `break` expressions.
248 ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
249 let fun = unpack!(block = this.as_local_operand(block, &this.thir[fun]));
250 let args: Vec<_> = args
253 .map(|arg| unpack!(block = this.as_local_call_operand(block, &this.thir[arg])))
256 let success = this.cfg.start_new_block();
258 this.record_operands_moved(&args);
260 debug!("expr_into_dest: fn_span={:?}", fn_span);
265 TerminatorKind::Call {
270 // The presence or absence of a return edge affects control-flow sensitive
271 // MIR checks and ultimately whether code is accepted or not. We can only
272 // omit the return edge if a return type is visibly uninhabited to a module
273 // that makes the call.
274 target: if this.tcx.is_ty_uninhabited_from(
287 this.diverge_from(block);
290 ExprKind::Use { source } => this.expr_into_dest(destination, block, &this.thir[source]),
291 ExprKind::Borrow { arg, borrow_kind } => {
292 let arg = &this.thir[arg];
293 // We don't do this in `as_rvalue` because we use `as_place`
294 // for borrow expressions, so we cannot create an `RValue` that
295 // remains valid across user code. `as_rvalue` is usually called
296 // by this method anyway, so this shouldn't cause too many
297 // unnecessary temporaries.
298 let arg_place = match borrow_kind {
299 BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
300 _ => unpack!(block = this.as_place(block, arg)),
302 let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
303 this.cfg.push_assign(block, source_info, destination, borrow);
306 ExprKind::AddressOf { mutability, arg } => {
307 let arg = &this.thir[arg];
308 let place = match mutability {
309 hir::Mutability::Not => this.as_read_only_place(block, arg),
310 hir::Mutability::Mut => this.as_place(block, arg),
312 let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
313 this.cfg.push_assign(block, source_info, destination, address_of);
316 ExprKind::Adt(box AdtExpr {
324 // See the notes for `ExprKind::Array` in `as_rvalue` and for
325 // `ExprKind::Borrow` above.
326 let is_union = adt_def.is_union();
327 let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
329 let scope = this.local_scope();
331 // first process the set of fields that were provided
332 // (evaluating them in order given by user)
333 let fields_map: FxHashMap<_, _> = fields
336 let local_info = Box::new(LocalInfo::AggregateTemp);
340 block = this.as_operand(
345 NeedsTemporary::Maybe,
352 let field_names: Vec<_> =
353 (0..adt_def.variant(variant_index).fields.len()).map(Field::new).collect();
355 let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
357 unpack!(block = this.as_place_builder(block, &this.thir[*base]));
359 // MIR does not natively support FRU, so for each
360 // base-supplied field, generate an operand that
361 // reads it from the base.
362 iter::zip(field_names, &**field_types)
363 .map(|(n, ty)| match fields_map.get(&n) {
364 Some(v) => v.clone(),
366 let place_builder = place_builder.clone();
367 this.consume_by_copy_or_move(
368 place_builder.field(n, *ty).into_place(this),
374 field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
377 let inferred_ty = expr.ty;
378 let user_ty = user_ty.as_ref().map(|user_ty| {
379 this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
380 span: source_info.span,
381 user_ty: user_ty.clone(),
385 let adt = Box::new(AggregateKind::Adt(
392 this.cfg.push_assign(
396 Rvalue::Aggregate(adt, fields),
400 ExprKind::InlineAsm(box InlineAsmExpr {
406 use rustc_middle::{mir, thir};
407 let operands = operands
409 .map(|op| match *op {
410 thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
412 value: unpack!(block = this.as_local_operand(block, &this.thir[expr])),
414 thir::InlineAsmOperand::Out { reg, late, expr } => {
415 mir::InlineAsmOperand::Out {
418 place: expr.map(|expr| {
419 unpack!(block = this.as_place(block, &this.thir[expr]))
423 thir::InlineAsmOperand::InOut { reg, late, expr } => {
424 let place = unpack!(block = this.as_place(block, &this.thir[expr]));
425 mir::InlineAsmOperand::InOut {
428 // This works because asm operands must be Copy
429 in_value: Operand::Copy(place),
430 out_place: Some(place),
433 thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
434 mir::InlineAsmOperand::InOut {
438 block = this.as_local_operand(block, &this.thir[in_expr])
440 out_place: out_expr.map(|out_expr| {
441 unpack!(block = this.as_place(block, &this.thir[out_expr]))
445 thir::InlineAsmOperand::Const { value, span } => {
446 mir::InlineAsmOperand::Const {
447 value: Box::new(Constant { span, user_ty: None, literal: value }),
450 thir::InlineAsmOperand::SymFn { value, span } => {
451 mir::InlineAsmOperand::SymFn {
452 value: Box::new(Constant { span, user_ty: None, literal: value }),
455 thir::InlineAsmOperand::SymStatic { def_id } => {
456 mir::InlineAsmOperand::SymStatic { def_id }
461 if !options.contains(InlineAsmOptions::NORETURN) {
462 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
465 let destination_block = this.cfg.start_new_block();
469 TerminatorKind::InlineAsm {
474 destination: if options.contains(InlineAsmOptions::NORETURN) {
477 Some(destination_block)
482 if options.contains(InlineAsmOptions::MAY_UNWIND) {
483 this.diverge_from(block);
485 destination_block.unit()
488 // These cases don't actually need a destination
489 ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
490 unpack!(block = this.stmt_expr(block, expr, None));
491 this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
495 ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
496 unpack!(block = this.stmt_expr(block, expr, None));
497 // No assign, as these have type `!`.
501 // Avoid creating a temporary
502 ExprKind::VarRef { .. }
503 | ExprKind::UpvarRef { .. }
504 | ExprKind::PlaceTypeAscription { .. }
505 | ExprKind::ValueTypeAscription { .. } => {
506 debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
508 let place = unpack!(block = this.as_place(block, expr));
509 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
510 this.cfg.push_assign(block, source_info, destination, rvalue);
513 ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
514 debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
516 // Create a "fake" temporary variable so that we check that the
517 // value is Sized. Usually, this is caught in type checking, but
518 // in the case of box expr there is no such check.
519 if !destination.projection.is_empty() {
520 this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
523 let place = unpack!(block = this.as_place(block, expr));
524 let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
525 this.cfg.push_assign(block, source_info, destination, rvalue);
529 ExprKind::Yield { value } => {
530 let scope = this.local_scope();
532 block = this.as_operand(
540 let resume = this.cfg.start_new_block();
544 TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
546 this.generator_drop_cleanup(block);
550 // these are the cases that are more naturally handled by some other mode
551 ExprKind::Unary { .. }
552 | ExprKind::Binary { .. }
553 | ExprKind::Box { .. }
554 | ExprKind::Cast { .. }
555 | ExprKind::Pointer { .. }
556 | ExprKind::Repeat { .. }
557 | ExprKind::Array { .. }
558 | ExprKind::Tuple { .. }
559 | ExprKind::Closure { .. }
560 | ExprKind::ConstBlock { .. }
561 | ExprKind::Literal { .. }
562 | ExprKind::NamedConst { .. }
563 | ExprKind::NonHirLiteral { .. }
564 | ExprKind::ZstLiteral { .. }
565 | ExprKind::ConstParam { .. }
566 | ExprKind::ThreadLocalRef(_)
567 | ExprKind::StaticRef { .. } => {
568 debug_assert!(match Category::of(&expr.kind).unwrap() {
569 // should be handled above
570 Category::Rvalue(RvalueFunc::Into) => false,
572 // must be handled above or else we get an
573 // infinite loop in the builder; see
574 // e.g., `ExprKind::VarRef` above
575 Category::Place => false,
580 let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
581 this.cfg.push_assign(block, source_info, destination, rvalue);
586 if !expr_is_block_or_scope {
587 let popped = this.block_context.pop();
588 assert!(popped.is_some());
594 fn is_let(&self, expr: ExprId) -> bool {
595 match self.thir[expr].kind {
596 ExprKind::Let { .. } => true,
597 ExprKind::Scope { value, .. } => self.is_let(value),