@@ -78,6 +78,8 @@ use rustc_middle::mir::{self, dump_mir, MirPass};
7878use rustc_middle:: ty:: { self , InstanceKind , Ty , TyCtxt , TypeVisitableExt } ;
7979use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
8080
81+ use crate :: pass_manager:: validate_body;
82+
8183pub struct ByMoveBody ;
8284
8385impl < ' tcx > MirPass < ' tcx > for ByMoveBody {
@@ -131,20 +133,40 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
131133 |( parent_field_idx, parent_capture) , ( child_field_idx, child_capture) | {
132134 // Store this set of additional projections (fields and derefs).
133135 // 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 ( ) ;
136139
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 ,
144156 "`FnOnce` coroutine-closures return coroutines that capture from \
145157 their body; it will always result in a borrowck error!"
146158 ) ;
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.
148170
149171 // Finally, store the type of the parent's captured place. We need
150172 // this when building the field projection in the MIR body later on.
@@ -164,7 +186,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
164186 (
165187 FieldIdx :: from_usize ( parent_field_idx + num_args) ,
166188 parent_capture_ty,
167- needs_deref ,
189+ peel_deref ,
168190 child_precise_captures,
169191 ) ,
170192 )
@@ -192,6 +214,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
192214 let mut by_move_body = body. clone ( ) ;
193215 MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty } . visit_body ( & mut by_move_body) ;
194216 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+
195221 // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body.
196222 by_move_body. source = mir:: MirSource :: from_instance ( InstanceKind :: CoroutineKindShim {
197223 coroutine_def_id : coroutine_def_id. to_def_id ( ) ,
@@ -202,7 +228,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
202228
203229struct MakeByMoveBody < ' tcx > {
204230 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 > > ) > ,
206232 by_move_coroutine_ty : Ty < ' tcx > ,
207233}
208234
@@ -223,14 +249,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
223249 if place. local == ty:: CAPTURE_STRUCT_LOCAL
224250 && let Some ( ( & mir:: ProjectionElem :: Field ( idx, _) , projection) ) =
225251 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) ) =
227253 self . field_remapping . get ( & idx)
228254 {
229255 // As noted before, if the parent closure captures a field by value, and
230256 // the child captures a field by ref, then for the by-move body we're
231257 // generating, we also are taking that field by value. Peel off a deref,
232258 // since a layer of ref'ing has now become redundant.
233- let final_projections = if needs_deref {
259+ let final_projections = if peel_deref {
234260 let Some ( ( mir:: ProjectionElem :: Deref , projection) ) = projection. split_first ( )
235261 else {
236262 bug ! (
0 commit comments