rustc_mir_build/builder/expr/
as_operand.rs

1//! See docs in build/expr/mod.rs
2
3use rustc_middle::mir::*;
4use rustc_middle::thir::*;
5use tracing::{debug, instrument};
6
7use crate::builder::expr::category::Category;
8use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
9
10impl<'a, 'tcx> Builder<'a, 'tcx> {
11    /// Construct a temporary lifetime restricted to just the local scope
12    pub(crate) fn local_temp_lifetime(&self) -> TempLifetime {
13        let local_scope = self.local_scope();
14        TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }
15    }
16
17    /// Returns an operand suitable for use until the end of the current
18    /// scope expression.
19    ///
20    /// The operand returned from this function will *not be valid*
21    /// after the current enclosing `ExprKind::Scope` has ended, so
22    /// please do *not* return it from functions to avoid bad
23    /// miscompiles.
24    pub(crate) fn as_local_operand(
25        &mut self,
26        block: BasicBlock,
27        expr_id: ExprId,
28    ) -> BlockAnd<Operand<'tcx>> {
29        self.as_operand(
30            block,
31            self.local_temp_lifetime(),
32            expr_id,
33            LocalInfo::Boring,
34            NeedsTemporary::Maybe,
35        )
36    }
37
38    /// Returns an operand suitable for use until the end of the current scope expression and
39    /// suitable also to be passed as function arguments.
40    ///
41    /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
42    /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
43    /// operand suitable for use as a call argument. This is almost always equivalent to
44    /// `as_operand`, except for the particular case of passing values of (potentially) unsized
45    /// types "by value" (see details below).
46    ///
47    /// The operand returned from this function will *not be valid*
48    /// after the current enclosing `ExprKind::Scope` has ended, so
49    /// please do *not* return it from functions to avoid bad
50    /// miscompiles.
51    ///
52    /// # Parameters of unsized types
53    ///
54    /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
55    /// local variable of unsized type. For example, consider this program:
56    ///
57    /// ```
58    /// #![feature(unsized_locals, unsized_fn_params)]
59    /// # use core::fmt::Debug;
60    /// fn foo(p: dyn Debug) { dbg!(p); }
61    ///
62    /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
63    /// ```
64    ///
65    /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
66    ///
67    /// ```ignore (illustrative)
68    /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
69    /// foo(tmp0)
70    /// ```
71    ///
72    /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
73    /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
74    /// that we create *stores the entire box*, and the parameter to the call itself will be
75    /// `*tmp0`:
76    ///
77    /// ```ignore (illustrative)
78    /// let tmp0 = box_p; call foo(*tmp0)
79    /// ```
80    ///
81    /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
82    /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
83    /// calls are compiled means that this parameter will be passed "by reference", meaning that we
84    /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
85    /// value to the stack.
86    ///
87    /// See #68304 for more details.
88    pub(crate) fn as_local_call_operand(
89        &mut self,
90        block: BasicBlock,
91        expr: ExprId,
92    ) -> BlockAnd<Operand<'tcx>> {
93        self.as_call_operand(block, self.local_temp_lifetime(), expr)
94    }
95
96    /// Compile `expr` into a value that can be used as an operand.
97    /// If `expr` is a place like `x`, this will introduce a
98    /// temporary `tmp = x`, so that we capture the value of `x` at
99    /// this time.
100    ///
101    /// If we end up needing to create a temporary, then we will use
102    /// `local_info` as its `LocalInfo`, unless `as_temporary`
103    /// has already assigned it a non-`None` `LocalInfo`.
104    /// Normally, you should use `None` for `local_info`
105    ///
106    /// The operand is known to be live until the end of `scope`.
107    ///
108    /// Like `as_local_call_operand`, except that the argument will
109    /// not be valid once `scope` ends.
110    #[instrument(level = "debug", skip(self, scope))]
111    pub(crate) fn as_operand(
112        &mut self,
113        mut block: BasicBlock,
114        scope: TempLifetime,
115        expr_id: ExprId,
116        local_info: LocalInfo<'tcx>,
117        needs_temporary: NeedsTemporary,
118    ) -> BlockAnd<Operand<'tcx>> {
119        let this = self;
120
121        let expr = &this.thir[expr_id];
122        if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
123            let source_info = this.source_info(expr.span);
124            let region_scope = (region_scope, source_info);
125            return this.in_scope(region_scope, lint_level, |this| {
126                this.as_operand(block, scope, value, local_info, needs_temporary)
127            });
128        }
129
130        let category = Category::of(&expr.kind).unwrap();
131        debug!(?category, ?expr.kind);
132        match category {
133            Category::Constant
134                if matches!(needs_temporary, NeedsTemporary::No)
135                    || !expr.ty.needs_drop(this.tcx, this.typing_env()) =>
136            {
137                let constant = this.as_constant(expr);
138                block.and(Operand::Constant(Box::new(constant)))
139            }
140            Category::Constant | Category::Place | Category::Rvalue(..) => {
141                let operand = unpack!(block = this.as_temp(block, scope, expr_id, Mutability::Mut));
142                // Overwrite temp local info if we have something more interesting to record.
143                if !matches!(local_info, LocalInfo::Boring) {
144                    let decl_info =
145                        this.local_decls[operand].local_info.as_mut().assert_crate_local();
146                    if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info {
147                        **decl_info = local_info;
148                    }
149                }
150                block.and(Operand::Move(Place::from(operand)))
151            }
152        }
153    }
154
155    pub(crate) fn as_call_operand(
156        &mut self,
157        mut block: BasicBlock,
158        scope: TempLifetime,
159        expr_id: ExprId,
160    ) -> BlockAnd<Operand<'tcx>> {
161        let this = self;
162        let expr = &this.thir[expr_id];
163        debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
164
165        if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
166            let source_info = this.source_info(expr.span);
167            let region_scope = (region_scope, source_info);
168            return this.in_scope(region_scope, lint_level, |this| {
169                this.as_call_operand(block, scope, value)
170            });
171        }
172
173        let tcx = this.tcx;
174
175        if tcx.features().unsized_fn_params() {
176            let ty = expr.ty;
177            if !ty.is_sized(tcx, this.typing_env()) {
178                // !sized means !copy, so this is an unsized move
179                assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty));
180
181                // As described above, detect the case where we are passing a value of unsized
182                // type, and that value is coming from the deref of a box.
183                if let ExprKind::Deref { arg } = expr.kind {
184                    // Generate let tmp0 = arg0
185                    let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
186
187                    // Return the operand *tmp0 to be used as the call argument
188                    let place = Place {
189                        local: operand,
190                        projection: tcx.mk_place_elems(&[PlaceElem::Deref]),
191                    };
192
193                    return block.and(Operand::Move(place));
194                }
195            }
196        }
197
198        this.as_operand(block, scope, expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
199    }
200}