Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs')
| -rw-r--r-- | crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs | 207 |
1 files changed, 114 insertions, 93 deletions
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index eb20d3c51f..fb942e336e 100644 --- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -1,24 +1,27 @@ //! Interface with `rustc_pattern_analysis`. -use std::cell::LazyCell; -use std::fmt; +use std::{cell::LazyCell, fmt}; -use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use hir_def::{EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; use intern::sym; use rustc_pattern_analysis::{ IndexVec, PatCx, PrivateUninhabitedField, constructor::{Constructor, ConstructorSet, VariantVisibility}, usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness}, }; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind, + TraitEnvironment, db::HirDatabase, - infer::normalize, inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, + next_solver::{ + Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, + }, }; use super::{FieldPat, Pat, PatKind}; @@ -26,10 +29,12 @@ use super::{FieldPat, Pat, PatKind}; use Constructor::*; // Re-export r-a-specific versions of all these types. -pub(crate) type DeconstructedPat<'db> = - rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'db>>; -pub(crate) type MatchArm<'db> = rustc_pattern_analysis::MatchArm<'db, MatchCheckCtx<'db>>; -pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'db>>; +pub(crate) type DeconstructedPat<'a, 'db> = + rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'a, 'db>>; +pub(crate) type MatchArm<'a, 'b, 'db> = + rustc_pattern_analysis::MatchArm<'b, MatchCheckCtx<'a, 'db>>; +pub(crate) type WitnessPat<'a, 'db> = + rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'a, 'db>>; /// [Constructor] uses this in unimplemented variants. /// It allows porting match expressions from upstream algorithm without losing semantics. @@ -65,37 +70,37 @@ impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex { } #[derive(Clone)] -pub(crate) struct MatchCheckCtx<'db> { +pub(crate) struct MatchCheckCtx<'a, 'db> { module: ModuleId, - body: DefWithBodyId, pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, env: Arc<TraitEnvironment<'db>>, + infcx: &'a InferCtxt<'db>, } -impl<'db> MatchCheckCtx<'db> { +impl<'a, 'db> MatchCheckCtx<'a, 'db> { pub(crate) fn new( module: ModuleId, - body: DefWithBodyId, - db: &'db dyn HirDatabase, + infcx: &'a InferCtxt<'db>, env: Arc<TraitEnvironment<'db>>, ) -> Self { + let db = infcx.interner.db; let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); - Self { module, body, db, exhaustive_patterns, env } + Self { module, db, exhaustive_patterns, env, infcx } } - pub(crate) fn compute_match_usefulness( + pub(crate) fn compute_match_usefulness<'b>( &self, - arms: &[MatchArm<'db>], - scrut_ty: Ty, + arms: &[MatchArm<'a, 'b, 'db>], + scrut_ty: Ty<'db>, known_valid_scrutinee: Option<bool>, - ) -> Result<UsefulnessReport<'db, Self>, ()> { - if scrut_ty.contains_unknown() { + ) -> Result<UsefulnessReport<'b, Self>, ()> { + if scrut_ty.references_non_lt_error() { return Err(()); } for arm in arms { - if arm.pat.ty().contains_unknown() { + if arm.pat.ty().references_non_lt_error() { return Err(()); } } @@ -106,8 +111,8 @@ impl<'db> MatchCheckCtx<'db> { compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) } - fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone()) + fn is_uninhabited(&self, ty: Ty<'db>) -> bool { + is_ty_uninhabited_from(self.infcx, ty, self.module, self.env.clone()) } /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. @@ -140,23 +145,27 @@ impl<'db> MatchCheckCtx<'db> { // This lists the fields of a variant along with their types. fn list_variant_fields( &self, - ty: &Ty, + ty: Ty<'db>, variant: VariantId, - ) -> impl Iterator<Item = (LocalFieldId, Ty)> { + ) -> impl Iterator<Item = (LocalFieldId, Ty<'db>)> { let (_, substs) = ty.as_adt().unwrap(); let field_tys = self.db.field_types(variant); let fields_len = variant.fields(self.db).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| { - let ty = field_tys[fid].clone().substitute(Interner, substs); - let ty = normalize(self.db, self.db.trait_environment_for_body(self.body), ty); + let ty = field_tys[fid].instantiate(self.infcx.interner, substs); + let ty = self + .infcx + .at(&ObligationCause::dummy(), self.env.env) + .deeply_normalize(ty) + .unwrap_or(ty); (fid, ty) }) } - pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'db> { - let singleton = |pat: DeconstructedPat<'db>| vec![pat.at_index(0)]; + pub(crate) fn lower_pat(&self, pat: &Pat<'db>) -> DeconstructedPat<'a, 'db> { + let singleton = |pat: DeconstructedPat<'a, 'db>| vec![pat.at_index(0)]; let ctor; let mut fields: Vec<_>; let arity; @@ -169,7 +178,7 @@ impl<'db> MatchCheckCtx<'db> { arity = 0; } PatKind::Deref { subpattern } => { - ctor = match pat.ty.kind(Interner) { + ctor = match pat.ty.kind() { TyKind::Ref(..) => Ref, _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); @@ -187,12 +196,13 @@ impl<'db> MatchCheckCtx<'db> { self.lower_pat(&pat.pattern).at_index(idx as usize) }) .collect(); - match pat.ty.kind(Interner) { - TyKind::Tuple(_, substs) => { + match pat.ty.kind() { + TyKind::Tuple(substs) => { ctor = Struct; - arity = substs.len(Interner); + arity = substs.len(); } - &TyKind::Adt(AdtId(adt), _) => { + TyKind::Adt(adt_def, _) => { + let adt = adt_def.def_id().0; ctor = match pat.kind.as_ref() { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { UnionField @@ -240,15 +250,15 @@ impl<'db> MatchCheckCtx<'db> { arity = pats.len(); } } - DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), ()) + DeconstructedPat::new(ctor, fields, arity, pat.ty, ()) } - pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat { + pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'a, 'db>) -> Pat<'db> { let mut subpatterns = pat.iter_fields().map(|p| self.hoist_witness_pat(p)); let kind = match pat.ctor() { &Bool(value) => PatKind::LiteralBool { value }, IntRange(_) => unimplemented!(), - Struct | Variant(_) | UnionField => match pat.ty().kind(Interner) { + Struct | Variant(_) | UnionField => match pat.ty().kind() { TyKind::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .zip(0u32..) @@ -259,15 +269,16 @@ impl<'db> MatchCheckCtx<'db> { .collect(), }, TyKind::Adt(adt, substs) => { - let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap(); + let variant = + Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap(); let subpatterns = self - .list_variant_fields(pat.ty(), variant) + .list_variant_fields(*pat.ty(), variant) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); if let VariantId::EnumVariantId(enum_variant) = variant { - PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns } + PatKind::Variant { substs, enum_variant, subpatterns } } else { PatKind::Leaf { subpatterns } } @@ -293,13 +304,13 @@ impl<'db> MatchCheckCtx<'db> { PatKind::Wild } }; - Pat { ty: pat.ty().clone(), kind: Box::new(kind) } + Pat { ty: *pat.ty(), kind: Box::new(kind) } } } -impl PatCx for MatchCheckCtx<'_> { +impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { type Error = (); - type Ty = Ty; + type Ty = Ty<'db>; type VariantIdx = EnumVariantContiguousIndex; type StrLit = Void; type ArmData = (); @@ -315,10 +326,11 @@ impl PatCx for MatchCheckCtx<'_> { ty: &Self::Ty, ) -> usize { match ctor { - Struct | Variant(_) | UnionField => match *ty.kind(Interner) { - TyKind::Tuple(arity, ..) => arity, - TyKind::Adt(AdtId(adt), ..) => { - let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); + Struct | Variant(_) | UnionField => match ty.kind() { + TyKind::Tuple(tys) => tys.len(), + TyKind::Adt(adt_def, ..) => { + let variant = + Self::variant_id_for_adt(self.db, ctor, adt_def.def_id().0).unwrap(); variant.fields(self.db).fields().len() } _ => { @@ -346,24 +358,24 @@ impl PatCx for MatchCheckCtx<'_> { ) -> impl ExactSizeIterator<Item = (Self::Ty, PrivateUninhabitedField)> { let single = |ty| smallvec![(ty, PrivateUninhabitedField(false))]; let tys: SmallVec<[_; 2]> = match ctor { - Struct | Variant(_) | UnionField => match ty.kind(Interner) { - TyKind::Tuple(_, substs) => { - let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner)); - tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect() + Struct | Variant(_) | UnionField => match ty.kind() { + TyKind::Tuple(substs) => { + substs.iter().map(|ty| (ty, PrivateUninhabitedField(false))).collect() } - TyKind::Ref(.., rty) => single(rty.clone()), - &TyKind::Adt(AdtId(adt), ..) => { + TyKind::Ref(_, rty, _) => single(rty), + TyKind::Adt(adt_def, ..) => { + let adt = adt_def.def_id().0; let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); let visibilities = LazyCell::new(|| self.db.field_visibilities(variant)); - self.list_variant_fields(ty, variant) + self.list_variant_fields(*ty, variant) .map(move |(fid, ty)| { let is_visible = || { matches!(adt, hir_def::AdtId::EnumId(..)) || visibilities[fid].is_visible_from(self.db, self.module) }; - let is_uninhabited = self.is_uninhabited(&ty); + let is_uninhabited = self.is_uninhabited(ty); let private_uninhabited = is_uninhabited && !is_visible(); (ty, PrivateUninhabitedField(private_uninhabited)) }) @@ -371,14 +383,14 @@ impl PatCx for MatchCheckCtx<'_> { } ty_kind => { never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); - single(ty.clone()) + single(*ty) } }, - Ref => match ty.kind(Interner) { - TyKind::Ref(.., rty) => single(rty.clone()), + Ref => match ty.kind() { + TyKind::Ref(_, rty, _) => single(rty), ty_kind => { never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); - single(ty.clone()) + single(*ty) } }, Slice(_) => unreachable!("Found a `Slice` constructor in match checking"), @@ -414,42 +426,51 @@ impl PatCx for MatchCheckCtx<'_> { // returned list of constructors. // Invariant: this is empty if and only if the type is uninhabited (as determined by // `cx.is_uninhabited()`). - Ok(match ty.kind(Interner) { - TyKind::Scalar(Scalar::Bool) => ConstructorSet::Bool, - TyKind::Scalar(Scalar::Char) => unhandled(), - TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), + Ok(match ty.kind() { + TyKind::Bool => ConstructorSet::Bool, + TyKind::Char => unhandled(), + TyKind::Int(..) | TyKind::Uint(..) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), - &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => { - let enum_data = enum_id.enum_variants(cx.db); - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt); - - if enum_data.variants.is_empty() && !is_declared_nonexhaustive { - ConstructorSet::NoConstructors - } else { - let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { - let is_uninhabited = is_enum_variant_uninhabited_from( - cx.db, - variant, - subst, - cx.module, - self.env.clone(), - ); - let visibility = if is_uninhabited { - VariantVisibility::Empty + TyKind::Adt(adt_def, subst) => { + let adt = adt_def.def_id().0; + match adt { + hir_def::AdtId::EnumId(enum_id) => { + let enum_data = enum_id.enum_variants(cx.db); + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt); + + if enum_data.variants.is_empty() && !is_declared_nonexhaustive { + ConstructorSet::NoConstructors } else { - VariantVisibility::Visible - }; - variants.push(visibility); - } + let mut variants = IndexVec::with_capacity(enum_data.variants.len()); + for &(variant, _, _) in enum_data.variants.iter() { + let is_uninhabited = is_enum_variant_uninhabited_from( + cx.infcx, + variant, + subst, + cx.module, + self.env.clone(), + ); + let visibility = if is_uninhabited { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + }; + variants.push(visibility); + } - ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive } + ConstructorSet::Variants { + variants, + non_exhaustive: is_declared_nonexhaustive, + } + } + } + hir_def::AdtId::UnionId(_) => ConstructorSet::Union, + hir_def::AdtId::StructId(_) => { + ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) } + } } } - TyKind::Adt(AdtId(hir_def::AdtId::UnionId(_)), _) => ConstructorSet::Union, - TyKind::Adt(..) | TyKind::Tuple(..) => { - ConstructorSet::Struct { empty: cx.is_uninhabited(ty) } - } + TyKind::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(*ty) }, TyKind::Ref(..) => ConstructorSet::Ref, TyKind::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. @@ -492,14 +513,14 @@ impl PatCx for MatchCheckCtx<'_> { fn report_mixed_deref_pat_ctors( &self, - _deref_pat: &DeconstructedPat<'_>, - _normal_pat: &DeconstructedPat<'_>, + _deref_pat: &DeconstructedPat<'a, 'db>, + _normal_pat: &DeconstructedPat<'a, 'db>, ) { // FIXME(deref_patterns): This could report an error comparable to the one in rustc. } } -impl fmt::Debug for MatchCheckCtx<'_> { +impl fmt::Debug for MatchCheckCtx<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MatchCheckCtx").finish() } |