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