@@ -78,6 +78,8 @@ use rustc_middle::mir::{self, dump_mir, MirPass};
78
78
use rustc_middle:: ty:: { self , InstanceKind , Ty , TyCtxt , TypeVisitableExt } ;
79
79
use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
80
80
81
+ use crate :: pass_manager:: validate_body;
82
+
81
83
pub struct ByMoveBody ;
82
84
83
85
impl < ' tcx > MirPass < ' tcx > for ByMoveBody {
@@ -131,20 +133,40 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
131
133
|( parent_field_idx, parent_capture) , ( child_field_idx, child_capture) | {
132
134
// Store this set of additional projections (fields and derefs).
133
135
// We need to re-apply them later.
134
- let child_precise_captures =
135
- & child_capture. place . projections [ parent_capture. place . projections . len ( ) ..] ;
136
+ let mut child_precise_captures = child_capture. place . projections
137
+ [ parent_capture. place . projections . len ( ) ..]
138
+ . to_vec ( ) ;
136
139
137
- // If the parent captures by-move, and the child captures by-ref, then we
138
- // need to peel an additional `deref` off of the body of the child.
139
- let needs_deref = child_capture. is_by_ref ( ) && !parent_capture. is_by_ref ( ) ;
140
- if needs_deref {
141
- assert_ne ! (
142
- coroutine_kind,
143
- ty:: ClosureKind :: FnOnce ,
140
+ // If the parent capture is by-ref, then we need to apply an additional
141
+ // deref before applying any further projections to this place.
142
+ if parent_capture. is_by_ref ( ) {
143
+ child_precise_captures. insert (
144
+ 0 ,
145
+ Projection { ty : parent_capture. place . ty ( ) , kind : ProjectionKind :: Deref } ,
146
+ ) ;
147
+ }
148
+ // If the child capture is by-ref, then we need to apply a "ref"
149
+ // projection (i.e. `&`) at the end. But wait! We don't have that
150
+ // as a projection kind. So instead, we can apply its dual and
151
+ // *peel* a deref off of the place when it shows up in the MIR body.
152
+ // Luckily, by construction this is always possible.
153
+ let peel_deref = if child_capture. is_by_ref ( ) {
154
+ assert ! (
155
+ parent_capture. is_by_ref( ) || coroutine_kind != ty:: ClosureKind :: FnOnce ,
144
156
"`FnOnce` coroutine-closures return coroutines that capture from \
145
157
their body; it will always result in a borrowck error!"
146
158
) ;
147
- }
159
+ true
160
+ } else {
161
+ false
162
+ } ;
163
+
164
+ // Regarding the behavior above, you may think that it's redundant to both
165
+ // insert a deref and then peel a deref if the parent and child are both
166
+ // captured by-ref. This would be correct, except for the case where we have
167
+ // precise capturing projections, since the inserted deref is to the *beginning*
168
+ // and the peeled deref is at the *end*. I cannot seem to actually find a
169
+ // case where this happens, though, but let's keep this code flexible.
148
170
149
171
// Finally, store the type of the parent's captured place. We need
150
172
// this when building the field projection in the MIR body later on.
@@ -164,7 +186,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
164
186
(
165
187
FieldIdx :: from_usize ( parent_field_idx + num_args) ,
166
188
parent_capture_ty,
167
- needs_deref ,
189
+ peel_deref ,
168
190
child_precise_captures,
169
191
) ,
170
192
)
@@ -192,6 +214,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
192
214
let mut by_move_body = body. clone ( ) ;
193
215
MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty } . visit_body ( & mut by_move_body) ;
194
216
dump_mir ( tcx, false , "coroutine_by_move" , & 0 , & by_move_body, |_, _| Ok ( ( ) ) ) ;
217
+
218
+ // Let's just always validate this body.
219
+ validate_body ( tcx, & mut by_move_body, "Initial coroutine_by_move body" . to_string ( ) ) ;
220
+
195
221
// FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body.
196
222
by_move_body. source = mir:: MirSource :: from_instance ( InstanceKind :: CoroutineKindShim {
197
223
coroutine_def_id : coroutine_def_id. to_def_id ( ) ,
@@ -202,7 +228,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
202
228
203
229
struct MakeByMoveBody < ' tcx > {
204
230
tcx : TyCtxt < ' tcx > ,
205
- field_remapping : UnordMap < FieldIdx , ( FieldIdx , Ty < ' tcx > , bool , & ' tcx [ Projection < ' tcx > ] ) > ,
231
+ field_remapping : UnordMap < FieldIdx , ( FieldIdx , Ty < ' tcx > , bool , Vec < Projection < ' tcx > > ) > ,
206
232
by_move_coroutine_ty : Ty < ' tcx > ,
207
233
}
208
234
@@ -223,14 +249,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
223
249
if place. local == ty:: CAPTURE_STRUCT_LOCAL
224
250
&& let Some ( ( & mir:: ProjectionElem :: Field ( idx, _) , projection) ) =
225
251
place. projection . split_first ( )
226
- && let Some ( & ( remapped_idx, remapped_ty, needs_deref , bridging_projections) ) =
252
+ && let Some ( & ( remapped_idx, remapped_ty, peel_deref , ref bridging_projections) ) =
227
253
self . field_remapping . get ( & idx)
228
254
{
229
255
// As noted before, if the parent closure captures a field by value, and
230
256
// the child captures a field by ref, then for the by-move body we're
231
257
// generating, we also are taking that field by value. Peel off a deref,
232
258
// since a layer of ref'ing has now become redundant.
233
- let final_projections = if needs_deref {
259
+ let final_projections = if peel_deref {
234
260
let Some ( ( mir:: ProjectionElem :: Deref , projection) ) = projection. split_first ( )
235
261
else {
236
262
bug ! (
0 commit comments