From bde303ad755926e59d20942e7cdb22e992c3f48f Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 3 Dec 2025 11:35:22 +0100 Subject: [PATCH 1/2] Unify Field and ClosureField, and use local indexes for both (no variant ID / tuple ID needed) bring back env for projection_ty cleanups --- crates/hir-def/src/lib.rs | 8 + crates/hir-ty/src/mir.rs | 338 ++++++++++++------ crates/hir-ty/src/mir/borrowck.rs | 30 +- crates/hir-ty/src/mir/eval.rs | 58 ++- crates/hir-ty/src/mir/lower.rs | 83 ++--- .../hir-ty/src/mir/lower/pattern_matching.rs | 48 +-- crates/hir-ty/src/mir/pretty.rs | 80 +++-- crates/hir-ty/src/next_solver/ty.rs | 10 + 8 files changed, 391 insertions(+), 264 deletions(-) diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index a1bb82e7f25c..2172e2412b89 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -1017,6 +1017,14 @@ pub enum VariantId { impl_from!(EnumVariantId, StructId, UnionId for VariantId); impl VariantId { + pub fn from_non_enum(adt_id: AdtId) -> Option { + Some(match adt_id { + AdtId::StructId(struct_id) => struct_id.into(), + AdtId::UnionId(union_id) => union_id.into(), + AdtId::EnumId(_) => return None, + }) + } + pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { VariantFields::of(db, self) } diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index e82038907c44..d0b7e5f6544b 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -2,14 +2,13 @@ use std::{fmt::Display, iter}; -use base_db::Crate; -use either::Either; use hir_def::{ - FieldId, StaticId, TupleFieldId, UnionId, VariantId, + FieldId, LocalFieldId, StaticId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; +use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; use rustc_type_ir::{ @@ -17,13 +16,11 @@ use rustc_type_ir::{ inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use smallvec::{SmallVec, smallvec}; -use stdx::{impl_from, never}; +use stdx::impl_from; use crate::{ CallableDefId, InferBodyId, InferenceResult, MemoryMap, - consteval::usize_const, db::{HirDatabase, InternedClosureId}, - display::{DisplayTarget, HirDisplay}, infer::PointerCast, next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, @@ -52,8 +49,6 @@ pub use monomorphization::{ monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; -use super::consteval::try_const_usize; - pub type BasicBlockId = Idx; pub type LocalId = Idx; @@ -61,7 +56,7 @@ fn return_slot() -> LocalId { LocalId::from_raw(RawIdx::from(0)) } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Local { pub ty: StoredTy, } @@ -150,116 +145,75 @@ impl<'db> Operand { } } +/// The index of a field (whether of a struct/enum variant, tuple, or closure). +/// For a struct/enum it converts from and to the LocalFieldId, for a tuple or closure it's simply the index. +#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::Update, PartialOrd, Ord, Debug)] +pub struct FieldIndex(pub u32); + +impl FieldIndex { + pub fn to_local_field_id(self) -> LocalFieldId { + LocalFieldId::from_raw(RawIdx::from_u32(self.0)) + } +} + +impl From for FieldIndex { + fn from(value: LocalFieldId) -> Self { + FieldIndex(value.into_raw().into_u32()) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, - Field(Either), - // FIXME: get rid of this, and use FieldId for tuples and closures - ClosureField(usize), + /// A field (e.g., `f` in `_1.f`). + Field(FieldIndex), + /// Index into a slice/array. Index(V), - ConstantIndex { offset: u64, from_end: bool }, - Subslice { from: u64, to: u64 }, - //Downcast(Option, VariantIdx), - OpaqueCast(std::convert::Infallible), + /// These indices are generated by slice patterns. + ConstantIndex { + offset: u64, + from_end: bool, + }, + /// These indices are generated by slice patterns. + Subslice { + from: u64, + to: u64, + }, + /// "Downcast" to a variant of an enum or a coroutine. + Downcast(VariantId), + OpaqueCast(std::convert::Infallible), // TODO remove this } impl ProjectionElem { - pub fn projected_ty<'db>( - &self, - infcx: &InferCtxt<'db>, - env: ParamEnv<'db>, - mut base: Ty<'db>, - krate: Crate, - ) -> Ty<'db> { - let interner = infcx.interner; - let db = interner.db; - - // we only bail on mir building when there are type mismatches - // but error types may pop up resulting in us still attempting to build the mir - // so just propagate the error type - if base.is_ty_error() { - return Ty::new_error(interner, ErrorGuaranteed); - } - - if matches!(base.kind(), TyKind::Alias(..)) { - let mut ocx = ObligationCtxt::new(infcx); - match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) { - Ok(it) => base = it, - Err(_) => return Ty::new_error(interner, ErrorGuaranteed), + pub fn map(self, v: impl FnOnce(V) -> V2) -> ProjectionElem { + match self { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index), + ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)), + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } } + ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, + ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id), + ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), } + } - match self { - ProjectionElem::Deref => match base.kind() { - TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner, - TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0), - _ => { - never!( - "Overloaded deref on type {} is not a projection", - base.display(db, DisplayTarget::from_crate(db, krate)) - ); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - ProjectionElem::Field(Either::Left(f)) => match base.kind() { - TyKind::Adt(_, subst) => db.field_types(f.parent)[f.local_id] - .get() - .instantiate(interner, subst) - .skip_norm_wip(), - ty => { - never!("Only adt has field, found {:?}", ty); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - ProjectionElem::Field(Either::Right(f)) => match base.kind() { - TyKind::Tuple(subst) => { - subst.as_slice().get(f.index as usize).copied().unwrap_or_else(|| { - never!("Out of bound tuple field"); - Ty::new_error(interner, ErrorGuaranteed) - }) - } - ty => { - never!("Only tuple has tuple field: {:?}", ty); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - ProjectionElem::ClosureField(f) => match base.kind() { - TyKind::Closure(_, args) => args.as_closure().tupled_upvars_ty().tuple_fields()[*f], - _ => { - never!("Only closure has closure field"); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => match base.kind() { - TyKind::Array(inner, _) | TyKind::Slice(inner) => inner, - _ => { - never!("Overloaded index is not a projection"); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - &ProjectionElem::Subslice { from, to } => match base.kind() { - TyKind::Array(inner, c) => { - let next_c = usize_const( - db, - match try_const_usize(db, c) { - None => None, - Some(x) => x.checked_sub(u128::from(from + to)), - }, - krate, - ); - Ty::new_array_with_const_len(interner, inner, next_c) - } - TyKind::Slice(_) => base, - _ => { - never!("Subslice projection should only happen on slice and array"); - Ty::new_error(interner, ErrorGuaranteed) - } - }, - ProjectionElem::OpaqueCast(_) => { - never!("We don't emit these yet"); - Ty::new_error(interner, ErrorGuaranteed) + pub fn try_map( + self, + v: impl FnOnce(V) -> Option, + ) -> Option> { + Some(match self { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Field(field_index) => ProjectionElem::Field(field_index), + ProjectionElem::Index(idx) => ProjectionElem::Index(v(idx)?), + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } } - } + ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, + ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id), + ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), + }) } } @@ -367,6 +321,13 @@ impl<'db> PlaceRef<'db> { pub fn store(&self) -> Place { Place { local: self.local, projection: self.projection.store() } } + pub fn ty(&self, body: &MirBody, infcx: &InferCtxt<'db>, env: ParamEnv<'db>) -> PlaceTy<'db> { + PlaceTy::from_ty(body.locals[self.local].ty.as_ref()).multi_projection_ty( + infcx, + env, + self.projection.as_slice(), + ) + } } impl<'db> From for PlaceRef<'db> { @@ -1243,3 +1204,164 @@ impl From<&ExprId> for MirSpan { (*value).into() } } + +impl<'tcx> PlaceRef<'tcx> { + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + #[inline] + pub fn as_local(&self) -> Option { + match *self { + PlaceRef { local, projection } if projection.as_slice().is_empty() => Some(local), + _ => None, + } + } +} + +/// To determine the type of a place, we need to keep track of the variant that has been downcast to, in order to find the correct fields. +/// This type does that. +#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Hash, PartialEq, Eq)] +pub struct PlaceTy<'db> { + pub ty: Ty<'db>, + /// Downcast to a particular variant of an enum or a coroutine, if included. + #[type_foldable(identity)] + #[type_visitable(ignore)] + pub variant_id: Option, +} + +impl<'db> PlaceTy<'db> { + #[inline] + pub fn from_ty(ty: Ty<'db>) -> PlaceTy<'db> { + PlaceTy { ty, variant_id: None } + } + + pub fn multi_projection_ty( + self, + infcx: &InferCtxt<'db>, + env: ParamEnv<'db>, + elems: &[PlaceElem], + ) -> PlaceTy<'db> { + elems.iter().fold(self, |place_ty, elem| place_ty.projection_ty(infcx, elem, env)) + } + + fn field_ty( + infcx: &InferCtxt<'db>, + self_ty: Ty<'db>, + variant: Option, + f: FieldIndex, + ) -> Ty<'db> { + if let Some(variant_id) = variant { + match self_ty.kind() { + TyKind::Adt(adt_def, args) if adt_def.is_enum() => { + infcx.interner.db().field_types(variant_id)[f.to_local_field_id()] + .get() + .instantiate(infcx.interner, args) + .skip_norm_wip() + } + // TODO TyKind::Coroutine... + _ => panic!("can't downcast non-adt non-coroutine type: {self_ty:?}"), + } + } else { + match self_ty.kind() { + TyKind::Adt(adt_def, args) if !adt_def.is_enum() => { + let variant_id = VariantId::from_non_enum(adt_def.def_id()).unwrap(); + infcx.interner.db().field_types(variant_id)[f.to_local_field_id()] + .get() + .instantiate(infcx.interner, args) + .skip_norm_wip() + } + TyKind::Closure(_, args) => { + args.as_closure().tupled_upvars_ty().tuple_fields()[f.0 as usize] + } + // TODO TyKind::Coroutine / TyKind::CoroutineClosure... + TyKind::Tuple(tys) => tys + .get(f.0 as usize) + .cloned() + .unwrap_or_else(|| panic!("field {f:?} out of range: {self_ty:?}")), + _ => panic!("can't project out of {self_ty:?}"), + } + } + } + + /// Convenience wrapper around `projection_ty_core` for `PlaceElem`. + pub fn projection_ty( + self, + infcx: &InferCtxt<'db>, + elem: &ProjectionElem, + env: ParamEnv<'db>, + ) -> PlaceTy<'db> { + self.projection_ty_core( + infcx.interner, + &elem, + |ty| { + if matches!(ty.kind(), TyKind::Alias(..)) { + let mut ocx = ObligationCtxt::new(infcx); + match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty) { + Ok(it) => it, + Err(_) => Ty::new_error(infcx.interner, ErrorGuaranteed), + } + } else { + ty + } + }, + |self_ty, variant, field_id| Self::field_ty(infcx, self_ty, variant, field_id), + ) + } + + /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` + /// projects `place_ty` onto `elem`, returning the appropriate + /// `Ty` or downcast variant corresponding to that projection. + /// The `handle_field` callback must map a `FieldIndex` to its `Ty` + pub fn projection_ty_core( + self, + tcx: DbInterner<'db>, + elem: &ProjectionElem, + mut structurally_normalize: impl FnMut(Ty<'db>) -> Ty<'db>, + mut handle_field: impl FnMut(Ty<'db>, Option, FieldIndex /*, T*/) -> Ty<'db>, + ) -> PlaceTy<'db> + where + V: ::std::fmt::Debug, + { + // we only bail on mir building when there are type mismatches + // but error types may pop up resulting in us still attempting to build the mir + // so just propagate the error type + if self.ty.is_ty_error() { + return PlaceTy::from_ty(Ty::new_error(tcx, ErrorGuaranteed)); + } + if self.variant_id.is_some() && !matches!(elem, ProjectionElem::Field(..)) { + panic!("cannot use non field projection on downcasted place") + } + let answer = match *elem { + ProjectionElem::Deref => { + let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| { + panic!("deref projection of non-dereferenceable ty {:?}", self) + }); + PlaceTy::from_ty(ty) + } + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { + PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap()) + } + ProjectionElem::Subslice { from, to /*, from_end*/ } => { + PlaceTy::from_ty(match structurally_normalize(self.ty).kind() { + TyKind::Slice(..) => self.ty, + TyKind::Array(inner, _) /*if !from_end*/ => Ty::new_array_opt(tcx, inner, to.checked_sub(from).map(|x| x.into())), + // TyKind::Array(inner, size) if from_end => { + // let size = size + // .try_to_target_usize(tcx) + // .expect("expected subslice projection on fixed-size array"); + // let len = size - from - to; + // Ty::new_array(tcx, *inner, len) + // } + _ => panic!("cannot subslice non-array type: `{:?}`", self), + }) + } + ProjectionElem::Downcast(index) => PlaceTy { ty: self.ty, variant_id: Some(index) }, + ProjectionElem::Field(f) => PlaceTy::from_ty(handle_field( + structurally_normalize(self.ty), + self.variant_id.clone(), + f, + )), + ProjectionElem::OpaqueCast(_ty) => unimplemented!("not emitted, to be removed"), + }; + answer + } +} diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index ff963fc12120..bf241aed7207 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -16,9 +16,9 @@ use crate::{ closure_analysis::ProjectionKind as HirProjectionKind, db::{HirDatabase, InternedClosureId}, display::DisplayTarget, - mir::OperandKind, + mir::{OperandKind, PlaceTy}, next_solver::{ - DbInterner, ParamEnv, StoredTy, Ty, TypingMode, + DbInterner, ParamEnv, StoredTy, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -197,19 +197,19 @@ fn moved_out_of_ref<'db>( let mut result = vec![]; let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); + let mut ty = PlaceTy::from_ty(body.locals[p.local].ty.as_ref()); let mut is_dereference_of_ref = false; for proj in p.projection.lookup() { - if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { + if *proj == ProjectionElem::Deref && ty.ty.as_reference().is_some() { is_dereference_of_ref = true; } - ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); + ty = ty.projection_ty(infcx, proj, env); } if is_dereference_of_ref - && !infcx.type_is_copy_modulo_regions(env, ty) - && !ty.references_non_lt_error() + && !infcx.type_is_copy_modulo_regions(env, ty.ty) + && !ty.ty.references_non_lt_error() { - result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.store() }); + result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.ty.store() }); } } OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {} @@ -292,10 +292,7 @@ fn partially_moved<'db>( let mut result = vec![]; let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); - for proj in p.projection.lookup() { - ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); - } + let ty = p.as_ref().ty(body, infcx, env).ty; if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { result.push(PartiallyMoved { span, ty: ty.store(), local: p.local }); } @@ -427,23 +424,22 @@ fn place_case<'db>( body: &MirBody, lvalue: &Place, ) -> ProjectionCase { - let db = infcx.interner.db; let mut is_part_of = false; - let mut ty = body.locals[lvalue.local].ty.as_ref(); + let mut ty = PlaceTy::from_ty(body.locals[lvalue.local].ty.as_ref()); for proj in lvalue.projection.lookup().iter() { match proj { - ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw + ProjectionElem::Deref if ty.ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box` | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Field(_) - | ProjectionElem::ClosureField(_) | ProjectionElem::Index(_) => { is_part_of = true; } + ProjectionElem::Downcast(_) | ProjectionElem::OpaqueCast(_) => (), } - ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); + ty = ty.projection_ty(infcx, proj, env); } if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct } } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 78b70edeeef7..f8bf3cc3ae4b 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -56,8 +56,8 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, StatementKind, - TerminatorKind, UnOp, return_slot, + Operand, OperandKind, Place, PlaceElem, PlaceRef, PlaceTy, ProjectionElem, Rvalue, + StatementKind, TerminatorKind, UnOp, return_slot, }; mod shim; @@ -187,7 +187,7 @@ pub struct Evaluator<'a, 'db> { stdout: Vec, stderr: Vec, layout_cache: RefCell, Arc>>, - projected_ty_cache: RefCell, PlaceElem), Ty<'db>>>, + projected_ty_cache: RefCell, PlaceElem), PlaceTy<'db>>>, not_special_fn_cache: RefCell>, mir_or_dyn_index_cache: RefCell), MirOrDynIndex<'a>>>, /// Constantly dropping and creating `Locals` is very costly. We store @@ -738,14 +738,14 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { self.cached_ptr_size } - fn projected_ty(&self, ty: Ty<'db>, proj: PlaceElem) -> Ty<'db> { + fn projected_ty(&self, ty: PlaceTy<'db>, proj: PlaceElem) -> PlaceTy<'db> { let pair = (ty, proj); if let Some(r) = self.projected_ty_cache.borrow().get(&pair) { - return *r; + return r.clone(); } let (ty, proj) = pair; - let r = proj.projected_ty(&self.infcx, self.param_env.param_env, ty, self.crate_id); - self.projected_ty_cache.borrow_mut().insert((ty, proj), r); + let r = ty.clone().projection_ty(&self.infcx, &proj, self.param_env.param_env); + self.projected_ty_cache.borrow_mut().insert((ty, proj), r.clone()); r } @@ -755,14 +755,14 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { locals: &'b Locals<'a>, ) -> Result<'db, (Address, Ty<'db>, Option)> { let mut addr = locals.ptr[p.local].addr; - let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); + let mut ty = PlaceTy::from_ty(locals.body.locals[p.local].ty.as_ref()); let mut metadata: Option = None; // locals are always sized for proj in p.projection.lookup() { let prev_ty = ty; ty = self.projected_ty(ty, *proj); match proj { ProjectionElem::Deref => { - metadata = if self.size_align_of(ty, locals)?.is_none() { + metadata = if self.size_align_of(ty.ty, locals)?.is_none() { Some( Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() } .into(), @@ -780,12 +780,12 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ); metadata = None; // Result of index is always sized let ty_size = - self.size_of_sized(ty, locals, "array inner type should be sized")?; + self.size_of_sized(ty.ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::ConstantIndex { from_end, offset } => { let offset = if from_end { - let len = match prev_ty.kind() { + let len = match prev_ty.ty.kind() { TyKind::Array(_, c) => match try_const_usize(self.db, c) { Some(it) => it as u64, None => { @@ -804,11 +804,11 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { }; metadata = None; // Result of index is always sized let ty_size = - self.size_of_sized(ty, locals, "array inner type should be sized")?; + self.size_of_sized(ty.ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::Subslice { from, to } => { - let inner_ty = match ty.kind() { + let inner_ty = match ty.ty.kind() { TyKind::Array(inner, _) | TyKind::Slice(inner) => inner, _ => Ty::new_error(self.interner(), ErrorGuaranteed), }; @@ -825,25 +825,13 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { self.size_of_sized(inner_ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * (from as usize)); } - &ProjectionElem::ClosureField(f) => { - let layout = self.layout(prev_ty)?; - let offset = layout.fields.offset(f).bytes_usize(); - addr = addr.offset(offset); - metadata = None; - } - ProjectionElem::Field(Either::Right(f)) => { - let layout = self.layout(prev_ty)?; - let offset = layout.fields.offset(f.index as usize).bytes_usize(); - addr = addr.offset(offset); - metadata = None; // tuple field is always sized FIXME: This is wrong, the tail can be unsized - } - ProjectionElem::Field(Either::Left(f)) => { - let layout = self.layout(prev_ty)?; + ProjectionElem::Field(f) => { + let layout = self.layout(prev_ty.ty)?; let variant_layout = match &layout.variants { Variants::Single { .. } | Variants::Empty => &layout, Variants::Multiple { variants, .. } => { - &variants[match f.parent { - hir_def::VariantId::EnumVariantId(it) => { + &variants[match prev_ty.variant_id { + Some(hir_def::VariantId::EnumVariantId(it)) => { RustcEnumVariantIdx(it.index(self.db)) } _ => { @@ -854,20 +842,20 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { }] } }; - let offset = variant_layout - .fields - .offset(u32::from(f.local_id.into_raw()) as usize) - .bytes_usize(); + let offset = variant_layout.fields.offset(f.0 as usize).bytes_usize(); addr = addr.offset(offset); // Unsized field metadata is equal to the metadata of the struct - if self.size_align_of(ty, locals)?.is_some() { + if self.size_align_of(ty.ty, locals)?.is_some() { metadata = None; } } + ProjectionElem::Downcast(_) => { + // no runtime effect + } ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), } } - Ok((addr, ty, metadata)) + Ok((addr, ty.ty, metadata)) } fn layout(&self, ty: Ty<'db>) -> Result<'db, Arc> { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 4e52c1f7c305..ddc864b09379 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -5,11 +5,11 @@ use std::{fmt::Write, iter, mem}; use base_db::Crate; use hir_def::{ AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, - ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId, + ItemContainerId, LocalFieldId, Lookup, TraitId, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId, - LabelId, Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + LabelId, Literal, MatchArm, Pat, PatId, RecordLitField, RecordSpread, generics::GenericParams, }, item_tree::FieldsShape, @@ -19,7 +19,7 @@ use hir_def::{ }; use hir_expand::name::Name; use itertools::{EitherOrBoth, Itertools}; -use la_arena::ArenaMap; +use la_arena::{ArenaMap, RawIdx}; use rustc_apfloat::Float; use rustc_hash::FxHashMap; use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; @@ -43,11 +43,11 @@ use crate::{ layout::LayoutError, method_resolution::CandidateId, mir::{ - AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, - FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, RawIdx, - Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, TupleFieldId, - Ty, UnOp, VariantId, return_slot, + AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Expr, + FieldIndex, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, + Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue, + Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId, + return_slot, }, next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, @@ -919,20 +919,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let rvalue = Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst.store()), match spread_place { - Some(sp) => operands + Some(sp) if let VariantId::StructId(_) = variant_id => operands .into_iter() .enumerate() .map(|(i, it)| match it { Some(it) => it, None => { - let p = sp.project(ProjectionElem::Field( - Either::Left(FieldId { - parent: variant_id, - local_id: LocalFieldId::from_raw(RawIdx::from( - i as u32, - )), - }), - )); + let p = sp.project(ProjectionElem::Field(FieldIndex( + i as u32, + ))); Operand { kind: OperandKind::Copy(p.store()), span: None, @@ -940,6 +935,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } }) .collect(), + Some(_) => { + return Err(MirLowerError::TypeError( + "functional record update syntax requires a struct", + )); + } None => operands.into_iter().collect::>().ok_or( MirLowerError::TypeError("missing field in record literal"), )?, @@ -948,16 +948,13 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } - VariantId::UnionId(union_id) => { + VariantId::UnionId(_union_id) => { let [RecordLitField { name, expr }] = fields.as_ref() else { not_supported!("Union record literal with more than one field"); }; let local_id = variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project(PlaceElem::Field(Either::Left(FieldId { - parent: union_id.into(), - local_id, - }))); + let place = place.project(PlaceElem::Field(local_id.into())); self.lower_expr_to_place(*expr, place, current) } } @@ -1410,13 +1407,13 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; - *place = place.project(ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy as its unused - index, - }))) + *place = place.project(ProjectionElem::Field(FieldIndex(index))) } else { - let field = - self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; + let field = self + .infer + .field_resolution(expr_id) + .ok_or(MirLowerError::UnresolvedField)? + .either(|f| f.local_id.into(), |t| FieldIndex(t.index)); *place = place.project(ProjectionElem::Field(field)); } } else { @@ -2086,35 +2083,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } fn convert_closure_capture_projections( - db: &dyn HirDatabase, + _db: &dyn HirDatabase, place: &HirPlace, ) -> impl Iterator { place.projections.iter().enumerate().map(|(i, proj)| match proj.kind { HirProjectionKind::Deref => ProjectionElem::Deref, - HirProjectionKind::Field { field_idx, variant_idx } => { + HirProjectionKind::Field { field_idx, variant_idx: _ } => { let ty = place.ty_before_projection(i); match ty.kind() { - TyKind::Tuple(_) => { - ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // Dummy as it's unused - index: field_idx, - })) - } - TyKind::Adt(adt_def, _) => { + TyKind::Tuple(_) => ProjectionElem::Field(FieldIndex(field_idx)), + TyKind::Adt(_, _) => { let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); - let field = match adt_def.def_id() { - AdtId::StructId(id) => { - FieldId { parent: id.into(), local_id: local_field_id } - } - AdtId::UnionId(id) => { - FieldId { parent: id.into(), local_id: local_field_id } - } - AdtId::EnumId(id) => { - let variant = id.enum_variants(db).variants[variant_idx as usize].0; - FieldId { parent: variant.into(), local_id: local_field_id } - } - }; - ProjectionElem::Field(Either::Left(field)) + ProjectionElem::Field(local_field_id.into()) } _ => panic!("unexpected type"), } @@ -2207,7 +2187,7 @@ pub fn mir_body_for_closure_query<'db>( if is_by_ref_closure { projections.push(ProjectionElem::Deref); } - projections.push(ProjectionElem::ClosureField(capture_idx)); + projections.push(ProjectionElem::Field(FieldIndex(capture_idx as u32))); let capture_param_place = Place { local: closure_local, projection: Projection::new_from_slice(&projections).store(), @@ -2239,7 +2219,6 @@ pub fn mir_body_for_closure_query<'db>( let current = ctx.pop_drop_scope_assert_finished(current, root.into())?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } - let mut err = None; ctx.result.walk_places(|mir_place| { let mir_projections = mir_place.projection.lookup(); diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index c306b6ca15f8..254d561a66e6 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -1,17 +1,19 @@ //! MIR lowering for patterns -use hir_def::{hir::ExprId, signatures::VariantFields}; +use hir_def::{ + hir::{ExprId, RecordFieldPat}, + signatures::VariantFields, +}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, + FieldIndex, LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, lower::{ - BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, - MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, - RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, - TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, + BasicBlockId, BinOp, BindingId, BorrowKind, Expr, Idx, MemoryMap, MirLowerCtx, + MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, ResolveValueResult, + Result, Rvalue, SwitchTargets, TerminatorKind, Ty, TyKind, ValueNs, VariantId, }, }, }; @@ -148,12 +150,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else, args, *ellipsis, - (0..subst.len()).map(|i| { - PlaceElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // Dummy as it is unused - index: i as u32, - })) - }), + (0..subst.len()).map(|i| PlaceElem::Field(FieldIndex(i as u32))), &cond_place, mode, )? @@ -662,6 +659,11 @@ impl<'db> MirLowerCtx<'_, 'db> { cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { + let downcast_place = if matches!(v, VariantId::EnumVariantId(_)) { + cond_place.project(ProjectionElem::Downcast(v)) + } else { + cond_place.clone() + }; Ok(match shape { AdtPatternShape::Record { args } => { let it = args @@ -669,28 +671,26 @@ impl<'db> MirLowerCtx<'_, 'db> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; - Ok(( - PlaceElem::Field(Either::Left(FieldId { - parent: v, - local_id: field_id, - })), - x.pat, - )) + Ok((PlaceElem::Field(field_id.into()), x.pat)) }) .collect::>>()?; - self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? + self.pattern_match_adt( + current, + current_else, + it.into_iter(), + &downcast_place, + mode, + )? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, _)| { - PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) - }); + let fields = variant_data.fields().iter().map(|(x, _)| PlaceElem::Field(x.into())); self.pattern_match_tuple_like( current, current_else, args, ellipsis, fields, - cond_place, + &downcast_place, mode, )? } diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index 905498706602..09824d282bf8 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -5,20 +5,22 @@ use std::{ mem, }; -use either::Either; use hir_def::{ + HasModule, VariantId, expr_store::ExpressionStore, hir::BindingId, signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature}, }; use hir_expand::{Lookup, name::Name}; use la_arena::ArenaMap; +use rustc_type_ir::inherent::IntoKind; use crate::{ InferBodyId, db::{HirDatabase, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, - mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind}, + mir::{PlaceElem, PlaceTy, ProjectionElem, StatementKind, TerminatorKind}, + next_solver::{DbInterner, TyKind, infer::DbInternerInferExt}, }; use super::{ @@ -330,34 +332,56 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { f(this, local, head); w!(this, ")"); } - ProjectionElem::Field(Either::Left(field)) => { - let variant_fields = field.parent.fields(this.db); - let name = &variant_fields.fields()[field.local_id].name; - match field.parent { - hir_def::VariantId::EnumVariantId(e) => { - w!(this, "("); - f(this, local, head); - let loc = e.lookup(this.db); - w!( - this, - " as {}).{}", - loc.name.display(this.db, this.display_target.edition), - name.display(this.db, this.display_target.edition) - ); - } - hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => { - f(this, local, head); - w!(this, ".{}", name.display(this.db, this.display_target.edition)); - } + ProjectionElem::Downcast(variant_id) => match variant_id { + hir_def::VariantId::EnumVariantId(e) => { + w!(this, "("); + f(this, local, head); + let loc = e.lookup(this.db); + w!(this, " as {})", loc.name.display(this.db, this.display_target.edition),); } - } - ProjectionElem::Field(Either::Right(field)) => { - f(this, local, head); - w!(this, ".{}", field.index); - } - ProjectionElem::ClosureField(it) => { + _ => { + f(this, local, head); + w!(this, ".{:?}", last); + } + }, + ProjectionElem::Field(field) => { f(this, local, head); - w!(this, ".{}", it); + + // we need to get the base type to decide how to display the field / get the field name + let infcx = DbInterner::new_with(this.db, this.body.owner.krate(this.db)) + .infer_ctxt() + .build(rustc_type_ir::TypingMode::PostAnalysis); + let env = this.db.trait_environment(this.body.owner.generic_def(this.db)); + let place_ty = PlaceTy::from_ty(this.body.locals[local].ty.as_ref()) + .multi_projection_ty(&infcx, env, projections); + if let Some(variant_id) = place_ty.variant_id { + let variant_fields = variant_id.fields(this.db); + w!( + this, + ".{}", + variant_fields.fields()[field.to_local_field_id()] + .name + .display(this.db, this.display_target.edition) + ); + } else { + match place_ty.ty.kind() { + TyKind::Adt(adt_def, _) if !adt_def.is_enum() => { + let variant_id = VariantId::from_non_enum(adt_def.def_id()).unwrap(); + let fields = variant_id.fields(this.db); + w!( + this, + ".{}", + fields.fields()[field.to_local_field_id()] + .name + .display(this.db, this.display_target.edition) + ); + } + TyKind::Tuple(_) | TyKind::Closure(..) => w!(this, ".{}", field.0), + _ => { + w!(this, ".{:?}", last); + } + } + }; } ProjectionElem::Index(l) => { f(this, local, head); diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index c43e04b9d0ca..fe31d44207df 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -196,6 +196,16 @@ impl<'db> Ty<'db> { ) } + pub fn new_array_opt(interner: DbInterner<'db>, ty: Ty<'db>, n: Option) -> Ty<'db> { + Ty::new( + interner, + TyKind::Array( + ty, + crate::consteval::usize_const(interner.db, n, interner.expect_crate()), + ), + ) + } + fn new_generic_adt(interner: DbInterner<'db>, adt_id: AdtId, ty_param: Ty<'db>) -> Ty<'db> { let args = GenericArgs::fill_with_defaults( interner, From f10f7ae1f9c1fca945155c4428477dadd77a70f7 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 6 Dec 2025 22:23:32 +0100 Subject: [PATCH 2/2] Remove ProjectionElem::OpaqueCast --- crates/hir-ty/src/mir.rs | 8 ++------ crates/hir-ty/src/mir/borrowck.rs | 3 +-- crates/hir-ty/src/mir/eval.rs | 1 - 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index d0b7e5f6544b..3b5abc8c7dec 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -181,7 +181,6 @@ pub enum ProjectionElem { }, /// "Downcast" to a variant of an enum or a coroutine. Downcast(VariantId), - OpaqueCast(std::convert::Infallible), // TODO remove this } impl ProjectionElem { @@ -195,7 +194,6 @@ impl ProjectionElem { } ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id), - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), } } @@ -212,7 +210,6 @@ impl ProjectionElem { } ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, ProjectionElem::Downcast(variant_id) => ProjectionElem::Downcast(variant_id), - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), }) } } @@ -1257,7 +1254,7 @@ impl<'db> PlaceTy<'db> { .instantiate(infcx.interner, args) .skip_norm_wip() } - // TODO TyKind::Coroutine... + // FIXME TyKind::Coroutine... _ => panic!("can't downcast non-adt non-coroutine type: {self_ty:?}"), } } else { @@ -1272,7 +1269,7 @@ impl<'db> PlaceTy<'db> { TyKind::Closure(_, args) => { args.as_closure().tupled_upvars_ty().tuple_fields()[f.0 as usize] } - // TODO TyKind::Coroutine / TyKind::CoroutineClosure... + // FIXME TyKind::Coroutine / TyKind::CoroutineClosure... TyKind::Tuple(tys) => tys .get(f.0 as usize) .cloned() @@ -1360,7 +1357,6 @@ impl<'db> PlaceTy<'db> { self.variant_id.clone(), f, )), - ProjectionElem::OpaqueCast(_ty) => unimplemented!("not emitted, to be removed"), }; answer } diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index bf241aed7207..c5367f630e1a 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -436,8 +436,7 @@ fn place_case<'db>( | ProjectionElem::Index(_) => { is_part_of = true; } - ProjectionElem::Downcast(_) | - ProjectionElem::OpaqueCast(_) => (), + ProjectionElem::Downcast(_) => (), } ty = ty.projection_ty(infcx, proj, env); } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index f8bf3cc3ae4b..ab3e8e846fa5 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -852,7 +852,6 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ProjectionElem::Downcast(_) => { // no runtime effect } - ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), } } Ok((addr, ty.ty, metadata))