Skip to content

Introduce ProjectionElem::try_map. #145695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#![feature(try_trait_v2_residual)]
#![feature(try_trait_v2_yeet)]
#![feature(type_alias_impl_trait)]
#![feature(unwrap_infallible)]
#![feature(yeet_expr)]
#![recursion_limit = "256"]
// tidy-alphabetical-end
Expand Down
36 changes: 35 additions & 1 deletion compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ impl<'tcx> PlaceTy<'tcx> {
/// Convenience wrapper around `projection_ty_core` for `PlaceElem`,
/// where we can just use the `Ty` that is already stored inline on
/// field projection elems.
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
pub fn projection_ty<V: ::std::fmt::Debug>(
self,
tcx: TyCtxt<'tcx>,
elem: ProjectionElem<V, Ty<'tcx>>,
) -> PlaceTy<'tcx> {
self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty)
}

Expand Down Expand Up @@ -290,6 +294,36 @@ impl<V, T> ProjectionElem<V, T> {
Self::UnwrapUnsafeBinder(..) => false,
}
}

/// Returns the `ProjectionKind` associated to this projection.
pub fn kind(self) -> ProjectionKind {
self.try_map(|_| Some(()), |_| ()).unwrap()
}

/// Apply functions to types and values in this projection and return the result.
pub fn try_map<V2, T2>(
self,
v: impl FnOnce(V) -> Option<V2>,
t: impl FnOnce(T) -> T2,
) -> Option<ProjectionElem<V2, T2>> {
Some(match self {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Downcast(name, read_variant) => {
ProjectionElem::Downcast(name, read_variant)
}
ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, t(ty)),
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)),
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)),
ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)),
ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?),
})
}
}

/// Alias for projections as they appear in `UserTypeProjection`, where we
Expand Down
38 changes: 0 additions & 38 deletions compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs

This file was deleted.

5 changes: 2 additions & 3 deletions compiler/rustc_mir_dataflow/src/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use rustc_middle::{bug, span_bug};
use smallvec::{SmallVec, smallvec};
use tracing::debug;

use super::abs_domain::Lift;
use super::{
Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut,
MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
Expand Down Expand Up @@ -241,7 +240,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
if union_path.is_none() {
// inlined from add_move_path because of a borrowck conflict with the iterator
base =
*data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| {
*data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| {
new_move_path(
&mut data.move_paths,
&mut data.path_map,
Expand Down Expand Up @@ -272,7 +271,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
tcx,
..
} = self;
*rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || {
*rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || {
new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
})
}
Expand Down
17 changes: 13 additions & 4 deletions compiler/rustc_mir_dataflow/src/move_paths/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
//! The move-analysis portion of borrowck needs to work in an abstract domain of lifted `Place`s.
//! Most of the `Place` variants fall into a one-to-one mapping between the concrete and abstract
//! (e.g., a field projection on a local variable, `x.field`, has the same meaning in both
//! domains). In other words, all field projections for the same field on the same local do not
//! have meaningfully different types if ever. Indexed projections are the exception: `a[x]` needs
//! to be treated as mapping to the same move path as `a[y]` as well as `a[13]`, etc. So we map
//! these `x`/`y` values to `()`.
//!
//! (In theory, the analysis could be extended to work with sets of paths, so that `a[0]` and
//! `a[13]` could be kept distinct, while `a[x]` would still overlap them both. But that is not
//! what this representation does today.)

use std::fmt;
use std::ops::{Index, IndexMut};

Expand All @@ -8,11 +20,8 @@ use rustc_middle::ty::{Ty, TyCtxt};
use rustc_span::Span;
use smallvec::SmallVec;

use self::abs_domain::Lift;
use crate::un_derefer::UnDerefer;

mod abs_domain;

rustc_index::newtype_index! {
#[orderable]
#[debug_format = "mp{}"]
Expand Down Expand Up @@ -324,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> {
};

for (_, elem) in self.un_derefer.iter_projections(place) {
if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
if let Some(&subpath) = self.projections.get(&(result, elem.kind())) {
result = subpath;
} else {
return LookupResult::Parent(Some(result));
Expand Down
62 changes: 15 additions & 47 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,26 +447,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {

Projection(base, elem) => {
let base = self.evaluated[base].as_ref()?;
let elem = match elem {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Downcast(name, read_variant) => {
ProjectionElem::Downcast(name, read_variant)
}
ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty),
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty),
ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty),
ProjectionElem::UnwrapUnsafeBinder(()) => {
ProjectionElem::UnwrapUnsafeBinder(ty.ty)
}
// This should have been replaced by a `ConstantIndex` earlier.
ProjectionElem::Index(_) => return None,
};
// `Index` by constants should have been replaced by `ConstantIndex` by
// `simplify_place_projection`.
let elem = elem.try_map(|_| None, |()| ty.ty)?;
self.ecx.project(base, elem).discard_err()?
}
Address { place, kind: _, provenance: _ } => {
Expand All @@ -476,13 +459,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let local = self.locals[place.local]?;
let pointer = self.evaluated[local].as_ref()?;
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
for proj in place.projection.iter().skip(1) {
// We have no call stack to associate a local with a value, so we cannot
// interpret indexing.
if matches!(proj, ProjectionElem::Index(_)) {
return None;
}
mplace = self.ecx.project(&mplace, proj).discard_err()?;
for elem in place.projection.iter().skip(1) {
// `Index` by constants should have been replaced by `ConstantIndex` by
// `simplify_place_projection`.
let elem = elem.try_map(|_| None, |ty| ty)?;
mplace = self.ecx.project(&mplace, elem).discard_err()?;
}
let pointer = mplace.to_ref(&self.ecx);
ImmTy::from_immediate(pointer, ty).into()
Expand Down Expand Up @@ -902,27 +883,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
proj: ProjectionElem<VnIndex, ()>,
loc: Location,
) -> Option<PlaceElem<'tcx>> {
Some(match proj {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty),
ProjectionElem::Index(idx) => {
let Some(local) = self.try_as_local(idx, loc) else {
return None;
};
proj.try_map(
|value| {
let local = self.try_as_local(value, loc)?;
self.reused_locals.insert(local);
ProjectionElem::Index(local)
}
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty),
ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty),
ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty),
})
Some(local)
},
|()| ty,
)
}

fn simplify_aggregate_to_copy(
Expand Down
Loading