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