rustc_mir_build/build/expr/
as_operand.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! See docs in build/expr/mod.rs

use rustc_middle::mir::*;
use rustc_middle::thir::*;
use tracing::{debug, instrument};

use crate::build::expr::category::Category;
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};

impl<'a, 'tcx> Builder<'a, 'tcx> {
    /// Construct a temporary lifetime restricted to just the local scope
    pub(crate) fn local_temp_lifetime(&self) -> TempLifetime {
        let local_scope = self.local_scope();
        TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }
    }

    /// Returns an operand suitable for use until the end of the current
    /// scope expression.
    ///
    /// The operand returned from this function will *not be valid*
    /// after the current enclosing `ExprKind::Scope` has ended, so
    /// please do *not* return it from functions to avoid bad
    /// miscompiles.
    pub(crate) fn as_local_operand(
        &mut self,
        block: BasicBlock,
        expr_id: ExprId,
    ) -> BlockAnd<Operand<'tcx>> {
        self.as_operand(
            block,
            self.local_temp_lifetime(),
            expr_id,
            LocalInfo::Boring,
            NeedsTemporary::Maybe,
        )
    }

    /// Returns an operand suitable for use until the end of the current scope expression and
    /// suitable also to be passed as function arguments.
    ///
    /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
    /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
    /// operand suitable for use as a call argument. This is almost always equivalent to
    /// `as_operand`, except for the particular case of passing values of (potentially) unsized
    /// types "by value" (see details below).
    ///
    /// The operand returned from this function will *not be valid*
    /// after the current enclosing `ExprKind::Scope` has ended, so
    /// please do *not* return it from functions to avoid bad
    /// miscompiles.
    ///
    /// # Parameters of unsized types
    ///
    /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
    /// local variable of unsized type. For example, consider this program:
    ///
    /// ```
    /// #![feature(unsized_locals, unsized_fn_params)]
    /// # use core::fmt::Debug;
    /// fn foo(p: dyn Debug) { dbg!(p); }
    ///
    /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
    /// ```
    ///
    /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
    ///
    /// ```ignore (illustrative)
    /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
    /// foo(tmp0)
    /// ```
    ///
    /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
    /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
    /// that we create *stores the entire box*, and the parameter to the call itself will be
    /// `*tmp0`:
    ///
    /// ```ignore (illustrative)
    /// let tmp0 = box_p; call foo(*tmp0)
    /// ```
    ///
    /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
    /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
    /// calls are compiled means that this parameter will be passed "by reference", meaning that we
    /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
    /// value to the stack.
    ///
    /// See #68304 for more details.
    pub(crate) fn as_local_call_operand(
        &mut self,
        block: BasicBlock,
        expr: ExprId,
    ) -> BlockAnd<Operand<'tcx>> {
        self.as_call_operand(block, self.local_temp_lifetime(), expr)
    }

    /// Compile `expr` into a value that can be used as an operand.
    /// If `expr` is a place like `x`, this will introduce a
    /// temporary `tmp = x`, so that we capture the value of `x` at
    /// this time.
    ///
    /// If we end up needing to create a temporary, then we will use
    /// `local_info` as its `LocalInfo`, unless `as_temporary`
    /// has already assigned it a non-`None` `LocalInfo`.
    /// Normally, you should use `None` for `local_info`
    ///
    /// The operand is known to be live until the end of `scope`.
    ///
    /// Like `as_local_call_operand`, except that the argument will
    /// not be valid once `scope` ends.
    #[instrument(level = "debug", skip(self, scope))]
    pub(crate) fn as_operand(
        &mut self,
        mut block: BasicBlock,
        scope: TempLifetime,
        expr_id: ExprId,
        local_info: LocalInfo<'tcx>,
        needs_temporary: NeedsTemporary,
    ) -> BlockAnd<Operand<'tcx>> {
        let this = self;

        let expr = &this.thir[expr_id];
        if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
            let source_info = this.source_info(expr.span);
            let region_scope = (region_scope, source_info);
            return this.in_scope(region_scope, lint_level, |this| {
                this.as_operand(block, scope, value, local_info, needs_temporary)
            });
        }

        let category = Category::of(&expr.kind).unwrap();
        debug!(?category, ?expr.kind);
        match category {
            Category::Constant
                if matches!(needs_temporary, NeedsTemporary::No)
                    || !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 = unpack!(block = this.as_temp(block, scope, expr_id, Mutability::Mut));
                // Overwrite temp local info if we have something more interesting to record.
                if !matches!(local_info, LocalInfo::Boring) {
                    let decl_info =
                        this.local_decls[operand].local_info.as_mut().assert_crate_local();
                    if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info {
                        **decl_info = local_info;
                    }
                }
                block.and(Operand::Move(Place::from(operand)))
            }
        }
    }

    pub(crate) fn as_call_operand(
        &mut self,
        mut block: BasicBlock,
        scope: TempLifetime,
        expr_id: ExprId,
    ) -> BlockAnd<Operand<'tcx>> {
        let this = self;
        let expr = &this.thir[expr_id];
        debug!("as_call_operand(block={:?}, expr={:?})", block, expr);

        if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
            let source_info = this.source_info(expr.span);
            let region_scope = (region_scope, source_info);
            return this.in_scope(region_scope, lint_level, |this| {
                this.as_call_operand(block, scope, value)
            });
        }

        let tcx = this.tcx;

        if tcx.features().unsized_fn_params() {
            let ty = expr.ty;
            if !ty.is_sized(tcx, this.typing_env()) {
                // !sized means !copy, so this is an unsized move
                assert!(!ty.is_copy_modulo_regions(tcx, this.typing_env()));

                // As described above, detect the case where we are passing a value of unsized
                // type, and that value is coming from the deref of a box.
                if let ExprKind::Deref { arg } = expr.kind {
                    // Generate let tmp0 = arg0
                    let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));

                    // Return the operand *tmp0 to be used as the call argument
                    let place = Place {
                        local: operand,
                        projection: tcx.mk_place_elems(&[PlaceElem::Deref]),
                    };

                    return block.and(Operand::Move(place));
                }
            }
        }

        this.as_operand(block, scope, expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
    }
}