rustc_mir_build/builder/expr/
into.rs

1//! See docs in build/expr/mod.rs
2
3use rustc_ast::{AsmMacro, InlineAsmOptions};
4use rustc_data_structures::fx::FxHashMap;
5use rustc_data_structures::stack::ensure_sufficient_stack;
6use rustc_hir as hir;
7use rustc_middle::mir::*;
8use rustc_middle::span_bug;
9use rustc_middle::thir::*;
10use rustc_middle::ty::CanonicalUserTypeAnnotation;
11use rustc_span::source_map::Spanned;
12use tracing::{debug, instrument};
13
14use crate::builder::expr::category::{Category, RvalueFunc};
15use crate::builder::matches::DeclareLetBindings;
16use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
17
18impl<'a, 'tcx> Builder<'a, 'tcx> {
19    /// Compile `expr`, storing the result into `destination`, which
20    /// is assumed to be uninitialized.
21    #[instrument(level = "debug", skip(self))]
22    pub(crate) fn expr_into_dest(
23        &mut self,
24        destination: Place<'tcx>,
25        mut block: BasicBlock,
26        expr_id: ExprId,
27    ) -> BlockAnd<()> {
28        // since we frequently have to reference `self` from within a
29        // closure, where `self` would be shadowed, it's easier to
30        // just use the name `this` uniformly
31        let this = self;
32        let expr = &this.thir[expr_id];
33        let expr_span = expr.span;
34        let source_info = this.source_info(expr_span);
35
36        let expr_is_block_or_scope =
37            matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
38
39        if !expr_is_block_or_scope {
40            this.block_context.push(BlockFrame::SubExpr);
41        }
42
43        let block_and = match expr.kind {
44            ExprKind::Scope { region_scope, lint_level, value } => {
45                let region_scope = (region_scope, source_info);
46                ensure_sufficient_stack(|| {
47                    this.in_scope(region_scope, lint_level, |this| {
48                        this.expr_into_dest(destination, block, value)
49                    })
50                })
51            }
52            ExprKind::Block { block: ast_block } => {
53                this.ast_block(destination, block, ast_block, source_info)
54            }
55            ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr(
56                destination,
57                block,
58                scrutinee,
59                arms,
60                expr_span,
61                this.thir[scrutinee].span,
62            ),
63            ExprKind::If { cond, then, else_opt, if_then_scope } => {
64                let then_span = this.thir[then].span;
65                let then_source_info = this.source_info(then_span);
66                let condition_scope = this.local_scope();
67
68                let then_and_else_blocks = this.in_scope(
69                    (if_then_scope, then_source_info),
70                    LintLevel::Inherited,
71                    |this| {
72                        // FIXME: Does this need extra logic to handle let-chains?
73                        let source_info = if this.is_let(cond) {
74                            let variable_scope =
75                                this.new_source_scope(then_span, LintLevel::Inherited);
76                            this.source_scope = variable_scope;
77                            SourceInfo { span: then_span, scope: variable_scope }
78                        } else {
79                            this.source_info(then_span)
80                        };
81
82                        // Lower the condition, and have it branch into `then` and `else` blocks.
83                        let (then_block, else_block) =
84                            this.in_if_then_scope(condition_scope, then_span, |this| {
85                                let then_blk = this
86                                    .then_else_break(
87                                        block,
88                                        cond,
89                                        Some(condition_scope), // Temp scope
90                                        source_info,
91                                        DeclareLetBindings::Yes, // Declare `let` bindings normally
92                                    )
93                                    .into_block();
94
95                                // Lower the `then` arm into its block.
96                                this.expr_into_dest(destination, then_blk, then)
97                            });
98
99                        // Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
100                        then_block.and(else_block)
101                    },
102                );
103
104                // Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
105                let (then_blk, mut else_blk);
106                else_blk = unpack!(then_blk = then_and_else_blocks);
107
108                // If there is an `else` arm, lower it into `else_blk`.
109                if let Some(else_expr) = else_opt {
110                    else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
111                } else {
112                    // There is no `else` arm, so we know both arms have type `()`.
113                    // Generate the implicit `else {}` by assigning unit.
114                    let correct_si = this.source_info(expr_span.shrink_to_hi());
115                    this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
116                }
117
118                // The `then` and `else` arms have been lowered into their respective
119                // blocks, so make both of them meet up in a new block.
120                let join_block = this.cfg.start_new_block();
121                this.cfg.goto(then_blk, source_info, join_block);
122                this.cfg.goto(else_blk, source_info, join_block);
123                join_block.unit()
124            }
125            ExprKind::Let { .. } => {
126                // After desugaring, `let` expressions should only appear inside `if`
127                // expressions or `match` guards, possibly nested within a let-chain.
128                // In both cases they are specifically handled by the lowerings of
129                // those expressions, so this case is currently unreachable.
130                span_bug!(expr_span, "unexpected let expression outside of if or match-guard");
131            }
132            ExprKind::NeverToAny { source } => {
133                let source_expr = &this.thir[source];
134                let is_call =
135                    matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
136
137                // (#66975) Source could be a const of type `!`, so has to
138                // exist in the generated MIR.
139                unpack!(
140                    block =
141                        this.as_temp(block, this.local_temp_lifetime(), source, Mutability::Mut)
142                );
143
144                // This is an optimization. If the expression was a call then we already have an
145                // unreachable block. Don't bother to terminate it and create a new one.
146                if is_call {
147                    block.unit()
148                } else {
149                    this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
150                    let end_block = this.cfg.start_new_block();
151                    end_block.unit()
152                }
153            }
154            ExprKind::LogicalOp { op, lhs, rhs } => {
155                let condition_scope = this.local_scope();
156                let source_info = this.source_info(expr.span);
157
158                this.visit_coverage_branch_operation(op, expr.span);
159
160                // We first evaluate the left-hand side of the predicate ...
161                let (then_block, else_block) =
162                    this.in_if_then_scope(condition_scope, expr.span, |this| {
163                        this.then_else_break(
164                            block,
165                            lhs,
166                            Some(condition_scope), // Temp scope
167                            source_info,
168                            // This flag controls how inner `let` expressions are lowered,
169                            // but either way there shouldn't be any of those in here.
170                            DeclareLetBindings::LetNotPermitted,
171                        )
172                    });
173                let (short_circuit, continuation, constant) = match op {
174                    LogicalOp::And => (else_block, then_block, false),
175                    LogicalOp::Or => (then_block, else_block, true),
176                };
177                // At this point, the control flow splits into a short-circuiting path
178                // and a continuation path.
179                // - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
180                //   failing it leads to the short-circuting path which assigns `false` to the place.
181                // - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
182                //   passing it leads to the short-circuting path which assigns `true` to the place.
183                this.cfg.push_assign_constant(
184                    short_circuit,
185                    source_info,
186                    destination,
187                    ConstOperand {
188                        span: expr.span,
189                        user_ty: None,
190                        const_: Const::from_bool(this.tcx, constant),
191                    },
192                );
193                let mut rhs_block =
194                    this.expr_into_dest(destination, continuation, rhs).into_block();
195                // Instrument the lowered RHS's value for condition coverage.
196                // (Does nothing if condition coverage is not enabled.)
197                this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);
198
199                let target = this.cfg.start_new_block();
200                this.cfg.goto(rhs_block, source_info, target);
201                this.cfg.goto(short_circuit, source_info, target);
202                target.unit()
203            }
204            ExprKind::Loop { body } => {
205                // [block]
206                //    |
207                //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
208                //    |        ^                                         |
209                // false link  |                                         |
210                //    |        +-----------------------------------------+
211                //    +-> [diverge_cleanup]
212                // The false link is required to make sure borrowck considers unwinds through the
213                // body, even when the exact code in the body cannot unwind
214
215                let loop_block = this.cfg.start_new_block();
216
217                // Start the loop.
218                this.cfg.goto(block, source_info, loop_block);
219
220                this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| {
221                    // conduct the test, if necessary
222                    let body_block = this.cfg.start_new_block();
223                    this.cfg.terminate(
224                        loop_block,
225                        source_info,
226                        TerminatorKind::FalseUnwind {
227                            real_target: body_block,
228                            unwind: UnwindAction::Continue,
229                        },
230                    );
231                    this.diverge_from(loop_block);
232
233                    // The “return” value of the loop body must always be a unit. We therefore
234                    // introduce a unit temporary as the destination for the loop body.
235                    let tmp = this.get_unit_temp();
236                    // Execute the body, branching back to the test.
237                    let body_block_end = this.expr_into_dest(tmp, body_block, body).into_block();
238                    this.cfg.goto(body_block_end, source_info, loop_block);
239
240                    // Loops are only exited by `break` expressions.
241                    None
242                })
243            }
244            ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
245                let fun = unpack!(block = this.as_local_operand(block, fun));
246                let args: Box<[_]> = args
247                    .into_iter()
248                    .copied()
249                    .map(|arg| Spanned {
250                        node: unpack!(block = this.as_local_call_operand(block, arg)),
251                        span: this.thir.exprs[arg].span,
252                    })
253                    .collect();
254
255                let success = this.cfg.start_new_block();
256
257                this.record_operands_moved(&args);
258
259                debug!("expr_into_dest: fn_span={:?}", fn_span);
260
261                this.cfg.terminate(
262                    block,
263                    source_info,
264                    TerminatorKind::Call {
265                        func: fun,
266                        args,
267                        unwind: UnwindAction::Continue,
268                        destination,
269                        // The presence or absence of a return edge affects control-flow sensitive
270                        // MIR checks and ultimately whether code is accepted or not. We can only
271                        // omit the return edge if a return type is visibly uninhabited to a module
272                        // that makes the call.
273                        target: expr
274                            .ty
275                            .is_inhabited_from(
276                                this.tcx,
277                                this.parent_module,
278                                this.infcx.typing_env(this.param_env),
279                            )
280                            .then_some(success),
281                        call_source: if from_hir_call {
282                            CallSource::Normal
283                        } else {
284                            CallSource::OverloadedOperator
285                        },
286                        fn_span,
287                    },
288                );
289                this.diverge_from(block);
290                success.unit()
291            }
292            ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
293            ExprKind::Borrow { arg, borrow_kind } => {
294                // We don't do this in `as_rvalue` because we use `as_place`
295                // for borrow expressions, so we cannot create an `RValue` that
296                // remains valid across user code. `as_rvalue` is usually called
297                // by this method anyway, so this shouldn't cause too many
298                // unnecessary temporaries.
299                let arg_place = match borrow_kind {
300                    BorrowKind::Shared => {
301                        unpack!(block = this.as_read_only_place(block, arg))
302                    }
303                    _ => unpack!(block = this.as_place(block, arg)),
304                };
305                let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
306                this.cfg.push_assign(block, source_info, destination, borrow);
307                block.unit()
308            }
309            ExprKind::RawBorrow { mutability, arg } => {
310                let place = match mutability {
311                    hir::Mutability::Not => this.as_read_only_place(block, arg),
312                    hir::Mutability::Mut => this.as_place(block, arg),
313                };
314                let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place));
315                this.cfg.push_assign(block, source_info, destination, address_of);
316                block.unit()
317            }
318            ExprKind::Adt(box AdtExpr {
319                adt_def,
320                variant_index,
321                args,
322                ref user_ty,
323                ref fields,
324                ref base,
325            }) => {
326                // See the notes for `ExprKind::Array` in `as_rvalue` and for
327                // `ExprKind::Borrow` above.
328                let is_union = adt_def.is_union();
329                let active_field_index = is_union.then(|| fields[0].name);
330
331                let scope = this.local_temp_lifetime();
332
333                // first process the set of fields that were provided
334                // (evaluating them in order given by user)
335                let fields_map: FxHashMap<_, _> = fields
336                    .into_iter()
337                    .map(|f| {
338                        (
339                            f.name,
340                            unpack!(
341                                block = this.as_operand(
342                                    block,
343                                    scope,
344                                    f.expr,
345                                    LocalInfo::AggregateTemp,
346                                    NeedsTemporary::Maybe,
347                                )
348                            ),
349                        )
350                    })
351                    .collect();
352
353                let variant = adt_def.variant(variant_index);
354                let field_names = variant.fields.indices();
355
356                let fields = match base {
357                    AdtExprBase::None => {
358                        field_names.filter_map(|n| fields_map.get(&n).cloned()).collect()
359                    }
360                    AdtExprBase::Base(FruInfo { base, field_types }) => {
361                        let place_builder = unpack!(block = this.as_place_builder(block, *base));
362
363                        // We desugar FRU as we lower to MIR, so for each
364                        // base-supplied field, generate an operand that
365                        // reads it from the base.
366                        itertools::zip_eq(field_names, &**field_types)
367                            .map(|(n, ty)| match fields_map.get(&n) {
368                                Some(v) => v.clone(),
369                                None => {
370                                    let place =
371                                        place_builder.clone_project(PlaceElem::Field(n, *ty));
372                                    this.consume_by_copy_or_move(place.to_place(this))
373                                }
374                            })
375                            .collect()
376                    }
377                    AdtExprBase::DefaultFields(field_types) => {
378                        itertools::zip_eq(field_names, field_types)
379                            .map(|(n, &ty)| match fields_map.get(&n) {
380                                Some(v) => v.clone(),
381                                None => match variant.fields[n].value {
382                                    Some(def) => {
383                                        let value = Const::Unevaluated(
384                                            UnevaluatedConst::new(def, args),
385                                            ty,
386                                        );
387                                        Operand::Constant(Box::new(ConstOperand {
388                                            span: expr_span,
389                                            user_ty: None,
390                                            const_: value,
391                                        }))
392                                    }
393                                    None => {
394                                        let name = variant.fields[n].name;
395                                        span_bug!(
396                                            expr_span,
397                                            "missing mandatory field `{name}` of type `{ty}`",
398                                        );
399                                    }
400                                },
401                            })
402                            .collect()
403                    }
404                };
405
406                let inferred_ty = expr.ty;
407                let user_ty = user_ty.as_ref().map(|user_ty| {
408                    this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
409                        span: source_info.span,
410                        user_ty: user_ty.clone(),
411                        inferred_ty,
412                    })
413                });
414                let adt = Box::new(AggregateKind::Adt(
415                    adt_def.did(),
416                    variant_index,
417                    args,
418                    user_ty,
419                    active_field_index,
420                ));
421                this.cfg.push_assign(
422                    block,
423                    source_info,
424                    destination,
425                    Rvalue::Aggregate(adt, fields),
426                );
427                block.unit()
428            }
429            ExprKind::InlineAsm(box InlineAsmExpr {
430                asm_macro,
431                template,
432                ref operands,
433                options,
434                line_spans,
435            }) => {
436                use rustc_middle::{mir, thir};
437
438                let destination_block = this.cfg.start_new_block();
439                let mut targets =
440                    if asm_macro.diverges(options) { vec![] } else { vec![destination_block] };
441
442                let operands = operands
443                    .into_iter()
444                    .map(|op| match *op {
445                        thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
446                            reg,
447                            value: unpack!(block = this.as_local_operand(block, expr)),
448                        },
449                        thir::InlineAsmOperand::Out { reg, late, expr } => {
450                            mir::InlineAsmOperand::Out {
451                                reg,
452                                late,
453                                place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
454                            }
455                        }
456                        thir::InlineAsmOperand::InOut { reg, late, expr } => {
457                            let place = unpack!(block = this.as_place(block, expr));
458                            mir::InlineAsmOperand::InOut {
459                                reg,
460                                late,
461                                // This works because asm operands must be Copy
462                                in_value: Operand::Copy(place),
463                                out_place: Some(place),
464                            }
465                        }
466                        thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
467                            mir::InlineAsmOperand::InOut {
468                                reg,
469                                late,
470                                in_value: unpack!(block = this.as_local_operand(block, in_expr)),
471                                out_place: out_expr.map(|out_expr| {
472                                    unpack!(block = this.as_place(block, out_expr))
473                                }),
474                            }
475                        }
476                        thir::InlineAsmOperand::Const { value, span } => {
477                            mir::InlineAsmOperand::Const {
478                                value: Box::new(ConstOperand {
479                                    span,
480                                    user_ty: None,
481                                    const_: value,
482                                }),
483                            }
484                        }
485                        thir::InlineAsmOperand::SymFn { value, span } => {
486                            mir::InlineAsmOperand::SymFn {
487                                value: Box::new(ConstOperand {
488                                    span,
489                                    user_ty: None,
490                                    const_: value,
491                                }),
492                            }
493                        }
494                        thir::InlineAsmOperand::SymStatic { def_id } => {
495                            mir::InlineAsmOperand::SymStatic { def_id }
496                        }
497                        thir::InlineAsmOperand::Label { block } => {
498                            let target = this.cfg.start_new_block();
499                            let target_index = targets.len();
500                            targets.push(target);
501
502                            let tmp = this.get_unit_temp();
503                            let target =
504                                this.ast_block(tmp, target, block, source_info).into_block();
505                            this.cfg.terminate(
506                                target,
507                                source_info,
508                                TerminatorKind::Goto { target: destination_block },
509                            );
510
511                            mir::InlineAsmOperand::Label { target_index }
512                        }
513                    })
514                    .collect();
515
516                if !expr.ty.is_never() {
517                    this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
518                }
519
520                let asm_macro = match asm_macro {
521                    AsmMacro::Asm => InlineAsmMacro::Asm,
522                    AsmMacro::GlobalAsm => {
523                        span_bug!(expr_span, "unexpected global_asm! in inline asm")
524                    }
525                    AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm,
526                };
527
528                this.cfg.terminate(
529                    block,
530                    source_info,
531                    TerminatorKind::InlineAsm {
532                        asm_macro,
533                        template,
534                        operands,
535                        options,
536                        line_spans,
537                        targets: targets.into_boxed_slice(),
538                        unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) {
539                            UnwindAction::Continue
540                        } else {
541                            UnwindAction::Unreachable
542                        },
543                    },
544                );
545                if options.contains(InlineAsmOptions::MAY_UNWIND) {
546                    this.diverge_from(block);
547                }
548                destination_block.unit()
549            }
550
551            // These cases don't actually need a destination
552            ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
553                block = this.stmt_expr(block, expr_id, None).into_block();
554                this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
555                block.unit()
556            }
557
558            ExprKind::Continue { .. }
559            | ExprKind::Break { .. }
560            | ExprKind::Return { .. }
561            | ExprKind::Become { .. } => {
562                block = this.stmt_expr(block, expr_id, None).into_block();
563                // No assign, as these have type `!`.
564                block.unit()
565            }
566
567            // Avoid creating a temporary
568            ExprKind::VarRef { .. }
569            | ExprKind::UpvarRef { .. }
570            | ExprKind::PlaceTypeAscription { .. }
571            | ExprKind::ValueTypeAscription { .. }
572            | ExprKind::PlaceUnwrapUnsafeBinder { .. }
573            | ExprKind::ValueUnwrapUnsafeBinder { .. } => {
574                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
575
576                let place = unpack!(block = this.as_place(block, expr_id));
577                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
578                this.cfg.push_assign(block, source_info, destination, rvalue);
579                block.unit()
580            }
581            ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
582                debug_assert_eq!(Category::of(&expr.kind), Some(Category::Place));
583
584                // Create a "fake" temporary variable so that we check that the
585                // value is Sized. Usually, this is caught in type checking, but
586                // in the case of box expr there is no such check.
587                if !destination.projection.is_empty() {
588                    this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
589                }
590
591                let place = unpack!(block = this.as_place(block, expr_id));
592                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
593                this.cfg.push_assign(block, source_info, destination, rvalue);
594                block.unit()
595            }
596
597            ExprKind::Yield { value } => {
598                let scope = this.local_temp_lifetime();
599                let value = unpack!(
600                    block =
601                        this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)
602                );
603                let resume = this.cfg.start_new_block();
604                this.cfg.terminate(
605                    block,
606                    source_info,
607                    TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
608                );
609                this.coroutine_drop_cleanup(block);
610                resume.unit()
611            }
612
613            // these are the cases that are more naturally handled by some other mode
614            ExprKind::Unary { .. }
615            | ExprKind::Binary { .. }
616            | ExprKind::Box { .. }
617            | ExprKind::Cast { .. }
618            | ExprKind::PointerCoercion { .. }
619            | ExprKind::Repeat { .. }
620            | ExprKind::Array { .. }
621            | ExprKind::Tuple { .. }
622            | ExprKind::Closure { .. }
623            | ExprKind::ConstBlock { .. }
624            | ExprKind::Literal { .. }
625            | ExprKind::NamedConst { .. }
626            | ExprKind::NonHirLiteral { .. }
627            | ExprKind::ZstLiteral { .. }
628            | ExprKind::ConstParam { .. }
629            | ExprKind::ThreadLocalRef(_)
630            | ExprKind::StaticRef { .. }
631            | ExprKind::OffsetOf { .. }
632            | ExprKind::WrapUnsafeBinder { .. } => {
633                debug_assert!(match Category::of(&expr.kind).unwrap() {
634                    // should be handled above
635                    Category::Rvalue(RvalueFunc::Into) => false,
636
637                    // must be handled above or else we get an
638                    // infinite loop in the builder; see
639                    // e.g., `ExprKind::VarRef` above
640                    Category::Place => false,
641
642                    _ => true,
643                });
644
645                let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
646                this.cfg.push_assign(block, source_info, destination, rvalue);
647                block.unit()
648            }
649        };
650
651        if !expr_is_block_or_scope {
652            let popped = this.block_context.pop();
653            assert!(popped.is_some());
654        }
655
656        block_and
657    }
658
659    fn is_let(&self, expr: ExprId) -> bool {
660        match self.thir[expr].kind {
661            ExprKind::Let { .. } => true,
662            ExprKind::Scope { value, .. } => self.is_let(value),
663            _ => false,
664        }
665    }
666}