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