1use std::cell::Cell;
2use std::fmt;
3use std::iter::once;
4
5use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
6use rustc_arena::DroplessArena;
7use rustc_hir::HirId;
8use rustc_hir::def_id::DefId;
9use rustc_index::{Idx, IndexVec};
10use rustc_middle::middle::stability::EvalResult;
11use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
12use rustc_middle::ty::layout::IntegerExt;
13use rustc_middle::ty::{
14 self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
15};
16use rustc_middle::{bug, span_bug};
17use rustc_session::lint;
18use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
19
20use crate::constructor::Constructor::*;
21use crate::constructor::{
22 IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
23};
24use crate::lints::lint_nonexhaustive_missing_variants;
25use crate::pat_column::PatternColumn;
26use crate::rustc::print::EnumInfo;
27use crate::usefulness::{PlaceValidity, compute_match_usefulness};
28use crate::{PatCx, PrivateUninhabitedField, errors};
29
30mod print;
31
32pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p, 'tcx>>;
34pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>;
35pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>;
36pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>;
37pub type RedundancyExplanation<'p, 'tcx> =
38 crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>;
39pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>;
40pub type UsefulnessReport<'p, 'tcx> =
41 crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>;
42pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
43
44#[repr(transparent)]
50#[derive(Clone, Copy, PartialEq, Eq, Hash)]
51pub struct RevealedTy<'tcx>(Ty<'tcx>);
52
53impl<'tcx> fmt::Display for RevealedTy<'tcx> {
54 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
55 self.0.fmt(fmt)
56 }
57}
58
59impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
60 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
61 self.0.fmt(fmt)
62 }
63}
64
65impl<'tcx> std::ops::Deref for RevealedTy<'tcx> {
66 type Target = Ty<'tcx>;
67 fn deref(&self) -> &Self::Target {
68 &self.0
69 }
70}
71
72impl<'tcx> RevealedTy<'tcx> {
73 pub fn inner(self) -> Ty<'tcx> {
74 self.0
75 }
76}
77
78#[derive(Clone)]
79pub struct RustcPatCtxt<'p, 'tcx: 'p> {
80 pub tcx: TyCtxt<'tcx>,
81 pub typeck_results: &'tcx ty::TypeckResults<'tcx>,
82 pub module: DefId,
88 pub typing_env: ty::TypingEnv<'tcx>,
89 pub dropless_arena: &'p DroplessArena,
91 pub match_lint_level: HirId,
93 pub whole_match_span: Option<Span>,
95 pub scrut_span: Span,
97 pub refutable: bool,
99 pub known_valid_scrutinee: bool,
102 pub internal_state: RustcPatCtxtState,
103}
104
105#[derive(Clone, Default)]
107pub struct RustcPatCtxtState {
108 has_lowered_deref_pat: Cell<bool>,
112}
113
114impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 f.debug_struct("RustcPatCtxt").finish()
117 }
118}
119
120impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
121 #[inline]
127 pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
128 fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
129 let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!() };
130 if let Some(local_def_id) = alias_ty.def_id.as_local() {
131 let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
132 if let Some(ty) = cx.reveal_opaque_key(key) {
133 return RevealedTy(ty);
134 }
135 }
136 RevealedTy(ty)
137 }
138 if let ty::Alias(ty::Opaque, _) = ty.kind() {
139 reveal_inner(self, ty)
140 } else {
141 RevealedTy(ty)
142 }
143 }
144
145 fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
148 self.typeck_results
149 .concrete_opaque_types
150 .get(&key.def_id)
151 .map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
152 }
153 pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
155 !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
156 self.tcx,
157 self.typing_env,
158 self.module,
159 &|key| self.reveal_opaque_key(key),
160 )
161 }
162
163 pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
165 match ty.kind() {
166 ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
167 _ => false,
168 }
169 }
170
171 pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
174 ty.is_ptr_sized_integral() && {
175 let lo = self.hoist_pat_range_bdy(range.lo, ty);
180 matches!(lo, PatRangeBoundary::PosInfinity)
181 || matches!(range.hi, MaybeInfiniteInt::Finite(0))
182 }
183 }
184
185 pub(crate) fn variant_sub_tys(
186 &self,
187 ty: RevealedTy<'tcx>,
188 variant: &'tcx VariantDef,
189 ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> {
190 let ty::Adt(_, args) = ty.kind() else { bug!() };
191 variant.fields.iter().map(move |field| {
192 let ty = field.ty(self.tcx, args);
193 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
195 let ty = self.reveal_opaque_ty(ty);
196 (field, ty)
197 })
198 }
199
200 pub(crate) fn variant_index_for_adt(
201 ctor: &Constructor<'p, 'tcx>,
202 adt: ty::AdtDef<'tcx>,
203 ) -> VariantIdx {
204 match *ctor {
205 Variant(idx) => idx,
206 Struct | UnionField => {
207 assert!(!adt.is_enum());
208 FIRST_VARIANT
209 }
210 _ => bug!("bad constructor {:?} for adt {:?}", ctor, adt),
211 }
212 }
213
214 pub(crate) fn ctor_sub_tys(
217 &self,
218 ctor: &Constructor<'p, 'tcx>,
219 ty: RevealedTy<'tcx>,
220 ) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)> + ExactSizeIterator {
221 fn reveal_and_alloc<'a, 'tcx>(
222 cx: &'a RustcPatCtxt<'_, 'tcx>,
223 iter: impl Iterator<Item = Ty<'tcx>>,
224 ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
225 cx.dropless_arena.alloc_from_iter(
226 iter.map(|ty| cx.reveal_opaque_ty(ty))
227 .map(|ty| (ty, PrivateUninhabitedField(false))),
228 )
229 }
230 let cx = self;
231 let slice = match ctor {
232 Struct | Variant(_) | UnionField => match ty.kind() {
233 ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
234 ty::Adt(adt, _) => {
235 let variant = &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
236 let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
237 let is_visible =
238 adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
239 let is_uninhabited = cx.is_uninhabited(*ty);
240 let skip = is_uninhabited && !is_visible;
241 (ty, PrivateUninhabitedField(skip))
242 });
243 cx.dropless_arena.alloc_from_iter(tys)
244 }
245 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
246 },
247 Ref => match ty.kind() {
248 ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
249 _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
250 },
251 Slice(slice) => match ty.builtin_index() {
252 Some(ty) => {
253 let arity = slice.arity();
254 reveal_and_alloc(cx, (0..arity).map(|_| ty))
255 }
256 None => bug!("bad slice pattern {:?} {:?}", ctor, ty),
257 },
258 DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
259 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
260 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
261 | PrivateUninhabited | Wildcard => &[],
262 Or => {
263 bug!("called `Fields::wildcards` on an `Or` ctor")
264 }
265 };
266 slice.iter().copied()
267 }
268
269 pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
271 match ctor {
272 Struct | Variant(_) | UnionField => match ty.kind() {
273 ty::Tuple(fs) => fs.len(),
274 ty::Adt(adt, ..) => {
275 let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
276 adt.variant(variant_idx).fields.len()
277 }
278 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
279 },
280 Ref | DerefPattern(_) => 1,
281 Slice(slice) => slice.arity(),
282 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
283 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
284 | PrivateUninhabited | Wildcard => 0,
285 Or => bug!("The `Or` constructor doesn't have a fixed arity"),
286 }
287 }
288
289 pub fn ctors_for_ty(
293 &self,
294 ty: RevealedTy<'tcx>,
295 ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
296 let cx = self;
297 let make_uint_range = |start, end| {
298 IntRange::from_range(
299 MaybeInfiniteInt::new_finite_uint(start),
300 MaybeInfiniteInt::new_finite_uint(end),
301 RangeEnd::Included,
302 )
303 };
304 ty.error_reported()?;
306 Ok(match ty.kind() {
309 ty::Bool => ConstructorSet::Bool,
310 ty::Char => {
311 ConstructorSet::Integers {
313 range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128),
314 range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
315 }
316 }
317 &ty::Int(ity) => {
318 let range = if ty.is_ptr_sized_integral() {
319 IntRange {
321 lo: MaybeInfiniteInt::NegInfinity,
322 hi: MaybeInfiniteInt::PosInfinity,
323 }
324 } else {
325 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
326 let min = 1u128 << (size - 1);
327 let max = min - 1;
328 let min = MaybeInfiniteInt::new_finite_int(min, size);
329 let max = MaybeInfiniteInt::new_finite_int(max, size);
330 IntRange::from_range(min, max, RangeEnd::Included)
331 };
332 ConstructorSet::Integers { range_1: range, range_2: None }
333 }
334 &ty::Uint(uty) => {
335 let range = if ty.is_ptr_sized_integral() {
336 let lo = MaybeInfiniteInt::new_finite_uint(0);
338 IntRange { lo, hi: MaybeInfiniteInt::PosInfinity }
339 } else {
340 let size = Integer::from_uint_ty(&cx.tcx, uty).size();
341 let max = size.truncate(u128::MAX);
342 make_uint_range(0, max)
343 };
344 ConstructorSet::Integers { range_1: range, range_2: None }
345 }
346 ty::Slice(sub_ty) => ConstructorSet::Slice {
347 array_len: None,
348 subtype_is_empty: cx.is_uninhabited(*sub_ty),
349 },
350 ty::Array(sub_ty, len) => {
351 ConstructorSet::Slice {
353 array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize),
354 subtype_is_empty: cx.is_uninhabited(*sub_ty),
355 }
356 }
357 ty::Adt(def, args) if def.is_enum() => {
358 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
359 if def.variants().is_empty() && !is_declared_nonexhaustive {
360 ConstructorSet::NoConstructors
361 } else {
362 let mut variants =
363 IndexVec::from_elem(VariantVisibility::Visible, def.variants());
364 for (idx, v) in def.variants().iter_enumerated() {
365 let variant_def_id = def.variant(idx).def_id;
366 let is_inhabited = v
368 .inhabited_predicate(cx.tcx, *def)
369 .instantiate(cx.tcx, args)
370 .apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
371 cx.reveal_opaque_key(key)
372 });
373 let is_unstable = matches!(
375 cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
376 EvalResult::Deny { .. }
377 );
378 let is_doc_hidden =
380 cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
381 let visibility = if !is_inhabited {
382 VariantVisibility::Empty
384 } else if is_unstable || is_doc_hidden {
385 VariantVisibility::Hidden
386 } else {
387 VariantVisibility::Visible
388 };
389 variants[idx] = visibility;
390 }
391
392 ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
393 }
394 }
395 ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
396 ty::Adt(..) | ty::Tuple(..) => {
397 ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
398 }
399 ty::Ref(..) => ConstructorSet::Ref,
400 ty::Never => ConstructorSet::NoConstructors,
401 ty::Float(_)
404 | ty::Str
405 | ty::Foreign(_)
406 | ty::RawPtr(_, _)
407 | ty::FnDef(_, _)
408 | ty::FnPtr(..)
409 | ty::Pat(_, _)
410 | ty::Dynamic(_, _, _)
411 | ty::Closure(..)
412 | ty::CoroutineClosure(..)
413 | ty::Coroutine(_, _)
414 | ty::UnsafeBinder(_)
415 | ty::Alias(_, _)
416 | ty::Param(_)
417 | ty::Error(_) => ConstructorSet::Unlistable,
418 ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
419 bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
420 }
421 })
422 }
423
424 pub(crate) fn lower_pat_range_bdy(
425 &self,
426 bdy: PatRangeBoundary<'tcx>,
427 ty: RevealedTy<'tcx>,
428 ) -> MaybeInfiniteInt {
429 match bdy {
430 PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
431 PatRangeBoundary::Finite(value) => {
432 let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked();
433 match *ty.kind() {
434 ty::Int(ity) => {
435 let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
436 MaybeInfiniteInt::new_finite_int(bits, size)
437 }
438 _ => MaybeInfiniteInt::new_finite_uint(bits),
439 }
440 }
441 PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity,
442 }
443 }
444
445 pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
448 let cx = self;
449 let ty = cx.reveal_opaque_ty(pat.ty);
450 let ctor;
451 let arity;
452 let fields: Vec<_>;
453 match &pat.kind {
454 PatKind::AscribeUserType { subpattern, .. }
455 | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern),
456 PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
457 PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
458 ctor = Wildcard;
459 fields = vec![];
460 arity = 0;
461 }
462 PatKind::Deref { subpattern } => {
463 fields = vec![self.lower_pat(subpattern).at_index(0)];
464 arity = 1;
465 ctor = match ty.kind() {
466 ty::Ref(..) => Ref,
467 _ => span_bug!(
468 pat.span,
469 "pattern has unexpected type: pat: {:?}, ty: {:?}",
470 pat.kind,
471 ty.inner()
472 ),
473 };
474 }
475 PatKind::DerefPattern { subpattern, .. } => {
476 fields = vec![self.lower_pat(subpattern).at_index(0)];
482 arity = 1;
483 ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
484 self.internal_state.has_lowered_deref_pat.set(true);
485 }
486 PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
487 match ty.kind() {
488 ty::Tuple(fs) => {
489 ctor = Struct;
490 arity = fs.len();
491 fields = subpatterns
492 .iter()
493 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
494 .collect();
495 }
496 ty::Adt(adt, _) => {
497 ctor = match pat.kind {
498 PatKind::Leaf { .. } if adt.is_union() => UnionField,
499 PatKind::Leaf { .. } => Struct,
500 PatKind::Variant { variant_index, .. } => Variant(variant_index),
501 _ => bug!(),
502 };
503 let variant =
504 &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
505 arity = variant.fields.len();
506 fields = subpatterns
507 .iter()
508 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
509 .collect();
510 }
511 _ => span_bug!(
512 pat.span,
513 "pattern has unexpected type: pat: {:?}, ty: {}",
514 pat.kind,
515 ty.inner()
516 ),
517 }
518 }
519 PatKind::Constant { value } => {
520 match ty.kind() {
521 ty::Bool => {
522 ctor = Bool(value.try_to_bool().unwrap());
523 fields = vec![];
524 arity = 0;
525 }
526 ty::Char | ty::Int(_) | ty::Uint(_) => {
527 ctor = {
528 let bits = value.valtree.unwrap_leaf().to_bits_unchecked();
529 let x = match *ty.kind() {
530 ty::Int(ity) => {
531 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
532 MaybeInfiniteInt::new_finite_int(bits, size)
533 }
534 _ => MaybeInfiniteInt::new_finite_uint(bits),
535 };
536 IntRange(IntRange::from_singleton(x))
537 };
538 fields = vec![];
539 arity = 0;
540 }
541 ty::Float(ty::FloatTy::F16) => {
542 use rustc_apfloat::Float;
543 let bits = value.valtree.unwrap_leaf().to_u16();
544 let value = rustc_apfloat::ieee::Half::from_bits(bits.into());
545 ctor = F16Range(value, value, RangeEnd::Included);
546 fields = vec![];
547 arity = 0;
548 }
549 ty::Float(ty::FloatTy::F32) => {
550 use rustc_apfloat::Float;
551 let bits = value.valtree.unwrap_leaf().to_u32();
552 let value = rustc_apfloat::ieee::Single::from_bits(bits.into());
553 ctor = F32Range(value, value, RangeEnd::Included);
554 fields = vec![];
555 arity = 0;
556 }
557 ty::Float(ty::FloatTy::F64) => {
558 use rustc_apfloat::Float;
559 let bits = value.valtree.unwrap_leaf().to_u64();
560 let value = rustc_apfloat::ieee::Double::from_bits(bits.into());
561 ctor = F64Range(value, value, RangeEnd::Included);
562 fields = vec![];
563 arity = 0;
564 }
565 ty::Float(ty::FloatTy::F128) => {
566 use rustc_apfloat::Float;
567 let bits = value.valtree.unwrap_leaf().to_u128();
568 let value = rustc_apfloat::ieee::Quad::from_bits(bits);
569 ctor = F128Range(value, value, RangeEnd::Included);
570 fields = vec![];
571 arity = 0;
572 }
573 ty::Ref(_, t, _) if t.is_str() => {
574 let ty = self.reveal_opaque_ty(*t);
582 let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
583 ctor = Ref;
584 fields = vec![subpattern.at_index(0)];
585 arity = 1;
586 }
587 _ => {
591 ctor = Opaque(OpaqueId::new());
592 fields = vec![];
593 arity = 0;
594 }
595 }
596 }
597 PatKind::Range(patrange) => {
598 let PatRange { lo, hi, end, .. } = patrange.as_ref();
599 let end = match end {
600 rustc_hir::RangeEnd::Included => RangeEnd::Included,
601 rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
602 };
603 ctor = match ty.kind() {
604 ty::Char | ty::Int(_) | ty::Uint(_) => {
605 let lo = cx.lower_pat_range_bdy(*lo, ty);
606 let hi = cx.lower_pat_range_bdy(*hi, ty);
607 IntRange(IntRange::from_range(lo, hi, end))
608 }
609 ty::Float(fty) => {
610 use rustc_apfloat::Float;
611 let lo = lo
612 .as_finite()
613 .map(|c| c.try_to_scalar_int().unwrap().to_bits_unchecked());
614 let hi = hi
615 .as_finite()
616 .map(|c| c.try_to_scalar_int().unwrap().to_bits_unchecked());
617 match fty {
618 ty::FloatTy::F16 => {
619 use rustc_apfloat::ieee::Half;
620 let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
621 let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
622 F16Range(lo, hi, end)
623 }
624 ty::FloatTy::F32 => {
625 use rustc_apfloat::ieee::Single;
626 let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
627 let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
628 F32Range(lo, hi, end)
629 }
630 ty::FloatTy::F64 => {
631 use rustc_apfloat::ieee::Double;
632 let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
633 let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
634 F64Range(lo, hi, end)
635 }
636 ty::FloatTy::F128 => {
637 use rustc_apfloat::ieee::Quad;
638 let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
639 let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
640 F128Range(lo, hi, end)
641 }
642 }
643 }
644 _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()),
645 };
646 fields = vec![];
647 arity = 0;
648 }
649 PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
650 let array_len = match ty.kind() {
651 ty::Array(_, length) => Some(
652 length
653 .try_to_target_usize(cx.tcx)
654 .expect("expected len of array pat to be definite")
655 as usize,
656 ),
657 ty::Slice(_) => None,
658 _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()),
659 };
660 let kind = if slice.is_some() {
661 SliceKind::VarLen(prefix.len(), suffix.len())
662 } else {
663 SliceKind::FixedLen(prefix.len() + suffix.len())
664 };
665 ctor = Slice(Slice::new(array_len, kind));
666 fields = prefix
667 .iter()
668 .chain(suffix.iter())
669 .map(|p| self.lower_pat(&*p))
670 .enumerate()
671 .map(|(i, p)| p.at_index(i))
672 .collect();
673 arity = kind.arity();
674 }
675 PatKind::Or { .. } => {
676 ctor = Or;
677 let pats = expand_or_pat(pat);
678 fields = pats
679 .into_iter()
680 .map(|p| self.lower_pat(p))
681 .enumerate()
682 .map(|(i, p)| p.at_index(i))
683 .collect();
684 arity = fields.len();
685 }
686 PatKind::Never => {
687 ctor = Wildcard;
691 fields = vec![];
692 arity = 0;
693 }
694 PatKind::Error(_) => {
695 ctor = Opaque(OpaqueId::new());
696 fields = vec![];
697 arity = 0;
698 }
699 }
700 DeconstructedPat::new(ctor, fields, arity, ty, pat)
701 }
702
703 fn hoist_pat_range_bdy(
708 &self,
709 miint: MaybeInfiniteInt,
710 ty: RevealedTy<'tcx>,
711 ) -> PatRangeBoundary<'tcx> {
712 use MaybeInfiniteInt::*;
713 let tcx = self.tcx;
714 match miint {
715 NegInfinity => PatRangeBoundary::NegInfinity,
716 Finite(_) => {
717 let size = ty.primitive_size(tcx);
718 let bits = match *ty.kind() {
719 ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(),
720 _ => miint.as_finite_uint().unwrap(),
721 };
722 match ScalarInt::try_from_uint(bits, size) {
723 Some(scalar) => {
724 let valtree = ty::ValTree::from_scalar_int(tcx, scalar);
725 PatRangeBoundary::Finite(valtree)
726 }
727 None => PatRangeBoundary::PosInfinity,
731 }
732 }
733 PosInfinity => PatRangeBoundary::PosInfinity,
734 }
735 }
736
737 fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
739 use MaybeInfiniteInt::*;
740 let cx = self;
741 if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
742 "_".to_string()
743 } else if range.is_singleton() {
744 let lo = cx.hoist_pat_range_bdy(range.lo, ty);
745 let value = ty::Value { ty: ty.inner(), valtree: lo.as_finite().unwrap() };
746 value.to_string()
747 } else {
748 let mut end = rustc_hir::RangeEnd::Included;
750 let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
751 if matches!(lo, PatRangeBoundary::PosInfinity) {
752 let max = ty.numeric_max_val(cx.tcx).unwrap();
758 let max = ty::ValTree::from_scalar_int(cx.tcx, max.try_to_scalar_int().unwrap());
759 lo = PatRangeBoundary::Finite(max);
760 }
761 let hi = if let Some(hi) = range.hi.minus_one() {
762 hi
763 } else {
764 end = rustc_hir::RangeEnd::Excluded;
766 range.hi
767 };
768 let hi = cx.hoist_pat_range_bdy(hi, ty);
769 PatRange { lo, hi, end, ty: ty.inner() }.to_string()
770 }
771 }
772
773 pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
777 let cx = self;
778 let print = |p| cx.print_witness_pat(p);
779 match pat.ctor() {
780 Bool(b) => b.to_string(),
781 Str(s) => s.to_string(),
782 IntRange(range) => return self.print_pat_range(range, *pat.ty()),
783 Struct | Variant(_) | UnionField => {
784 let enum_info = match *pat.ty().kind() {
785 ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
786 adt_def,
787 variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
788 },
789 ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
790 _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
791 };
792
793 let subpatterns = pat
794 .iter_fields()
795 .enumerate()
796 .map(|(i, pat)| print::FieldPat {
797 field: FieldIdx::new(i),
798 pattern: print(pat),
799 is_wildcard: would_print_as_wildcard(cx.tcx, pat),
800 })
801 .collect::<Vec<_>>();
802
803 let mut s = String::new();
804 print::write_struct_like(
805 &mut s,
806 self.tcx,
807 pat.ty().inner(),
808 &enum_info,
809 &subpatterns,
810 )
811 .unwrap();
812 s
813 }
814 Ref => {
815 let mut s = String::new();
816 print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
817 s
818 }
819 DerefPattern(_) if pat.ty().is_box() && !self.tcx.features().deref_patterns() => {
820 format!("box {}", print(&pat.fields[0]))
826 }
827 DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
828 Slice(slice) => {
829 let (prefix_len, has_dot_dot) = match slice.kind {
830 SliceKind::FixedLen(len) => (len, false),
831 SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
832 };
833
834 let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
835
836 if has_dot_dot && slice.array_len.is_some() {
842 while let [rest @ .., last] = prefix
843 && would_print_as_wildcard(cx.tcx, last)
844 {
845 prefix = rest;
846 }
847 while let [first, rest @ ..] = suffix
848 && would_print_as_wildcard(cx.tcx, first)
849 {
850 suffix = rest;
851 }
852 }
853
854 let prefix = prefix.iter().map(print).collect::<Vec<_>>();
855 let suffix = suffix.iter().map(print).collect::<Vec<_>>();
856
857 let mut s = String::new();
858 print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
859 s
860 }
861 Never if self.tcx.features().never_patterns() => "!".to_string(),
862 Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
863 Missing { .. } => bug!(
864 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
865 `Missing` should have been processed in `apply_constructors`"
866 ),
867 F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
868 bug!("can't convert to pattern: {:?}", pat)
869 }
870 }
871 }
872}
873
874fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
876 match p.ctor() {
877 Constructor::IntRange(IntRange {
878 lo: MaybeInfiniteInt::NegInfinity,
879 hi: MaybeInfiniteInt::PosInfinity,
880 })
881 | Constructor::Wildcard
882 | Constructor::NonExhaustive
883 | Constructor::Hidden
884 | Constructor::PrivateUninhabited => true,
885 Constructor::Never if !tcx.features().never_patterns() => true,
886 _ => false,
887 }
888}
889
890impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
891 type Ty = RevealedTy<'tcx>;
892 type Error = ErrorGuaranteed;
893 type VariantIdx = VariantIdx;
894 type StrLit = ty::Value<'tcx>;
895 type ArmData = HirId;
896 type PatData = &'p Pat<'tcx>;
897
898 fn is_exhaustive_patterns_feature_on(&self) -> bool {
899 self.tcx.features().exhaustive_patterns()
900 }
901
902 fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
903 self.ctor_arity(ctor, *ty)
904 }
905 fn ctor_sub_tys(
906 &self,
907 ctor: &crate::constructor::Constructor<Self>,
908 ty: &Self::Ty,
909 ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator {
910 self.ctor_sub_tys(ctor, *ty)
911 }
912 fn ctors_for_ty(
913 &self,
914 ty: &Self::Ty,
915 ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
916 self.ctors_for_ty(*ty)
917 }
918
919 fn write_variant_name(
920 f: &mut fmt::Formatter<'_>,
921 ctor: &crate::constructor::Constructor<Self>,
922 ty: &Self::Ty,
923 ) -> fmt::Result {
924 if let ty::Adt(adt, _) = ty.kind() {
925 let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
926 write!(f, "{}", variant.name)?;
927 }
928 Ok(())
929 }
930
931 fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error {
932 span_bug!(self.scrut_span, "{}", fmt)
933 }
934
935 fn lint_overlapping_range_endpoints(
936 &self,
937 pat: &crate::pat::DeconstructedPat<Self>,
938 overlaps_on: IntRange,
939 overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
940 ) {
941 let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
942 let overlaps: Vec<_> = overlaps_with
943 .iter()
944 .map(|pat| pat.data().span)
945 .map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
946 .collect();
947 let pat_span = pat.data().span;
948 self.tcx.emit_node_span_lint(
949 lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
950 self.match_lint_level,
951 pat_span,
952 errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
953 );
954 }
955
956 fn complexity_exceeded(&self) -> Result<(), Self::Error> {
957 let span = self.whole_match_span.unwrap_or(self.scrut_span);
958 Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
959 }
960
961 fn lint_non_contiguous_range_endpoints(
962 &self,
963 pat: &crate::pat::DeconstructedPat<Self>,
964 gap: IntRange,
965 gapped_with: &[&crate::pat::DeconstructedPat<Self>],
966 ) {
967 let &thir_pat = pat.data();
968 let thir::PatKind::Range(range) = &thir_pat.kind else { return };
969 if range.end != rustc_hir::RangeEnd::Excluded {
971 return;
972 }
973 let suggested_range: String = {
976 let mut suggested_range = PatRange::clone(range);
978 suggested_range.end = rustc_hir::RangeEnd::Included;
979 suggested_range.to_string()
980 };
981 let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
982 if gapped_with.is_empty() {
983 self.tcx.emit_node_span_lint(
985 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
986 self.match_lint_level,
987 thir_pat.span,
988 errors::ExclusiveRangeMissingMax {
989 first_range: thir_pat.span,
991 max: gap_as_pat,
993 suggestion: suggested_range,
995 },
996 );
997 } else {
998 self.tcx.emit_node_span_lint(
999 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1000 self.match_lint_level,
1001 thir_pat.span,
1002 errors::ExclusiveRangeMissingGap {
1003 first_range: thir_pat.span,
1005 gap: gap_as_pat.to_string(),
1007 suggestion: suggested_range,
1009 gap_with: gapped_with
1012 .iter()
1013 .map(|pat| errors::GappedRange {
1014 span: pat.data().span,
1015 gap: gap_as_pat.to_string(),
1016 first_range: range.to_string(),
1017 })
1018 .collect(),
1019 },
1020 );
1021 }
1022 }
1023
1024 fn match_may_contain_deref_pats(&self) -> bool {
1025 self.internal_state.has_lowered_deref_pat.get()
1026 }
1027
1028 fn report_mixed_deref_pat_ctors(
1029 &self,
1030 deref_pat: &crate::pat::DeconstructedPat<Self>,
1031 normal_pat: &crate::pat::DeconstructedPat<Self>,
1032 ) -> Self::Error {
1033 let deref_pattern_label = deref_pat.data().span;
1034 let normal_constructor_label = normal_pat.data().span;
1035 self.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors {
1036 spans: vec![deref_pattern_label, normal_constructor_label],
1037 smart_pointer_ty: deref_pat.ty().inner(),
1038 deref_pattern_label,
1039 normal_constructor_label,
1040 })
1041 }
1042}
1043
1044fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
1046 fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
1047 if let PatKind::Or { pats } = &pat.kind {
1048 for pat in pats.iter() {
1049 expand(pat, vec);
1050 }
1051 } else {
1052 vec.push(pat)
1053 }
1054 }
1055
1056 let mut pats = Vec::new();
1057 expand(pat, &mut pats);
1058 pats
1059}
1060
1061pub fn analyze_match<'p, 'tcx>(
1064 tycx: &RustcPatCtxt<'p, 'tcx>,
1065 arms: &[MatchArm<'p, 'tcx>],
1066 scrut_ty: Ty<'tcx>,
1067) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
1068 let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
1069
1070 let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
1071 let report = compute_match_usefulness(
1072 tycx,
1073 arms,
1074 scrut_ty,
1075 scrut_validity,
1076 tycx.tcx.pattern_complexity_limit().0,
1077 )?;
1078
1079 if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
1082 let pat_column = PatternColumn::new(arms);
1083 lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
1084 }
1085
1086 Ok(report)
1087}