Rollup merge of #145695 - cjgillot:place-elem-map, r=oli-obk,lcnr
Introduce ProjectionElem::try_map. Small utility function useful to manipulate MIR place projections.
This commit is contained in:
@@ -60,6 +60,7 @@
|
|||||||
#![feature(try_trait_v2_residual)]
|
#![feature(try_trait_v2_residual)]
|
||||||
#![feature(try_trait_v2_yeet)]
|
#![feature(try_trait_v2_yeet)]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![feature(unwrap_infallible)]
|
||||||
#![feature(yeet_expr)]
|
#![feature(yeet_expr)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
// tidy-alphabetical-end
|
// tidy-alphabetical-end
|
||||||
|
|||||||
@@ -160,7 +160,11 @@ impl<'tcx> PlaceTy<'tcx> {
|
|||||||
/// Convenience wrapper around `projection_ty_core` for `PlaceElem`,
|
/// Convenience wrapper around `projection_ty_core` for `PlaceElem`,
|
||||||
/// where we can just use the `Ty` that is already stored inline on
|
/// where we can just use the `Ty` that is already stored inline on
|
||||||
/// field projection elems.
|
/// 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)
|
self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,6 +294,36 @@ impl<V, T> ProjectionElem<V, T> {
|
|||||||
Self::UnwrapUnsafeBinder(..) => false,
|
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
|
/// Alias for projections as they appear in `UserTypeProjection`, where we
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
//! 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-deref on a local variable, `x.field`, has the same meaning
|
|
||||||
//! in both domains). 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 this
|
|
||||||
//! representation does today.)
|
|
||||||
|
|
||||||
use rustc_middle::mir::{PlaceElem, ProjectionElem, ProjectionKind};
|
|
||||||
|
|
||||||
pub(crate) trait Lift {
|
|
||||||
fn lift(&self) -> ProjectionKind;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Lift for PlaceElem<'tcx> {
|
|
||||||
fn lift(&self) -> ProjectionKind {
|
|
||||||
match *self {
|
|
||||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
|
||||||
ProjectionElem::Field(f, _ty) => ProjectionElem::Field(f, ()),
|
|
||||||
ProjectionElem::OpaqueCast(_ty) => ProjectionElem::OpaqueCast(()),
|
|
||||||
ProjectionElem::Index(_i) => ProjectionElem::Index(()),
|
|
||||||
ProjectionElem::Subslice { from, to, from_end } => {
|
|
||||||
ProjectionElem::Subslice { from, to, from_end }
|
|
||||||
}
|
|
||||||
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
|
|
||||||
ProjectionElem::ConstantIndex { offset, min_length, from_end }
|
|
||||||
}
|
|
||||||
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
|
|
||||||
ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()),
|
|
||||||
ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ use rustc_middle::{bug, span_bug};
|
|||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use super::abs_domain::Lift;
|
|
||||||
use super::{
|
use super::{
|
||||||
Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut,
|
Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut,
|
||||||
MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
|
MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
|
||||||
@@ -241,7 +240,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||||||
if union_path.is_none() {
|
if union_path.is_none() {
|
||||||
// inlined from add_move_path because of a borrowck conflict with the iterator
|
// inlined from add_move_path because of a borrowck conflict with the iterator
|
||||||
base =
|
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(
|
new_move_path(
|
||||||
&mut data.move_paths,
|
&mut data.move_paths,
|
||||||
&mut data.path_map,
|
&mut data.path_map,
|
||||||
@@ -272,7 +271,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||||||
tcx,
|
tcx,
|
||||||
..
|
..
|
||||||
} = self;
|
} = 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))
|
new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::fmt;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
@@ -8,11 +20,8 @@ use rustc_middle::ty::{Ty, TyCtxt};
|
|||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use self::abs_domain::Lift;
|
|
||||||
use crate::un_derefer::UnDerefer;
|
use crate::un_derefer::UnDerefer;
|
||||||
|
|
||||||
mod abs_domain;
|
|
||||||
|
|
||||||
rustc_index::newtype_index! {
|
rustc_index::newtype_index! {
|
||||||
#[orderable]
|
#[orderable]
|
||||||
#[debug_format = "mp{}"]
|
#[debug_format = "mp{}"]
|
||||||
@@ -324,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (_, elem) in self.un_derefer.iter_projections(place) {
|
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;
|
result = subpath;
|
||||||
} else {
|
} else {
|
||||||
return LookupResult::Parent(Some(result));
|
return LookupResult::Parent(Some(result));
|
||||||
|
|||||||
@@ -447,26 +447,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||||||
|
|
||||||
Projection(base, elem) => {
|
Projection(base, elem) => {
|
||||||
let base = self.evaluated[base].as_ref()?;
|
let base = self.evaluated[base].as_ref()?;
|
||||||
let elem = match elem {
|
// `Index` by constants should have been replaced by `ConstantIndex` by
|
||||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
// `simplify_place_projection`.
|
||||||
ProjectionElem::Downcast(name, read_variant) => {
|
let elem = elem.try_map(|_| None, |()| ty.ty)?;
|
||||||
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,
|
|
||||||
};
|
|
||||||
self.ecx.project(base, elem).discard_err()?
|
self.ecx.project(base, elem).discard_err()?
|
||||||
}
|
}
|
||||||
Address { place, kind: _, provenance: _ } => {
|
Address { place, kind: _, provenance: _ } => {
|
||||||
@@ -476,13 +459,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||||||
let local = self.locals[place.local]?;
|
let local = self.locals[place.local]?;
|
||||||
let pointer = self.evaluated[local].as_ref()?;
|
let pointer = self.evaluated[local].as_ref()?;
|
||||||
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
|
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
|
||||||
for proj in place.projection.iter().skip(1) {
|
for elem in place.projection.iter().skip(1) {
|
||||||
// We have no call stack to associate a local with a value, so we cannot
|
// `Index` by constants should have been replaced by `ConstantIndex` by
|
||||||
// interpret indexing.
|
// `simplify_place_projection`.
|
||||||
if matches!(proj, ProjectionElem::Index(_)) {
|
let elem = elem.try_map(|_| None, |ty| ty)?;
|
||||||
return None;
|
mplace = self.ecx.project(&mplace, elem).discard_err()?;
|
||||||
}
|
|
||||||
mplace = self.ecx.project(&mplace, proj).discard_err()?;
|
|
||||||
}
|
}
|
||||||
let pointer = mplace.to_ref(&self.ecx);
|
let pointer = mplace.to_ref(&self.ecx);
|
||||||
ImmTy::from_immediate(pointer, ty).into()
|
ImmTy::from_immediate(pointer, ty).into()
|
||||||
@@ -902,27 +883,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||||||
proj: ProjectionElem<VnIndex, ()>,
|
proj: ProjectionElem<VnIndex, ()>,
|
||||||
loc: Location,
|
loc: Location,
|
||||||
) -> Option<PlaceElem<'tcx>> {
|
) -> Option<PlaceElem<'tcx>> {
|
||||||
Some(match proj {
|
proj.try_map(
|
||||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
|value| {
|
||||||
ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty),
|
let local = self.try_as_local(value, loc)?;
|
||||||
ProjectionElem::Index(idx) => {
|
|
||||||
let Some(local) = self.try_as_local(idx, loc) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
self.reused_locals.insert(local);
|
self.reused_locals.insert(local);
|
||||||
ProjectionElem::Index(local)
|
Some(local)
|
||||||
}
|
},
|
||||||
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
|
|()| ty,
|
||||||
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),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simplify_aggregate_to_copy(
|
fn simplify_aggregate_to_copy(
|
||||||
|
|||||||
Reference in New Issue
Block a user