@@ -21,6 +21,64 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2121 self . as_operand ( block, local_scope, expr)
2222 }
2323
24+ /// Returns an operand suitable for use until the end of the current scope expression and
25+ /// suitable also to be passed as function arguments.
26+ ///
27+ /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
28+ /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
29+ /// operand suitable for use as a call argument. This is almost always equivalent to
30+ /// `as_operand`, except for the particular case of passing values of (potentially) unsized
31+ /// types "by value" (see details below).
32+ ///
33+ /// As with `as_operand`, the operand returned from this function will *not be valid* after an
34+ /// `ExprKind::Scope` is passed, so do not return it from functions.
35+ ///
36+ /// # Parameters of unsized types
37+ ///
38+ /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
39+ /// local variable of unsized type. For example, consider this program:
40+ ///
41+ /// ```rust
42+ /// fn foo(p: dyn Debug) { ... }
43+ ///
44+ /// fn bar(box_p: Box<dyn Debug>) { foo(*p); }
45+ /// ```
46+ ///
47+ /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
48+ ///
49+ /// ```rust
50+ /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
51+ /// foo(tmp0)
52+ /// ```
53+ ///
54+ /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
55+ /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
56+ /// that we create *stores the entire box*, and the parameter to the call itself will be
57+ /// `*tmp0`:
58+ ///
59+ /// ```rust
60+ /// let tmp0 = box_p; call foo(*tmp0)
61+ /// ```
62+ ///
63+ /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
64+ /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
65+ /// calls are compiled means that this parameter will be passed "by reference", meaning that we
66+ /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
67+ /// value to the stack.
68+ ///
69+ /// See #68034 for more details.
70+ crate fn as_local_call_operand < M > (
71+ & mut self ,
72+ block : BasicBlock ,
73+ expr : M ,
74+ ) -> BlockAnd < Operand < ' tcx > >
75+ where
76+ M : Mirror < ' tcx , Output = Expr < ' tcx > > ,
77+ {
78+ let local_scope = self . local_scope ( ) ;
79+ self . as_call_operand ( block, local_scope, expr)
80+ }
81+
2482 /// Compile `expr` into a value that can be used as an operand.
2583 /// If `expr` is a place like `x`, this will introduce a
2684 /// temporary `tmp = x`, so that we capture the value of `x` at
@@ -40,6 +98,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4098 self . expr_as_operand ( block, scope, expr)
4199 }
42100
101+ fn as_call_operand < M > (
102+ & mut self ,
103+ block : BasicBlock ,
104+ scope : Option < region:: Scope > ,
105+ expr : M ,
106+ ) -> BlockAnd < Operand < ' tcx > >
107+ where
108+ M : Mirror < ' tcx , Output = Expr < ' tcx > > ,
109+ {
110+ let expr = self . hir . mirror ( expr) ;
111+ self . expr_as_call_operand ( block, scope, expr)
112+ }
113+
43114 fn expr_as_operand (
44115 & mut self ,
45116 mut block : BasicBlock ,
@@ -69,4 +140,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
69140 }
70141 }
71142 }
143+
144+ fn expr_as_call_operand (
145+ & mut self ,
146+ mut block : BasicBlock ,
147+ scope : Option < region:: Scope > ,
148+ expr : Expr < ' tcx > ,
149+ ) -> BlockAnd < Operand < ' tcx > > {
150+ debug ! ( "expr_as_call_operand(block={:?}, expr={:?})" , block, expr) ;
151+ let this = self ;
152+
153+ if let ExprKind :: Scope { region_scope, lint_level, value } = expr. kind {
154+ let source_info = this. source_info ( expr. span ) ;
155+ let region_scope = ( region_scope, source_info) ;
156+ return this. in_scope ( region_scope, lint_level, |this| {
157+ this. as_call_operand ( block, scope, value)
158+ } ) ;
159+ }
160+
161+ let tcx = this. hir . tcx ( ) ;
162+
163+ if tcx. features ( ) . unsized_locals {
164+ let ty = expr. ty ;
165+ let span = expr. span ;
166+ let param_env = this. hir . param_env ;
167+
168+ if !ty. is_sized ( tcx. at ( span) , param_env) {
169+ // !sized means !copy, so this is an unsized move
170+ assert ! ( !ty. is_copy_modulo_regions( tcx, param_env, span) ) ;
171+
172+ // As described above, detect the case where we are passing a value of unsized
173+ // type, and that value is coming from the deref of a box.
174+ if let ExprKind :: Deref { ref arg } = expr. kind {
175+ let arg = this. hir . mirror ( arg. clone ( ) ) ;
176+
177+ // Generate let tmp0 = arg0
178+ let operand = unpack ! ( block = this. as_temp( block, scope, arg, Mutability :: Mut ) ) ;
179+
180+ // Return the operand *tmp0 to be used as the call argument
181+ let place = Place {
182+ local : operand,
183+ projection : tcx. intern_place_elems ( & [ PlaceElem :: Deref ] ) ,
184+ } ;
185+
186+ return block. and ( Operand :: Move ( place) ) ;
187+ }
188+ }
189+ }
190+
191+ this. expr_as_operand ( block, scope, expr)
192+ }
72193}
0 commit comments