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