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/layout.rs b/crates/hir-ty/src/layout.rs index 2b0a93e61d59..59f1d6e57528 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -418,17 +418,15 @@ pub fn layout_of_ty_query( .iter() .map(|pat| match pat.kind() { PatternKind::Range { start, end } => Ok::<_, LayoutError>(( - extract_const_value(start) - .unwrap() + extract_const_value(start)? .try_to_bits(db, trait_env.as_ref()) .ok_or(LayoutError::Unknown)?, - extract_const_value(end) - .unwrap() + extract_const_value(end)? .try_to_bits(db, trait_env.as_ref()) .ok_or(LayoutError::Unknown)?, )), PatternKind::NotNull | PatternKind::Or(_) => { - unreachable!("mixed or patterns are not allowed") + Err(LayoutError::Unknown) } }) .collect(); diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index afbafb3fea7d..b9ee38c44fe4 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -535,13 +535,21 @@ fn non_zero_and_non_null() { } check_size_and_align( r#" -const END: usize = 10; -struct Goal(core::pattern_type!(usize is 0..=END)); - "#, + const END: usize = 10; + struct Goal(core::pattern_type!(usize is 0..=END)); + "#, "//- minicore: pat\n", 8, 8, ); + check_size_and_align( + r#" +pub struct Goal(core::pattern_type!(i32 is ..0 | 1..)); + "#, + "//- minicore: pat\n", + 4, + 4, + ); } #[test] diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index df83b2abb870..985a3ddc6a52 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -55,7 +55,7 @@ use thin_vec::ThinVec; use tracing::debug; use crate::{ - ImplTraitId, Span, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + ImplTraitId, Span, TyLoweringDiagnostic, consteval::{create_anon_const, path_to_const}, db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId}, generics::{Generics, SingleGenerics, generics}, @@ -302,8 +302,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self } - pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { - self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); + pub(crate) fn push_diagnostic(&mut self, diagnostic: TyLoweringDiagnostic) { + self.diagnostics.push(diagnostic); + } + + fn push_infer_vars_not_allowed(&mut self, span: Span) { + if !span.is_dummy() { + self.push_diagnostic(TyLoweringDiagnostic::InferVarsNotAllowed { source: span }); + } } #[track_caller] @@ -315,7 +321,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { match &mut self.infer_vars { Some(infer_vars) => infer_vars.next_ty_var(span), None => { - // FIXME: Emit an error: no infer vars allowed here. + self.push_infer_vars_not_allowed(span); self.types.types.error } } @@ -325,7 +331,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { match &mut self.infer_vars { Some(infer_vars) => infer_vars.next_const_var(span), None => { - // FIXME: Emit an error: no infer vars allowed here. + self.push_infer_vars_not_allowed(span); self.types.consts.error } } @@ -335,7 +341,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { match &mut self.infer_vars { Some(infer_vars) => infer_vars.next_region_var(span), None => { - // FIXME: Emit an error: no infer vars allowed here. + self.push_infer_vars_not_allowed(span); self.types.regions.error } } @@ -634,7 +640,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { data: Either::Left(PathDiagnosticCallbackData(type_ref)), callback: |data, this, diag| { let type_ref = data.as_ref().left().unwrap().0; - this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + this.push_diagnostic(TyLoweringDiagnostic::PathDiagnostic { + source: type_ref, + diag, + }) }, } } diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs index 2565fb46ce8c..35ee99cb95a3 100644 --- a/crates/hir-ty/src/lower/diagnostics.rs +++ b/crates/hir-ty/src/lower/diagnostics.rs @@ -1,17 +1,13 @@ //! This files contains the declaration of diagnostics kinds for ty and path lowering. -use hir_def::type_ref::TypeRefId; -use hir_def::{GenericDefId, GenericParamId}; +use hir_def::{GenericDefId, GenericParamId, type_ref::TypeRefId}; -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct TyLoweringDiagnostic { - pub source: TypeRefId, - pub kind: TyLoweringDiagnosticKind, -} +use crate::Span; #[derive(Debug, PartialEq, Eq, Clone)] -pub enum TyLoweringDiagnosticKind { - PathDiagnostic(PathLoweringDiagnostic), +pub enum TyLoweringDiagnostic { + PathDiagnostic { source: TypeRefId, diag: PathLoweringDiagnostic }, + InferVarsNotAllowed { source: Span }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index e82038907c44..7c5c0ea5646c 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,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 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), } 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), } + } - 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), + }) } } @@ -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 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 { + 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() + } + // 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( + 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> { + // 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 ff963fc12120..c5367f630e1a 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` | 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 78b70edeeef7..f0e2218cde62 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,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)> { 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,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> { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 6499c703a81e..4359a1acb51c 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) } } @@ -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 { 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 c306b6ca15f8..f273a823ba18 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 + }; 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..3e414544241c 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 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, diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index f3188c9aada5..ed073cb43789 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -11,12 +11,13 @@ use hir_def::{ ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, }, - hir::ExprOrPatId, + hir::{ExprId, ExprOrPatId, PatId}, + type_ref::TypeRefId, }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, - PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, @@ -118,6 +119,7 @@ diagnostics![AnyDiagnostic<'db> -> IncorrectCase, IncorrectGenericsLen, IncorrectGenericsOrder, + InferVarsNotAllowed, InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, @@ -564,6 +566,11 @@ pub struct BadRtn { pub rtn: InFile>, } +#[derive(Debug)] +pub struct InferVarsNotAllowed { + pub node: InFile, +} + #[derive(Debug)] pub struct IncorrectGenericsLen { /// Points at the name if there are no generics. @@ -799,41 +806,14 @@ impl<'db> AnyDiagnostic<'db> { sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, type_owner: TypeOwnerId, ) -> Option> { - let expr_syntax = |expr| { - source_map - .expr_syntax(expr) - .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr")) - .ok() - }; - let pat_syntax = |pat| { - source_map - .pat_syntax(pat) - .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern")) - .ok() - }; - let type_syntax = |pat| { - source_map - .type_syntax(pat) - .inspect_err(|_| stdx::never!("inference diagnostic in desugared type")) - .ok() - }; + let expr_syntax = |expr| Self::expr_syntax(expr, source_map); + let pat_syntax = |pat| Self::pat_syntax(pat, source_map); let expr_or_pat_syntax = |id| match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) }; - let span_syntax = |span| match span { - hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), - hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), - hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()), - hir_ty::Span::BindingId(idx) => { - pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast()) - } - hir_ty::Span::Dummy => { - never!("should never create a diagnostic for dummy spans"); - None - } - }; + let span_syntax = |span| Self::span_syntax(span, source_map); Some(match d { &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { @@ -1238,21 +1218,73 @@ impl<'db> AnyDiagnostic<'db> { }) } + fn expr_syntax( + expr: ExprId, + source_map: &ExpressionStoreSourceMap, + ) -> Option> { + source_map + .expr_syntax(expr) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared expr")) + .ok() + } + + fn pat_syntax( + pat: PatId, + source_map: &ExpressionStoreSourceMap, + ) -> Option> { + source_map + .pat_syntax(pat) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern")) + .ok() + } + + fn type_syntax( + type_ref: TypeRefId, + source_map: &ExpressionStoreSourceMap, + ) -> Option>> { + source_map + .type_syntax(type_ref) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared type")) + .ok() + } + + fn span_syntax( + span: hir_ty::Span, + source_map: &ExpressionStoreSourceMap, + ) -> Option>> { + Some(match span { + hir_ty::Span::ExprId(idx) => Self::expr_syntax(idx, source_map)?.map(|it| it.upcast()), + hir_ty::Span::PatId(idx) => Self::pat_syntax(idx, source_map)?.map(|it| it.upcast()), + hir_ty::Span::TypeRefId(idx) => { + Self::type_syntax(idx, source_map)?.map(|it| it.upcast()) + } + hir_ty::Span::BindingId(idx) => { + let &pat = source_map.patterns_for_binding(idx).first()?; + Self::pat_syntax(pat, source_map)?.map(|it| it.upcast()) + } + hir_ty::Span::Dummy => { + never!("should never create a diagnostic for dummy spans"); + return None; + } + }) + } + pub(crate) fn ty_diagnostic( diag: &TyLoweringDiagnostic, source_map: &ExpressionStoreSourceMap, db: &'db dyn HirDatabase, ) -> Option> { - let Ok(source) = source_map.type_syntax(diag.source) else { - stdx::never!("error on synthetic type syntax"); - return None; - }; - let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); - Some(match &diag.kind { - TyLoweringDiagnosticKind::PathDiagnostic(diag) => { - let ast::Type::PathType(syntax) = syntax() else { return None }; + Some(match diag { + TyLoweringDiagnostic::PathDiagnostic { source, diag } => { + let source = Self::type_syntax(*source, source_map)?; + let syntax = source.value.to_node(&db.parse_or_expand(source.file_id)); + let ast::Type::PathType(syntax) = syntax else { return None }; Self::path_diagnostic(diag, source.with_value(syntax.path()?))? } + TyLoweringDiagnostic::InferVarsNotAllowed { source } => { + let source = Self::span_syntax(*source, source_map)?; + InferVarsNotAllowed { node: source.map(Into::into) }.into() + } }) } } diff --git a/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs b/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs new file mode 100644 index 000000000000..cf369a1aa0e3 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/infer_vars_not_allowed.rs @@ -0,0 +1,49 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: infer-vars-not-allowed +// +// This diagnostic is triggered when `_` is used where type +// inference is not allowed. +pub(crate) fn infer_vars_not_allowed( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InferVarsNotAllowed, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0121"), + "the type placeholder `_` is not allowed within types on item signatures", + d.node, + ) +} +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + #[test] + fn type_alias() { + check_diagnostics( + r#" +type Foo = _; + // ^ error: the type placeholder `_` is not allowed within types on item signatures + "#, + ); + } + #[test] + fn const_item() { + check_diagnostics( + r#" +const X: _ = 0; + // ^ error: the type placeholder `_` is not allowed within types on item signatures + "#, + ); + } + + #[test] + fn static_item() { + check_diagnostics( + r#" +static Y: _ = 0; + // ^ error: the type placeholder `_` is not allowed within types on item signatures + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index aec68b55c78d..ea0d2d954eb0 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -50,6 +50,7 @@ mod handlers { pub(crate) mod incorrect_case; pub(crate) mod incorrect_generics_len; pub(crate) mod incorrect_generics_order; + pub(crate) mod infer_vars_not_allowed; pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod invalid_lhs_of_assignment; @@ -440,6 +441,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::InferVarsNotAllowed(d) => handlers::infer_vars_not_allowed::infer_vars_not_allowed(&ctx, &d), AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( &ctx, &d, diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs index f92afde60de1..db0185331cdb 100644 --- a/crates/parser/src/grammar/types.rs +++ b/crates/parser/src/grammar/types.rs @@ -1,4 +1,4 @@ -use crate::grammar::entry::prefix::pat; +use crate::grammar::entry::prefix::pat_top; use super::*; @@ -430,7 +430,7 @@ fn pattern_type(p: &mut Parser<'_>) { if !p.eat_contextual_kw(T![is]) { p.error("expected `is`") } - pat(p); + pat_top(p); p.expect(T![')']); m.complete(p, PATTERN_TYPE); } else { diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs index 5bdc9d8ca315..81d9786809b7 100644 --- a/crates/rust-analyzer/src/target_spec.rs +++ b/crates/rust-analyzer/src/target_spec.rs @@ -99,7 +99,7 @@ impl ProjectJsonTargetSpec { arg.replace("{label}", &this.label) }), RunnableKind::Test { test_id, .. } => { - self.find_replace_runnable(project_json::RunnableKind::Run, &|this, arg| { + self.find_replace_runnable(project_json::RunnableKind::TestOne, &|this, arg| { arg.replace("{label}", &this.label).replace("{test_id}", &test_id.to_string()) }) } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index c5ebc0d24880..0588b1c77d1c 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -2353,6 +2353,18 @@ pub mod pat { } } + impl const RangePattern for i32 { + const MIN: i32 = 0x80_00_00_00; + const MAX: i32 = 0x7F_FF_FF_FF; + fn sub_one(self) -> Self { + if self == Self::MIN { + panic!("exclusive range end at minimum value of type") + } else { + self - 1 + } + } + } + // region:coerce_unsized impl crate::ops::CoerceUnsized for pattern_type!(*const T is !null)