@@ -586,7 +586,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
586586                                                context :  Context , 
587587                                                ( lvalue,  span) :  ( & Lvalue < ' gcx > ,  Span ) , 
588588                                                flow_state :  & InProgress < ' b ,  ' gcx > )  { 
589-         let  move_data = flow_state . inits . base_results . operator ( ) . move_data ( ) ; 
589+         let  move_data = self . move_data ; 
590590
591591        // determine if this path has a non-mut owner (and thus needs checking). 
592592        let  mut  l = lvalue; 
@@ -611,7 +611,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
611611            } 
612612        } 
613613
614-         if  let  Some ( mpi)  = self . move_path_for_lvalue ( context ,  move_data ,   lvalue)  { 
614+         if  let  Some ( mpi)  = self . move_path_for_lvalue ( lvalue)  { 
615615            if  flow_state. inits . curr_state . contains ( & mpi)  { 
616616                // may already be assigned before reaching this statement; 
617617                // report error. 
@@ -642,29 +642,115 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
642642        let  lvalue = self . base_path ( lvalue_span. 0 ) ; 
643643
644644        let  maybe_uninits = & flow_state. uninits ; 
645-         let  move_data = maybe_uninits. base_results . operator ( ) . move_data ( ) ; 
646-         if  let  Some ( mpi)  = self . move_path_for_lvalue ( context,  move_data,  lvalue)  { 
647-             if  maybe_uninits. curr_state . contains ( & mpi)  { 
648-                 // find and report move(s) that could cause this to be uninitialized 
645+ 
646+         // Bad scenarios: 
647+         // 
648+         // 1. Move of `a.b.c`, use of `a.b.c` 
649+         // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`) 
650+         // 3. Move of `a.b.c`, use of `a` or `a.b` 
651+         // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with 
652+         //    partial initialization support, one might have `a.x` 
653+         //    initialized but not `a.b`. 
654+         // 
655+         // OK scenarios: 
656+         // 
657+         // 5. Move of `a.b.c`, use of `a.b.d` 
658+         // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b` 
659+         // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b` 
660+         //    must have been initialized for the use to be sound. 
661+         // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d` 
662+ 
663+         // The dataflow tracks shallow prefixes distinctly (that is, 
664+         // field-accesses on P distinctly from P itself), in order to 
665+         // track substructure initialization separately from the whole 
666+         // structure. 
667+         // 
668+         // E.g., when looking at (*a.b.c).d, if the closest prefix for 
669+         // which we have a MovePath is `a.b`, then that means that the 
670+         // initialization state of `a.b` is all we need to inspect to 
671+         // know if `a.b.c` is valid (and from that we infer that the 
672+         // dereference and `.d` access is also valid, since we assume 
673+         // `a.b.c` is assigned a reference to a initialized and 
674+         // well-formed record structure.) 
675+ 
676+         // Therefore, if we seek out the *closest* prefix for which we 
677+         // have a MovePath, that should capture the initialization 
678+         // state for the lvalue scenario. 
679+         // 
680+         // This code covers scenarios 1, 2, and 4. 
681+ 
682+         debug ! ( "check_if_path_is_moved part1 lvalue: {:?}" ,  lvalue) ; 
683+         match  self . move_path_closest_to ( lvalue)  { 
684+             Ok ( mpi)  => { 
685+                 if  maybe_uninits. curr_state . contains ( & mpi)  { 
686+                     self . report_use_of_moved ( context,  desired_action,  lvalue_span) ; 
687+                     return ;  // don't bother finding other problems. 
688+                 } 
689+             } 
690+             Err ( NoMovePathFound :: ReachedStatic )  => { 
691+                 // Okay: we do not build MoveData for static variables 
692+             } 
693+ 
694+             // Only query longest prefix with a MovePath, not further 
695+             // ancestors; dataflow recurs on children when parents 
696+             // move (to support partial (re)inits). 
697+             // 
698+             // (I.e. querying parents breaks scenario 8; but may want 
699+             // to do such a query based on partial-init feature-gate.) 
700+         } 
701+ 
702+         // A move of any shallow suffix of `lvalue` also interferes 
703+         // with an attempt to use `lvalue`. This is scenario 3 above. 
704+         // 
705+         // (Distinct from handling of scenarios 1+2+4 above because 
706+         // `lvalue` does not interfere with suffixes of its prefixes, 
707+         // e.g. `a.b.c` does not interfere with `a.b.d`) 
708+ 
709+         debug ! ( "check_if_path_is_moved part2 lvalue: {:?}" ,  lvalue) ; 
710+         if  let  Some ( mpi)  = self . move_path_for_lvalue ( lvalue)  { 
711+             if  let  Some ( _)  = maybe_uninits. has_any_child_of ( mpi)  { 
649712                self . report_use_of_moved ( context,  desired_action,  lvalue_span) ; 
650-             }  else  { 
651-                 // sanity check: initialized on *some* path, right? 
652-                 assert ! ( flow_state. inits. curr_state. contains( & mpi) ) ; 
713+                 return ;  // don't bother finding other problems. 
653714            } 
654715        } 
655716    } 
656717
718+     /// Currently MoveData does not store entries for all lvalues in 
719+ /// the input MIR. For example it will currently filter out 
720+ /// lvalues that are Copy; thus we do not track lvalues of shared 
721+ /// reference type. This routine will walk up an lvalue along its 
722+ /// prefixes, searching for a foundational lvalue that *is* 
723+ /// tracked in the MoveData. 
724+ /// 
725+ /// An Err result includes a tag indicated why the search failed. 
726+ /// Currenly this can only occur if the lvalue is built off of a 
727+ /// static variable, as we do not track those in the MoveData. 
728+ fn  move_path_closest_to ( & mut  self ,  lvalue :  & Lvalue < ' gcx > ) 
729+                             -> Result < MovePathIndex ,  NoMovePathFound > 
730+     { 
731+         let  mut  last_prefix = lvalue; 
732+         for  prefix in  self . prefixes ( lvalue,  PrefixSet :: All )  { 
733+             if  let  Some ( mpi)  = self . move_path_for_lvalue ( prefix)  { 
734+                 return  Ok ( mpi) ; 
735+             } 
736+             last_prefix = prefix; 
737+         } 
738+         match  * last_prefix { 
739+             Lvalue :: Local ( _)  => panic ! ( "should have move path for every Local" ) , 
740+             Lvalue :: Projection ( _)  => panic ! ( "PrefixSet::All meant dont stop for Projection" ) , 
741+             Lvalue :: Static ( _)  => return  Err ( NoMovePathFound :: ReachedStatic ) , 
742+         } 
743+     } 
744+ 
657745    fn  move_path_for_lvalue ( & mut  self , 
658-                             _context :  Context , 
659-                             move_data :  & MoveData < ' gcx > , 
660746                            lvalue :  & Lvalue < ' gcx > ) 
661747                            -> Option < MovePathIndex > 
662748    { 
663749        // If returns None, then there is no move path corresponding 
664750        // to a direct owner of `lvalue` (which means there is nothing 
665751        // that borrowck tracks for its analysis). 
666752
667-         match  move_data. rev_lookup . find ( lvalue)  { 
753+         match  self . move_data . rev_lookup . find ( lvalue)  { 
668754            LookupResult :: Parent ( _)  => None , 
669755            LookupResult :: Exact ( mpi)  => Some ( mpi) , 
670756        } 
@@ -733,6 +819,11 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
733819    } 
734820} 
735821
822+ #[ derive( Copy ,  Clone ,  PartialEq ,  Eq ,  Debug ) ]  
823+ enum  NoMovePathFound  { 
824+     ReachedStatic , 
825+ } 
826+ 
736827impl < ' c ,  ' b ,  ' a :  ' b +' c ,  ' gcx ,  ' tcx :  ' a >  MirBorrowckCtxt < ' c ,  ' b ,  ' a ,  ' gcx ,  ' tcx >  { 
737828    fn  each_borrow_involving_path < F > ( & mut  self , 
738829                                     _context :  Context , 
@@ -846,12 +937,19 @@ mod prefixes {
846937
847938    #[ derive( Copy ,  Clone ,  PartialEq ,  Eq ,  Debug ) ]  
848939    pub ( super )  enum  PrefixSet  { 
940+         /// Doesn't stop until it returns the base case (a Local or 
941+ /// Static prefix). 
849942All , 
943+         /// Stops at any dereference. 
850944Shallow , 
945+         /// Stops at the deref of a shared reference. 
851946Supporting , 
852947    } 
853948
854949    impl < ' c ,  ' b ,  ' a :  ' b +' c ,  ' gcx ,  ' tcx :  ' a >  MirBorrowckCtxt < ' c ,  ' b ,  ' a ,  ' gcx ,  ' tcx >  { 
950+         /// Returns an iterator over the prefixes of `lvalue` 
951+ /// (inclusive) from longest to smallest, potentially 
952+ /// terminating the iteration early based on `kind`. 
855953pub ( super )  fn  prefixes < ' d > ( & self , 
856954                                   lvalue :  & ' d  Lvalue < ' gcx > , 
857955                                   kind :  PrefixSet ) 
@@ -1340,6 +1438,35 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
13401438    } 
13411439} 
13421440
1441+ impl < ' b ,  ' tcx >  FlowInProgress < MaybeUninitializedLvals < ' b ,  ' tcx > >  { 
1442+     fn  has_any_child_of ( & self ,  mpi :  MovePathIndex )  -> Option < MovePathIndex >  { 
1443+         let  move_data = self . base_results . operator ( ) . move_data ( ) ; 
1444+ 
1445+         let  mut  todo = vec ! [ mpi] ; 
1446+         let  mut  push_siblings = false ;  // don't look at siblings of original `mpi`. 
1447+         while  let  Some ( mpi)  = todo. pop ( )  { 
1448+             if  self . curr_state . contains ( & mpi)  { 
1449+                 return  Some ( mpi) ; 
1450+             } 
1451+             let  move_path = & move_data. move_paths [ mpi] ; 
1452+             if  let  Some ( child)  = move_path. first_child  { 
1453+                 todo. push ( child) ; 
1454+             } 
1455+             if  push_siblings { 
1456+                 if  let  Some ( sibling)  = move_path. next_sibling  { 
1457+                     todo. push ( sibling) ; 
1458+                 } 
1459+             }  else  { 
1460+                 // after we've processed the original `mpi`, we should 
1461+                 // always traverse the siblings of any of its 
1462+                 // children. 
1463+                 push_siblings = true ; 
1464+             } 
1465+         } 
1466+         return  None ; 
1467+     } 
1468+ } 
1469+ 
13431470impl < BD >  FlowInProgress < BD >  where  BD :  BitDenotation  { 
13441471    fn  each_state_bit < F > ( & self ,  f :  F )  where  F :  FnMut ( BD :: Idx )  { 
13451472        self . curr_state . each_bit ( self . base_results . operator ( ) . bits_per_block ( ) ,  f) 
0 commit comments