1use std::fmt;
2use std::iter::once;
3use std::ops::ControlFlow;
4
5use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
6use rustc_arena::DroplessArena;
7use rustc_hir::HirId;
8use rustc_hir::def_id::DefId;
9use rustc_index::{Idx, IndexVec};
10use rustc_middle::middle::stability::EvalResult;
11use rustc_middle::mir::{self, Const};
12use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
13use rustc_middle::ty::layout::IntegerExt;
14use rustc_middle::ty::{
15 self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
16 TypeVisitableExt, TypeVisitor, VariantDef,
17};
18use rustc_middle::{bug, span_bug};
19use rustc_session::lint;
20use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym};
21
22use crate::constructor::Constructor::*;
23use crate::constructor::{
24 IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
25};
26use crate::lints::lint_nonexhaustive_missing_variants;
27use crate::pat_column::PatternColumn;
28use crate::rustc::print::EnumInfo;
29use crate::usefulness::{PlaceValidity, compute_match_usefulness};
30use crate::{PatCx, PrivateUninhabitedField, errors};
31
32mod print;
33
34pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p, 'tcx>>;
36pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>;
37pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>;
38pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>;
39pub type RedundancyExplanation<'p, 'tcx> =
40 crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>;
41pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>;
42pub type UsefulnessReport<'p, 'tcx> =
43 crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>;
44pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
45
46#[repr(transparent)]
52#[derive(Clone, Copy, PartialEq, Eq, Hash)]
53pub struct RevealedTy<'tcx>(Ty<'tcx>);
54
55impl<'tcx> fmt::Display for RevealedTy<'tcx> {
56 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
57 self.0.fmt(fmt)
58 }
59}
60
61impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
62 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
63 self.0.fmt(fmt)
64 }
65}
66
67impl<'tcx> std::ops::Deref for RevealedTy<'tcx> {
68 type Target = Ty<'tcx>;
69 fn deref(&self) -> &Self::Target {
70 &self.0
71 }
72}
73
74impl<'tcx> RevealedTy<'tcx> {
75 pub fn inner(self) -> Ty<'tcx> {
76 self.0
77 }
78}
79
80#[derive(Clone)]
81pub struct RustcPatCtxt<'p, 'tcx: 'p> {
82 pub tcx: TyCtxt<'tcx>,
83 pub typeck_results: &'tcx ty::TypeckResults<'tcx>,
84 pub module: DefId,
90 pub typing_env: ty::TypingEnv<'tcx>,
91 pub dropless_arena: &'p DroplessArena,
93 pub match_lint_level: HirId,
95 pub whole_match_span: Option<Span>,
97 pub scrut_span: Span,
99 pub refutable: bool,
101 pub known_valid_scrutinee: bool,
104}
105
106impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_struct("RustcPatCtxt").finish()
109 }
110}
111
112impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
113 #[inline]
119 pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
120 fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
121 let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!() };
122 if let Some(local_def_id) = alias_ty.def_id.as_local() {
123 let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
124 if let Some(ty) = cx.reveal_opaque_key(key) {
125 return RevealedTy(ty);
126 }
127 }
128 RevealedTy(ty)
129 }
130 if let ty::Alias(ty::Opaque, _) = ty.kind() {
131 reveal_inner(self, ty)
132 } else {
133 RevealedTy(ty)
134 }
135 }
136
137 fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
140 if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) {
141 let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args);
142 if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() {
143 Some(ty)
144 } else {
145 None
150 }
151 } else {
152 None
153 }
154 }
155
156 pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
158 !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
159 self.tcx,
160 self.typing_env,
161 self.module,
162 &|key| self.reveal_opaque_key(key),
163 )
164 }
165
166 pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
168 match ty.kind() {
169 ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
170 _ => false,
171 }
172 }
173
174 pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
177 ty.is_ptr_sized_integral() && {
178 let lo = self.hoist_pat_range_bdy(range.lo, ty);
183 matches!(lo, PatRangeBoundary::PosInfinity)
184 || matches!(range.hi, MaybeInfiniteInt::Finite(0))
185 }
186 }
187
188 pub(crate) fn variant_sub_tys(
189 &self,
190 ty: RevealedTy<'tcx>,
191 variant: &'tcx VariantDef,
192 ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> {
193 let ty::Adt(_, args) = ty.kind() else { bug!() };
194 variant.fields.iter().map(move |field| {
195 let ty = field.ty(self.tcx, args);
196 let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
198 let ty = self.reveal_opaque_ty(ty);
199 (field, ty)
200 })
201 }
202
203 pub(crate) fn variant_index_for_adt(
204 ctor: &Constructor<'p, 'tcx>,
205 adt: ty::AdtDef<'tcx>,
206 ) -> VariantIdx {
207 match *ctor {
208 Variant(idx) => idx,
209 Struct | UnionField => {
210 assert!(!adt.is_enum());
211 FIRST_VARIANT
212 }
213 _ => bug!("bad constructor {:?} for adt {:?}", ctor, adt),
214 }
215 }
216
217 pub(crate) fn ctor_sub_tys(
220 &self,
221 ctor: &Constructor<'p, 'tcx>,
222 ty: RevealedTy<'tcx>,
223 ) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)> + ExactSizeIterator {
224 fn reveal_and_alloc<'a, 'tcx>(
225 cx: &'a RustcPatCtxt<'_, 'tcx>,
226 iter: impl Iterator<Item = Ty<'tcx>>,
227 ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
228 cx.dropless_arena.alloc_from_iter(
229 iter.map(|ty| cx.reveal_opaque_ty(ty))
230 .map(|ty| (ty, PrivateUninhabitedField(false))),
231 )
232 }
233 let cx = self;
234 let slice = match ctor {
235 Struct | Variant(_) | UnionField => match ty.kind() {
236 ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
237 ty::Adt(adt, args) => {
238 if adt.is_box() {
239 reveal_and_alloc(cx, once(args.type_at(0)))
242 } else {
243 let variant =
244 &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
245 let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
246 let is_visible =
247 adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
248 let is_uninhabited = cx.is_uninhabited(*ty);
249 let is_unstable =
250 cx.tcx.lookup_stability(field.did).is_some_and(|stab| {
251 stab.is_unstable() && stab.feature != sym::rustc_private
252 });
253 let skip = is_uninhabited && (!is_visible || is_unstable);
254 (ty, PrivateUninhabitedField(skip))
255 });
256 cx.dropless_arena.alloc_from_iter(tys)
257 }
258 }
259 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
260 },
261 Ref => match ty.kind() {
262 ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
263 _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
264 },
265 Slice(slice) => match *ty.kind() {
266 ty::Slice(ty) | ty::Array(ty, _) => {
267 let arity = slice.arity();
268 reveal_and_alloc(cx, (0..arity).map(|_| ty))
269 }
270 _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
271 },
272 DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
273 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
274 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
275 | PrivateUninhabited | Wildcard => &[],
276 Or => {
277 bug!("called `Fields::wildcards` on an `Or` ctor")
278 }
279 };
280 slice.iter().copied()
281 }
282
283 pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
285 match ctor {
286 Struct | Variant(_) | UnionField => match ty.kind() {
287 ty::Tuple(fs) => fs.len(),
288 ty::Adt(adt, ..) => {
289 if adt.is_box() {
290 1
293 } else {
294 let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
295 adt.variant(variant_idx).fields.len()
296 }
297 }
298 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
299 },
300 Ref | DerefPattern(_) => 1,
301 Slice(slice) => slice.arity(),
302 Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
303 | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
304 | PrivateUninhabited | Wildcard => 0,
305 Or => bug!("The `Or` constructor doesn't have a fixed arity"),
306 }
307 }
308
309 pub fn ctors_for_ty(
313 &self,
314 ty: RevealedTy<'tcx>,
315 ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
316 let cx = self;
317 let make_uint_range = |start, end| {
318 IntRange::from_range(
319 MaybeInfiniteInt::new_finite_uint(start),
320 MaybeInfiniteInt::new_finite_uint(end),
321 RangeEnd::Included,
322 )
323 };
324 ty.error_reported()?;
326 Ok(match ty.kind() {
329 ty::Bool => ConstructorSet::Bool,
330 ty::Char => {
331 ConstructorSet::Integers {
333 range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128),
334 range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
335 }
336 }
337 &ty::Int(ity) => {
338 let range = if ty.is_ptr_sized_integral() {
339 IntRange {
341 lo: MaybeInfiniteInt::NegInfinity,
342 hi: MaybeInfiniteInt::PosInfinity,
343 }
344 } else {
345 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
346 let min = 1u128 << (size - 1);
347 let max = min - 1;
348 let min = MaybeInfiniteInt::new_finite_int(min, size);
349 let max = MaybeInfiniteInt::new_finite_int(max, size);
350 IntRange::from_range(min, max, RangeEnd::Included)
351 };
352 ConstructorSet::Integers { range_1: range, range_2: None }
353 }
354 &ty::Uint(uty) => {
355 let range = if ty.is_ptr_sized_integral() {
356 let lo = MaybeInfiniteInt::new_finite_uint(0);
358 IntRange { lo, hi: MaybeInfiniteInt::PosInfinity }
359 } else {
360 let size = Integer::from_uint_ty(&cx.tcx, uty).size();
361 let max = size.truncate(u128::MAX);
362 make_uint_range(0, max)
363 };
364 ConstructorSet::Integers { range_1: range, range_2: None }
365 }
366 ty::Slice(sub_ty) => ConstructorSet::Slice {
367 array_len: None,
368 subtype_is_empty: cx.is_uninhabited(*sub_ty),
369 },
370 ty::Array(sub_ty, len) => {
371 ConstructorSet::Slice {
373 array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize),
374 subtype_is_empty: cx.is_uninhabited(*sub_ty),
375 }
376 }
377 ty::Adt(def, args) if def.is_enum() => {
378 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
379 if def.variants().is_empty() && !is_declared_nonexhaustive {
380 ConstructorSet::NoConstructors
381 } else {
382 let mut variants =
383 IndexVec::from_elem(VariantVisibility::Visible, def.variants());
384 for (idx, v) in def.variants().iter_enumerated() {
385 let variant_def_id = def.variant(idx).def_id;
386 let is_inhabited = v
388 .inhabited_predicate(cx.tcx, *def)
389 .instantiate(cx.tcx, args)
390 .apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
391 cx.reveal_opaque_key(key)
392 });
393 let is_unstable = matches!(
395 cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
396 EvalResult::Deny { .. }
397 );
398 let is_doc_hidden =
400 cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
401 let visibility = if !is_inhabited {
402 VariantVisibility::Empty
404 } else if is_unstable || is_doc_hidden {
405 VariantVisibility::Hidden
406 } else {
407 VariantVisibility::Visible
408 };
409 variants[idx] = visibility;
410 }
411
412 ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
413 }
414 }
415 ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
416 ty::Adt(..) | ty::Tuple(..) => {
417 ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
418 }
419 ty::Ref(..) => ConstructorSet::Ref,
420 ty::Never => ConstructorSet::NoConstructors,
421 ty::Float(_)
424 | ty::Str
425 | ty::Foreign(_)
426 | ty::RawPtr(_, _)
427 | ty::FnDef(_, _)
428 | ty::FnPtr(..)
429 | ty::Pat(_, _)
430 | ty::Dynamic(_, _, _)
431 | ty::Closure(..)
432 | ty::CoroutineClosure(..)
433 | ty::Coroutine(_, _)
434 | ty::UnsafeBinder(_)
435 | ty::Alias(_, _)
436 | ty::Param(_)
437 | ty::Error(_) => ConstructorSet::Unlistable,
438 ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
439 bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
440 }
441 })
442 }
443
444 pub(crate) fn lower_pat_range_bdy(
445 &self,
446 bdy: PatRangeBoundary<'tcx>,
447 ty: RevealedTy<'tcx>,
448 ) -> MaybeInfiniteInt {
449 match bdy {
450 PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
451 PatRangeBoundary::Finite(value) => {
452 let bits = value.eval_bits(self.tcx, self.typing_env);
453 match *ty.kind() {
454 ty::Int(ity) => {
455 let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
456 MaybeInfiniteInt::new_finite_int(bits, size)
457 }
458 _ => MaybeInfiniteInt::new_finite_uint(bits),
459 }
460 }
461 PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity,
462 }
463 }
464
465 pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
468 let cx = self;
469 let ty = cx.reveal_opaque_ty(pat.ty);
470 let ctor;
471 let arity;
472 let fields: Vec<_>;
473 match &pat.kind {
474 PatKind::AscribeUserType { subpattern, .. }
475 | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern),
476 PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
477 PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
478 ctor = Wildcard;
479 fields = vec![];
480 arity = 0;
481 }
482 PatKind::Deref { subpattern } => {
483 fields = vec![self.lower_pat(subpattern).at_index(0)];
484 arity = 1;
485 ctor = match ty.kind() {
486 ty::Adt(adt, ..) if adt.is_box() => Struct,
488 ty::Ref(..) => Ref,
489 _ => span_bug!(
490 pat.span,
491 "pattern has unexpected type: pat: {:?}, ty: {:?}",
492 pat.kind,
493 ty.inner()
494 ),
495 };
496 }
497 PatKind::DerefPattern { subpattern, .. } => {
498 fields = vec![self.lower_pat(subpattern).at_index(0)];
504 arity = 1;
505 ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
506 }
507 PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
508 match ty.kind() {
509 ty::Tuple(fs) => {
510 ctor = Struct;
511 arity = fs.len();
512 fields = subpatterns
513 .iter()
514 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
515 .collect();
516 }
517 ty::Adt(adt, _) if adt.is_box() => {
518 let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
531 if let Some(pat) = pattern {
532 fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
533 } else {
534 fields = vec![];
535 }
536 ctor = Struct;
537 arity = 1;
538 }
539 ty::Adt(adt, _) => {
540 ctor = match pat.kind {
541 PatKind::Leaf { .. } if adt.is_union() => UnionField,
542 PatKind::Leaf { .. } => Struct,
543 PatKind::Variant { variant_index, .. } => Variant(variant_index),
544 _ => bug!(),
545 };
546 let variant =
547 &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
548 arity = variant.fields.len();
549 fields = subpatterns
550 .iter()
551 .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
552 .collect();
553 }
554 _ => span_bug!(
555 pat.span,
556 "pattern has unexpected type: pat: {:?}, ty: {}",
557 pat.kind,
558 ty.inner()
559 ),
560 }
561 }
562 PatKind::Constant { value } => {
563 match ty.kind() {
564 ty::Bool => {
565 ctor = match value.try_eval_bool(cx.tcx, cx.typing_env) {
566 Some(b) => Bool(b),
567 None => Opaque(OpaqueId::new()),
568 };
569 fields = vec![];
570 arity = 0;
571 }
572 ty::Char | ty::Int(_) | ty::Uint(_) => {
573 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
574 Some(bits) => {
575 let x = match *ty.kind() {
576 ty::Int(ity) => {
577 let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
578 MaybeInfiniteInt::new_finite_int(bits, size)
579 }
580 _ => MaybeInfiniteInt::new_finite_uint(bits),
581 };
582 IntRange(IntRange::from_singleton(x))
583 }
584 None => Opaque(OpaqueId::new()),
585 };
586 fields = vec![];
587 arity = 0;
588 }
589 ty::Float(ty::FloatTy::F16) => {
590 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
591 Some(bits) => {
592 use rustc_apfloat::Float;
593 let value = rustc_apfloat::ieee::Half::from_bits(bits);
594 F16Range(value, value, RangeEnd::Included)
595 }
596 None => Opaque(OpaqueId::new()),
597 };
598 fields = vec![];
599 arity = 0;
600 }
601 ty::Float(ty::FloatTy::F32) => {
602 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
603 Some(bits) => {
604 use rustc_apfloat::Float;
605 let value = rustc_apfloat::ieee::Single::from_bits(bits);
606 F32Range(value, value, RangeEnd::Included)
607 }
608 None => Opaque(OpaqueId::new()),
609 };
610 fields = vec![];
611 arity = 0;
612 }
613 ty::Float(ty::FloatTy::F64) => {
614 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
615 Some(bits) => {
616 use rustc_apfloat::Float;
617 let value = rustc_apfloat::ieee::Double::from_bits(bits);
618 F64Range(value, value, RangeEnd::Included)
619 }
620 None => Opaque(OpaqueId::new()),
621 };
622 fields = vec![];
623 arity = 0;
624 }
625 ty::Float(ty::FloatTy::F128) => {
626 ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
627 Some(bits) => {
628 use rustc_apfloat::Float;
629 let value = rustc_apfloat::ieee::Quad::from_bits(bits);
630 F128Range(value, value, RangeEnd::Included)
631 }
632 None => Opaque(OpaqueId::new()),
633 };
634 fields = vec![];
635 arity = 0;
636 }
637 ty::Ref(_, t, _) if t.is_str() => {
638 let ty = self.reveal_opaque_ty(*t);
646 let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
647 ctor = Ref;
648 fields = vec![subpattern.at_index(0)];
649 arity = 1;
650 }
651 _ => {
655 ctor = Opaque(OpaqueId::new());
656 fields = vec![];
657 arity = 0;
658 }
659 }
660 }
661 PatKind::Range(patrange) => {
662 let PatRange { lo, hi, end, .. } = patrange.as_ref();
663 let end = match end {
664 rustc_hir::RangeEnd::Included => RangeEnd::Included,
665 rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
666 };
667 ctor = match ty.kind() {
668 ty::Char | ty::Int(_) | ty::Uint(_) => {
669 let lo = cx.lower_pat_range_bdy(*lo, ty);
670 let hi = cx.lower_pat_range_bdy(*hi, ty);
671 IntRange(IntRange::from_range(lo, hi, end))
672 }
673 ty::Float(fty) => {
674 use rustc_apfloat::Float;
675 let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
676 let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
677 match fty {
678 ty::FloatTy::F16 => {
679 use rustc_apfloat::ieee::Half;
680 let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
681 let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
682 F16Range(lo, hi, end)
683 }
684 ty::FloatTy::F32 => {
685 use rustc_apfloat::ieee::Single;
686 let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
687 let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
688 F32Range(lo, hi, end)
689 }
690 ty::FloatTy::F64 => {
691 use rustc_apfloat::ieee::Double;
692 let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
693 let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
694 F64Range(lo, hi, end)
695 }
696 ty::FloatTy::F128 => {
697 use rustc_apfloat::ieee::Quad;
698 let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
699 let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
700 F128Range(lo, hi, end)
701 }
702 }
703 }
704 _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()),
705 };
706 fields = vec![];
707 arity = 0;
708 }
709 PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
710 let array_len = match ty.kind() {
711 ty::Array(_, length) => Some(
712 length
713 .try_to_target_usize(cx.tcx)
714 .expect("expected len of array pat to be definite")
715 as usize,
716 ),
717 ty::Slice(_) => None,
718 _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()),
719 };
720 let kind = if slice.is_some() {
721 SliceKind::VarLen(prefix.len(), suffix.len())
722 } else {
723 SliceKind::FixedLen(prefix.len() + suffix.len())
724 };
725 ctor = Slice(Slice::new(array_len, kind));
726 fields = prefix
727 .iter()
728 .chain(suffix.iter())
729 .map(|p| self.lower_pat(&*p))
730 .enumerate()
731 .map(|(i, p)| p.at_index(i))
732 .collect();
733 arity = kind.arity();
734 }
735 PatKind::Or { .. } => {
736 ctor = Or;
737 let pats = expand_or_pat(pat);
738 fields = pats
739 .into_iter()
740 .map(|p| self.lower_pat(p))
741 .enumerate()
742 .map(|(i, p)| p.at_index(i))
743 .collect();
744 arity = fields.len();
745 }
746 PatKind::Never => {
747 ctor = Wildcard;
751 fields = vec![];
752 arity = 0;
753 }
754 PatKind::Error(_) => {
755 ctor = Opaque(OpaqueId::new());
756 fields = vec![];
757 arity = 0;
758 }
759 }
760 DeconstructedPat::new(ctor, fields, arity, ty, pat)
761 }
762
763 fn hoist_pat_range_bdy(
768 &self,
769 miint: MaybeInfiniteInt,
770 ty: RevealedTy<'tcx>,
771 ) -> PatRangeBoundary<'tcx> {
772 use MaybeInfiniteInt::*;
773 let tcx = self.tcx;
774 match miint {
775 NegInfinity => PatRangeBoundary::NegInfinity,
776 Finite(_) => {
777 let size = ty.primitive_size(tcx);
778 let bits = match *ty.kind() {
779 ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(),
780 _ => miint.as_finite_uint().unwrap(),
781 };
782 match ScalarInt::try_from_uint(bits, size) {
783 Some(scalar) => {
784 let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner());
785 PatRangeBoundary::Finite(value)
786 }
787 None => PatRangeBoundary::PosInfinity,
791 }
792 }
793 PosInfinity => PatRangeBoundary::PosInfinity,
794 }
795 }
796
797 fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
799 use MaybeInfiniteInt::*;
800 let cx = self;
801 if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
802 "_".to_string()
803 } else if range.is_singleton() {
804 let lo = cx.hoist_pat_range_bdy(range.lo, ty);
805 let value = lo.as_finite().unwrap();
806 value.to_string()
807 } else {
808 let mut end = rustc_hir::RangeEnd::Included;
810 let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
811 if matches!(lo, PatRangeBoundary::PosInfinity) {
812 lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap());
818 }
819 let hi = if let Some(hi) = range.hi.minus_one() {
820 hi
821 } else {
822 end = rustc_hir::RangeEnd::Excluded;
824 range.hi
825 };
826 let hi = cx.hoist_pat_range_bdy(hi, ty);
827 PatRange { lo, hi, end, ty: ty.inner() }.to_string()
828 }
829 }
830
831 pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
835 let cx = self;
836 let print = |p| cx.print_witness_pat(p);
837 match pat.ctor() {
838 Bool(b) => b.to_string(),
839 Str(s) => s.to_string(),
840 IntRange(range) => return self.print_pat_range(range, *pat.ty()),
841 Struct if pat.ty().is_box() => {
842 format!("box {}", print(&pat.fields[0]))
845 }
846 Struct | Variant(_) | UnionField => {
847 let enum_info = match *pat.ty().kind() {
848 ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
849 adt_def,
850 variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
851 },
852 ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
853 _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
854 };
855
856 let subpatterns = pat
857 .iter_fields()
858 .enumerate()
859 .map(|(i, pat)| print::FieldPat {
860 field: FieldIdx::new(i),
861 pattern: print(pat),
862 is_wildcard: would_print_as_wildcard(cx.tcx, pat),
863 })
864 .collect::<Vec<_>>();
865
866 let mut s = String::new();
867 print::write_struct_like(
868 &mut s,
869 self.tcx,
870 pat.ty().inner(),
871 &enum_info,
872 &subpatterns,
873 )
874 .unwrap();
875 s
876 }
877 Ref => {
878 let mut s = String::new();
879 print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
880 s
881 }
882 DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
883 Slice(slice) => {
884 let (prefix_len, has_dot_dot) = match slice.kind {
885 SliceKind::FixedLen(len) => (len, false),
886 SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
887 };
888
889 let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
890
891 if has_dot_dot && slice.array_len.is_some() {
897 while let [rest @ .., last] = prefix
898 && would_print_as_wildcard(cx.tcx, last)
899 {
900 prefix = rest;
901 }
902 while let [first, rest @ ..] = suffix
903 && would_print_as_wildcard(cx.tcx, first)
904 {
905 suffix = rest;
906 }
907 }
908
909 let prefix = prefix.iter().map(print).collect::<Vec<_>>();
910 let suffix = suffix.iter().map(print).collect::<Vec<_>>();
911
912 let mut s = String::new();
913 print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
914 s
915 }
916 Never if self.tcx.features().never_patterns() => "!".to_string(),
917 Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
918 Missing { .. } => bug!(
919 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
920 `Missing` should have been processed in `apply_constructors`"
921 ),
922 F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
923 bug!("can't convert to pattern: {:?}", pat)
924 }
925 }
926 }
927}
928
929fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
931 match p.ctor() {
932 Constructor::IntRange(IntRange {
933 lo: MaybeInfiniteInt::NegInfinity,
934 hi: MaybeInfiniteInt::PosInfinity,
935 })
936 | Constructor::Wildcard
937 | Constructor::NonExhaustive
938 | Constructor::Hidden
939 | Constructor::PrivateUninhabited => true,
940 Constructor::Never if !tcx.features().never_patterns() => true,
941 _ => false,
942 }
943}
944
945impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
946 type Ty = RevealedTy<'tcx>;
947 type Error = ErrorGuaranteed;
948 type VariantIdx = VariantIdx;
949 type StrLit = Const<'tcx>;
950 type ArmData = HirId;
951 type PatData = &'p Pat<'tcx>;
952
953 fn is_exhaustive_patterns_feature_on(&self) -> bool {
954 self.tcx.features().exhaustive_patterns()
955 }
956
957 fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
958 self.ctor_arity(ctor, *ty)
959 }
960 fn ctor_sub_tys(
961 &self,
962 ctor: &crate::constructor::Constructor<Self>,
963 ty: &Self::Ty,
964 ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator {
965 self.ctor_sub_tys(ctor, *ty)
966 }
967 fn ctors_for_ty(
968 &self,
969 ty: &Self::Ty,
970 ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
971 self.ctors_for_ty(*ty)
972 }
973
974 fn write_variant_name(
975 f: &mut fmt::Formatter<'_>,
976 ctor: &crate::constructor::Constructor<Self>,
977 ty: &Self::Ty,
978 ) -> fmt::Result {
979 if let ty::Adt(adt, _) = ty.kind() {
980 if adt.is_box() {
981 write!(f, "Box")?
982 } else {
983 let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
984 write!(f, "{}", variant.name)?;
985 }
986 }
987 Ok(())
988 }
989
990 fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error {
991 span_bug!(self.scrut_span, "{}", fmt)
992 }
993
994 fn lint_overlapping_range_endpoints(
995 &self,
996 pat: &crate::pat::DeconstructedPat<Self>,
997 overlaps_on: IntRange,
998 overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
999 ) {
1000 let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
1001 let overlaps: Vec<_> = overlaps_with
1002 .iter()
1003 .map(|pat| pat.data().span)
1004 .map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
1005 .collect();
1006 let pat_span = pat.data().span;
1007 self.tcx.emit_node_span_lint(
1008 lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
1009 self.match_lint_level,
1010 pat_span,
1011 errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
1012 );
1013 }
1014
1015 fn complexity_exceeded(&self) -> Result<(), Self::Error> {
1016 let span = self.whole_match_span.unwrap_or(self.scrut_span);
1017 Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
1018 }
1019
1020 fn lint_non_contiguous_range_endpoints(
1021 &self,
1022 pat: &crate::pat::DeconstructedPat<Self>,
1023 gap: IntRange,
1024 gapped_with: &[&crate::pat::DeconstructedPat<Self>],
1025 ) {
1026 let &thir_pat = pat.data();
1027 let thir::PatKind::Range(range) = &thir_pat.kind else { return };
1028 if range.end != rustc_hir::RangeEnd::Excluded {
1030 return;
1031 }
1032 let suggested_range: String = {
1035 let mut suggested_range = PatRange::clone(range);
1037 suggested_range.end = rustc_hir::RangeEnd::Included;
1038 suggested_range.to_string()
1039 };
1040 let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
1041 if gapped_with.is_empty() {
1042 self.tcx.emit_node_span_lint(
1044 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1045 self.match_lint_level,
1046 thir_pat.span,
1047 errors::ExclusiveRangeMissingMax {
1048 first_range: thir_pat.span,
1050 max: gap_as_pat,
1052 suggestion: suggested_range,
1054 },
1055 );
1056 } else {
1057 self.tcx.emit_node_span_lint(
1058 lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1059 self.match_lint_level,
1060 thir_pat.span,
1061 errors::ExclusiveRangeMissingGap {
1062 first_range: thir_pat.span,
1064 gap: gap_as_pat.to_string(),
1066 suggestion: suggested_range,
1068 gap_with: gapped_with
1071 .iter()
1072 .map(|pat| errors::GappedRange {
1073 span: pat.data().span,
1074 gap: gap_as_pat.to_string(),
1075 first_range: range.to_string(),
1076 })
1077 .collect(),
1078 },
1079 );
1080 }
1081 }
1082}
1083
1084fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
1086 fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
1087 if let PatKind::Or { pats } = &pat.kind {
1088 for pat in pats.iter() {
1089 expand(pat, vec);
1090 }
1091 } else {
1092 vec.push(pat)
1093 }
1094 }
1095
1096 let mut pats = Vec::new();
1097 expand(pat, &mut pats);
1098 pats
1099}
1100
1101pub fn analyze_match<'p, 'tcx>(
1104 tycx: &RustcPatCtxt<'p, 'tcx>,
1105 arms: &[MatchArm<'p, 'tcx>],
1106 scrut_ty: Ty<'tcx>,
1107) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
1108 let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
1109
1110 if tycx.tcx.features().deref_patterns() {
1113 let pat_column = PatternColumn::new(arms);
1114 detect_mixed_deref_pat_ctors(tycx, &pat_column)?;
1115 }
1116
1117 let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
1118 let report = compute_match_usefulness(
1119 tycx,
1120 arms,
1121 scrut_ty,
1122 scrut_validity,
1123 tycx.tcx.pattern_complexity_limit().0,
1124 )?;
1125
1126 if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
1129 let pat_column = PatternColumn::new(arms);
1130 lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
1131 }
1132
1133 Ok(report)
1134}
1135
1136fn detect_mixed_deref_pat_ctors<'p, 'tcx>(
1141 cx: &RustcPatCtxt<'p, 'tcx>,
1142 column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>,
1143) -> Result<(), ErrorGuaranteed> {
1144 let Some(&ty) = column.head_ty() else {
1145 return Ok(());
1146 };
1147
1148 let mut normal_ctor_span = None;
1150 let mut deref_pat_span = None;
1151 for pat in column.iter() {
1152 match pat.ctor() {
1153 Wildcard | Opaque(_) => {}
1155 DerefPattern(_) => deref_pat_span = Some(pat.data().span),
1156 _ => normal_ctor_span = Some(pat.data().span),
1158 }
1159 }
1160 if let Some(normal_constructor_label) = normal_ctor_span
1161 && let Some(deref_pattern_label) = deref_pat_span
1162 {
1163 return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors {
1164 spans: vec![deref_pattern_label, normal_constructor_label],
1165 smart_pointer_ty: ty.inner(),
1166 deref_pattern_label,
1167 normal_constructor_label,
1168 }));
1169 }
1170
1171 let set = column.analyze_ctors(cx, &ty)?;
1173 for ctor in set.present {
1174 for specialized_column in column.specialize(cx, &ty, &ctor).iter() {
1175 detect_mixed_deref_pat_ctors(cx, specialized_column)?;
1176 }
1177 }
1178 Ok(())
1179}
1180
1181struct RecursiveOpaque {
1182 def_id: DefId,
1183}
1184impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for RecursiveOpaque {
1185 type Result = ControlFlow<()>;
1186
1187 fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
1188 if let ty::Alias(ty::Opaque, alias_ty) = t.kind() {
1189 if alias_ty.def_id == self.def_id {
1190 return ControlFlow::Break(());
1191 }
1192 }
1193
1194 if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) }
1195 }
1196}