1use std::cell::LazyCell;
4use std::fmt;
5
6use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
7use intern::sym;
8use rustc_pattern_analysis::{
9 Captures, IndexVec, PatCx, PrivateUninhabitedField,
10 constructor::{Constructor, ConstructorSet, VariantVisibility},
11 usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness},
12};
13use smallvec::{SmallVec, smallvec};
14use stdx::never;
15
16use crate::{
17 AdtId, Interner, Scalar, Ty, TyExt, TyKind,
18 db::HirDatabase,
19 infer::normalize,
20 inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
21};
22
23use super::{FieldPat, Pat, PatKind, is_box};
24
25use Constructor::*;
26
27pub(crate) type DeconstructedPat<'db> =
29 rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'db>>;
30pub(crate) type MatchArm<'db> = rustc_pattern_analysis::MatchArm<'db, MatchCheckCtx<'db>>;
31pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'db>>;
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36pub(crate) enum Void {}
37
38#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
41pub(crate) struct EnumVariantContiguousIndex(usize);
42
43impl EnumVariantContiguousIndex {
44 fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self {
45 use hir_def::Lookup;
47 let i = target_evid.lookup(db.upcast()).index as usize;
48 EnumVariantContiguousIndex(i)
49 }
50
51 fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
52 db.enum_variants(eid).variants[self.0].0
53 }
54}
55
56impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex {
57 fn new(idx: usize) -> Self {
58 EnumVariantContiguousIndex(idx)
59 }
60
61 fn index(self) -> usize {
62 self.0
63 }
64}
65
66#[derive(Clone)]
67pub(crate) struct MatchCheckCtx<'db> {
68 module: ModuleId,
69 body: DefWithBodyId,
70 pub(crate) db: &'db dyn HirDatabase,
71 exhaustive_patterns: bool,
72}
73
74impl<'db> MatchCheckCtx<'db> {
75 pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
76 let def_map = db.crate_def_map(module.krate());
77 let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
78 Self { module, body, db, exhaustive_patterns }
79 }
80
81 pub(crate) fn compute_match_usefulness(
82 &self,
83 arms: &[MatchArm<'db>],
84 scrut_ty: Ty,
85 known_valid_scrutinee: Option<bool>,
86 ) -> Result<UsefulnessReport<'db, Self>, ()> {
87 if scrut_ty.contains_unknown() {
88 return Err(());
89 }
90 for arm in arms {
91 if arm.pat.ty().contains_unknown() {
92 return Err(());
93 }
94 }
95
96 let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
97 let complexity_limit = 500000;
99 compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
100 }
101
102 fn is_uninhabited(&self, ty: &Ty) -> bool {
103 is_ty_uninhabited_from(self.db, ty, self.module)
104 }
105
106 fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
108 let is_local = adt.krate(self.db.upcast()) == self.module.krate();
109 !is_local && self.db.attrs(adt.into()).by_key(&sym::non_exhaustive).exists()
110 }
111
112 fn variant_id_for_adt(
113 db: &'db dyn HirDatabase,
114 ctor: &Constructor<Self>,
115 adt: hir_def::AdtId,
116 ) -> Option<VariantId> {
117 match ctor {
118 Variant(id) => {
119 let hir_def::AdtId::EnumId(eid) = adt else {
120 panic!("bad constructor {ctor:?} for adt {adt:?}")
121 };
122 Some(id.to_enum_variant_id(db, eid).into())
123 }
124 Struct | UnionField => match adt {
125 hir_def::AdtId::EnumId(_) => None,
126 hir_def::AdtId::StructId(id) => Some(id.into()),
127 hir_def::AdtId::UnionId(id) => Some(id.into()),
128 },
129 _ => panic!("bad constructor {ctor:?} for adt {adt:?}"),
130 }
131 }
132
133 fn list_variant_fields<'a>(
135 &'a self,
136 ty: &'a Ty,
137 variant: VariantId,
138 ) -> impl Iterator<Item = (LocalFieldId, Ty)> + Captures<'a> + Captures<'db> {
139 let (_, substs) = ty.as_adt().unwrap();
140
141 let field_tys = self.db.field_types(variant);
142 let fields_len = variant.variant_data(self.db.upcast()).fields().len() as u32;
143
144 (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| {
145 let ty = field_tys[fid].clone().substitute(Interner, substs);
146 let ty = normalize(self.db, self.db.trait_environment_for_body(self.body), ty);
147 (fid, ty)
148 })
149 }
150
151 pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'db> {
152 let singleton = |pat: DeconstructedPat<'db>| vec![pat.at_index(0)];
153 let ctor;
154 let mut fields: Vec<_>;
155 let arity;
156
157 match pat.kind.as_ref() {
158 PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
159 PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
160 ctor = Wildcard;
161 fields = Vec::new();
162 arity = 0;
163 }
164 PatKind::Deref { subpattern } => {
165 ctor = match pat.ty.kind(Interner) {
166 TyKind::Adt(adt, _) if is_box(self.db, adt.0) => Struct,
168 TyKind::Ref(..) => Ref,
169 _ => {
170 never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
171 Wildcard
172 }
173 };
174 fields = singleton(self.lower_pat(subpattern));
175 arity = 1;
176 }
177 PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
178 fields = subpatterns
179 .iter()
180 .map(|pat| {
181 let idx: u32 = pat.field.into_raw().into();
182 self.lower_pat(&pat.pattern).at_index(idx as usize)
183 })
184 .collect();
185 match pat.ty.kind(Interner) {
186 TyKind::Tuple(_, substs) => {
187 ctor = Struct;
188 arity = substs.len(Interner);
189 }
190 TyKind::Adt(adt, _) if is_box(self.db, adt.0) => {
191 fields.retain(|ipat| ipat.idx == 0);
204 ctor = Struct;
205 arity = 1;
206 }
207 &TyKind::Adt(AdtId(adt), _) => {
208 ctor = match pat.kind.as_ref() {
209 PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
210 UnionField
211 }
212 PatKind::Leaf { .. } => Struct,
213 PatKind::Variant { enum_variant, .. } => {
214 Variant(EnumVariantContiguousIndex::from_enum_variant_id(
215 self.db,
216 *enum_variant,
217 ))
218 }
219 _ => {
220 never!();
221 Wildcard
222 }
223 };
224 let variant = Self::variant_id_for_adt(self.db, &ctor, adt).unwrap();
225 arity = variant.variant_data(self.db.upcast()).fields().len();
226 }
227 _ => {
228 never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
229 ctor = Wildcard;
230 fields.clear();
231 arity = 0;
232 }
233 }
234 }
235 &PatKind::LiteralBool { value } => {
236 ctor = Bool(value);
237 fields = Vec::new();
238 arity = 0;
239 }
240 PatKind::Never => {
241 ctor = Never;
242 fields = Vec::new();
243 arity = 0;
244 }
245 PatKind::Or { pats } => {
246 ctor = Or;
247 fields = pats
248 .iter()
249 .enumerate()
250 .map(|(i, pat)| self.lower_pat(pat).at_index(i))
251 .collect();
252 arity = pats.len();
253 }
254 }
255 DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), ())
256 }
257
258 pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat {
259 let mut subpatterns = pat.iter_fields().map(|p| self.hoist_witness_pat(p));
260 let kind = match pat.ctor() {
261 &Bool(value) => PatKind::LiteralBool { value },
262 IntRange(_) => unimplemented!(),
263 Struct | Variant(_) | UnionField => match pat.ty().kind(Interner) {
264 TyKind::Tuple(..) => PatKind::Leaf {
265 subpatterns: subpatterns
266 .zip(0u32..)
267 .map(|(p, i)| FieldPat {
268 field: LocalFieldId::from_raw(i.into()),
269 pattern: p,
270 })
271 .collect(),
272 },
273 TyKind::Adt(adt, _) if is_box(self.db, adt.0) => {
274 PatKind::Deref { subpattern: subpatterns.next().unwrap() }
278 }
279 TyKind::Adt(adt, substs) => {
280 let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap();
281 let subpatterns = self
282 .list_variant_fields(pat.ty(), variant)
283 .zip(subpatterns)
284 .map(|((field, _ty), pattern)| FieldPat { field, pattern })
285 .collect();
286
287 if let VariantId::EnumVariantId(enum_variant) = variant {
288 PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns }
289 } else {
290 PatKind::Leaf { subpatterns }
291 }
292 }
293 _ => {
294 never!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty());
295 PatKind::Wild
296 }
297 },
298 Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
303 Slice(_) => unimplemented!(),
304 &Str(void) => match void {},
305 Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
306 Never => PatKind::Never,
307 Missing | F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..)
308 | Or => {
309 never!("can't convert to pattern: {:?}", pat.ctor());
310 PatKind::Wild
311 }
312 };
313 Pat { ty: pat.ty().clone(), kind: Box::new(kind) }
314 }
315}
316
317impl PatCx for MatchCheckCtx<'_> {
318 type Error = ();
319 type Ty = Ty;
320 type VariantIdx = EnumVariantContiguousIndex;
321 type StrLit = Void;
322 type ArmData = ();
323 type PatData = ();
324
325 fn is_exhaustive_patterns_feature_on(&self) -> bool {
326 self.exhaustive_patterns
327 }
328
329 fn ctor_arity(
330 &self,
331 ctor: &rustc_pattern_analysis::constructor::Constructor<Self>,
332 ty: &Self::Ty,
333 ) -> usize {
334 match ctor {
335 Struct | Variant(_) | UnionField => match *ty.kind(Interner) {
336 TyKind::Tuple(arity, ..) => arity,
337 TyKind::Adt(AdtId(adt), ..) => {
338 if is_box(self.db, adt) {
339 1
342 } else {
343 let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
344 variant.variant_data(self.db.upcast()).fields().len()
345 }
346 }
347 _ => {
348 never!("Unexpected type for `Single` constructor: {:?}", ty);
349 0
350 }
351 },
352 Ref => 1,
353 Slice(..) => unimplemented!(),
354 Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
355 | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
356 | Hidden | Missing | Wildcard => 0,
357 Or => {
358 never!("The `Or` constructor doesn't have a fixed arity");
359 0
360 }
361 }
362 }
363
364 fn ctor_sub_tys(
365 &self,
366 ctor: &rustc_pattern_analysis::constructor::Constructor<Self>,
367 ty: &Self::Ty,
368 ) -> impl ExactSizeIterator<Item = (Self::Ty, PrivateUninhabitedField)> {
369 let single = |ty| smallvec![(ty, PrivateUninhabitedField(false))];
370 let tys: SmallVec<[_; 2]> = match ctor {
371 Struct | Variant(_) | UnionField => match ty.kind(Interner) {
372 TyKind::Tuple(_, substs) => {
373 let tys = substs.iter(Interner).map(|ty| ty.assert_ty_ref(Interner));
374 tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect()
375 }
376 TyKind::Ref(.., rty) => single(rty.clone()),
377 &TyKind::Adt(AdtId(adt), ref substs) => {
378 if is_box(self.db, adt) {
379 let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
382 single(subst_ty)
383 } else {
384 let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
385
386 let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
387
388 self.list_variant_fields(ty, variant)
389 .map(move |(fid, ty)| {
390 let is_visible = || {
391 matches!(adt, hir_def::AdtId::EnumId(..))
392 || visibilities[fid]
393 .is_visible_from(self.db.upcast(), self.module)
394 };
395 let is_uninhabited = self.is_uninhabited(&ty);
396 let private_uninhabited = is_uninhabited && !is_visible();
397 (ty, PrivateUninhabitedField(private_uninhabited))
398 })
399 .collect()
400 }
401 }
402 ty_kind => {
403 never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
404 single(ty.clone())
405 }
406 },
407 Ref => match ty.kind(Interner) {
408 TyKind::Ref(.., rty) => single(rty.clone()),
409 ty_kind => {
410 never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
411 single(ty.clone())
412 }
413 },
414 Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
415 Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
416 | F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
417 | Hidden | Missing | Wildcard => {
418 smallvec![]
419 }
420 Or => {
421 never!("called `Fields::wildcards` on an `Or` ctor");
422 smallvec![]
423 }
424 };
425 tys.into_iter()
426 }
427
428 fn ctors_for_ty(
429 &self,
430 ty: &Self::Ty,
431 ) -> Result<rustc_pattern_analysis::constructor::ConstructorSet<Self>, Self::Error> {
432 let cx = self;
433
434 let unhandled = || ConstructorSet::Unlistable;
437
438 Ok(match ty.kind(Interner) {
447 TyKind::Scalar(Scalar::Bool) => ConstructorSet::Bool,
448 TyKind::Scalar(Scalar::Char) => unhandled(),
449 TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
450 TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
451 &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
452 let enum_data = cx.db.enum_variants(enum_id);
453 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
454
455 if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
456 ConstructorSet::NoConstructors
457 } else {
458 let mut variants = IndexVec::with_capacity(enum_data.variants.len());
459 for &(variant, _) in enum_data.variants.iter() {
460 let is_uninhabited =
461 is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
462 let visibility = if is_uninhabited {
463 VariantVisibility::Empty
464 } else {
465 VariantVisibility::Visible
466 };
467 variants.push(visibility);
468 }
469
470 ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
471 }
472 }
473 TyKind::Adt(AdtId(hir_def::AdtId::UnionId(_)), _) => ConstructorSet::Union,
474 TyKind::Adt(..) | TyKind::Tuple(..) => {
475 ConstructorSet::Struct { empty: cx.is_uninhabited(ty) }
476 }
477 TyKind::Ref(..) => ConstructorSet::Ref,
478 TyKind::Never => ConstructorSet::NoConstructors,
479 _ => ConstructorSet::Unlistable,
481 })
482 }
483
484 fn write_variant_name(
485 f: &mut fmt::Formatter<'_>,
486 _ctor: &Constructor<Self>,
487 _ty: &Self::Ty,
488 ) -> fmt::Result {
489 write!(f, "<write_variant_name unsupported>")
490 }
508
509 fn bug(&self, fmt: fmt::Arguments<'_>) {
510 never!("{}", fmt)
511 }
512
513 fn complexity_exceeded(&self) -> Result<(), Self::Error> {
514 Err(())
515 }
516}
517
518impl fmt::Debug for MatchCheckCtx<'_> {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 f.debug_struct("MatchCheckCtx").finish()
521 }
522}