diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 63b16aa95a6a5..24d11ac718129 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -46,6 +46,7 @@ pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionError
 pub(crate) use region_name::{RegionName, RegionNameSource};
 pub(crate) use rustc_const_eval::util::CallKind;
 
+#[derive(Copy, Clone)]
 pub(super) struct DescribePlaceOpt {
     pub including_downcast: bool,
 
@@ -265,7 +266,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
                 ProjectionElem::Index(index) => {
                     buf.push('[');
-                    if self.append_local_to_string(*index, &mut buf).is_err() {
+                    if let Some(index) = self.describe_place_with_options(index.as_ref(), opt) {
+                        buf.push_str(&index);
+                    } else {
                         buf.push('_');
                     }
                     buf.push(']');
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 89d955e8bf2e1..2f313d2e5a6fe 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -851,8 +851,8 @@ pub(crate) fn codegen_place<'tcx>(
             PlaceElem::Field(field, _ty) => {
                 cplace = cplace.place_field(fx, field);
             }
-            PlaceElem::Index(local) => {
-                let index = fx.get_local_place(local).to_cvalue(fx).load_scalar(fx);
+            PlaceElem::Index(index) => {
+                let index = codegen_place(fx, index).to_cvalue(fx).load_scalar(fx);
                 cplace = cplace.place_index(fx, index);
             }
             PlaceElem::ConstantIndex { offset, min_length: _, from_end } => {
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index c7617d2e464fa..f685d72139a8d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -141,9 +141,9 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx,
             // HACK(eddyb) this emulates the old `visit_projection_elem`, this
             // entire `visit_place`-like `process_place` method should be rewritten,
             // now that we have moved to the "slice of projections" representation.
-            if let mir::ProjectionElem::Index(local) = elem {
-                self.visit_local(
-                    local,
+            if let mir::ProjectionElem::Index(ref index) = elem {
+                self.visit_place(
+                    index,
                     PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                     location,
                 );
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 291464ab58ae2..08bfda487fc86 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -360,9 +360,9 @@ where
             Field(field, _) => self.place_field(base, field.index())?,
             Downcast(_, variant) => self.place_downcast(base, variant)?,
             Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
-            Index(local) => {
+            Index(place) => {
                 let layout = self.layout_of(self.tcx.types.usize)?;
-                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.eval_place_to_op(place, Some(layout))?;
                 let n = self.read_machine_usize(&n)?;
                 self.place_index(base, n)?
             }
@@ -389,9 +389,9 @@ where
             Field(field, _) => self.operand_field(base, field.index())?,
             Downcast(_, variant) => self.operand_downcast(base, variant)?,
             Deref => self.deref_operand(base)?.into(),
-            Index(local) => {
+            Index(place) => {
                 let layout = self.layout_of(self.tcx.types.usize)?;
-                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.eval_place_to_op(place, Some(layout))?;
                 let n = self.read_machine_usize(&n)?;
                 self.operand_index(base, n)?
             }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 8ca3fdf400eb3..b1ef3b5fa0ddb 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -298,7 +298,9 @@ where
     let mut place = place;
     while let Some((place_base, elem)) = place.last_projection() {
         match elem {
-            ProjectionElem::Index(index) if in_local(index) => return true,
+            ProjectionElem::Index(index) if in_place::<Q, _>(cx, in_local, index.as_ref()) => {
+                return true;
+            }
 
             ProjectionElem::Deref
             | ProjectionElem::Field(_, _)
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 04ce701452b90..67c85098db916 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -361,27 +361,28 @@ impl<'tcx> Validator<'_, 'tcx> {
 
                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {}
 
-                    ProjectionElem::Index(local) => {
+                    ProjectionElem::Index(place) => {
                         let mut promotable = false;
                         // Only accept if we can predict the index and are indexing an array.
-                        let val =
-                            if let TempState::Defined { location: loc, .. } = self.temps[local] {
-                                let block = &self.body[loc.block];
-                                if loc.statement_index < block.statements.len() {
-                                    let statement = &block.statements[loc.statement_index];
-                                    match &statement.kind {
-                                        StatementKind::Assign(box (
-                                            _,
-                                            Rvalue::Use(Operand::Constant(c)),
-                                        )) => c.literal.try_eval_usize(self.tcx, self.param_env),
-                                        _ => None,
-                                    }
-                                } else {
-                                    None
+                        let val = if let Some(local) = place.as_local()
+                            && let TempState::Defined { location: loc, .. } = self.temps[local]
+                        {
+                            let block = &self.body[loc.block];
+                            if loc.statement_index < block.statements.len() {
+                                let statement = &block.statements[loc.statement_index];
+                                match &statement.kind {
+                                    StatementKind::Assign(box (
+                                        _,
+                                        Rvalue::Use(Operand::Constant(c)),
+                                    )) => c.literal.try_eval_usize(self.tcx, self.param_env),
+                                    _ => None,
                                 }
                             } else {
                                 None
-                            };
+                            }
+                        } else {
+                            None
+                        };
                         if let Some(idx) = val {
                             // Determine the type of the thing we are indexing.
                             let ty = place_base.ty(self.body, self.tcx).ty;
@@ -403,7 +404,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                             return Err(Unpromotable);
                         }
 
-                        self.validate_local(local)?;
+                        self.validate_place(place.as_ref())?;
                     }
 
                     ProjectionElem::Field(..) => {
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 94e1b95a0eb3c..34723b13ec018 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -204,8 +204,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
         location: Location,
     ) {
         match elem {
-            ProjectionElem::Index(index) => {
-                let index_ty = self.body.local_decls[index].ty;
+            ProjectionElem::Index(ref index) => {
+                let index_ty = Place::ty(index, &self.body.local_decls, self.tcx).ty;
                 if index_ty != self.tcx.types.usize {
                     self.fail(location, format!("bad index ({:?} != usize)", index_ty))
                 }
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 6b4489026d3d3..98243c9ded3d2 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -956,7 +956,7 @@ pub enum ProjectionElem<V, T> {
 
 /// Alias for projections as they appear in places, where the base is a place
 /// and the index is a local.
-pub type PlaceElem<'tcx> = ProjectionElem<Local, Ty<'tcx>>;
+pub type PlaceElem<'tcx> = ProjectionElem<Place<'tcx>, Ty<'tcx>>;
 
 ///////////////////////////////////////////////////////////////////////////
 // Operands
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 1a264d2d5af9a..4a1ea5198f6fd 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1078,15 +1078,15 @@ macro_rules! visit_place_fns {
             location: Location,
         ) -> Option<PlaceElem<'tcx>> {
             match elem {
-                PlaceElem::Index(local) => {
-                    let mut new_local = local;
-                    self.visit_local(
-                        &mut new_local,
+                PlaceElem::Index(place) => {
+                    let mut new_place = place;
+                    self.visit_place(
+                        &mut new_place,
                         PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                         location,
                     );
 
-                    if new_local == local { None } else { Some(PlaceElem::Index(new_local)) }
+                    if new_place == place { None } else { Some(PlaceElem::Index(new_place)) }
                 }
                 PlaceElem::Field(field, ty) => {
                     let mut new_ty = ty;
@@ -1170,9 +1170,9 @@ macro_rules! visit_place_fns {
                 ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => {
                     self.visit_ty(ty, TyContext::Location(location));
                 }
-                ProjectionElem::Index(local) => {
-                    self.visit_local(
-                        local,
+                ProjectionElem::Index(ref place) => {
+                    self.visit_place(
+                        place,
                         PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                         location,
                     );
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 5de414077a2b1..a767bff1eb9bf 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -2048,7 +2048,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn mk_place_index(self, place: Place<'tcx>, index: Local) -> Place<'tcx> {
-        self.mk_place_elem(place, PlaceElem::Index(index))
+        self.mk_place_elem(place, PlaceElem::Index(index.into()))
     }
 
     /// This method copies `Place`'s projection, add an element and reintern it. Should not be used
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index dca4906c07de5..7940697f262eb 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -190,7 +190,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
                     _ => (*arg, PlaceElem::Deref),
                 )
             },
-            ExprKind::Index { lhs, index } => (*lhs, PlaceElem::Index(self.parse_local(*index)?)),
+            ExprKind::Index { lhs, index } => (*lhs, PlaceElem::Index(self.parse_place(*index)?)),
             ExprKind::Field { lhs, name: field, .. } => (*lhs, PlaceElem::Field(*field, expr.ty)),
             _ => {
                 let place = self.parse_local(expr_id).map(Place::from)?;
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index e22fa6365dcb4..e41df49e03fef 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -308,7 +308,7 @@ impl<'tcx> PlaceBuilder<'tcx> {
     }
 
     fn index(self, index: Local) -> Self {
-        self.project(PlaceElem::Index(index))
+        self.project(PlaceElem::Index(index.into()))
     }
 
     pub(crate) fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
index 7806e8f45d3ad..6de8e9122c438 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
@@ -11,7 +11,7 @@
 //! `a[x]` would still overlap them both. But that is not this
 //! representation does today.)
 
-use rustc_middle::mir::{Local, Operand, PlaceElem, ProjectionElem};
+use rustc_middle::mir::{Local, Operand, Place, PlaceElem, ProjectionElem};
 use rustc_middle::ty::Ty;
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -36,6 +36,12 @@ impl Lift for Local {
         AbstractOperand
     }
 }
+impl<'tcx> Lift for Place<'tcx> {
+    type Abstract = AbstractOperand;
+    fn lift(&self) -> Self::Abstract {
+        AbstractOperand
+    }
+}
 impl<'tcx> Lift for Ty<'tcx> {
     type Abstract = AbstractType;
     fn lift(&self) -> Self::Abstract {
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index c097af6161159..7b8e40f074ad5 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -128,13 +128,7 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> {
                 self.tcx,
             );
         } else {
-            self.visit_local(&mut place.local, context, location);
-
-            for elem in place.projection.iter() {
-                if let PlaceElem::Index(local) = elem {
-                    assert_ne!(local, SELF_ARG);
-                }
-            }
+            self.super_place(place, context, location);
         }
     }
 }
@@ -167,13 +161,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
                 self.tcx,
             );
         } else {
-            self.visit_local(&mut place.local, context, location);
-
-            for elem in place.projection.iter() {
-                if let PlaceElem::Index(local) = elem {
-                    assert_ne!(local, SELF_ARG);
-                }
-            }
+            self.super_place(place, context, location);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 4219e6280ebbc..fc214c0868673 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -1054,12 +1054,6 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
     }
 
     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
-        for elem in place.projection {
-            // FIXME: Make sure that return place is not used in an indexing projection, since it
-            // won't be rebased as it is supposed to be.
-            assert_ne!(ProjectionElem::Index(RETURN_PLACE), elem);
-        }
-
         // If this is the `RETURN_PLACE`, we need to rebase any projections onto it.
         let dest_proj_len = self.destination.projection.len();
         if place.local == RETURN_PLACE && dest_proj_len > 0 {
diff --git a/src/test/mir-opt/inline/issue_106141.rs b/src/test/mir-opt/inline/issue_106141.rs
new file mode 100644
index 0000000000000..538d687a47699
--- /dev/null
+++ b/src/test/mir-opt/inline/issue_106141.rs
@@ -0,0 +1,23 @@
+pub fn outer() -> usize {
+    inner()
+}
+
+fn index() -> usize {
+    loop {}
+}
+
+#[inline]
+fn inner() -> usize {
+    let buffer = &[true];
+    let index = index();
+    // DestProp may unify `index` with `_0`.  Check that inlining does not ICE.
+    if buffer[index] {
+        index
+    } else {
+        0
+    }
+}
+
+fn main() {
+    let _ = outer();
+}