@@ -586,7 +586,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
586
586
context : Context ,
587
587
( lvalue, span) : ( & Lvalue < ' gcx > , Span ) ,
588
588
flow_state : & InProgress < ' b , ' gcx > ) {
589
- let move_data = flow_state . inits . base_results . operator ( ) . move_data ( ) ;
589
+ let move_data = self . move_data ;
590
590
591
591
// determine if this path has a non-mut owner (and thus needs checking).
592
592
let mut l = lvalue;
@@ -611,7 +611,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
611
611
}
612
612
}
613
613
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) {
615
615
if flow_state. inits . curr_state . contains ( & mpi) {
616
616
// may already be assigned before reaching this statement;
617
617
// report error.
@@ -642,29 +642,115 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
642
642
let lvalue = self . base_path ( lvalue_span. 0 ) ;
643
643
644
644
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) {
649
712
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.
653
714
}
654
715
}
655
716
}
656
717
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
+
657
745
fn move_path_for_lvalue ( & mut self ,
658
- _context : Context ,
659
- move_data : & MoveData < ' gcx > ,
660
746
lvalue : & Lvalue < ' gcx > )
661
747
-> Option < MovePathIndex >
662
748
{
663
749
// If returns None, then there is no move path corresponding
664
750
// to a direct owner of `lvalue` (which means there is nothing
665
751
// that borrowck tracks for its analysis).
666
752
667
- match move_data. rev_lookup . find ( lvalue) {
753
+ match self . move_data . rev_lookup . find ( lvalue) {
668
754
LookupResult :: Parent ( _) => None ,
669
755
LookupResult :: Exact ( mpi) => Some ( mpi) ,
670
756
}
@@ -733,6 +819,11 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
733
819
}
734
820
}
735
821
822
+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
823
+ enum NoMovePathFound {
824
+ ReachedStatic ,
825
+ }
826
+
736
827
impl < ' c , ' b , ' a : ' b +' c , ' gcx , ' tcx : ' a > MirBorrowckCtxt < ' c , ' b , ' a , ' gcx , ' tcx > {
737
828
fn each_borrow_involving_path < F > ( & mut self ,
738
829
_context : Context ,
@@ -846,12 +937,19 @@ mod prefixes {
846
937
847
938
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
848
939
pub ( super ) enum PrefixSet {
940
+ /// Doesn't stop until it returns the base case (a Local or
941
+ /// Static prefix).
849
942
All ,
943
+ /// Stops at any dereference.
850
944
Shallow ,
945
+ /// Stops at the deref of a shared reference.
851
946
Supporting ,
852
947
}
853
948
854
949
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`.
855
953
pub ( super ) fn prefixes < ' d > ( & self ,
856
954
lvalue : & ' d Lvalue < ' gcx > ,
857
955
kind : PrefixSet )
@@ -1340,6 +1438,35 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
1340
1438
}
1341
1439
}
1342
1440
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
+
1343
1470
impl < BD > FlowInProgress < BD > where BD : BitDenotation {
1344
1471
fn each_state_bit < F > ( & self , f : F ) where F : FnMut ( BD :: Idx ) {
1345
1472
self . curr_state . each_bit ( self . base_results . operator ( ) . bits_per_block ( ) , f)
0 commit comments