Skip to main content

rustc_mir_build/builder/expr/
into.rs

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