Skip to content
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
8 changes: 6 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let projected_ty = curr_projected_ty.projection_ty_core(
tcx,
proj,
|ty| self.normalize(ty, locations),
|()| None,
|ty, variant_index, field_index, ()| {
self.normalize(
PlaceTy::field_ty(tcx, ty, variant_index, field_index),
locations,
)
},
|_| unreachable!(),
);
curr_projected_ty = projected_ty;
Expand Down
47 changes: 20 additions & 27 deletions compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! Functionality for statements, operands, places, and things that appear in them.

use std::fmt::Debug;
use std::ops;

use rustc_data_structures::outline;
use thin_vec::ThinVec;
use tracing::{debug, instrument};
use tracing::instrument;

use super::interpret::GlobalAlloc;
use super::*;
Expand Down Expand Up @@ -186,48 +187,46 @@ 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<V: ::std::fmt::Debug>(
pub fn projection_ty<V: Debug>(
self,
tcx: TyCtxt<'tcx>,
elem: ProjectionElem<V, Ty<'tcx>>,
) -> PlaceTy<'tcx> {
self.projection_ty_core(tcx, &elem, |ty| ty.skip_norm_wip(), |ty| Some(ty), |ty| ty)
self.projection_ty_core(tcx, &elem, |_, _, _, ty| ty, |ty| ty)
}

/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
/// projects `place_ty` onto `elem`, returning the appropriate
/// `Ty` or downcast variant corresponding to that projection.
/// `trivial_field_ty` is used for when `T` = `Ty`, otherwise,
/// `PlaceTy::field_ty` is used to map a `FieldIdx` to its `Ty`.
/// The `handle_field` callback must map a `FieldIdx` to its `Ty`,
/// (which should be trivial when `T` = `Ty`).
#[instrument(level = "debug", skip(tcx, handle_field, handle_opaque_cast_and_subtype), ret)]
pub fn projection_ty_core<V, T>(
self,
tcx: TyCtxt<'tcx>,
elem: &ProjectionElem<V, T>,
// FIXME(#155345): This should only normalize when actually required.
mut normalize: impl FnMut(Unnormalized<'tcx, Ty<'tcx>>) -> Ty<'tcx>,
trivial_field_ty: impl Fn(T) -> Option<Ty<'tcx>>,
mut handle_field: impl FnMut(Ty<'tcx>, Option<VariantIdx>, FieldIdx, T) -> Ty<'tcx>,
mut handle_opaque_cast_and_subtype: impl FnMut(T) -> Ty<'tcx>,
) -> PlaceTy<'tcx>
where
V: ::std::fmt::Debug,
T: ::std::fmt::Debug + Copy,
V: Debug,
T: Debug + Copy,
{

@adwinwhite adwinwhite Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe they should be normalized too. Is it a good idea that we add a debug assert to ensure the invariant holds? like debug_assert!(!tcx.next_trait_solver() || !self.ty.has_non_rigid_aliases()))
Though we seem to have no way of asserting this for the old solver.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context for others: after discussion, this gets difficult, because we might be looking at serialized MIR from a different crate (e.g. std) that was compiled under the old solver, and hence has non-rigid aliases.

Additionally, even if we build std under the next solver, there's quite a few places that have nonrigid aliases in MIR that should theoretically be cleaned up at some point, but is a bit out of scope for this PR. For example, I think here is one:

EarlyBinder::bind(tcx, body.clone()).instantiate(tcx, args).skip_norm_wip();

if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) {
bug!("cannot use non field projection on downcasted place")
}
let answer = match *elem {
match *elem {
ProjectionElem::Deref => {
let ty =
normalize(Unnormalized::new_wip(self.ty)).builtin_deref(true).unwrap_or_else(
|| bug!("deref projection of non-dereferenceable ty {:?}", self),
);
let ty = self.ty.builtin_deref(true).unwrap_or_else(|| {
bug!("deref projection of non-dereferenceable ty {:?}", self)
});
PlaceTy::from_ty(ty)
}
ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
PlaceTy::from_ty(normalize(Unnormalized::new_wip(self.ty)).builtin_index().unwrap())
PlaceTy::from_ty(self.ty.builtin_index().unwrap())
}
ProjectionElem::Subslice { from, to, from_end } => {
PlaceTy::from_ty(match normalize(Unnormalized::new_wip(self.ty)).kind() {
PlaceTy::from_ty(match self.ty.kind() {
ty::Slice(..) => self.ty,
ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
ty::Array(inner, size) if from_end => {
Expand All @@ -243,22 +242,16 @@ impl<'tcx> PlaceTy<'tcx> {
ProjectionElem::Downcast(_name, index) => {
PlaceTy { ty: self.ty, variant_index: Some(index) }
}
ProjectionElem::Field(f, fty) => PlaceTy::from_ty(match trivial_field_ty(fty) {
Some(ty) => ty,
None => {
let self_ty = normalize(Unnormalized::new_wip(self.ty));
normalize(PlaceTy::field_ty(tcx, self_ty, self.variant_index, f))
}
}),
ProjectionElem::Field(f, fty) => {
PlaceTy::from_ty(handle_field(self.ty, self.variant_index, f, fty))
}
ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)),

// FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
ProjectionElem::UnwrapUnsafeBinder(ty) => {
PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty))
}
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
}
}
}

Expand Down
Loading