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