1use std::fmt;
2use std::iter::once;
3
4use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
5use rustc_arena::DroplessArena;
6use rustc_hir::HirId;
7use rustc_hir::def_id::DefId;
8use rustc_index::{Idx, IndexVec};
9use rustc_middle::middle::stability::EvalResult;
10use rustc_middle::mir::{self, Const};
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::{Captures, 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}
103
104impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.debug_struct("RustcPatCtxt").finish()
107 }
108}
109
110impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
111 #[inline]
117 pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
118 fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
119 let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!() };
120 if let Some(local_def_id) = alias_ty.def_id.as_local() {
121 let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
122 if let Some(ty) = cx.reveal_opaque_key(key) {
123 return RevealedTy(ty);
124 }
125 }
126 RevealedTy(ty)
127 }
128 if let ty::Alias(ty::Opaque, _) = ty.kind() {
129 reveal_inner(self, ty)
130 } else {
131 RevealedTy(ty)
132 }
133 }
134
135 fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
138 self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
139 }
140 pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
142 !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
143 self.tcx,
144 self.typing_env,
145 self.module,
146 &|key| self.reveal_opaque_key(key),
147 )
148 }
149
150 pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
152 match ty.kind() {
153 ty::Adt(def, ..) => {
154 def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
155 }
156 _ => false,
157 }
158 }
159
160 pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
163 ty.is_ptr_sized_integral() && {
164 let lo = self.hoist_pat_range_bdy(range.lo, ty);
169 matches!(lo, PatRangeBoundary::PosInfinity)
170 || matches!(range.hi, MaybeInfiniteInt::Finite(0))
171 }
172 }
173
174 pub(crate) fn variant_sub_tys(
175 &self,
176 ty: RevealedTy<'tcx>,
177 variant: &'tcx VariantDef,
178 ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_>
179 {
180 let ty::Adt(_, args) = ty.kind() else { bug!() };
181 variant.fields.iter().map(move |field| {
182 let ty = field.ty(self.tcx, args);
183 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
185 let ty = self.reveal_opaque_ty(ty);
186 (field, ty)
187 })
188 }
189
190 pub(crate) fn variant_index_for_adt(
191 ctor: &Constructor<'p, 'tcx>,
192 adt: ty::AdtDef<'tcx>,
193 ) -> VariantIdx {
194 match *ctor {
195 Variant(idx) => idx,
196 Struct | UnionField => {
197 assert!(!adt.is_enum());
198 FIRST_VARIANT
199 }
200 _ => bug!("bad constructor {:?} for adt {:?}", ctor, adt),
201 }
202 }
203
204 pub(crate) fn ctor_sub_tys<'a>(
207 &'a self,
208 ctor: &'a Constructor<'p, 'tcx>,
209 ty: RevealedTy<'tcx>,
210 ) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)>
211 + ExactSizeIterator
212 + Captures<'a> {
213 fn reveal_and_alloc<'a, 'tcx>(
214 cx: &'a RustcPatCtxt<'_, 'tcx>,
215 iter: impl Iterator<Item = Ty<'tcx>>,
216 ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
217 cx.dropless_arena.alloc_from_iter(
218 iter.map(|ty| cx.reveal_opaque_ty(ty))
219 .map(|ty| (ty, PrivateUninhabitedField(false))),
220 )
221 }
222 let cx = self;
223 let slice = match ctor {
224 Struct | Variant(_) | UnionField => match ty.kind() {
225 ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
226 ty::Adt(adt, args) => {
227 if adt.is_box() {
228 reveal_and_alloc(cx, once(args.type_at(0)))
231 } else {
232 let variant =
233 &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
234 let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
235 let is_visible =
236 adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
237 let is_uninhabited = cx.is_uninhabited(*ty);
238 let skip = is_uninhabited && !is_visible;
239 (ty, PrivateUninhabitedField(skip))
240 });
241 cx.dropless_arena.alloc_from_iter(tys)
242 }
243 }
244 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
245 },
246 Ref => match ty.kind() {
247 ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
248 _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
249 },
250 Slice(slice) => match *ty.kind() {
251 ty::Slice(ty) | ty::Array(ty, _) => {
252 let arity = slice.arity();
253 reveal_and_alloc(cx, (0..arity).map(|_| ty))
254 }
255 _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
256 },
257 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
258 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
259 | PrivateUninhabited | Wildcard => &[],
260 Or => {
261 bug!("called `Fields::wildcards` on an `Or` ctor")
262 }
263 };
264 slice.iter().copied()
265 }
266
267 pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
269 match ctor {
270 Struct | Variant(_) | UnionField => match ty.kind() {
271 ty::Tuple(fs) => fs.len(),
272 ty::Adt(adt, ..) => {
273 if adt.is_box() {
274 1
277 } else {
278 let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
279 adt.variant(variant_idx).fields.len()
280 }
281 }
282 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
283 },
284 Ref => 1,
285 Slice(slice) => slice.arity(),
286 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
287 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
288 | PrivateUninhabited | Wildcard => 0,
289 Or => bug!("The `Or` constructor doesn't have a fixed arity"),
290 }
291 }
292
293 pub fn ctors_for_ty(
297 &self,
298 ty: RevealedTy<'tcx>,
299 ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
300 let cx = self;
301 let make_uint_range = |start, end| {
302 IntRange::from_range(
303 MaybeInfiniteInt::new_finite_uint(start),
304 MaybeInfiniteInt::new_finite_uint(end),
305 RangeEnd::Included,
306 )
307 };
308 ty.error_reported()?;
310 Ok(match ty.kind() {
313 ty::Bool => ConstructorSet::Bool,
314 ty::Char => {
315 ConstructorSet::Integers {
317 range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128),
318 range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
319 }
320 }
321 &ty::Int(ity) => {
322 let range = if ty.is_ptr_sized_integral() {
323 IntRange {
325 lo: MaybeInfiniteInt::NegInfinity,
326 hi: MaybeInfiniteInt::PosInfinity,
327 }
328 } else {
329 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
330 let min = 1u128 << (size - 1);
331 let max = min - 1;
332 let min = MaybeInfiniteInt::new_finite_int(min, size);
333 let max = MaybeInfiniteInt::new_finite_int(max, size);
334 IntRange::from_range(min, max, RangeEnd::Included)
335 };
336 ConstructorSet::Integers { range_1: range, range_2: None }
337 }
338 &ty::Uint(uty) => {
339 let range = if ty.is_ptr_sized_integral() {
340 let lo = MaybeInfiniteInt::new_finite_uint(0);
342 IntRange { lo, hi: MaybeInfiniteInt::PosInfinity }
343 } else {
344 let size = Integer::from_uint_ty(&cx.tcx, uty).size();
345 let max = size.truncate(u128::MAX);
346 make_uint_range(0, max)
347 };
348 ConstructorSet::Integers { range_1: range, range_2: None }
349 }
350 ty::Slice(sub_ty) => ConstructorSet::Slice {
351 array_len: None,
352 subtype_is_empty: cx.is_uninhabited(*sub_ty),
353 },
354 ty::Array(sub_ty, len) => {
355 ConstructorSet::Slice {
357 array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize),
358 subtype_is_empty: cx.is_uninhabited(*sub_ty),
359 }
360 }
361 ty::Adt(def, args) if def.is_enum() => {
362 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
363 if def.variants().is_empty() && !is_declared_nonexhaustive {
364 ConstructorSet::NoConstructors
365 } else {
366 let mut variants =
367 IndexVec::from_elem(VariantVisibility::Visible, def.variants());
368 for (idx, v) in def.variants().iter_enumerated() {
369 let variant_def_id = def.variant(idx).def_id;
370 let is_inhabited = v
372 .inhabited_predicate(cx.tcx, *def)
373 .instantiate(cx.tcx, args)
374 .apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
375 cx.reveal_opaque_key(key)
376 });
377 let is_unstable = matches!(
379 cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
380 EvalResult::Deny { .. }
381 );
382 let is_doc_hidden =
384 cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
385 let visibility = if !is_inhabited {
386 VariantVisibility::Empty
388 } else if is_unstable || is_doc_hidden {
389 VariantVisibility::Hidden
390 } else {
391 VariantVisibility::Visible
392 };
393 variants[idx] = visibility;
394 }
395
396 ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
397 }
398 }
399 ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
400 ty::Adt(..) | ty::Tuple(..) => {
401 ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
402 }
403 ty::Ref(..) => ConstructorSet::Ref,
404 ty::Never => ConstructorSet::NoConstructors,
405 ty::Float(_)
408 | ty::Str
409 | ty::Foreign(_)
410 | ty::RawPtr(_, _)
411 | ty::FnDef(_, _)
412 | ty::FnPtr(..)
413 | ty::Pat(_, _)
414 | ty::Dynamic(_, _, _)
415 | ty::Closure(..)
416 | ty::CoroutineClosure(..)
417 | ty::Coroutine(_, _)
418 | ty::UnsafeBinder(_)
419 | ty::Alias(_, _)
420 | ty::Param(_)
421 | ty::Error(_) => ConstructorSet::Unlistable,
422 ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
423 bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
424 }
425 })
426 }
427
428 pub(crate) fn lower_pat_range_bdy(
429 &self,
430 bdy: PatRangeBoundary<'tcx>,
431 ty: RevealedTy<'tcx>,
432 ) -> MaybeInfiniteInt {
433 match bdy {
434 PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
435 PatRangeBoundary::Finite(value) => {
436 let bits = value.eval_bits(self.tcx, self.typing_env);
437 match *ty.kind() {
438 ty::Int(ity) => {
439 let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
440 MaybeInfiniteInt::new_finite_int(bits, size)
441 }
442 _ => MaybeInfiniteInt::new_finite_uint(bits),
443 }
444 }
445 PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity,
446 }
447 }
448
449 pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
452 let cx = self;
453 let ty = cx.reveal_opaque_ty(pat.ty);
454 let ctor;
455 let arity;
456 let fields: Vec<_>;
457 match &pat.kind {
458 PatKind::AscribeUserType { subpattern, .. }
459 | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern),
460 PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
461 PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
462 ctor = Wildcard;
463 fields = vec![];
464 arity = 0;
465 }
466 PatKind::Deref { subpattern } => {
467 fields = vec![self.lower_pat(subpattern).at_index(0)];
468 arity = 1;
469 ctor = match ty.kind() {
470 ty::Adt(adt, ..) if adt.is_box() => Struct,
472 ty::Ref(..) => Ref,
473 _ => span_bug!(
474 pat.span,
475 "pattern has unexpected type: pat: {:?}, ty: {:?}",
476 pat.kind,
477 ty.inner()
478 ),
479 };
480 }
481 PatKind::DerefPattern { .. } => {
482 fields = vec![];
484 arity = 0;
485 ctor = Opaque(OpaqueId::new());
486 }
487 PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
488 match ty.kind() {
489 ty::Tuple(fs) => {
490 ctor = Struct;
491 arity = fs.len();
492 fields = subpatterns
493 .iter()
494 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
495 .collect();
496 }
497 ty::Adt(adt, _) if adt.is_box() => {
498 let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
511 if let Some(pat) = pattern {
512 fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
513 } else {
514 fields = vec![];
515 }
516 ctor = Struct;
517 arity = 1;
518 }
519 ty::Adt(adt, _) => {
520 ctor = match pat.kind {
521 PatKind::Leaf { .. } if adt.is_union() => UnionField,
522 PatKind::Leaf { .. } => Struct,
523 PatKind::Variant { variant_index, .. } => Variant(variant_index),
524 _ => bug!(),
525 };
526 let variant =
527 &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
528 arity = variant.fields.len();
529 fields = subpatterns
530 .iter()
531 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
532 .collect();
533 }
534 _ => span_bug!(
535 pat.span,
536 "pattern has unexpected type: pat: {:?}, ty: {}",
537 pat.kind,
538 ty.inner()
539 ),
540 }
541 }
542 PatKind::Constant { value } => {
543 match ty.kind() {
544 ty::Bool => {
545 ctor = match value.try_eval_bool(cx.tcx, cx.typing_env) {
546 Some(b) => Bool(b),
547 None => Opaque(OpaqueId::new()),
548 };
549 fields = vec![];
550 arity = 0;
551 }
552 ty::Char | ty::Int(_) | ty::Uint(_) => {
553 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
554 Some(bits) => {
555 let x = match *ty.kind() {
556 ty::Int(ity) => {
557 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
558 MaybeInfiniteInt::new_finite_int(bits, size)
559 }
560 _ => MaybeInfiniteInt::new_finite_uint(bits),
561 };
562 IntRange(IntRange::from_singleton(x))
563 }
564 None => Opaque(OpaqueId::new()),
565 };
566 fields = vec![];
567 arity = 0;
568 }
569 ty::Float(ty::FloatTy::F16) => {
570 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
571 Some(bits) => {
572 use rustc_apfloat::Float;
573 let value = rustc_apfloat::ieee::Half::from_bits(bits);
574 F16Range(value, value, RangeEnd::Included)
575 }
576 None => Opaque(OpaqueId::new()),
577 };
578 fields = vec![];
579 arity = 0;
580 }
581 ty::Float(ty::FloatTy::F32) => {
582 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
583 Some(bits) => {
584 use rustc_apfloat::Float;
585 let value = rustc_apfloat::ieee::Single::from_bits(bits);
586 F32Range(value, value, RangeEnd::Included)
587 }
588 None => Opaque(OpaqueId::new()),
589 };
590 fields = vec![];
591 arity = 0;
592 }
593 ty::Float(ty::FloatTy::F64) => {
594 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
595 Some(bits) => {
596 use rustc_apfloat::Float;
597 let value = rustc_apfloat::ieee::Double::from_bits(bits);
598 F64Range(value, value, RangeEnd::Included)
599 }
600 None => Opaque(OpaqueId::new()),
601 };
602 fields = vec![];
603 arity = 0;
604 }
605 ty::Float(ty::FloatTy::F128) => {
606 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
607 Some(bits) => {
608 use rustc_apfloat::Float;
609 let value = rustc_apfloat::ieee::Quad::from_bits(bits);
610 F128Range(value, value, RangeEnd::Included)
611 }
612 None => Opaque(OpaqueId::new()),
613 };
614 fields = vec![];
615 arity = 0;
616 }
617 ty::Ref(_, t, _) if t.is_str() => {
618 let ty = self.reveal_opaque_ty(*t);
626 let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
627 ctor = Ref;
628 fields = vec![subpattern.at_index(0)];
629 arity = 1;
630 }
631 _ => {
635 ctor = Opaque(OpaqueId::new());
636 fields = vec![];
637 arity = 0;
638 }
639 }
640 }
641 PatKind::Range(patrange) => {
642 let PatRange { lo, hi, end, .. } = patrange.as_ref();
643 let end = match end {
644 rustc_hir::RangeEnd::Included => RangeEnd::Included,
645 rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
646 };
647 ctor = match ty.kind() {
648 ty::Char | ty::Int(_) | ty::Uint(_) => {
649 let lo = cx.lower_pat_range_bdy(*lo, ty);
650 let hi = cx.lower_pat_range_bdy(*hi, ty);
651 IntRange(IntRange::from_range(lo, hi, end))
652 }
653 ty::Float(fty) => {
654 use rustc_apfloat::Float;
655 let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
656 let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
657 match fty {
658 ty::FloatTy::F16 => {
659 use rustc_apfloat::ieee::Half;
660 let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
661 let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
662 F16Range(lo, hi, end)
663 }
664 ty::FloatTy::F32 => {
665 use rustc_apfloat::ieee::Single;
666 let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
667 let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
668 F32Range(lo, hi, end)
669 }
670 ty::FloatTy::F64 => {
671 use rustc_apfloat::ieee::Double;
672 let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
673 let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
674 F64Range(lo, hi, end)
675 }
676 ty::FloatTy::F128 => {
677 use rustc_apfloat::ieee::Quad;
678 let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
679 let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
680 F128Range(lo, hi, end)
681 }
682 }
683 }
684 _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()),
685 };
686 fields = vec![];
687 arity = 0;
688 }
689 PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
690 let array_len = match ty.kind() {
691 ty::Array(_, length) => Some(
692 length
693 .try_to_target_usize(cx.tcx)
694 .expect("expected len of array pat to be definite")
695 as usize,
696 ),
697 ty::Slice(_) => None,
698 _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()),
699 };
700 let kind = if slice.is_some() {
701 SliceKind::VarLen(prefix.len(), suffix.len())
702 } else {
703 SliceKind::FixedLen(prefix.len() + suffix.len())
704 };
705 ctor = Slice(Slice::new(array_len, kind));
706 fields = prefix
707 .iter()
708 .chain(suffix.iter())
709 .map(|p| self.lower_pat(&*p))
710 .enumerate()
711 .map(|(i, p)| p.at_index(i))
712 .collect();
713 arity = kind.arity();
714 }
715 PatKind::Or { .. } => {
716 ctor = Or;
717 let pats = expand_or_pat(pat);
718 fields = pats
719 .into_iter()
720 .map(|p| self.lower_pat(p))
721 .enumerate()
722 .map(|(i, p)| p.at_index(i))
723 .collect();
724 arity = fields.len();
725 }
726 PatKind::Never => {
727 ctor = Wildcard;
731 fields = vec![];
732 arity = 0;
733 }
734 PatKind::Error(_) => {
735 ctor = Opaque(OpaqueId::new());
736 fields = vec![];
737 arity = 0;
738 }
739 }
740 DeconstructedPat::new(ctor, fields, arity, ty, pat)
741 }
742
743 fn hoist_pat_range_bdy(
748 &self,
749 miint: MaybeInfiniteInt,
750 ty: RevealedTy<'tcx>,
751 ) -> PatRangeBoundary<'tcx> {
752 use MaybeInfiniteInt::*;
753 let tcx = self.tcx;
754 match miint {
755 NegInfinity => PatRangeBoundary::NegInfinity,
756 Finite(_) => {
757 let size = ty.primitive_size(tcx);
758 let bits = match *ty.kind() {
759 ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(),
760 _ => miint.as_finite_uint().unwrap(),
761 };
762 match ScalarInt::try_from_uint(bits, size) {
763 Some(scalar) => {
764 let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner());
765 PatRangeBoundary::Finite(value)
766 }
767 None => PatRangeBoundary::PosInfinity,
771 }
772 }
773 PosInfinity => PatRangeBoundary::PosInfinity,
774 }
775 }
776
777 fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
779 use MaybeInfiniteInt::*;
780 let cx = self;
781 if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
782 "_".to_string()
783 } else if range.is_singleton() {
784 let lo = cx.hoist_pat_range_bdy(range.lo, ty);
785 let value = lo.as_finite().unwrap();
786 value.to_string()
787 } else {
788 let mut end = rustc_hir::RangeEnd::Included;
790 let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
791 if matches!(lo, PatRangeBoundary::PosInfinity) {
792 lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap());
798 }
799 let hi = if let Some(hi) = range.hi.minus_one() {
800 hi
801 } else {
802 end = rustc_hir::RangeEnd::Excluded;
804 range.hi
805 };
806 let hi = cx.hoist_pat_range_bdy(hi, ty);
807 PatRange { lo, hi, end, ty: ty.inner() }.to_string()
808 }
809 }
810
811 pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
815 let cx = self;
816 let print = |p| cx.print_witness_pat(p);
817 match pat.ctor() {
818 Bool(b) => b.to_string(),
819 Str(s) => s.to_string(),
820 IntRange(range) => return self.print_pat_range(range, *pat.ty()),
821 Struct if pat.ty().is_box() => {
822 format!("box {}", print(&pat.fields[0]))
825 }
826 Struct | Variant(_) | UnionField => {
827 let enum_info = match *pat.ty().kind() {
828 ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
829 adt_def,
830 variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
831 },
832 ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
833 _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
834 };
835
836 let subpatterns = pat
837 .iter_fields()
838 .enumerate()
839 .map(|(i, pat)| print::FieldPat {
840 field: FieldIdx::new(i),
841 pattern: print(pat),
842 is_wildcard: would_print_as_wildcard(cx.tcx, pat),
843 })
844 .collect::<Vec<_>>();
845
846 let mut s = String::new();
847 print::write_struct_like(
848 &mut s,
849 self.tcx,
850 pat.ty().inner(),
851 &enum_info,
852 &subpatterns,
853 )
854 .unwrap();
855 s
856 }
857 Ref => {
858 let mut s = String::new();
859 print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
860 s
861 }
862 Slice(slice) => {
863 let (prefix_len, has_dot_dot) = match slice.kind {
864 SliceKind::FixedLen(len) => (len, false),
865 SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
866 };
867
868 let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
869
870 if has_dot_dot && slice.array_len.is_some() {
876 while let [rest @ .., last] = prefix
877 && would_print_as_wildcard(cx.tcx, last)
878 {
879 prefix = rest;
880 }
881 while let [first, rest @ ..] = suffix
882 && would_print_as_wildcard(cx.tcx, first)
883 {
884 suffix = rest;
885 }
886 }
887
888 let prefix = prefix.iter().map(print).collect::<Vec<_>>();
889 let suffix = suffix.iter().map(print).collect::<Vec<_>>();
890
891 let mut s = String::new();
892 print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
893 s
894 }
895 Never if self.tcx.features().never_patterns() => "!".to_string(),
896 Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
897 Missing { .. } => bug!(
898 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
899 `Missing` should have been processed in `apply_constructors`"
900 ),
901 F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
902 bug!("can't convert to pattern: {:?}", pat)
903 }
904 }
905 }
906}
907
908fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
910 match p.ctor() {
911 Constructor::IntRange(IntRange {
912 lo: MaybeInfiniteInt::NegInfinity,
913 hi: MaybeInfiniteInt::PosInfinity,
914 })
915 | Constructor::Wildcard
916 | Constructor::NonExhaustive
917 | Constructor::Hidden
918 | Constructor::PrivateUninhabited => true,
919 Constructor::Never if !tcx.features().never_patterns() => true,
920 _ => false,
921 }
922}
923
924impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
925 type Ty = RevealedTy<'tcx>;
926 type Error = ErrorGuaranteed;
927 type VariantIdx = VariantIdx;
928 type StrLit = Const<'tcx>;
929 type ArmData = HirId;
930 type PatData = &'p Pat<'tcx>;
931
932 fn is_exhaustive_patterns_feature_on(&self) -> bool {
933 self.tcx.features().exhaustive_patterns()
934 }
935
936 fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
937 self.ctor_arity(ctor, *ty)
938 }
939 fn ctor_sub_tys<'a>(
940 &'a self,
941 ctor: &'a crate::constructor::Constructor<Self>,
942 ty: &'a Self::Ty,
943 ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a>
944 {
945 self.ctor_sub_tys(ctor, *ty)
946 }
947 fn ctors_for_ty(
948 &self,
949 ty: &Self::Ty,
950 ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
951 self.ctors_for_ty(*ty)
952 }
953
954 fn write_variant_name(
955 f: &mut fmt::Formatter<'_>,
956 ctor: &crate::constructor::Constructor<Self>,
957 ty: &Self::Ty,
958 ) -> fmt::Result {
959 if let ty::Adt(adt, _) = ty.kind() {
960 if adt.is_box() {
961 write!(f, "Box")?
962 } else {
963 let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
964 write!(f, "{}", variant.name)?;
965 }
966 }
967 Ok(())
968 }
969
970 fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error {
971 span_bug!(self.scrut_span, "{}", fmt)
972 }
973
974 fn lint_overlapping_range_endpoints(
975 &self,
976 pat: &crate::pat::DeconstructedPat<Self>,
977 overlaps_on: IntRange,
978 overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
979 ) {
980 let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
981 let overlaps: Vec<_> = overlaps_with
982 .iter()
983 .map(|pat| pat.data().span)
984 .map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
985 .collect();
986 let pat_span = pat.data().span;
987 self.tcx.emit_node_span_lint(
988 lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
989 self.match_lint_level,
990 pat_span,
991 errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
992 );
993 }
994
995 fn complexity_exceeded(&self) -> Result<(), Self::Error> {
996 let span = self.whole_match_span.unwrap_or(self.scrut_span);
997 Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
998 }
999
1000 fn lint_non_contiguous_range_endpoints(
1001 &self,
1002 pat: &crate::pat::DeconstructedPat<Self>,
1003 gap: IntRange,
1004 gapped_with: &[&crate::pat::DeconstructedPat<Self>],
1005 ) {
1006 let &thir_pat = pat.data();
1007 let thir::PatKind::Range(range) = &thir_pat.kind else { return };
1008 if range.end != rustc_hir::RangeEnd::Excluded {
1010 return;
1011 }
1012 let suggested_range: String = {
1015 let mut suggested_range = PatRange::clone(range);
1017 suggested_range.end = rustc_hir::RangeEnd::Included;
1018 suggested_range.to_string()
1019 };
1020 let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
1021 if gapped_with.is_empty() {
1022 self.tcx.emit_node_span_lint(
1024 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1025 self.match_lint_level,
1026 thir_pat.span,
1027 errors::ExclusiveRangeMissingMax {
1028 first_range: thir_pat.span,
1030 max: gap_as_pat,
1032 suggestion: suggested_range,
1034 },
1035 );
1036 } else {
1037 self.tcx.emit_node_span_lint(
1038 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1039 self.match_lint_level,
1040 thir_pat.span,
1041 errors::ExclusiveRangeMissingGap {
1042 first_range: thir_pat.span,
1044 gap: gap_as_pat.to_string(),
1046 suggestion: suggested_range,
1048 gap_with: gapped_with
1051 .iter()
1052 .map(|pat| errors::GappedRange {
1053 span: pat.data().span,
1054 gap: gap_as_pat.to_string(),
1055 first_range: range.to_string(),
1056 })
1057 .collect(),
1058 },
1059 );
1060 }
1061 }
1062}
1063
1064fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
1066 fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
1067 if let PatKind::Or { pats } = &pat.kind {
1068 for pat in pats.iter() {
1069 expand(pat, vec);
1070 }
1071 } else {
1072 vec.push(pat)
1073 }
1074 }
1075
1076 let mut pats = Vec::new();
1077 expand(pat, &mut pats);
1078 pats
1079}
1080
1081pub fn analyze_match<'p, 'tcx>(
1084 tycx: &RustcPatCtxt<'p, 'tcx>,
1085 arms: &[MatchArm<'p, 'tcx>],
1086 scrut_ty: Ty<'tcx>,
1087 pattern_complexity_limit: Option<usize>,
1088) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
1089 let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
1090 let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
1091 let report =
1092 compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?;
1093
1094 if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
1097 let pat_column = PatternColumn::new(arms);
1098 lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
1099 }
1100
1101 Ok(report)
1102}