about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/mir.rs137
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs12
-rw-r--r--crates/hir-ty/src/mir/eval.rs31
-rw-r--r--crates/hir-ty/src/mir/lower.rs154
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs16
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs58
-rw-r--r--crates/hir-ty/src/mir/pretty.rs2
7 files changed, 253 insertions, 157 deletions
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 9be083d0117..3ee141b5536 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -1,6 +1,6 @@
 //! MIR definitions and implementation
 
-use std::{fmt::Display, iter};
+use std::{collections::hash_map::Entry, fmt::Display, iter};
 
 use crate::{
     consteval::usize_const,
@@ -37,6 +37,7 @@ pub use monomorphization::{
     monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
     monomorphized_mir_body_query, monomorphized_mir_body_recover,
 };
+use rustc_hash::FxHashMap;
 use smallvec::{smallvec, SmallVec};
 use stdx::{impl_from, never};
 use triomphe::Arc;
@@ -223,35 +224,93 @@ impl<V, T> ProjectionElem<V, T> {
 
 type PlaceElem = ProjectionElem<LocalId, Ty>;
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ProjectionId(u32);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ProjectionStore {
+    id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
+    proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
+}
+
+impl Default for ProjectionStore {
+    fn default() -> Self {
+        let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
+        // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
+        this.intern(Box::new([]));
+        this
+    }
+}
+
+impl ProjectionStore {
+    fn shrink_to_fit(&mut self) {
+        self.id_to_proj.shrink_to_fit();
+        self.proj_to_id.shrink_to_fit();
+    }
+
+    fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
+        self.proj_to_id.get(projection).copied()
+    }
+
+    fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
+        let new_id = ProjectionId(self.proj_to_id.len() as u32);
+        match self.proj_to_id.entry(projection) {
+            Entry::Occupied(id) => *id.get(),
+            Entry::Vacant(e) => {
+                let key_clone = e.key().clone();
+                e.insert(new_id);
+                self.id_to_proj.insert(new_id, key_clone);
+                new_id
+            }
+        }
+    }
+}
+
+impl ProjectionId {
+    const EMPTY: ProjectionId = ProjectionId(0);
+
+    fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
+        store.id_to_proj.get(&self).unwrap()
+    }
+
+    fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
+        let mut current = self.lookup(store).to_vec();
+        current.push(projection);
+        store.intern(current.into())
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Place {
     pub local: LocalId,
-    pub projection: Box<[PlaceElem]>,
+    pub projection: ProjectionId,
 }
 
 impl Place {
-    fn is_parent(&self, child: &Place) -> bool {
-        self.local == child.local && child.projection.starts_with(&self.projection)
+    fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
+        self.local == child.local
+            && child.projection.lookup(store).starts_with(&self.projection.lookup(store))
     }
 
     /// The place itself is not included
-    fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
-        (0..self.projection.len())
-            .map(|x| &self.projection[0..x])
-            .map(|x| Place { local: self.local, projection: x.to_vec().into() })
+    fn iterate_over_parents<'a>(
+        &'a self,
+        store: &'a ProjectionStore,
+    ) -> impl Iterator<Item = Place> + 'a {
+        let projection = self.projection.lookup(store);
+        (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
+            Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
+        })
     }
 
-    fn project(&self, projection: PlaceElem) -> Place {
-        Place {
-            local: self.local,
-            projection: self.projection.iter().cloned().chain([projection]).collect(),
-        }
+    fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
+        Place { local: self.local, projection: self.projection.project(projection, store) }
     }
 }
 
 impl From<LocalId> for Place {
     fn from(local: LocalId) -> Self {
-        Self { local, projection: vec![].into() }
+        Self { local, projection: ProjectionId::EMPTY }
     }
 }
 
@@ -997,6 +1056,7 @@ pub struct BasicBlock {
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct MirBody {
+    pub projection_store: ProjectionStore,
     pub basic_blocks: Arena<BasicBlock>,
     pub locals: Arena<Local>,
     pub start_block: BasicBlockId,
@@ -1009,11 +1069,15 @@ pub struct MirBody {
 }
 
 impl MirBody {
-    fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
-        fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+    fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
+        fn for_operand(
+            op: &mut Operand,
+            f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
+            store: &mut ProjectionStore,
+        ) {
             match op {
                 Operand::Copy(p) | Operand::Move(p) => {
-                    f(p);
+                    f(p, store);
                 }
                 Operand::Constant(_) | Operand::Static(_) => (),
             }
@@ -1022,30 +1086,30 @@ impl MirBody {
             for statement in &mut block.statements {
                 match &mut statement.kind {
                     StatementKind::Assign(p, r) => {
-                        f(p);
+                        f(p, &mut self.projection_store);
                         match r {
                             Rvalue::ShallowInitBoxWithAlloc(_) => (),
                             Rvalue::ShallowInitBox(o, _)
                             | Rvalue::UnaryOp(_, o)
                             | Rvalue::Cast(_, o, _)
                             | Rvalue::Repeat(o, _)
-                            | Rvalue::Use(o) => for_operand(o, &mut f),
+                            | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store),
                             Rvalue::CopyForDeref(p)
                             | Rvalue::Discriminant(p)
                             | Rvalue::Len(p)
-                            | Rvalue::Ref(_, p) => f(p),
+                            | Rvalue::Ref(_, p) => f(p, &mut self.projection_store),
                             Rvalue::CheckedBinaryOp(_, o1, o2) => {
-                                for_operand(o1, &mut f);
-                                for_operand(o2, &mut f);
+                                for_operand(o1, &mut f, &mut self.projection_store);
+                                for_operand(o2, &mut f, &mut self.projection_store);
                             }
                             Rvalue::Aggregate(_, ops) => {
                                 for op in ops.iter_mut() {
-                                    for_operand(op, &mut f);
+                                    for_operand(op, &mut f, &mut self.projection_store);
                                 }
                             }
                         }
                     }
-                    StatementKind::Deinit(p) => f(p),
+                    StatementKind::Deinit(p) => f(p, &mut self.projection_store),
                     StatementKind::StorageLive(_)
                     | StatementKind::StorageDead(_)
                     | StatementKind::Nop => (),
@@ -1053,7 +1117,9 @@ impl MirBody {
             }
             match &mut block.terminator {
                 Some(x) => match &mut x.kind {
-                    TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+                    TerminatorKind::SwitchInt { discr, .. } => {
+                        for_operand(discr, &mut f, &mut self.projection_store)
+                    }
                     TerminatorKind::FalseEdge { .. }
                     | TerminatorKind::FalseUnwind { .. }
                     | TerminatorKind::Goto { .. }
@@ -1063,23 +1129,24 @@ impl MirBody {
                     | TerminatorKind::Return
                     | TerminatorKind::Unreachable => (),
                     TerminatorKind::Drop { place, .. } => {
-                        f(place);
+                        f(place, &mut self.projection_store);
                     }
                     TerminatorKind::DropAndReplace { place, value, .. } => {
-                        f(place);
-                        for_operand(value, &mut f);
+                        f(place, &mut self.projection_store);
+                        for_operand(value, &mut f, &mut self.projection_store);
                     }
                     TerminatorKind::Call { func, args, destination, .. } => {
-                        for_operand(func, &mut f);
-                        args.iter_mut().for_each(|x| for_operand(x, &mut f));
-                        f(destination);
+                        for_operand(func, &mut f, &mut self.projection_store);
+                        args.iter_mut()
+                            .for_each(|x| for_operand(x, &mut f, &mut self.projection_store));
+                        f(destination, &mut self.projection_store);
                     }
                     TerminatorKind::Assert { cond, .. } => {
-                        for_operand(cond, &mut f);
+                        for_operand(cond, &mut f, &mut self.projection_store);
                     }
                     TerminatorKind::Yield { value, resume_arg, .. } => {
-                        for_operand(value, &mut f);
-                        f(resume_arg);
+                        for_operand(value, &mut f, &mut self.projection_store);
+                        f(resume_arg, &mut self.projection_store);
                     }
                 },
                 None => (),
@@ -1096,7 +1163,9 @@ impl MirBody {
             binding_locals,
             param_locals,
             closures,
+            projection_store,
         } = self;
+        projection_store.shrink_to_fit();
         basic_blocks.shrink_to_fit();
         locals.shrink_to_fit();
         binding_locals.shrink_to_fit();
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index 20a439bd643..7f80c7be80b 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -88,7 +88,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
         Operand::Copy(p) | Operand::Move(p) => {
             let mut ty: Ty = body.locals[p.local].ty.clone();
             let mut is_dereference_of_ref = false;
-            for proj in &*p.projection {
+            for proj in p.projection.lookup(&body.projection_store) {
                 if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
                     is_dereference_of_ref = true;
                 }
@@ -195,7 +195,7 @@ enum ProjectionCase {
 fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
     let mut is_part_of = false;
     let mut ty = body.locals[lvalue.local].ty.clone();
-    for proj in lvalue.projection.iter() {
+    for proj in lvalue.projection.lookup(&body.projection_store).iter() {
         match proj {
             ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
             ProjectionElem::Deref // It's direct in case of `Box<T>`
@@ -254,7 +254,7 @@ fn ever_initialized_map(
         for statement in &block.statements {
             match &statement.kind {
                 StatementKind::Assign(p, _) => {
-                    if p.projection.len() == 0 && p.local == l {
+                    if p.projection.lookup(&body.projection_store).len() == 0 && p.local == l {
                         is_ever_initialized = true;
                     }
                 }
@@ -289,7 +289,9 @@ fn ever_initialized_map(
             | TerminatorKind::Return
             | TerminatorKind::Unreachable => (),
             TerminatorKind::Call { target, cleanup, destination, .. } => {
-                if destination.projection.len() == 0 && destination.local == l {
+                if destination.projection.lookup(&body.projection_store).len() == 0
+                    && destination.local == l
+                {
                     is_ever_initialized = true;
                 }
                 target
@@ -389,7 +391,7 @@ fn mutability_of_locals(
             | TerminatorKind::Assert { .. }
             | TerminatorKind::Yield { .. } => (),
             TerminatorKind::Call { destination, .. } => {
-                if destination.projection.len() == 0 {
+                if destination.projection.lookup(&body.projection_store).len() == 0 {
                     if ever_init_map.get(destination.local).copied().unwrap_or_default() {
                         push_mut_span(destination.local, MirSpan::Unknown);
                     } else {
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 3944feb128c..28c7a746525 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -46,8 +46,8 @@ use crate::{
 
 use super::{
     return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError,
-    MirSpan, Operand, Place, PlaceElem, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
-    UnOp,
+    MirSpan, Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind,
+    TerminatorKind, UnOp,
 };
 
 mod shim;
@@ -485,17 +485,18 @@ struct DropFlags {
 }
 
 impl DropFlags {
-    fn add_place(&mut self, p: Place) {
-        if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) {
+    fn add_place(&mut self, p: Place, store: &ProjectionStore) {
+        if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) {
             return;
         }
-        self.need_drop.retain(|it| !p.is_parent(it));
+        self.need_drop.retain(|it| !p.is_parent(it, store));
         self.need_drop.insert(p);
     }
 
-    fn remove_place(&mut self, p: &Place) -> bool {
+    fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool {
         // FIXME: replace parents with parts
-        if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) {
+        if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(&it))
+        {
             self.need_drop.remove(&parent);
             return true;
         }
@@ -656,7 +657,7 @@ impl Evaluator<'_> {
         let mut addr = locals.ptr[p.local].addr;
         let mut ty: Ty = locals.body.locals[p.local].ty.clone();
         let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
-        for proj in &*p.projection {
+        for proj in p.projection.lookup(&locals.body.projection_store) {
             let prev_ty = ty.clone();
             ty = self.projected_ty(ty, proj.clone());
             match proj {
@@ -837,7 +838,9 @@ impl Evaluator<'_> {
                                 let addr = self.place_addr(l, &locals)?;
                                 let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
                                 self.write_memory(addr, &result)?;
-                                locals.drop_flags.add_place(l.clone());
+                                locals
+                                    .drop_flags
+                                    .add_place(l.clone(), &locals.body.projection_store);
                             }
                             StatementKind::Deinit(_) => not_supported!("de-init statement"),
                             StatementKind::StorageLive(_)
@@ -889,7 +892,9 @@ impl Evaluator<'_> {
                                 )?,
                                 it => not_supported!("unknown function type {it:?}"),
                             };
-                            locals.drop_flags.add_place(destination.clone());
+                            locals
+                                .drop_flags
+                                .add_place(destination.clone(), &locals.body.projection_store);
                             if let Some(stack_frame) = stack_frame {
                                 self.code_stack.push(my_stack_frame);
                                 current_block_idx = stack_frame.locals.body.start_block;
@@ -970,7 +975,7 @@ impl Evaluator<'_> {
     ) -> Result<()> {
         let mut remain_args = body.param_locals.len();
         for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) {
-            locals.drop_flags.add_place(l.into());
+            locals.drop_flags.add_place(l.into(), &locals.body.projection_store);
             match value {
                 IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?,
                 IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?,
@@ -1646,7 +1651,7 @@ impl Evaluator<'_> {
     fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> {
         Ok(match it {
             Operand::Copy(p) | Operand::Move(p) => {
-                locals.drop_flags.remove_place(p);
+                locals.drop_flags.remove_place(p, &locals.body.projection_store);
                 self.eval_place(p, locals)?
             }
             Operand::Static(st) => {
@@ -2468,7 +2473,7 @@ impl Evaluator<'_> {
 
     fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> {
         let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
-        if !locals.drop_flags.remove_place(place) {
+        if !locals.drop_flags.remove_place(place, &locals.body.projection_store) {
             return Ok(());
         }
         let metadata = match metadata {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index b6408cea502..bceeb185003 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -244,6 +244,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
         let locals = Arena::new();
         let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
         let mir = MirBody {
+            projection_store: ProjectionStore::default(),
             basic_blocks,
             locals,
             start_block,
@@ -809,36 +810,34 @@ impl<'ctx> MirLowerCtx<'ctx> {
                             current = c;
                             operands[u32::from(field_id.into_raw()) as usize] = Some(op);
                         }
-                        self.push_assignment(
-                            current,
-                            place,
-                            Rvalue::Aggregate(
-                                AggregateKind::Adt(variant_id, subst),
-                                match spread_place {
-                                    Some(sp) => operands
-                                        .into_iter()
-                                        .enumerate()
-                                        .map(|(i, it)| match it {
-                                            Some(it) => it,
-                                            None => {
-                                                let p =
-                                                    sp.project(ProjectionElem::Field(FieldId {
-                                                        parent: variant_id,
-                                                        local_id: LocalFieldId::from_raw(
-                                                            RawIdx::from(i as u32),
-                                                        ),
-                                                    }));
-                                                Operand::Copy(p)
-                                            }
-                                        })
-                                        .collect(),
-                                    None => operands.into_iter().collect::<Option<_>>().ok_or(
-                                        MirLowerError::TypeError("missing field in record literal"),
-                                    )?,
-                                },
-                            ),
-                            expr_id.into(),
+                        let rvalue = Rvalue::Aggregate(
+                            AggregateKind::Adt(variant_id, subst),
+                            match spread_place {
+                                Some(sp) => operands
+                                    .into_iter()
+                                    .enumerate()
+                                    .map(|(i, it)| match it {
+                                        Some(it) => it,
+                                        None => {
+                                            let p = sp.project(
+                                                ProjectionElem::Field(FieldId {
+                                                    parent: variant_id,
+                                                    local_id: LocalFieldId::from_raw(RawIdx::from(
+                                                        i as u32,
+                                                    )),
+                                                }),
+                                                &mut self.result.projection_store,
+                                            );
+                                            Operand::Copy(p)
+                                        }
+                                    })
+                                    .collect(),
+                                None => operands.into_iter().collect::<Option<_>>().ok_or(
+                                    MirLowerError::TypeError("missing field in record literal"),
+                                )?,
+                            },
                         );
+                        self.push_assignment(current, place, rvalue, expr_id.into());
                         Ok(Some(current))
                     }
                     VariantId::UnionId(union_id) => {
@@ -847,10 +846,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
                         };
                         let local_id =
                             variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
-                        let place = place.project(PlaceElem::Field(FieldId {
-                            parent: union_id.into(),
-                            local_id,
-                        }));
+                        let place = place.project(
+                            PlaceElem::Field(FieldId { parent: union_id.into(), local_id }),
+                            &mut self.result.projection_store,
+                        );
                         self.lower_expr_to_place(*expr, place, current)
                     }
                 }
@@ -904,7 +903,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 else {
                     return Ok(None);
                 };
-                let p = place.project(ProjectionElem::Deref);
+                let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store);
                 self.push_assignment(current, p, operand.into(), expr_id.into());
                 Ok(Some(current))
             }
@@ -1126,27 +1125,31 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 for capture in captures.iter() {
                     let p = Place {
                         local: self.binding_local(capture.place.local)?,
-                        projection: capture
-                            .place
-                            .projections
-                            .clone()
-                            .into_iter()
-                            .map(|it| match it {
-                                ProjectionElem::Deref => ProjectionElem::Deref,
-                                ProjectionElem::Field(it) => ProjectionElem::Field(it),
-                                ProjectionElem::TupleOrClosureField(it) => {
-                                    ProjectionElem::TupleOrClosureField(it)
-                                }
-                                ProjectionElem::ConstantIndex { offset, from_end } => {
-                                    ProjectionElem::ConstantIndex { offset, from_end }
-                                }
-                                ProjectionElem::Subslice { from, to } => {
-                                    ProjectionElem::Subslice { from, to }
-                                }
-                                ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it),
-                                ProjectionElem::Index(it) => match it {},
-                            })
-                            .collect(),
+                        projection: self.result.projection_store.intern(
+                            capture
+                                .place
+                                .projections
+                                .clone()
+                                .into_iter()
+                                .map(|it| match it {
+                                    ProjectionElem::Deref => ProjectionElem::Deref,
+                                    ProjectionElem::Field(it) => ProjectionElem::Field(it),
+                                    ProjectionElem::TupleOrClosureField(it) => {
+                                        ProjectionElem::TupleOrClosureField(it)
+                                    }
+                                    ProjectionElem::ConstantIndex { offset, from_end } => {
+                                        ProjectionElem::ConstantIndex { offset, from_end }
+                                    }
+                                    ProjectionElem::Subslice { from, to } => {
+                                        ProjectionElem::Subslice { from, to }
+                                    }
+                                    ProjectionElem::OpaqueCast(it) => {
+                                        ProjectionElem::OpaqueCast(it)
+                                    }
+                                    ProjectionElem::Index(it) => match it {},
+                                })
+                                .collect(),
+                        ),
                     };
                     match &capture.kind {
                         CaptureKind::ByRef(bk) => {
@@ -1261,12 +1264,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
         match &self.body.exprs[lhs] {
             Expr::Tuple { exprs, is_assignee_expr: _ } => {
                 for (i, expr) in exprs.iter().enumerate() {
-                    let Some(c) = self.lower_destructing_assignment(
-                        current,
-                        *expr,
-                        rhs.project(ProjectionElem::TupleOrClosureField(i)),
-                        span,
-                    )?
+                    let rhs = rhs.project(
+                        ProjectionElem::TupleOrClosureField(i),
+                        &mut self.result.projection_store,
+                    );
+                    let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
                     else {
                         return Ok(None);
                     };
@@ -1323,17 +1325,21 @@ impl<'ctx> MirLowerCtx<'ctx> {
         placeholder_subst
     }
 
-    fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
+    fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> {
         if let Expr::Field { expr, name } = &self.body[expr_id] {
             if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
                 let index = name
                     .as_tuple_index()
                     .ok_or(MirLowerError::TypeError("named field on tuple"))?;
-                *place = place.project(ProjectionElem::TupleOrClosureField(index))
+                *place = place.project(
+                    ProjectionElem::TupleOrClosureField(index),
+                    &mut self.result.projection_store,
+                )
             } else {
                 let field =
                     self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
-                *place = place.project(ProjectionElem::Field(field));
+                *place =
+                    place.project(ProjectionElem::Field(field), &mut self.result.projection_store);
             }
         } else {
             not_supported!("")
@@ -1995,13 +2001,14 @@ pub fn mir_body_for_closure_query(
         FnTrait::FnOnce => vec![],
         FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
     };
-    ctx.result.walk_places(|p| {
+    ctx.result.walk_places(|p, store| {
         if let Some(it) = upvar_map.get(&p.local) {
             let r = it.iter().find(|it| {
-                if p.projection.len() < it.0.place.projections.len() {
+                if p.projection.lookup(&store).len() < it.0.place.projections.len() {
                     return false;
                 }
-                for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) {
+                for (it, y) in p.projection.lookup(&store).iter().zip(it.0.place.projections.iter())
+                {
                     match (it, y) {
                         (ProjectionElem::Deref, ProjectionElem::Deref) => (),
                         (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (),
@@ -2019,13 +2026,18 @@ pub fn mir_body_for_closure_query(
                     p.local = closure_local;
                     let mut next_projs = closure_projection.clone();
                     next_projs.push(PlaceElem::TupleOrClosureField(it.1));
-                    let prev_projs = mem::take(&mut p.projection);
+                    let prev_projs = p.projection;
                     if it.0.kind != CaptureKind::ByValue {
                         next_projs.push(ProjectionElem::Deref);
                     }
-                    next_projs
-                        .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len()));
-                    p.projection = next_projs.into();
+                    next_projs.extend(
+                        prev_projs
+                            .lookup(&store)
+                            .iter()
+                            .cloned()
+                            .skip(it.0.place.projections.len()),
+                    );
+                    p.projection = store.intern(next_projs.into());
                 }
                 None => err = Some(p.clone()),
             }
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 213f151ab67..8c078eb4ad7 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -70,7 +70,7 @@ impl MirLowerCtx<'_> {
                     else {
                         return Ok(None);
                     };
-                    it.0 = it.0.project(ProjectionElem::Deref);
+                    it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store);
                     Ok(Some(it))
                 }
                 Adjust::Deref(Some(od)) => {
@@ -152,7 +152,10 @@ impl MirLowerCtx<'_> {
                             Operand::Static(s).into(),
                             expr_id.into(),
                         );
-                        Ok(Some((temp.project(ProjectionElem::Deref), current)))
+                        Ok(Some((
+                            temp.project(ProjectionElem::Deref, &mut self.result.projection_store),
+                            current,
+                        )))
                     }
                     _ => try_rvalue(self),
                 }
@@ -203,7 +206,7 @@ impl MirLowerCtx<'_> {
                     else {
                         return Ok(None);
                     };
-                    r = r.project(ProjectionElem::Deref);
+                    r = r.project(ProjectionElem::Deref, &mut self.result.projection_store);
                     Ok(Some((r, current)))
                 }
                 _ => try_rvalue(self),
@@ -267,7 +270,8 @@ impl MirLowerCtx<'_> {
                 else {
                     return Ok(None);
                 };
-                p_base = p_base.project(ProjectionElem::Index(l_index));
+                p_base = p_base
+                    .project(ProjectionElem::Index(l_index), &mut self.result.projection_store);
                 Ok(Some((p_base, current)))
             }
             _ => try_rvalue(self),
@@ -308,7 +312,7 @@ impl MirLowerCtx<'_> {
         else {
             return Ok(None);
         };
-        result = result.project(ProjectionElem::Deref);
+        result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
         Ok(Some((result, current)))
     }
 
@@ -363,7 +367,7 @@ impl MirLowerCtx<'_> {
         else {
             return Ok(None);
         };
-        result = result.project(ProjectionElem::Deref);
+        result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
         Ok(Some((result, current)))
     }
 }
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 1cdfd919742..270f75ad967 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -81,13 +81,16 @@ impl MirLowerCtx<'_> {
         mode: MatchingMode,
     ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
         let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
-        cond_place.projection = cond_place
-            .projection
-            .iter()
-            .cloned()
-            .chain((0..cnt).map(|_| ProjectionElem::Deref))
-            .collect::<Vec<_>>()
-            .into();
+        cond_place.projection = self.result.projection_store.intern(
+            cond_place
+                .projection
+                .lookup(&self.result.projection_store)
+                .iter()
+                .cloned()
+                .chain((0..cnt).map(|_| ProjectionElem::Deref))
+                .collect::<Vec<_>>()
+                .into(),
+        );
         Ok(match &self.body.pats[pattern] {
             Pat::Missing => return Err(MirLowerError::IncompletePattern),
             Pat::Wild => (current, current_else),
@@ -262,20 +265,23 @@ impl MirLowerCtx<'_> {
                     }
                 }
                 for (i, &pat) in prefix.iter().enumerate() {
-                    let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
-                        offset: i as u64,
-                        from_end: false,
-                    });
+                    let next_place = (&mut cond_place).project(
+                        ProjectionElem::ConstantIndex { offset: i as u64, from_end: false },
+                        &mut self.result.projection_store,
+                    );
                     (current, current_else) =
                         self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
                 }
                 if let Some(slice) = slice {
                     if mode == MatchingMode::Bind {
                         if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
-                            let next_place = (&mut cond_place).project(ProjectionElem::Subslice {
-                                from: prefix.len() as u64,
-                                to: suffix.len() as u64,
-                            });
+                            let next_place = (&mut cond_place).project(
+                                ProjectionElem::Subslice {
+                                    from: prefix.len() as u64,
+                                    to: suffix.len() as u64,
+                                },
+                                &mut self.result.projection_store,
+                            );
                             (current, current_else) = self.pattern_match_binding(
                                 id,
                                 next_place,
@@ -287,10 +293,10 @@ impl MirLowerCtx<'_> {
                     }
                 }
                 for (i, &pat) in suffix.iter().enumerate() {
-                    let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
-                        offset: i as u64,
-                        from_end: true,
-                    });
+                    let next_place = (&mut cond_place).project(
+                        ProjectionElem::ConstantIndex { offset: i as u64, from_end: true },
+                        &mut self.result.projection_store,
+                    );
                     (current, current_else) =
                         self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
                 }
@@ -412,13 +418,11 @@ impl MirLowerCtx<'_> {
                     mode,
                 )?
             }
-            Pat::Ref { pat, mutability: _ } => self.pattern_match_inner(
-                current,
-                current_else,
-                cond_place.project(ProjectionElem::Deref),
-                *pat,
-                mode,
-            )?,
+            Pat::Ref { pat, mutability: _ } => {
+                let cond_place =
+                    cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
+                self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
+            }
             Pat::Box { .. } => not_supported!("box pattern"),
             Pat::ConstBlock(_) => not_supported!("const block pattern"),
         })
@@ -594,7 +598,7 @@ impl MirLowerCtx<'_> {
         mode: MatchingMode,
     ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
         for (proj, arg) in args {
-            let cond_place = cond_place.project(proj);
+            let cond_place = cond_place.project(proj, &mut self.result.projection_store);
             (current, current_else) =
                 self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
         }
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 781ffaecad5..0108859ff32 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -329,7 +329,7 @@ impl<'a> MirPrettyCtx<'a> {
                 }
             }
         }
-        f(self, p.local, &p.projection);
+        f(self, p.local, &p.projection.lookup(&self.body.projection_store));
     }
 
     fn operand(&mut self, r: &Operand) {