mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #103880 - b-naber:field-ty-mir, r=lcnr
Use non-ascribed type as field's type in mir Fixes https://github.com/rust-lang/rust/issues/96514 r? `@lcnr`
This commit is contained in:
commit
03770f0e2b
@ -1488,7 +1488,7 @@ impl<'tcx> StatementKind<'tcx> {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Places
|
||||
|
||||
impl<V, T> ProjectionElem<V, T> {
|
||||
impl<V, T, U> ProjectionElem<V, T, U> {
|
||||
/// Returns `true` if the target of this projection may refer to a different region of memory
|
||||
/// than the base.
|
||||
fn is_indirect(&self) -> bool {
|
||||
@ -1517,7 +1517,7 @@ impl<V, T> ProjectionElem<V, T> {
|
||||
|
||||
/// Alias for projections as they appear in `UserTypeProjection`, where we
|
||||
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
|
||||
pub type ProjectionKind = ProjectionElem<(), ()>;
|
||||
pub type ProjectionKind = ProjectionElem<(), (), ()>;
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// A [newtype'd][wrapper] index type in the MIR [control-flow graph][CFG]
|
||||
|
@ -890,11 +890,18 @@ pub struct Place<'tcx> {
|
||||
pub projection: &'tcx List<PlaceElem<'tcx>>,
|
||||
}
|
||||
|
||||
/// The different kinds of projections that can be used in the projection of a `Place`.
|
||||
///
|
||||
/// `T1` is the generic type for a field projection. For an actual projection on a `Place`
|
||||
/// this parameter will always be `Ty`, but the field type can be unavailable when
|
||||
/// building (by using `PlaceBuilder`) places that correspond to upvars.
|
||||
/// `T2` is the generic type for an `OpaqueCast` (is generic since it's abstracted over
|
||||
/// in dataflow analysis, see `AbstractElem`).
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub enum ProjectionElem<V, T> {
|
||||
pub enum ProjectionElem<V, T1, T2> {
|
||||
Deref,
|
||||
Field(Field, T),
|
||||
Field(Field, T1),
|
||||
/// Index into a slice/array.
|
||||
///
|
||||
/// Note that this does not also dereference, and so it does not exactly correspond to slice
|
||||
@ -950,12 +957,36 @@ pub enum ProjectionElem<V, T> {
|
||||
|
||||
/// Like an explicit cast from an opaque type to a concrete type, but without
|
||||
/// requiring an intermediate variable.
|
||||
OpaqueCast(T),
|
||||
OpaqueCast(T2),
|
||||
}
|
||||
|
||||
/// 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<Local, Ty<'tcx>, Ty<'tcx>>;
|
||||
|
||||
/// Alias for projections that appear in `PlaceBuilder::Upvar`, for which
|
||||
/// we cannot provide any field types.
|
||||
pub type UpvarProjectionElem<'tcx> = ProjectionElem<Local, (), Ty<'tcx>>;
|
||||
|
||||
impl<'tcx> From<PlaceElem<'tcx>> for UpvarProjectionElem<'tcx> {
|
||||
fn from(elem: PlaceElem<'tcx>) -> Self {
|
||||
match elem {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
ProjectionElem::Field(field, _) => ProjectionElem::Field(field, ()),
|
||||
ProjectionElem::Index(v) => ProjectionElem::Index(v),
|
||||
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(opt_sym, variant_idx) => {
|
||||
ProjectionElem::Downcast(opt_sym, variant_idx)
|
||||
}
|
||||
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Operands
|
||||
|
@ -28,8 +28,8 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||
/// `place_ty.field_ty(tcx, f)` computes the type at a given field
|
||||
/// of a record or enum-variant. (Most clients of `PlaceTy` can
|
||||
/// instead just extract the relevant type directly from their
|
||||
/// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
|
||||
/// not carry a `Ty` for `T`.)
|
||||
/// `PlaceElem`, but some instances of `ProjectionElem<V, T1, T2>` do
|
||||
/// not carry a `Ty` for `T1` or `T2`.)
|
||||
///
|
||||
/// Note that the resulting type has not been normalized.
|
||||
pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: Field) -> Ty<'tcx> {
|
||||
@ -64,17 +64,18 @@ impl<'tcx> PlaceTy<'tcx> {
|
||||
/// `Ty` or downcast variant corresponding to that projection.
|
||||
/// The `handle_field` callback must map a `Field` to its `Ty`,
|
||||
/// (which should be trivial when `T` = `Ty`).
|
||||
pub fn projection_ty_core<V, T>(
|
||||
pub fn projection_ty_core<V, T1, T2>(
|
||||
self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
elem: &ProjectionElem<V, T>,
|
||||
mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>,
|
||||
mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>,
|
||||
elem: &ProjectionElem<V, T1, T2>,
|
||||
mut handle_field: impl FnMut(&Self, Field, T1) -> Ty<'tcx>,
|
||||
mut handle_opaque_cast: impl FnMut(&Self, T2) -> Ty<'tcx>,
|
||||
) -> PlaceTy<'tcx>
|
||||
where
|
||||
V: ::std::fmt::Debug,
|
||||
T: ::std::fmt::Debug + Copy,
|
||||
T1: ::std::fmt::Debug + Copy,
|
||||
T2: ::std::fmt::Debug + Copy,
|
||||
{
|
||||
if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
|
||||
bug!("cannot use non field projection on downcasted place")
|
||||
|
@ -7,6 +7,7 @@ use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::hir::place::Projection as HirProjection;
|
||||
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::tcx::PlaceTy;
|
||||
use rustc_middle::mir::AssertKind::BoundsCheck;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
@ -18,23 +19,31 @@ use rustc_target::abi::VariantIdx;
|
||||
use rustc_index::vec::Idx;
|
||||
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::convert::From;
|
||||
use std::iter;
|
||||
|
||||
/// The "outermost" place that holds this value.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum PlaceBase {
|
||||
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
|
||||
/// place by pushing more and more projections onto the end, and then convert the final set into a
|
||||
/// place using the `into_place` method.
|
||||
///
|
||||
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
|
||||
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(in crate::build) enum PlaceBuilder<'tcx> {
|
||||
/// Denotes the start of a `Place`.
|
||||
Local(Local),
|
||||
///
|
||||
/// We use `PlaceElem` since this has all `Field` types available.
|
||||
Local { local: Local, projection: Vec<PlaceElem<'tcx>> },
|
||||
|
||||
/// When building place for an expression within a closure, the place might start off a
|
||||
/// captured path. When `capture_disjoint_fields` is enabled, we might not know the capture
|
||||
/// index (within the desugared closure) of the captured path until most of the projections
|
||||
/// are applied. We use `PlaceBase::Upvar` to keep track of the root variable off of which the
|
||||
/// are applied. We use `PlaceBuilder::Upvar` to keep track of the root variable off of which the
|
||||
/// captured path starts, the closure the capture belongs to and the trait the closure
|
||||
/// implements.
|
||||
///
|
||||
/// Once we have figured out the capture index, we can convert the place builder to start from
|
||||
/// `PlaceBase::Local`.
|
||||
/// Once we have figured out the capture index, we can convert the place builder to
|
||||
/// `PlaceBuilder::Local`.
|
||||
///
|
||||
/// Consider the following example
|
||||
/// ```rust
|
||||
@ -55,24 +64,16 @@ pub(crate) enum PlaceBase {
|
||||
///
|
||||
/// When `capture_disjoint_fields` is enabled, `t.0.0.0` is captured and we won't be able to
|
||||
/// figure out that it is captured until all the `Field` projections are applied.
|
||||
Upvar {
|
||||
/// HirId of the upvar
|
||||
var_hir_id: LocalVarId,
|
||||
/// DefId of the closure
|
||||
closure_def_id: LocalDefId,
|
||||
},
|
||||
///
|
||||
/// Note: in contrast to `PlaceBuilder::Local` we have not yet determined all `Field` types
|
||||
/// and will only do so once converting to `PlaceBuilder::Local`.
|
||||
Upvar { upvar: Upvar, projection: Vec<UpvarProjectionElem<'tcx>> },
|
||||
}
|
||||
|
||||
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
|
||||
/// place by pushing more and more projections onto the end, and then convert the final set into a
|
||||
/// place using the `to_place` method.
|
||||
///
|
||||
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
|
||||
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(in crate::build) struct PlaceBuilder<'tcx> {
|
||||
base: PlaceBase,
|
||||
projection: Vec<PlaceElem<'tcx>>,
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) struct Upvar {
|
||||
var_hir_id: LocalVarId,
|
||||
closure_def_id: LocalDefId,
|
||||
}
|
||||
|
||||
/// Given a list of MIR projections, convert them to list of HIR ProjectionKind.
|
||||
@ -82,7 +83,7 @@ pub(in crate::build) struct PlaceBuilder<'tcx> {
|
||||
/// part of a path that is captured by a closure. We stop applying projections once we see the first
|
||||
/// projection that isn't captured by a closure.
|
||||
fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
|
||||
mir_projections: &[PlaceElem<'tcx>],
|
||||
mir_projections: &[UpvarProjectionElem<'tcx>],
|
||||
) -> Vec<HirProjectionKind> {
|
||||
let mut hir_projections = Vec::new();
|
||||
let mut variant = None;
|
||||
@ -156,7 +157,7 @@ fn is_ancestor_or_same_capture(
|
||||
fn find_capture_matching_projections<'a, 'tcx>(
|
||||
upvars: &'a CaptureMap<'tcx>,
|
||||
var_hir_id: LocalVarId,
|
||||
projections: &[PlaceElem<'tcx>],
|
||||
projections: &[UpvarProjectionElem<'tcx>],
|
||||
) -> Option<(usize, &'a Capture<'tcx>)> {
|
||||
let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
|
||||
|
||||
@ -174,7 +175,7 @@ fn to_upvars_resolved_place_builder<'tcx>(
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
var_hir_id: LocalVarId,
|
||||
closure_def_id: LocalDefId,
|
||||
projection: &[PlaceElem<'tcx>],
|
||||
projection: &[UpvarProjectionElem<'tcx>],
|
||||
) -> Option<PlaceBuilder<'tcx>> {
|
||||
let Some((capture_index, capture)) =
|
||||
find_capture_matching_projections(
|
||||
@ -196,23 +197,32 @@ fn to_upvars_resolved_place_builder<'tcx>(
|
||||
var_hir_id, projection,
|
||||
);
|
||||
}
|
||||
|
||||
return None;
|
||||
};
|
||||
|
||||
// Access the capture by accessing the field within the Closure struct.
|
||||
let capture_info = &cx.upvars[capture_index];
|
||||
|
||||
let mut upvar_resolved_place_builder = PlaceBuilder::from(capture_info.use_place);
|
||||
let Place { local: upvar_resolved_local, projection: local_projection } =
|
||||
capture_info.use_place;
|
||||
|
||||
// We used some of the projections to build the capture itself,
|
||||
// now we apply the remaining to the upvar resolved place.
|
||||
trace!(?capture.captured_place, ?projection);
|
||||
let remaining_projections = strip_prefix(
|
||||
let upvar_projection = strip_prefix(
|
||||
capture.captured_place.place.base_ty,
|
||||
projection,
|
||||
&capture.captured_place.place.projections,
|
||||
);
|
||||
upvar_resolved_place_builder.projection.extend(remaining_projections);
|
||||
|
||||
let upvar_resolved_place_builder = PlaceBuilder::construct_local_place_builder(
|
||||
cx,
|
||||
upvar_resolved_local,
|
||||
local_projection.as_slice(),
|
||||
upvar_projection,
|
||||
);
|
||||
|
||||
assert!(matches!(upvar_resolved_place_builder, PlaceBuilder::Local { .. }));
|
||||
|
||||
Some(upvar_resolved_place_builder)
|
||||
}
|
||||
@ -225,15 +235,17 @@ fn to_upvars_resolved_place_builder<'tcx>(
|
||||
/// projection kinds are unsupported.
|
||||
fn strip_prefix<'a, 'tcx>(
|
||||
mut base_ty: Ty<'tcx>,
|
||||
projections: &'a [PlaceElem<'tcx>],
|
||||
projections: &'a [UpvarProjectionElem<'tcx>],
|
||||
prefix_projections: &[HirProjection<'tcx>],
|
||||
) -> impl Iterator<Item = PlaceElem<'tcx>> + 'a {
|
||||
) -> impl Iterator<Item = UpvarProjectionElem<'tcx>> + 'a {
|
||||
let mut iter = projections
|
||||
.iter()
|
||||
.copied()
|
||||
// Filter out opaque casts, they are unnecessary in the prefix.
|
||||
.filter(|elem| !matches!(elem, ProjectionElem::OpaqueCast(..)));
|
||||
for projection in prefix_projections {
|
||||
debug!(?projection, ?projection.ty);
|
||||
|
||||
match projection.kind {
|
||||
HirProjectionKind::Deref => {
|
||||
assert_matches!(iter.next(), Some(ProjectionElem::Deref));
|
||||
@ -248,8 +260,10 @@ fn strip_prefix<'a, 'tcx>(
|
||||
bug!("unexpected projection kind: {:?}", projection);
|
||||
}
|
||||
}
|
||||
|
||||
base_ty = projection.ty;
|
||||
}
|
||||
|
||||
iter
|
||||
}
|
||||
|
||||
@ -262,9 +276,9 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
||||
pub(in crate::build) fn try_to_place(&self, cx: &Builder<'_, 'tcx>) -> Option<Place<'tcx>> {
|
||||
let resolved = self.resolve_upvar(cx);
|
||||
let builder = resolved.as_ref().unwrap_or(self);
|
||||
let PlaceBase::Local(local) = builder.base else { return None };
|
||||
let projection = cx.tcx.intern_place_elems(&builder.projection);
|
||||
Some(Place { local, projection })
|
||||
let PlaceBuilder::Local{local, ref projection} = builder else { return None };
|
||||
let projection = cx.tcx.intern_place_elems(projection);
|
||||
Some(Place { local: *local, projection })
|
||||
}
|
||||
|
||||
/// Attempts to resolve the `PlaceBuilder`.
|
||||
@ -281,22 +295,31 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
||||
&self,
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
) -> Option<PlaceBuilder<'tcx>> {
|
||||
let PlaceBase::Upvar { var_hir_id, closure_def_id } = self.base else {
|
||||
let PlaceBuilder::Upvar{ upvar: Upvar {var_hir_id, closure_def_id }, projection} = self else {
|
||||
return None;
|
||||
};
|
||||
to_upvars_resolved_place_builder(cx, var_hir_id, closure_def_id, &self.projection)
|
||||
|
||||
to_upvars_resolved_place_builder(cx, *var_hir_id, *closure_def_id, &projection)
|
||||
}
|
||||
|
||||
pub(crate) fn base(&self) -> PlaceBase {
|
||||
self.base
|
||||
}
|
||||
#[instrument(skip(cx), level = "debug")]
|
||||
pub(crate) fn field(self, cx: &Builder<'_, 'tcx>, f: Field) -> Self {
|
||||
match self.clone() {
|
||||
PlaceBuilder::Local { local, projection } => {
|
||||
let base_place = PlaceBuilder::Local { local, projection };
|
||||
let PlaceTy { ty, variant_index } =
|
||||
base_place.to_place(cx).ty(&cx.local_decls, cx.tcx);
|
||||
let base_ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
|
||||
pub(crate) fn projection(&self) -> &[PlaceElem<'tcx>] {
|
||||
&self.projection
|
||||
}
|
||||
let field_ty = PlaceBuilder::compute_field_ty(cx, f, base_ty, variant_index);
|
||||
|
||||
pub(crate) fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
|
||||
self.project(PlaceElem::Field(f, ty))
|
||||
self.project(ProjectionElem::Field(f, field_ty))
|
||||
}
|
||||
PlaceBuilder::Upvar { upvar, mut projection } => {
|
||||
projection.push(ProjectionElem::Field(f, ()));
|
||||
PlaceBuilder::Upvar { upvar, projection }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn deref(self) -> Self {
|
||||
@ -311,35 +334,236 @@ impl<'tcx> PlaceBuilder<'tcx> {
|
||||
self.project(PlaceElem::Index(index))
|
||||
}
|
||||
|
||||
pub(crate) fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
|
||||
self.projection.push(elem);
|
||||
self
|
||||
#[instrument(level = "debug")]
|
||||
pub(crate) fn project(self, elem: PlaceElem<'tcx>) -> Self {
|
||||
let result = match self {
|
||||
PlaceBuilder::Local { local, mut projection } => {
|
||||
projection.push(elem);
|
||||
PlaceBuilder::Local { local, projection }
|
||||
}
|
||||
PlaceBuilder::Upvar { upvar, mut projection } => {
|
||||
projection.push(elem.into());
|
||||
PlaceBuilder::Upvar { upvar, projection }
|
||||
}
|
||||
};
|
||||
|
||||
debug!(?result);
|
||||
result
|
||||
}
|
||||
|
||||
/// Same as `.clone().project(..)` but more efficient
|
||||
pub(crate) fn clone_project(&self, elem: PlaceElem<'tcx>) -> Self {
|
||||
Self {
|
||||
base: self.base,
|
||||
projection: Vec::from_iter(self.projection.iter().copied().chain([elem])),
|
||||
match self {
|
||||
PlaceBuilder::Local { local, projection } => PlaceBuilder::Local {
|
||||
local: *local,
|
||||
projection: Vec::from_iter(projection.iter().copied().chain([elem.into()])),
|
||||
},
|
||||
PlaceBuilder::Upvar { upvar, projection } => PlaceBuilder::Upvar {
|
||||
upvar: *upvar,
|
||||
projection: Vec::from_iter(projection.iter().copied().chain([elem.into()])),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `Place::ty` but needed during mir building.
|
||||
///
|
||||
/// Applies the projections in the `PlaceBuilder` to the base
|
||||
/// type.
|
||||
///
|
||||
/// Fallible as the root of this place may be an upvar for
|
||||
/// which no base type can be determined.
|
||||
#[instrument(skip(cx), level = "debug")]
|
||||
fn compute_field_ty(
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
field: Field,
|
||||
base_ty: Ty<'tcx>,
|
||||
variant_index: Option<VariantIdx>,
|
||||
) -> Ty<'tcx> {
|
||||
let field_idx = field.as_usize();
|
||||
let field_ty = match base_ty.kind() {
|
||||
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
|
||||
let variant_idx = variant_index.unwrap();
|
||||
adt_def.variant(variant_idx).fields[field_idx].ty(cx.tcx, substs)
|
||||
}
|
||||
ty::Adt(adt_def, substs) => adt_def
|
||||
.all_fields()
|
||||
.nth(field_idx)
|
||||
.unwrap_or_else(|| {
|
||||
bug!(
|
||||
"expected to take field with idx {:?} of fields of {:?}",
|
||||
field_idx,
|
||||
adt_def
|
||||
)
|
||||
})
|
||||
.ty(cx.tcx, substs),
|
||||
ty::Tuple(elems) => elems.iter().nth(field_idx).unwrap_or_else(|| {
|
||||
bug!("expected to take field with idx {:?} of {:?}", field_idx, elems)
|
||||
}),
|
||||
ty::Closure(_, substs) => {
|
||||
let substs = substs.as_closure();
|
||||
let Some(f_ty) = substs.upvar_tys().nth(field_idx) else {
|
||||
bug!("expected to take field with idx {:?} of {:?}", field_idx, substs.upvar_tys().collect::<Vec<_>>());
|
||||
};
|
||||
|
||||
f_ty
|
||||
}
|
||||
&ty::Generator(def_id, substs, _) => {
|
||||
if let Some(var) = variant_index {
|
||||
let gen_body = cx.tcx.optimized_mir(def_id);
|
||||
let Some(layout) = gen_body.generator_layout() else {
|
||||
bug!("No generator layout for {:?}", base_ty);
|
||||
};
|
||||
|
||||
let Some(&local) = layout.variant_fields[var].get(field) else {
|
||||
bug!("expected to take field {:?} of {:?}", field, layout.variant_fields[var]);
|
||||
};
|
||||
|
||||
let Some(&f_ty) = layout.field_tys.get(local) else {
|
||||
bug!("expected to get element for {:?} in {:?}", local, layout.field_tys);
|
||||
};
|
||||
|
||||
f_ty
|
||||
} else {
|
||||
let Some(f_ty) = substs.as_generator().prefix_tys().nth(field.index()) else {
|
||||
bug!(
|
||||
"expected to take index {:?} in {:?}",
|
||||
field.index(),
|
||||
substs.as_generator().prefix_tys().collect::<Vec<_>>()
|
||||
);
|
||||
};
|
||||
|
||||
f_ty
|
||||
}
|
||||
}
|
||||
_ => bug!("couldn't create field type, unexpected base type: {:?}", base_ty),
|
||||
};
|
||||
|
||||
cx.tcx.normalize_erasing_regions(cx.param_env, field_ty)
|
||||
}
|
||||
|
||||
/// Creates a `PlaceBuilder::Local` from a `PlaceBuilder::Upvar` whose upvars
|
||||
/// are resolved. This function takes two kinds of projections: `local_projection`
|
||||
/// contains the projections of the captured upvar and `upvar_projection` the
|
||||
/// projections that are applied to the captured upvar. The main purpose of this
|
||||
/// function is to figure out the `Ty`s of the field projections in `upvar_projection`.
|
||||
#[instrument(skip(cx, local, upvar_projection))]
|
||||
fn construct_local_place_builder(
|
||||
cx: &Builder<'_, 'tcx>,
|
||||
local: Local,
|
||||
local_projection: &[PlaceElem<'tcx>],
|
||||
upvar_projection: impl Iterator<Item = UpvarProjectionElem<'tcx>>,
|
||||
) -> Self {
|
||||
// We maintain a `Ty` to which we apply a projection in each iteration over `upvar_projection`.
|
||||
// This `ancestor_ty` let's us infer the field type whenever we encounter a
|
||||
// `ProjectionElem::Field`.
|
||||
let (mut ancestor_ty, mut opt_variant_idx) =
|
||||
local_projections_to_ty(cx, local, local_projection);
|
||||
|
||||
// We add all projection elements we encounter to this `Vec`.
|
||||
let mut local_projection = local_projection.to_vec();
|
||||
|
||||
for (i, proj) in upvar_projection.enumerate() {
|
||||
debug!("i: {:?}, proj: {:?}, local_projection: {:?}", i, proj, local_projection);
|
||||
match proj {
|
||||
ProjectionElem::Field(field, _) => {
|
||||
let field_ty =
|
||||
PlaceBuilder::compute_field_ty(cx, field, ancestor_ty, opt_variant_idx);
|
||||
debug!(?field_ty);
|
||||
|
||||
local_projection.push(ProjectionElem::Field(field, field_ty));
|
||||
ancestor_ty = field_ty;
|
||||
opt_variant_idx = None;
|
||||
}
|
||||
_ => {
|
||||
let proj = upvar_proj_to_place_elem_no_field_proj(proj);
|
||||
(ancestor_ty, opt_variant_idx) = project_ty(cx.tcx, ancestor_ty, proj);
|
||||
local_projection.push(proj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlaceBuilder::Local { local, projection: local_projection }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> From<Local> for PlaceBuilder<'tcx> {
|
||||
fn from(local: Local) -> Self {
|
||||
Self { base: PlaceBase::Local(local), projection: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> From<PlaceBase> for PlaceBuilder<'tcx> {
|
||||
fn from(base: PlaceBase) -> Self {
|
||||
Self { base, projection: Vec::new() }
|
||||
Self::Local { local, projection: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> From<Place<'tcx>> for PlaceBuilder<'tcx> {
|
||||
fn from(p: Place<'tcx>) -> Self {
|
||||
Self { base: PlaceBase::Local(p.local), projection: p.projection.to_vec() }
|
||||
Self::Local { local: p.local, projection: p.projection.to_vec() }
|
||||
}
|
||||
}
|
||||
|
||||
fn project_ty<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
elem: PlaceElem<'tcx>,
|
||||
) -> (Ty<'tcx>, Option<VariantIdx>) {
|
||||
match elem {
|
||||
ProjectionElem::Deref => {
|
||||
let updated_ty = ty
|
||||
.builtin_deref(true)
|
||||
.unwrap_or_else(|| bug!("deref projection of non-dereferenceable ty {:?}", ty))
|
||||
.ty;
|
||||
|
||||
(updated_ty, None)
|
||||
}
|
||||
ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
|
||||
(ty.builtin_index().unwrap(), None)
|
||||
}
|
||||
ProjectionElem::Subslice { from, to, from_end } => {
|
||||
let ty = match ty.kind() {
|
||||
ty::Slice(..) => ty,
|
||||
ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64),
|
||||
ty::Array(inner, size) if from_end => {
|
||||
let size = size.eval_usize(tcx, ty::ParamEnv::empty());
|
||||
let len = size - (from as u64) - (to as u64);
|
||||
tcx.mk_array(*inner, len)
|
||||
}
|
||||
_ => bug!("cannot subslice non-array type: `{:?}`", ty),
|
||||
};
|
||||
|
||||
(ty, None)
|
||||
}
|
||||
ProjectionElem::Downcast(_, variant_idx) => (ty, Some(variant_idx)),
|
||||
ProjectionElem::Field(_, ty) => (ty, None),
|
||||
ProjectionElem::OpaqueCast(..) => bug!("didn't expect OpaqueCast"),
|
||||
}
|
||||
}
|
||||
|
||||
fn local_projections_to_ty<'a, 'tcx>(
|
||||
cx: &'a Builder<'a, 'tcx>,
|
||||
local: Local,
|
||||
projection: &'a [PlaceElem<'tcx>],
|
||||
) -> (Ty<'tcx>, Option<VariantIdx>) {
|
||||
let local_ty = cx.local_decls.local_decls()[local].ty;
|
||||
projection.iter().fold((local_ty, None), |ty_variant_idx, elem| {
|
||||
let ty = ty_variant_idx.0;
|
||||
project_ty(cx.tcx, ty, *elem)
|
||||
})
|
||||
}
|
||||
|
||||
// Converts an `UpvarProjectionElem` to `PlaceElem`, ICE'ing when being passed a
|
||||
// field projection.
|
||||
fn upvar_proj_to_place_elem_no_field_proj<'tcx>(
|
||||
upvar_proj: UpvarProjectionElem<'tcx>,
|
||||
) -> PlaceElem<'tcx> {
|
||||
match upvar_proj {
|
||||
ProjectionElem::Deref => ProjectionElem::Deref,
|
||||
ProjectionElem::Index(i) => ProjectionElem::Index(i),
|
||||
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(ty, variant_idx) => ProjectionElem::Downcast(ty, variant_idx),
|
||||
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
|
||||
ProjectionElem::Field(..) => bug!("should not be called with `ProjectionElem::Field`"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,6 +627,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
self.expr_as_place(block, expr, Mutability::Not, None)
|
||||
}
|
||||
|
||||
#[instrument(skip(self, fake_borrow_temps), level = "debug")]
|
||||
fn expr_as_place(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
@ -410,8 +635,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
mutability: Mutability,
|
||||
fake_borrow_temps: Option<&mut Vec<Local>>,
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
|
||||
|
||||
let this = self;
|
||||
let expr_span = expr.span;
|
||||
let source_info = this.source_info(expr_span);
|
||||
@ -425,12 +648,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let lhs = &this.thir[lhs];
|
||||
let mut place_builder =
|
||||
unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
|
||||
debug!(?place_builder);
|
||||
if let ty::Adt(adt_def, _) = lhs.ty.kind() {
|
||||
if adt_def.is_enum() {
|
||||
place_builder = place_builder.downcast(*adt_def, variant_index);
|
||||
}
|
||||
}
|
||||
block.and(place_builder.field(name, expr.ty))
|
||||
block.and(place_builder.field(this, name))
|
||||
}
|
||||
ExprKind::Deref { arg } => {
|
||||
let place_builder = unpack!(
|
||||
@ -572,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Lower a captured upvar. Note we might not know the actual capture index,
|
||||
/// so we create a place starting from `PlaceBase::Upvar`, which will be resolved
|
||||
/// so we create a place starting from `Upvar`, which will be resolved
|
||||
/// once all projections that allow us to identify a capture have been applied.
|
||||
fn lower_captured_upvar(
|
||||
&mut self,
|
||||
@ -580,7 +804,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
closure_def_id: LocalDefId,
|
||||
var_hir_id: LocalVarId,
|
||||
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||
block.and(PlaceBuilder::from(PlaceBase::Upvar { var_hir_id, closure_def_id }))
|
||||
block.and(PlaceBuilder::Upvar {
|
||||
upvar: Upvar { var_hir_id, closure_def_id },
|
||||
projection: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
/// Lower an index expression
|
||||
@ -671,8 +898,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
source_info: SourceInfo,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let place_ty = base_place.ty(&self.local_decls, tcx);
|
||||
|
||||
if let ty::Slice(_) = place_ty.ty.kind() {
|
||||
// We need to create fake borrows to ensure that the bounds
|
||||
// check that we just did stays valid. Since we can't assign to
|
||||
|
@ -4,9 +4,8 @@ use rustc_index::vec::Idx;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_target::abi::{Abi, Primitive};
|
||||
|
||||
use crate::build::expr::as_place::PlaceBase;
|
||||
use crate::build::expr::category::{Category, RvalueFunc};
|
||||
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
|
||||
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary, PlaceBuilder};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
@ -651,15 +650,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
let arg_place_builder = unpack!(block = this.as_place_builder(block, arg));
|
||||
|
||||
let mutability = match arg_place_builder.base() {
|
||||
let mutability = match arg_place_builder {
|
||||
// We are capturing a path that starts off a local variable in the parent.
|
||||
// The mutability of the current capture is same as the mutability
|
||||
// of the local declaration in the parent.
|
||||
PlaceBase::Local(local) => this.local_decls[local].mutability,
|
||||
PlaceBuilder::Local { local, .. } => this.local_decls[local].mutability,
|
||||
// Parent is a closure and we are capturing a path that is captured
|
||||
// by the parent itself. The mutability of the current capture
|
||||
// is same as that of the capture in the parent closure.
|
||||
PlaceBase::Upvar { .. } => {
|
||||
PlaceBuilder::Upvar { .. } => {
|
||||
let enclosing_upvars_resolved = arg_place_builder.to_place(this);
|
||||
|
||||
match enclosing_upvars_resolved.as_ref() {
|
||||
|
@ -355,11 +355,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// base-supplied field, generate an operand that
|
||||
// reads it from the base.
|
||||
iter::zip(field_names, &**field_types)
|
||||
.map(|(n, ty)| match fields_map.get(&n) {
|
||||
.map(|(n, _ty)| match fields_map.get(&n) {
|
||||
Some(v) => v.clone(),
|
||||
None => {
|
||||
let place = place_builder.clone_project(PlaceElem::Field(n, *ty));
|
||||
this.consume_by_copy_or_move(place.to_place(this))
|
||||
let place_builder = place_builder.clone();
|
||||
this.consume_by_copy_or_move(
|
||||
place_builder.field(this, n).to_place(this),
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
@ -272,9 +272,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if irrefutable {
|
||||
let place_builder = match_pair.place.downcast(adt_def, variant_index);
|
||||
candidate
|
||||
.match_pairs
|
||||
.extend(self.field_match_pairs(place_builder, subpatterns));
|
||||
let field_match_pairs =
|
||||
self.field_match_pairs(place_builder.clone(), subpatterns);
|
||||
candidate.match_pairs.extend(field_match_pairs);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(match_pair)
|
||||
|
@ -758,8 +758,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
|
||||
// e.g., `(x as Variant).0`
|
||||
let place = downcast_place
|
||||
.clone_project(PlaceElem::Field(subpattern.field, subpattern.pattern.ty));
|
||||
let place = downcast_place.clone().field(self, subpattern.field);
|
||||
// e.g., `(x as Variant).0 @ P1`
|
||||
MatchPair::new(place, &subpattern.pattern, self)
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::build::expr::as_place::PlaceBase;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::MatchPair;
|
||||
use crate::build::Builder;
|
||||
@ -17,8 +16,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
subpatterns
|
||||
.iter()
|
||||
.map(|fieldpat| {
|
||||
let place =
|
||||
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
|
||||
let place = place.clone().field(self, fieldpat.field);
|
||||
|
||||
MatchPair::new(place, &fieldpat.pattern, self)
|
||||
})
|
||||
.collect()
|
||||
@ -107,9 +106,9 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty = Place::ty_from(local, place.projection(), &cx.local_decls, cx.tcx).ty;
|
||||
let may_need_cast = match place {
|
||||
PlaceBuilder::Local { local, ref projection } => {
|
||||
let ty = Place::ty_from(local, projection, &cx.local_decls, cx.tcx).ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
}
|
||||
_ => true,
|
||||
|
@ -18,7 +18,7 @@ use rustc_middle::ty::Ty;
|
||||
pub struct AbstractOperand;
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct AbstractType;
|
||||
pub type AbstractElem = ProjectionElem<AbstractOperand, AbstractType>;
|
||||
pub type AbstractElem = ProjectionElem<AbstractOperand, AbstractType, AbstractType>;
|
||||
|
||||
pub trait Lift {
|
||||
type Abstract;
|
||||
|
@ -777,10 +777,10 @@ pub enum TrackElem {
|
||||
Field(Field),
|
||||
}
|
||||
|
||||
impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
|
||||
impl<V, T1, T2> TryFrom<ProjectionElem<V, T1, T2>> for TrackElem {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: ProjectionElem<V, T1, T2>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
|
||||
_ => Err(()),
|
||||
|
24
src/test/ui/mir/field-projection-invariant.rs
Normal file
24
src/test/ui/mir/field-projection-invariant.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// build-pass
|
||||
struct Inv<'a>(&'a mut &'a ());
|
||||
enum Foo<T> {
|
||||
Bar,
|
||||
Var(T),
|
||||
}
|
||||
type Supertype = Foo<for<'a> fn(Inv<'a>, Inv<'a>)>;
|
||||
|
||||
fn foo(x: Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>) {
|
||||
match x {
|
||||
Supertype::Bar => {}
|
||||
Supertype::Var(x) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn foo_nested(x: Foo<Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>>) {
|
||||
match x {
|
||||
Foo::Bar => {}
|
||||
Foo::Var(Supertype::Bar) => {}
|
||||
Foo::Var(Supertype::Var(x)) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
15
src/test/ui/mir/field-ty-ascription-enums.rs
Normal file
15
src/test/ui/mir/field-ty-ascription-enums.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// build-pass
|
||||
|
||||
enum Foo<T> {
|
||||
Var(T),
|
||||
} // `T` is covariant.
|
||||
|
||||
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
|
||||
let Foo::Var(x): Foo<fn(&'b ())> = x;
|
||||
}
|
||||
|
||||
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
|
||||
let Foo::Var(Foo::Var(x)): Foo<Foo<fn(&'b ())>> = x;
|
||||
}
|
||||
|
||||
fn main() {}
|
37
src/test/ui/mir/field-ty-ascription.rs
Normal file
37
src/test/ui/mir/field-ty-ascription.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// build-pass
|
||||
|
||||
struct Foo<T>(T); // `T` is covariant.
|
||||
|
||||
struct Bar<T> {
|
||||
x: T,
|
||||
} // `T` is covariant.
|
||||
|
||||
fn bar<'b>(x: Bar<for<'a> fn(&'a ())>) {
|
||||
let Bar { x }: Bar<fn(&'b ())> = x;
|
||||
}
|
||||
|
||||
fn bar_nested<'b>(x: Bar<Bar<for<'a> fn(&'a ())>>) {
|
||||
let Bar { x: Bar { x } }: Bar<Bar<fn(&'b ())>> = x;
|
||||
}
|
||||
|
||||
fn bar_foo_nested<'b>(x: Bar<Foo<for<'a> fn(&'a ())>>) {
|
||||
let Bar { x: Foo ( x ) }: Bar<Foo<fn(&'b ())>> = x;
|
||||
}
|
||||
|
||||
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
|
||||
let Foo(y): Foo<fn(&'b ())> = x;
|
||||
}
|
||||
|
||||
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
|
||||
let Foo(Foo(y)): Foo<Foo<fn(&'b ())>> = x;
|
||||
}
|
||||
|
||||
fn tuple<'b>(x: (u32, for<'a> fn(&'a ()))) {
|
||||
let (_, y): (u32, fn(&'b ())) = x;
|
||||
}
|
||||
|
||||
fn tuple_nested<'b>(x: (u32, (u32, for<'a> fn(&'a ())))) {
|
||||
let (_, (_, y)): (u32, (u32, fn(&'b ()))) = x;
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user