]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir_build/build/expr/as_operand.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / librustc_mir_build / 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};
5 use crate::hair::*;
6 use rustc_middle::middle::region;
7 use rustc_middle::mir::*;
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<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
18 where
19 M: Mirror<'tcx, Output = Expr<'tcx>>,
20 {
21 let local_scope = self.local_scope();
22 self.as_operand(block, local_scope, expr)
23 }
24
25 /// Returns an operand suitable for use until the end of the current scope expression and
26 /// suitable also to be passed as function arguments.
27 ///
28 /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
29 /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
30 /// operand suitable for use as a call argument. This is almost always equivalent to
31 /// `as_operand`, except for the particular case of passing values of (potentially) unsized
32 /// types "by value" (see details below).
33 ///
34 /// The operand returned from this function will *not be valid*
35 /// after the current enclosing `ExprKind::Scope` has ended, so
36 /// please do *not* return it from functions to avoid bad
37 /// miscompiles.
38 ///
39 /// # Parameters of unsized types
40 ///
41 /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
42 /// local variable of unsized type. For example, consider this program:
43 ///
44 /// ```rust
45 /// fn foo(p: dyn Debug) { ... }
46 ///
47 /// fn bar(box_p: Box<dyn Debug>) { foo(*p); }
48 /// ```
49 ///
50 /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
51 ///
52 /// ```rust
53 /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
54 /// foo(tmp0)
55 /// ```
56 ///
57 /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
58 /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
59 /// that we create *stores the entire box*, and the parameter to the call itself will be
60 /// `*tmp0`:
61 ///
62 /// ```rust
63 /// let tmp0 = box_p; call foo(*tmp0)
64 /// ```
65 ///
66 /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
67 /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
68 /// calls are compiled means that this parameter will be passed "by reference", meaning that we
69 /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
70 /// value to the stack.
71 ///
72 /// See #68034 for more details.
73 crate fn as_local_call_operand<M>(
74 &mut self,
75 block: BasicBlock,
76 expr: M,
77 ) -> BlockAnd<Operand<'tcx>>
78 where
79 M: Mirror<'tcx, Output = Expr<'tcx>>,
80 {
81 let local_scope = self.local_scope();
82 self.as_call_operand(block, 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 /// The operand is known to be live until the end of `scope`.
91 crate fn as_operand<M>(
92 &mut self,
93 block: BasicBlock,
94 scope: Option<region::Scope>,
95 expr: M,
96 ) -> BlockAnd<Operand<'tcx>>
97 where
98 M: Mirror<'tcx, Output = Expr<'tcx>>,
99 {
100 let expr = self.hir.mirror(expr);
101 self.expr_as_operand(block, scope, expr)
102 }
103
104 /// Like `as_local_call_operand`, except that the argument will
105 /// not be valid once `scope` ends.
106 fn as_call_operand<M>(
107 &mut self,
108 block: BasicBlock,
109 scope: Option<region::Scope>,
110 expr: M,
111 ) -> BlockAnd<Operand<'tcx>>
112 where
113 M: Mirror<'tcx, Output = Expr<'tcx>>,
114 {
115 let expr = self.hir.mirror(expr);
116 self.expr_as_call_operand(block, scope, expr)
117 }
118
119 fn expr_as_operand(
120 &mut self,
121 mut block: BasicBlock,
122 scope: Option<region::Scope>,
123 expr: Expr<'tcx>,
124 ) -> BlockAnd<Operand<'tcx>> {
125 debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
126 let this = self;
127
128 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
129 let source_info = this.source_info(expr.span);
130 let region_scope = (region_scope, source_info);
131 return this
132 .in_scope(region_scope, lint_level, |this| this.as_operand(block, scope, value));
133 }
134
135 let category = Category::of(&expr.kind).unwrap();
136 debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind);
137 match category {
138 Category::Constant => {
139 let constant = this.as_constant(expr);
140 block.and(Operand::Constant(box constant))
141 }
142 Category::Place | Category::Rvalue(..) => {
143 let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
144 block.and(Operand::Move(Place::from(operand)))
145 }
146 }
147 }
148
149 fn expr_as_call_operand(
150 &mut self,
151 mut block: BasicBlock,
152 scope: Option<region::Scope>,
153 expr: Expr<'tcx>,
154 ) -> BlockAnd<Operand<'tcx>> {
155 debug!("expr_as_call_operand(block={:?}, expr={:?})", block, expr);
156 let this = self;
157
158 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
159 let source_info = this.source_info(expr.span);
160 let region_scope = (region_scope, source_info);
161 return this.in_scope(region_scope, lint_level, |this| {
162 this.as_call_operand(block, scope, value)
163 });
164 }
165
166 let tcx = this.hir.tcx();
167
168 if tcx.features().unsized_locals {
169 let ty = expr.ty;
170 let span = expr.span;
171 let param_env = this.hir.param_env;
172
173 if !ty.is_sized(tcx.at(span), param_env) {
174 // !sized means !copy, so this is an unsized move
175 assert!(!ty.is_copy_modulo_regions(tcx.at(span), param_env));
176
177 // As described above, detect the case where we are passing a value of unsized
178 // type, and that value is coming from the deref of a box.
179 if let ExprKind::Deref { ref arg } = expr.kind {
180 let arg = this.hir.mirror(arg.clone());
181
182 // Generate let tmp0 = arg0
183 let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
184
185 // Return the operand *tmp0 to be used as the call argument
186 let place = Place {
187 local: operand,
188 projection: tcx.intern_place_elems(&[PlaceElem::Deref]),
189 };
190
191 return block.and(Operand::Move(place));
192 }
193 }
194 }
195
196 this.expr_as_operand(block, scope, expr)
197 }
198 }