Skip to main content

rustc_mir_build/builder/expr/
as_operand.rs

1//! See docs in build/expr/mod.rs
2
3use rustc_middle::middle::region::TempLifetime;
4use rustc_middle::mir::*;
5use rustc_middle::thir::*;
6use tracing::{debug, instrument};
7
8use crate::builder::expr::category::Category;
9use crate::builder::scope::LintLevel;
10use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
11
12impl<'a, 'tcx> Builder<'a, 'tcx> {
13    /// Construct a temporary lifetime restricted to just the local scope
14    pub(crate) fn local_temp_lifetime(&self) -> TempLifetime {
15        let local_scope = self.local_scope();
16        TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }
17    }
18
19    /// Returns an operand suitable for use until the end of the current
20    /// scope expression.
21    ///
22    /// The operand returned from this function will *not be valid*
23    /// after the current enclosing `ExprKind::Scope` has ended, so
24    /// please do *not* return it from functions to avoid bad
25    /// miscompiles.
26    pub(crate) fn as_local_operand(
27        &mut self,
28        block: BasicBlock,
29        expr_id: ExprId,
30    ) -> BlockAnd<Operand<'tcx>> {
31        self.as_operand(
32            block,
33            self.local_temp_lifetime(),
34            expr_id,
35            LocalInfo::Boring,
36            NeedsTemporary::Maybe,
37        )
38    }
39
40    /// Returns an operand suitable for use until the end of the current scope expression and
41    /// suitable also to be passed as function arguments.
42    ///
43    /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
44    /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
45    /// operand suitable for use as a call argument. This is almost always equivalent to
46    /// `as_operand`, except for the particular case of passing values of (potentially) unsized
47    /// types "by value" (see details below).
48    ///
49    /// The operand returned from this function will *not be valid*
50    /// after the current enclosing `ExprKind::Scope` has ended, so
51    /// please do *not* return it from functions to avoid bad
52    /// miscompiles.
53    ///
54    /// # Parameters of unsized types
55    ///
56    /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
57    /// local variable of unsized type. For example, consider this program:
58    ///
59    /// ```
60    /// #![feature(unsized_fn_params)]
61    /// # use core::fmt::Debug;
62    /// fn foo(_p: dyn Debug) {
63    ///     /* ... */
64    /// }
65    ///
66    /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
67    /// ```
68    ///
69    /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
70    ///
71    /// ```ignore (illustrative)
72    /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
73    /// foo(tmp0)
74    /// ```
75    ///
76    /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
77    /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
78    /// that we create *stores the entire box*, and the parameter to the call itself will be
79    /// `*tmp0`:
80    ///
81    /// ```ignore (illustrative)
82    /// let tmp0 = box_p; call foo(*tmp0)
83    /// ```
84    ///
85    /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
86    /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
87    /// calls are compiled means that this parameter will be passed "by reference", meaning that we
88    /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
89    /// value to the stack.
90    ///
91    /// See <https://github.com/rust-lang/rust/issues/68304> for more details.
92    pub(crate) fn as_local_call_operand(
93        &mut self,
94        block: BasicBlock,
95        expr: ExprId,
96    ) -> BlockAnd<Operand<'tcx>> {
97        self.as_call_operand(block, self.local_temp_lifetime(), expr)
98    }
99
100    /// Compile `expr` into a value that can be used as an operand.
101    /// If `expr` is a place like `x`, this will introduce a
102    /// temporary `tmp = x`, so that we capture the value of `x` at
103    /// this time.
104    ///
105    /// If we end up needing to create a temporary, then we will use
106    /// `local_info` as its `LocalInfo`, unless `as_temporary`
107    /// has already assigned it a non-`None` `LocalInfo`.
108    /// Normally, you should use `None` for `local_info`
109    ///
110    /// The operand is known to be live until the end of `scope`.
111    ///
112    /// Like `as_local_call_operand`, except that the argument will
113    /// not be valid once `scope` ends.
114    #[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("as_operand",
                                    "rustc_mir_build::builder::expr::as_operand",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
                                    ::tracing_core::__macro_support::Option::Some(114u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
                                    ::tracing_core::field::FieldSet::new(&["block", "expr_id",
                                                    "local_info", "needs_temporary"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr_id)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&local_info)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&needs_temporary)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: BlockAnd<Operand<'tcx>> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let this = self;
            let expr = &this.thir[expr_id];
            if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind
                {
                let source_info = this.source_info(expr.span);
                let region_scope = (region_scope, source_info);
                return this.in_scope(region_scope,
                        LintLevel::Explicit(hir_id),
                        |this|
                            {
                                this.as_operand(block, scope, value, local_info,
                                    needs_temporary)
                            });
            }
            let category = Category::of(&expr.kind).unwrap();
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/as_operand.rs:135",
                                    "rustc_mir_build::builder::expr::as_operand",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
                                    ::tracing_core::__macro_support::Option::Some(135u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
                                    ::tracing_core::field::FieldSet::new(&["category",
                                                    "expr.kind"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::EVENT)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let enabled =
                    ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        {
                            let interest = __CALLSITE.interest();
                            !interest.is_never() &&
                                ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                    interest)
                        };
                if enabled {
                    (|value_set: ::tracing::field::ValueSet|
                                {
                                    let meta = __CALLSITE.metadata();
                                    ::tracing::Event::dispatch(meta, &value_set);
                                    ;
                                })({
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            let mut iter = __CALLSITE.metadata().fields().iter();
                            __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                ::tracing::__macro_support::Option::Some(&debug(&category)
                                                        as &dyn Value)),
                                            (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                ::tracing::__macro_support::Option::Some(&debug(&expr.kind)
                                                        as &dyn Value))])
                        });
                } else { ; }
            };
            match category {
                Category::Constant if
                    #[allow(non_exhaustive_omitted_patterns)] match needs_temporary
                            {
                            NeedsTemporary::No => true,
                            _ => false,
                        } || !expr.ty.needs_drop(this.tcx, this.typing_env()) => {
                    let constant = this.as_constant(expr);
                    block.and(Operand::Constant(Box::new(constant)))
                }
                Category::Constant | Category::Place | Category::Rvalue(..) =>
                    {
                    let operand =
                        {
                            let BlockAnd(b, v) =
                                this.as_temp(block, scope, expr_id, Mutability::Mut);
                            block = b;
                            v
                        };
                    if !#[allow(non_exhaustive_omitted_patterns)] match local_info
                                {
                                LocalInfo::Boring => true,
                                _ => false,
                            } {
                        let decl_info =
                            this.local_decls[operand].local_info.as_mut().unwrap_crate_local();
                        if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) =
                                **decl_info {
                            **decl_info = local_info;
                        }
                    }
                    block.and(Operand::Move(Place::from(operand)))
                }
            }
        }
    }
}#[instrument(level = "debug", skip(self, scope))]
115    pub(crate) fn as_operand(
116        &mut self,
117        mut block: BasicBlock,
118        scope: TempLifetime,
119        expr_id: ExprId,
120        local_info: LocalInfo<'tcx>,
121        needs_temporary: NeedsTemporary,
122    ) -> BlockAnd<Operand<'tcx>> {
123        let this = self; // See "LET_THIS_SELF".
124
125        let expr = &this.thir[expr_id];
126        if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind {
127            let source_info = this.source_info(expr.span);
128            let region_scope = (region_scope, source_info);
129            return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
130                this.as_operand(block, scope, value, local_info, needs_temporary)
131            });
132        }
133
134        let category = Category::of(&expr.kind).unwrap();
135        debug!(?category, ?expr.kind);
136        match category {
137            Category::Constant
138                if matches!(needs_temporary, NeedsTemporary::No)
139                    || !expr.ty.needs_drop(this.tcx, this.typing_env()) =>
140            {
141                let constant = this.as_constant(expr);
142                block.and(Operand::Constant(Box::new(constant)))
143            }
144            Category::Constant | Category::Place | Category::Rvalue(..) => {
145                let operand = unpack!(block = this.as_temp(block, scope, expr_id, Mutability::Mut));
146                // Overwrite temp local info if we have something more interesting to record.
147                if !matches!(local_info, LocalInfo::Boring) {
148                    let decl_info =
149                        this.local_decls[operand].local_info.as_mut().unwrap_crate_local();
150                    if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info {
151                        **decl_info = local_info;
152                    }
153                }
154                block.and(Operand::Move(Place::from(operand)))
155            }
156        }
157    }
158
159    pub(crate) fn as_call_operand(
160        &mut self,
161        mut block: BasicBlock,
162        scope: TempLifetime,
163        expr_id: ExprId,
164    ) -> BlockAnd<Operand<'tcx>> {
165        let this = self; // See "LET_THIS_SELF".
166        let expr = &this.thir[expr_id];
167        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/as_operand.rs:167",
                        "rustc_mir_build::builder::expr::as_operand",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
                        ::tracing_core::__macro_support::Option::Some(167u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("as_call_operand(block={0:?}, expr={1:?})",
                                                    block, expr) as &dyn Value))])
            });
    } else { ; }
};debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
168
169        if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind {
170            let source_info = this.source_info(expr.span);
171            let region_scope = (region_scope, source_info);
172            return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
173                this.as_call_operand(block, scope, value)
174            });
175        }
176
177        let tcx = this.tcx;
178
179        if tcx.features().unsized_fn_params() {
180            let ty = expr.ty;
181            if !ty.is_sized(tcx, this.typing_env()) {
182                // !sized means !copy, so this is an unsized move
183                if !!tcx.type_is_copy_modulo_regions(this.typing_env(), ty) {
    ::core::panicking::panic("assertion failed: !tcx.type_is_copy_modulo_regions(this.typing_env(), ty)")
};assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty));
184
185                // As described above, detect the case where we are passing a value of unsized
186                // type, and that value is coming from the deref of a box.
187                if let ExprKind::Deref { arg } = expr.kind {
188                    // Generate let tmp0 = arg0
189                    let operand = {
    let BlockAnd(b, v) = this.as_temp(block, scope, arg, Mutability::Mut);
    block = b;
    v
}unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
190
191                    // Return the operand *tmp0 to be used as the call argument
192                    let place = Place {
193                        local: operand,
194                        projection: tcx.mk_place_elems(&[PlaceElem::Deref]),
195                    };
196
197                    return block.and(Operand::Move(place));
198                }
199            }
200        }
201
202        this.as_operand(block, scope, expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
203    }
204}