Skip to main content

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::scope::LintLevel;
20use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
21use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
22
23impl<'a, 'tcx> Builder<'a, 'tcx> {
24    /// Compile `expr`, storing the result into `destination`, which
25    /// is assumed to be uninitialized.
26    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("expr_into_dest",
                                    "rustc_mir_build::builder::expr::into",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/into.rs"),
                                    ::tracing_core::__macro_support::Option::Some(26u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::into"),
                                    ::tracing_core::field::FieldSet::new(&["destination",
                                                    "block", "expr_id"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&destination)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr_id)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

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