]>
Commit | Line | Data |
---|---|---|
e9174d1e SL |
1 | //! See docs in build/expr/mod.rs |
2 | ||
9fa01778 | 3 | use crate::build::expr::category::Category; |
04454e1e | 4 | use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; |
ba9703b0 XL |
5 | use rustc_middle::middle::region; |
6 | use rustc_middle::mir::*; | |
17df50a5 | 7 | use rustc_middle::thir::*; |
e9174d1e | 8 | |
dc9dc135 | 9 | impl<'a, 'tcx> Builder<'a, 'tcx> { |
8bb4bdeb XL |
10 | /// Returns an operand suitable for use until the end of the current |
11 | /// scope expression. | |
12 | /// | |
ba9703b0 XL |
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. | |
923072b8 | 17 | pub(crate) fn as_local_operand( |
6a06907d XL |
18 | &mut self, |
19 | block: BasicBlock, | |
17df50a5 | 20 | expr: &Expr<'tcx>, |
6a06907d | 21 | ) -> BlockAnd<Operand<'tcx>> { |
7cac9316 | 22 | let local_scope = self.local_scope(); |
04454e1e | 23 | self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe) |
8bb4bdeb XL |
24 | } |
25 | ||
ba9703b0 XL |
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 | /// | |
04454e1e FG |
45 | /// ``` |
46 | /// #![feature(unsized_locals, unsized_fn_params)] | |
47 | /// # use core::fmt::Debug; | |
48 | /// fn foo(p: dyn Debug) { dbg!(p); } | |
ba9703b0 | 49 | /// |
04454e1e | 50 | /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); } |
ba9703b0 XL |
51 | /// ``` |
52 | /// | |
53 | /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so: | |
54 | /// | |
04454e1e | 55 | /// ```ignore (illustrative) |
ba9703b0 XL |
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 | /// | |
04454e1e | 65 | /// ```ignore (illustrative) |
ba9703b0 XL |
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. | |
923072b8 | 76 | pub(crate) fn as_local_call_operand( |
ba9703b0 XL |
77 | &mut self, |
78 | block: BasicBlock, | |
17df50a5 | 79 | expr: &Expr<'tcx>, |
6a06907d | 80 | ) -> BlockAnd<Operand<'tcx>> { |
ba9703b0 | 81 | let local_scope = self.local_scope(); |
fc512014 | 82 | self.as_call_operand(block, Some(local_scope), expr) |
ba9703b0 XL |
83 | } |
84 | ||
e9174d1e | 85 | /// Compile `expr` into a value that can be used as an operand. |
ff7c6d11 | 86 | /// If `expr` is a place like `x`, this will introduce a |
e9174d1e SL |
87 | /// temporary `tmp = x`, so that we capture the value of `x` at |
88 | /// this time. | |
8bb4bdeb | 89 | /// |
c295e0f8 XL |
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 | /// | |
8bb4bdeb | 95 | /// The operand is known to be live until the end of `scope`. |
6a06907d | 96 | /// |
ba9703b0 XL |
97 | /// Like `as_local_call_operand`, except that the argument will |
98 | /// not be valid once `scope` ends. | |
04454e1e | 99 | #[instrument(level = "debug", skip(self, scope))] |
923072b8 | 100 | pub(crate) fn as_operand( |
b7449926 XL |
101 | &mut self, |
102 | mut block: BasicBlock, | |
103 | scope: Option<region::Scope>, | |
17df50a5 | 104 | expr: &Expr<'tcx>, |
c295e0f8 | 105 | local_info: Option<Box<LocalInfo<'tcx>>>, |
04454e1e | 106 | needs_temporary: NeedsTemporary, |
b7449926 | 107 | ) -> BlockAnd<Operand<'tcx>> { |
e9174d1e SL |
108 | let this = self; |
109 | ||
dfeec247 | 110 | if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { |
041b39d2 | 111 | let source_info = this.source_info(expr.span); |
ea8adc8c | 112 | let region_scope = (region_scope, source_info); |
17df50a5 | 113 | return this.in_scope(region_scope, lint_level, |this| { |
04454e1e | 114 | this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary) |
17df50a5 | 115 | }); |
e9174d1e SL |
116 | } |
117 | ||
118 | let category = Category::of(&expr.kind).unwrap(); | |
04454e1e | 119 | debug!(?category, ?expr.kind); |
e9174d1e | 120 | match category { |
04454e1e | 121 | Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => { |
e9174d1e | 122 | let constant = this.as_constant(expr); |
94222f64 | 123 | block.and(Operand::Constant(Box::new(constant))) |
e9174d1e | 124 | } |
04454e1e | 125 | Category::Constant | Category::Place | Category::Rvalue(..) => { |
b7449926 | 126 | let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut)); |
c295e0f8 XL |
127 | if this.local_decls[operand].local_info.is_none() { |
128 | this.local_decls[operand].local_info = local_info; | |
129 | } | |
dc9dc135 | 130 | block.and(Operand::Move(Place::from(operand))) |
e9174d1e SL |
131 | } |
132 | } | |
133 | } | |
ba9703b0 | 134 | |
923072b8 | 135 | pub(crate) fn as_call_operand( |
ba9703b0 XL |
136 | &mut self, |
137 | mut block: BasicBlock, | |
138 | scope: Option<region::Scope>, | |
17df50a5 | 139 | expr: &Expr<'tcx>, |
ba9703b0 | 140 | ) -> BlockAnd<Operand<'tcx>> { |
6a06907d | 141 | debug!("as_call_operand(block={:?}, expr={:?})", block, expr); |
ba9703b0 XL |
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| { | |
17df50a5 | 148 | this.as_call_operand(block, scope, &this.thir[value]) |
ba9703b0 XL |
149 | }); |
150 | } | |
151 | ||
6a06907d | 152 | let tcx = this.tcx; |
ba9703b0 | 153 | |
29967ef6 | 154 | if tcx.features().unsized_fn_params { |
ba9703b0 | 155 | let ty = expr.ty; |
6a06907d | 156 | let param_env = this.param_env; |
ba9703b0 | 157 | |
2b03887a | 158 | if !ty.is_sized(tcx, param_env) { |
ba9703b0 | 159 | // !sized means !copy, so this is an unsized move |
2b03887a | 160 | assert!(!ty.is_copy_modulo_regions(tcx, param_env)); |
ba9703b0 XL |
161 | |
162 | // As described above, detect the case where we are passing a value of unsized | |
163 | // type, and that value is coming from the deref of a box. | |
6a06907d | 164 | if let ExprKind::Deref { arg } = expr.kind { |
ba9703b0 | 165 | // Generate let tmp0 = arg0 |
17df50a5 XL |
166 | let operand = unpack!( |
167 | block = this.as_temp(block, scope, &this.thir[arg], Mutability::Mut) | |
168 | ); | |
ba9703b0 XL |
169 | |
170 | // Return the operand *tmp0 to be used as the call argument | |
171 | let place = Place { | |
172 | local: operand, | |
173 | projection: tcx.intern_place_elems(&[PlaceElem::Deref]), | |
174 | }; | |
175 | ||
176 | return block.and(Operand::Move(place)); | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
04454e1e | 181 | this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe) |
ba9703b0 | 182 | } |
e9174d1e | 183 | } |