11//! Functionality for statements, operands, places, and things that appear in them.
22
3+ use std:: iter:: once;
4+
35use smallvec:: SmallVec ;
46use tracing:: { debug, instrument} ;
57
@@ -556,153 +558,180 @@ impl From<Local> for PlaceRef<'_> {
556558 }
557559}
558560
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.)
563566pub type ProjectionFragment < ' tcx > = & ' tcx List < PlaceElem < ' tcx > > ;
564567pub type ProjectionFragmentRef < ' tcx > = & ' tcx [ PlaceElem < ' tcx > ] ;
565568
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 .
567570///
568571/// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these
569572/// kinds of places, requiring this struct to be used instead.
570573#[ derive( Copy , Clone , PartialEq , Eq , Hash , TyEncodable , HashStable , TypeFoldable , TypeVisitable ) ]
571574pub struct CompoundPlace < ' tcx > {
572575 pub local : Local ,
576+ /// The projection from `local` until the first deref.
577+ ///
573578 /// 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.
577585 pub projection_chain : & ' tcx List < ProjectionFragment < ' tcx > > ,
578586}
579587
580588/// Borrowed form of [`CompoundPlace`].
581589#[ derive( Clone , Copy , PartialEq , Eq , Hash ) ]
582590pub struct CompoundPlaceRef < ' tcx > {
583591 pub local : Local ,
592+ pub direct_projection : & ' tcx [ PlaceElem < ' tcx > ] ,
584593 /// `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.
586595 pub projection_chain : Option < ( & ' tcx [ ProjectionFragment < ' tcx > ] , ProjectionFragmentRef < ' tcx > ) > ,
587596}
588597
589- // these impls are bare-bones for now
590598impl < ' tcx > CompoundPlace < ' tcx > {
591599 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;
600618 }
601619 }
602620
603- if segment_start == 0 {
621+ if current_fragment_start == 0 {
622+ // don't try to re-intern the projection for no reason
604623 new_projection_chain. push ( place. projection ) ;
605624 } 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..] ) ) ;
612627 }
613628
614629 CompoundPlace {
615630 local : place. local ,
631+ direct_projection : tcx. mk_place_elems ( & place. projection [ ..first_deref] ) ,
616632 projection_chain : tcx. mk_place_elem_chain ( & new_projection_chain) ,
617633 }
618634 }
619635
620636 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+ }
622642 }
623643
624644 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 ( )
626646 }
627647
628648 pub fn local_or_deref_local ( & self ) -> Option < Local > {
629649 self . as_ref ( ) . local_or_deref_local ( )
630650 }
631651
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 ( )
638654 }
639655
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 }
657659 }
658660
659661 /// 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)
660664 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 ;
662666
663667 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
672678 } 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 ) )
674681 }
675682 }
676683
677684 pub fn iter_projections (
678685 self ,
679686 ) -> 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) | {
681698 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+ } ;
687707
688708 ( base, elem)
689709 } )
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 ( ) )
691719 }
692720
693721 pub fn ty < D : ?Sized > ( & self , local_decls : & D , tcx : TyCtxt < ' tcx > ) -> PlaceTy < ' tcx >
694722 where
695723 D : HasLocalDecls < ' tcx > ,
696724 {
697725 PlaceTy :: from_ty ( local_decls. local_decls ( ) [ self . local ] . ty )
726+ . multi_projection_ty ( tcx, self . direct_projection )
698727 . projection_chain_ty ( tcx, self . projection_chain )
699728 }
700729}
701730
702731impl From < Local > for CompoundPlace < ' _ > {
703732 #[ inline]
704733 fn from ( local : Local ) -> Self {
705- CompoundPlace { local, projection_chain : List :: empty ( ) }
734+ CompoundPlace { local, direct_projection : List :: empty ( ) , projection_chain : List :: empty ( ) }
706735 }
707736}
708737
@@ -711,46 +740,58 @@ impl<'tcx> CompoundPlaceRef<'tcx> {
711740 where
712741 D : HasLocalDecls < ' tcx > ,
713742 {
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 ) ;
715745
716746 match self . projection_chain {
717747 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)
719749 }
720- None => local_ty ,
750+ None => base_ty ,
721751 }
722752 }
723753
724- pub fn local_or_deref_local ( & self ) -> Option < Local > {
754+ pub fn as_local ( & self ) -> Option < Local > {
725755 match * self {
726- CompoundPlaceRef { local, projection_chain : None | Some ( ( [ ] , [ PlaceElem :: Deref ] ) ) } => {
756+ CompoundPlaceRef { local, direct_projection : [ ] , projection_chain : None } => {
727757 Some ( local)
728758 }
729759 _ => None ,
730760 }
731761 }
732762
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+ }
740772 }
741773
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 (
744778 stem : & ' tcx [ ProjectionFragment < ' tcx > ] ,
745779 suffix : ProjectionFragmentRef < ' tcx > ,
746- ) -> CompoundPlaceRef < ' tcx > {
747- let projection_chain = match ( stem, suffix) {
780+ ) -> Option < ( & ' tcx [ ProjectionFragment < ' tcx > ] , ProjectionFragmentRef < ' tcx > ) > {
781+ match ( stem, suffix) {
748782 ( [ ] , [ ] ) => None ,
749783 ( [ stem @ .., suffix] , [ ] ) => Some ( ( stem, suffix. as_slice ( ) ) ) ,
750784 _ => Some ( ( stem, suffix) ) ,
751- } ;
785+ }
786+ }
752787
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+ }
754795 }
755796}
756797
0 commit comments