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