@@ -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).
849942 All ,
943+ /// Stops at any dereference.
850944 Shallow ,
945+ /// Stops at the deref of a shared reference.
851946 Supporting ,
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`.
855953 pub ( 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