1
1
//! Functionality for statements, operands, places, and things that appear in them.
2
2
3
+ use std:: iter:: once;
4
+
3
5
use smallvec:: SmallVec ;
4
6
use tracing:: { debug, instrument} ;
5
7
@@ -556,153 +558,180 @@ impl From<Local> for PlaceRef<'_> {
556
558
}
557
559
}
558
560
559
- // In the future this will also have the type being projected alongside it.
560
- // This will be needed to represent derefs of non-pointer types like `Box`.
561
- //
562
- // (`Ty::builtin_deref` currently works on `Box` but that will be changed too)
561
+ /// A deref and subsequent direct projection of the pointee
562
+ ///
563
+ /// In the future this will also have the pointee type alongside the projection.
564
+ /// This will be needed to represent derefs of non-pointer types like `Box`.
565
+ /// (`Ty::builtin_deref` currently works on `Box` but that will be changed too.)
563
566
pub type ProjectionFragment < ' tcx > = & ' tcx List < PlaceElem < ' tcx > > ;
564
567
pub type ProjectionFragmentRef < ' tcx > = & ' tcx [ PlaceElem < ' tcx > ] ;
565
568
566
- /// A place possibly containing derefs in the middle of its projection by chaining projection lists .
569
+ /// A place with multiple direct projections seperated by derefs .
567
570
///
568
571
/// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these
569
572
/// kinds of places, requiring this struct to be used instead.
570
573
#[ derive( Copy , Clone , PartialEq , Eq , Hash , TyEncodable , HashStable , TypeFoldable , TypeVisitable ) ]
571
574
pub struct CompoundPlace < ' tcx > {
572
575
pub local : Local ,
576
+ /// The projection from `local` until the first deref.
577
+ ///
573
578
/// Invariants:
574
- /// - All segments in the chain other than the first must start with a deref.
575
- /// - Derefs may only appear at the start of a segment.
576
- /// - No segment may be empty.
579
+ /// - This does not contain derefs.
580
+ pub direct_projection : & ' tcx List < PlaceElem < ' tcx > > ,
581
+ /// A chain of projection fragments -- derefs followed by direct projections of the pointee place.
582
+ ///
583
+ /// Invariants:
584
+ /// - Each fragment begins with a deref and has no other derefs.
577
585
pub projection_chain : & ' tcx List < ProjectionFragment < ' tcx > > ,
578
586
}
579
587
580
588
/// Borrowed form of [`CompoundPlace`].
581
589
#[ derive( Clone , Copy , PartialEq , Eq , Hash ) ]
582
590
pub struct CompoundPlaceRef < ' tcx > {
583
591
pub local : Local ,
592
+ pub direct_projection : & ' tcx [ PlaceElem < ' tcx > ] ,
584
593
/// `None` is equivalent to an empty projection chain,
585
- /// `Some((stem, suffix))` is equivalent to `stem` with `suffix` appended to it
594
+ /// `Some((stem, suffix))` is equivalent to `stem` with `suffix` appended to it.
586
595
pub projection_chain : Option < ( & ' tcx [ ProjectionFragment < ' tcx > ] , ProjectionFragmentRef < ' tcx > ) > ,
587
596
}
588
597
589
- // these impls are bare-bones for now
590
598
impl < ' tcx > CompoundPlace < ' tcx > {
591
599
pub fn from_place ( place : Place < ' tcx > , tcx : TyCtxt < ' tcx > ) -> CompoundPlace < ' tcx > {
592
- let mut segment_start = 0 ;
593
- // size picked from sole user
594
- let mut new_projection_chain = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
595
-
596
- for ( i, elem) in place. projection . iter ( ) . enumerate ( ) {
597
- if elem == PlaceElem :: Deref && i > 0 {
598
- new_projection_chain. push ( tcx. mk_place_elems ( & place. projection [ segment_start..i] ) ) ;
599
- segment_start = i;
600
+ let Some ( first_deref) = place. projection . iter ( ) . position ( |elem| elem == PlaceElem :: Deref )
601
+ else {
602
+ // simple case, no derefs
603
+ return CompoundPlace {
604
+ local : place. local ,
605
+ direct_projection : place. projection ,
606
+ projection_chain : List :: empty ( ) ,
607
+ } ;
608
+ } ;
609
+
610
+ let mut current_fragment_start = first_deref;
611
+ let mut new_projection_chain = SmallVec :: < [ _ ; 1 ] > :: new ( ) ;
612
+
613
+ for i in first_deref + 1 ..place. projection . len ( ) {
614
+ if place. projection [ i] == PlaceElem :: Deref {
615
+ new_projection_chain
616
+ . push ( tcx. mk_place_elems ( & place. projection [ current_fragment_start..i] ) ) ;
617
+ current_fragment_start = i;
600
618
}
601
619
}
602
620
603
- if segment_start == 0 {
621
+ if current_fragment_start == 0 {
622
+ // don't try to re-intern the projection for no reason
604
623
new_projection_chain. push ( place. projection ) ;
605
624
} else {
606
- new_projection_chain. push ( tcx. mk_place_elems ( & place. projection [ segment_start..] ) ) ;
607
- }
608
-
609
- if cfg ! ( debug_assertions) {
610
- let new_projections: Vec < _ > = new_projection_chain. iter ( ) . copied ( ) . flatten ( ) . collect ( ) ;
611
- assert_eq ! ( new_projections. as_slice( ) , place. projection. as_slice( ) ) ;
625
+ new_projection_chain
626
+ . push ( tcx. mk_place_elems ( & place. projection [ current_fragment_start..] ) ) ;
612
627
}
613
628
614
629
CompoundPlace {
615
630
local : place. local ,
631
+ direct_projection : tcx. mk_place_elems ( & place. projection [ ..first_deref] ) ,
616
632
projection_chain : tcx. mk_place_elem_chain ( & new_projection_chain) ,
617
633
}
618
634
}
619
635
620
636
pub fn as_ref ( & self ) -> CompoundPlaceRef < ' tcx > {
621
- CompoundPlaceRef :: from_slice ( self . local , self . projection_chain )
637
+ CompoundPlaceRef {
638
+ local : self . local ,
639
+ direct_projection : self . direct_projection . as_slice ( ) ,
640
+ projection_chain : CompoundPlaceRef :: balance_chain ( self . projection_chain ) ,
641
+ }
622
642
}
623
643
624
644
pub fn as_local ( & self ) -> Option < Local > {
625
- if self . projection_chain . is_empty ( ) { Some ( self . local ) } else { None }
645
+ self . as_ref ( ) . as_local ( )
626
646
}
627
647
628
648
pub fn local_or_deref_local ( & self ) -> Option < Local > {
629
649
self . as_ref ( ) . local_or_deref_local ( )
630
650
}
631
651
632
- /// Returns a [`Place`] with only the first segment in the projection chain.
633
- pub fn base_place ( & self ) -> Place < ' tcx > {
634
- Place {
635
- local : self . local ,
636
- projection : self . projection_chain . first ( ) . copied ( ) . unwrap_or_default ( ) ,
637
- }
652
+ pub fn is_indirect ( & self ) -> bool {
653
+ !self . projection_chain . is_empty ( )
638
654
}
639
655
640
- /// Replaces the local and first segment of the projection with `new_base`.
641
- pub fn replace_base_place ( & mut self , new_base : Place < ' tcx > , tcx : TyCtxt < ' tcx > ) {
642
- self . local = new_base. local ;
643
- self . projection_chain =
644
- match ( new_base. projection . is_empty ( ) , self . projection_chain . is_empty ( ) ) {
645
- ( false , false ) => {
646
- let mut new_projection_chain = self . projection_chain . to_vec ( ) ;
647
- new_projection_chain[ 0 ] = new_base. projection ;
648
- tcx. mk_place_elem_chain ( & new_projection_chain)
649
- }
650
- ( false , true ) => tcx. mk_place_elem_chain ( & [ new_base. projection ] ) ,
651
-
652
- ( true , false ) => tcx. mk_place_elem_chain ( & self . projection_chain [ 1 ..] ) ,
653
- ( true , true ) => List :: empty ( ) ,
654
- }
655
- // FIXME: this logic is a mess
656
- // maybe seperate out projection before first deref?
656
+ /// Returns a [`Place`] with only `direct_projection`
657
+ pub fn base_place ( & self ) -> Place < ' tcx > {
658
+ Place { local : self . local , projection : self . direct_projection }
657
659
}
658
660
659
661
/// Replaces the local with `new_base`.
662
+ ///
663
+ /// `new_base` must be a post-derefer compatible local (no derefs after the start of the projection)
660
664
pub fn replace_local_with_place ( & mut self , new_base : Place < ' tcx > , tcx : TyCtxt < ' tcx > ) {
661
- let base_place = self . base_place ( ) ;
665
+ self . local = new_base . local ;
662
666
663
667
if new_base. projection . is_empty ( ) {
664
- self . local = new_base. local ;
665
- } else if base_place. is_indirect_first_projection ( ) {
666
- let mut new_projection_chain = Vec :: with_capacity ( self . projection_chain . len ( ) + 1 ) ;
667
- new_projection_chain. push ( new_base. projection ) ;
668
- new_projection_chain. extend_from_slice ( self . projection_chain ) ;
669
-
670
- self . local = new_base. local ;
671
- self . projection_chain = tcx. mk_place_elem_chain ( & new_projection_chain) ;
668
+ // already done
669
+ } else if new_base. is_indirect_first_projection ( ) {
670
+ let new_prefix = new_base. project_deeper ( self . direct_projection , tcx) ;
671
+
672
+ self . direct_projection = List :: empty ( ) ;
673
+ self . projection_chain = tcx. mk_place_elem_chain_from_iter (
674
+ once ( new_prefix. projection ) . chain ( self . projection_chain ) ,
675
+ )
676
+ } else if self . direct_projection . is_empty ( ) {
677
+ self . direct_projection = new_base. projection
672
678
} else {
673
- self . replace_base_place ( new_base. project_deeper ( base_place. projection , tcx) , tcx) ;
679
+ self . direct_projection = tcx
680
+ . mk_place_elems_from_iter ( new_base. projection . iter ( ) . chain ( self . direct_projection ) )
674
681
}
675
682
}
676
683
677
684
pub fn iter_projections (
678
685
self ,
679
686
) -> impl Iterator < Item = ( CompoundPlaceRef < ' tcx > , PlaceElem < ' tcx > ) > + DoubleEndedIterator {
680
- self . projection_chain . iter ( ) . enumerate ( ) . flat_map ( move |( i, projs) | {
687
+ let base_iter = self . direct_projection . iter ( ) . enumerate ( ) . map ( move |( i, elem) | {
688
+ let base = CompoundPlaceRef {
689
+ local : self . local ,
690
+ direct_projection : & self . direct_projection [ ..i] ,
691
+ projection_chain : None ,
692
+ } ;
693
+
694
+ ( base, elem)
695
+ } ) ;
696
+
697
+ let chain_iter = self . projection_chain . iter ( ) . enumerate ( ) . flat_map ( move |( i, projs) | {
681
698
projs. iter ( ) . enumerate ( ) . map ( move |( j, elem) | {
682
- let base = CompoundPlaceRef :: from_stem_with_suffix (
683
- self . local ,
684
- & self . projection_chain [ ..i] ,
685
- & projs[ ..j] ,
686
- ) ;
699
+ let base = CompoundPlaceRef {
700
+ local : self . local ,
701
+ direct_projection : self . direct_projection . as_slice ( ) ,
702
+ projection_chain : CompoundPlaceRef :: balance_stem_and_suffix (
703
+ & self . projection_chain [ ..i] ,
704
+ & projs[ ..j] ,
705
+ ) ,
706
+ } ;
687
707
688
708
( base, elem)
689
709
} )
690
- } )
710
+ } ) ;
711
+
712
+ base_iter. chain ( chain_iter)
713
+ }
714
+
715
+ pub fn iter_projection_elems (
716
+ & self ,
717
+ ) -> impl Iterator < Item = PlaceElem < ' tcx > > + DoubleEndedIterator {
718
+ self . direct_projection . iter ( ) . chain ( self . projection_chain . iter ( ) . flatten ( ) )
691
719
}
692
720
693
721
pub fn ty < D : ?Sized > ( & self , local_decls : & D , tcx : TyCtxt < ' tcx > ) -> PlaceTy < ' tcx >
694
722
where
695
723
D : HasLocalDecls < ' tcx > ,
696
724
{
697
725
PlaceTy :: from_ty ( local_decls. local_decls ( ) [ self . local ] . ty )
726
+ . multi_projection_ty ( tcx, self . direct_projection )
698
727
. projection_chain_ty ( tcx, self . projection_chain )
699
728
}
700
729
}
701
730
702
731
impl From < Local > for CompoundPlace < ' _ > {
703
732
#[ inline]
704
733
fn from ( local : Local ) -> Self {
705
- CompoundPlace { local, projection_chain : List :: empty ( ) }
734
+ CompoundPlace { local, direct_projection : List :: empty ( ) , projection_chain : List :: empty ( ) }
706
735
}
707
736
}
708
737
@@ -711,46 +740,58 @@ impl<'tcx> CompoundPlaceRef<'tcx> {
711
740
where
712
741
D : HasLocalDecls < ' tcx > ,
713
742
{
714
- let local_ty = PlaceTy :: from_ty ( local_decls. local_decls ( ) [ self . local ] . ty ) ;
743
+ let base_ty = PlaceTy :: from_ty ( local_decls. local_decls ( ) [ self . local ] . ty )
744
+ . multi_projection_ty ( tcx, self . direct_projection ) ;
715
745
716
746
match self . projection_chain {
717
747
Some ( ( stem, suffix) ) => {
718
- local_ty . projection_chain_ty ( tcx, stem) . multi_projection_ty ( tcx, suffix)
748
+ base_ty . projection_chain_ty ( tcx, stem) . multi_projection_ty ( tcx, suffix)
719
749
}
720
- None => local_ty ,
750
+ None => base_ty ,
721
751
}
722
752
}
723
753
724
- pub fn local_or_deref_local ( & self ) -> Option < Local > {
754
+ pub fn as_local ( & self ) -> Option < Local > {
725
755
match * self {
726
- CompoundPlaceRef { local, projection_chain : None | Some ( ( [ ] , [ PlaceElem :: Deref ] ) ) } => {
756
+ CompoundPlaceRef { local, direct_projection : [ ] , projection_chain : None } => {
727
757
Some ( local)
728
758
}
729
759
_ => None ,
730
760
}
731
761
}
732
762
733
- fn from_slice ( local : Local , chain : & ' tcx [ ProjectionFragment < ' tcx > ] ) -> CompoundPlaceRef < ' tcx > {
734
- let projection_chain = match chain {
735
- [ stem @ .., suffix] => Some ( ( stem, suffix. as_slice ( ) ) ) ,
736
- [ ] => None ,
737
- } ;
738
-
739
- CompoundPlaceRef { local, projection_chain }
763
+ pub fn local_or_deref_local ( & self ) -> Option < Local > {
764
+ match * self {
765
+ CompoundPlaceRef {
766
+ local,
767
+ direct_projection : [ ] ,
768
+ projection_chain : None | Some ( ( [ ] , [ PlaceElem :: Deref ] ) ) ,
769
+ } => Some ( local) ,
770
+ _ => None ,
771
+ }
740
772
}
741
773
742
- fn from_stem_with_suffix (
743
- local : Local ,
774
+ /// Balances `stem` and `suffix` into the layout expected by `CompoundPlaceRef`.
775
+ /// If `suffix` is empty and `stem` is not, `stem`'s last element is split off to replace `suffix`.
776
+ /// If both are empty, `None` is returned.
777
+ fn balance_stem_and_suffix (
744
778
stem : & ' tcx [ ProjectionFragment < ' tcx > ] ,
745
779
suffix : ProjectionFragmentRef < ' tcx > ,
746
- ) -> CompoundPlaceRef < ' tcx > {
747
- let projection_chain = match ( stem, suffix) {
780
+ ) -> Option < ( & ' tcx [ ProjectionFragment < ' tcx > ] , ProjectionFragmentRef < ' tcx > ) > {
781
+ match ( stem, suffix) {
748
782
( [ ] , [ ] ) => None ,
749
783
( [ stem @ .., suffix] , [ ] ) => Some ( ( stem, suffix. as_slice ( ) ) ) ,
750
784
_ => Some ( ( stem, suffix) ) ,
751
- } ;
785
+ }
786
+ }
752
787
753
- CompoundPlaceRef { local, projection_chain }
788
+ fn balance_chain (
789
+ projection_chain : & ' tcx [ ProjectionFragment < ' tcx > ] ,
790
+ ) -> Option < ( & ' tcx [ ProjectionFragment < ' tcx > ] , ProjectionFragmentRef < ' tcx > ) > {
791
+ match projection_chain {
792
+ [ ] => None ,
793
+ [ stem @ .., suffix] => Some ( ( stem, suffix. as_slice ( ) ) ) ,
794
+ }
754
795
}
755
796
}
756
797
0 commit comments