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