Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22510 from flodiebold/mir-salsa-projections
internal: Align MIR ProjectionElem more with rustc's version and simplify it
Florian Diebold 14 days ago
parent 6c0c0d7 · parent 71a9c63 · commit 9b9f59c
-rw-r--r--crates/hir-def/src/lib.rs8
-rw-r--r--crates/hir-ty/src/mir.rs328
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs31
-rw-r--r--crates/hir-ty/src/mir/eval.rs55
-rw-r--r--crates/hir-ty/src/mir/lower.rs83
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs48
-rw-r--r--crates/hir-ty/src/mir/pretty.rs81
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs10
8 files changed, 380 insertions, 264 deletions
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index a1bb82e7f2..2172e2412b 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<Self> {
+ 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 e82038907c..7c5c0ea564 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<BasicBlock>;
pub type LocalId = Idx<Local>;
@@ -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,72 @@ 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<LocalFieldId> for FieldIndex {
+ fn from(value: LocalFieldId) -> Self {
+ FieldIndex(value.into_raw().into_u32())
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ProjectionElem<V: PartialEq> {
Deref,
- Field(Either<FieldId, TupleFieldId>),
- // 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<Symbol>, 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),
}
impl<V: PartialEq> ProjectionElem<V> {
- 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<V2: PartialEq>(self, v: impl FnOnce(V) -> V2) -> ProjectionElem<V2> {
+ 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),
}
+ }
- 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<V2: PartialEq>(
+ self,
+ v: impl FnOnce(V) -> Option<V2>,
+ ) -> Option<ProjectionElem<V2>> {
+ 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),
+ })
}
}
@@ -367,6 +318,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<LocalId> for PlaceRef<'db> {
@@ -1243,3 +1201,157 @@ 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<LocalId> {
+ 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<VariantId>,
+}
+
+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<VariantId>,
+ 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()
+ }
+ // FIXME 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]
+ }
+ // FIXME 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<V: ::std::fmt::Debug + PartialEq>(
+ self,
+ infcx: &InferCtxt<'db>,
+ elem: &ProjectionElem<V>,
+ 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<V: PartialEq + ::std::fmt::Debug>(
+ self,
+ tcx: DbInterner<'db>,
+ elem: &ProjectionElem<V>,
+ mut structurally_normalize: impl FnMut(Ty<'db>) -> Ty<'db>,
+ mut handle_field: impl FnMut(Ty<'db>, Option<VariantId>, FieldIndex /*, T*/) -> Ty<'db>,
+ ) -> PlaceTy<'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 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")
+ }
+ 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, f))
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index ff963fc121..c5367f630e 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,21 @@ 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<T>`
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_)
- | ProjectionElem::ClosureField(_)
| ProjectionElem::Index(_) => {
is_part_of = true;
}
- ProjectionElem::OpaqueCast(_) => (),
+ ProjectionElem::Downcast(_) => (),
}
- 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 78b70edeee..f0e2218cde 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<u8>,
stderr: Vec<u8>,
layout_cache: RefCell<FxHashMap<Ty<'db>, Arc<Layout>>>,
- projected_ty_cache: RefCell<FxHashMap<(Ty<'db>, PlaceElem), Ty<'db>>>,
+ projected_ty_cache: RefCell<FxHashMap<(PlaceTy<'db>, PlaceElem), PlaceTy<'db>>>,
not_special_fn_cache: RefCell<FxHashSet<FunctionId>>,
mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex<'a>>>,
/// Constantly dropping and creating `Locals` is very costly. We store
@@ -738,13 +738,13 @@ 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;
}
let (ty, proj) = pair;
- let r = proj.projected_ty(&self.infcx, self.param_env.param_env, ty, self.crate_id);
+ let r = ty.projection_ty(&self.infcx, &proj, self.param_env.param_env);
self.projected_ty_cache.borrow_mut().insert((ty, proj), r);
r
}
@@ -755,14 +755,14 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> {
locals: &'b Locals<'a>,
) -> Result<'db, (Address, Ty<'db>, Option<IntervalOrOwned>)> {
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<IntervalOrOwned> = 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,19 @@ 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::OpaqueCast(_) => not_supported!("opaque cast"),
+ ProjectionElem::Downcast(_) => {
+ // no runtime effect
+ }
}
}
- Ok((addr, ty, metadata))
+ Ok((addr, ty.ty, metadata))
}
fn layout(&self, ty: Ty<'db>) -> Result<'db, Arc<Layout>> {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 6499c703a8..4359a1acb5 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::<Option<_>>().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)
}
}
@@ -1411,13 +1408,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 {
@@ -2087,35 +2084,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
}
fn convert_closure_capture_projections(
- db: &dyn HirDatabase,
+ _db: &dyn HirDatabase,
place: &HirPlace,
) -> impl Iterator<Item = PlaceElem> {
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"),
}
@@ -2208,7 +2188,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(),
@@ -2240,7 +2220,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 c306b6ca15..f273a823ba 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<BasicBlockId>)> {
+ let downcast_place = if matches!(v, VariantId::EnumVariantId(_)) {
+ cond_place.project(ProjectionElem::Downcast(v))
+ } else {
+ *cond_place
+ };
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::<Result<'db, Vec<_>>>()?;
- 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 9054987066..3e41454424 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,57 @@ 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 c43e04b9d0..fe31d44207 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<u128>) -> 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,