Skip to content

Commit 19b4411

Browse files
committed
tighter representation of CompoundPlace
1 parent 2759170 commit 19b4411

File tree

13 files changed

+217
-154
lines changed

13 files changed

+217
-154
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
559559
) {
560560
let var_info = self.body.var_debug_info.iter().find(|info| match info.value {
561561
VarDebugInfoContents::Place(ref p) => p
562-
.projection_chain
563-
.iter()
564-
.flatten()
562+
.iter_projection_elems()
565563
.enumerate()
566564
.all(|(i, elem)| place.projection.get(i) == Some(&elem)),
567565
_ => false,

compiler/rustc_codegen_ssa/src/mir/debuginfo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
571571
// FIXME change field to be projection chain
572572
projection: bx
573573
.tcx()
574-
.mk_place_elems_from_iter(place.projection_chain.iter().flatten()),
574+
.mk_place_elems_from_iter(place.iter_projection_elems()),
575575
});
576576
}
577577
mir::VarDebugInfoContents::Const(c) => {

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,9 +1284,11 @@ impl Debug for CompoundPlaceRef<'_> {
12841284
for projection in stem.iter().rev() {
12851285
pre_fmt_projection(projection, fmt)?;
12861286
}
1287+
pre_fmt_projection(self.direct_projection, fmt)?;
12871288

12881289
write!(fmt, "{:?}", self.local)?;
12891290

1291+
post_fmt_projection(self.direct_projection, fmt)?;
12901292
for projection in stem {
12911293
post_fmt_projection(projection, fmt)?;
12921294
}

compiler/rustc_middle/src/mir/statement.rs

Lines changed: 127 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Functionality for statements, operands, places, and things that appear in them.
22
3+
use std::iter::once;
4+
35
use smallvec::SmallVec;
46
use 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.)
563566
pub type ProjectionFragment<'tcx> = &'tcx List<PlaceElem<'tcx>>;
564567
pub 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)]
571574
pub 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)]
582590
pub 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
590598
impl<'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

702731
impl 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

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,12 @@ macro_rules! visit_place_fns {
12001200
) {
12011201
self.visit_local(&mut place.local, context, location);
12021202

1203+
if let Some(new_direct_projection) =
1204+
self.process_projection(&place.direct_projection, location)
1205+
{
1206+
place.direct_projection = self.tcx().mk_place_elems(&new_direct_projection);
1207+
}
1208+
12031209
if let Some(new_projection_chain) =
12041210
self.process_projection_chain(&place.projection_chain, location)
12051211
{

0 commit comments

Comments
 (0)