rustc_pattern_analysis/
rustc.rs

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
32// Re-export rustc-specific versions of all these types.
33pub 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/// A type which has gone through `cx.reveal_opaque_ty`, i.e. if it was opaque it was replaced by
45/// the hidden type if allowed in the current body. This ensures we consistently inspect the hidden
46/// types when we should.
47///
48/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
49#[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    /// The module in which the match occurs. This is necessary for
83    /// checking inhabited-ness of types because whether a type is (visibly)
84    /// inhabited can depend on whether it was defined in the current module or
85    /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty
86    /// outside its module and should not be matchable with an empty match statement.
87    pub module: DefId,
88    pub typing_env: ty::TypingEnv<'tcx>,
89    /// To allocate the result of `self.ctor_sub_tys()`
90    pub dropless_arena: &'p DroplessArena,
91    /// Lint level at the match.
92    pub match_lint_level: HirId,
93    /// The span of the whole match, if applicable.
94    pub whole_match_span: Option<Span>,
95    /// Span of the scrutinee.
96    pub scrut_span: Span,
97    /// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns.
98    pub refutable: bool,
99    /// Whether the data at the scrutinee is known to be valid. This is false if the scrutinee comes
100    /// from a union field, a pointer deref, or a reference deref (pending opsem decisions).
101    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    /// Type inference occasionally gives us opaque types in places where corresponding patterns
112    /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
113    /// types, we use the corresponding concrete type if possible.
114    // FIXME(#132279): This will be unnecessary once we have a TypingMode which supports revealing
115    // opaque types defined in a body.
116    #[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    /// Returns the hidden type corresponding to this key if the body under analysis is allowed to
136    /// know it.
137    fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
138        self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
139    }
140    // This can take a non-revealed `Ty` because it reveals opaques itself.
141    pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
142        !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
143            self.tcx,
144            self.typing_env,
145            self.module,
146            &|key| self.reveal_opaque_key(key),
147        )
148    }
149
150    /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
151    pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
152        match ty.kind() {
153            ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
154            _ => false,
155        }
156    }
157
158    /// Whether the range denotes the fictitious values before `isize::MIN` or after
159    /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist).
160    pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
161        ty.is_ptr_sized_integral() && {
162            // The two invalid ranges are `NegInfinity..isize::MIN` (represented as
163            // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy`
164            // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo`
165            // otherwise.
166            let lo = self.hoist_pat_range_bdy(range.lo, ty);
167            matches!(lo, PatRangeBoundary::PosInfinity)
168                || matches!(range.hi, MaybeInfiniteInt::Finite(0))
169        }
170    }
171
172    pub(crate) fn variant_sub_tys(
173        &self,
174        ty: RevealedTy<'tcx>,
175        variant: &'tcx VariantDef,
176    ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> {
177        let ty::Adt(_, args) = ty.kind() else { bug!() };
178        variant.fields.iter().map(move |field| {
179            let ty = field.ty(self.tcx, args);
180            // `field.ty()` doesn't normalize after instantiating.
181            let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
182            let ty = self.reveal_opaque_ty(ty);
183            (field, ty)
184        })
185    }
186
187    pub(crate) fn variant_index_for_adt(
188        ctor: &Constructor<'p, 'tcx>,
189        adt: ty::AdtDef<'tcx>,
190    ) -> VariantIdx {
191        match *ctor {
192            Variant(idx) => idx,
193            Struct | UnionField => {
194                assert!(!adt.is_enum());
195                FIRST_VARIANT
196            }
197            _ => bug!("bad constructor {:?} for adt {:?}", ctor, adt),
198        }
199    }
200
201    /// Returns the types of the fields for a given constructor. The result must have a length of
202    /// `ctor.arity()`.
203    pub(crate) fn ctor_sub_tys(
204        &self,
205        ctor: &Constructor<'p, 'tcx>,
206        ty: RevealedTy<'tcx>,
207    ) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)> + ExactSizeIterator {
208        fn reveal_and_alloc<'a, 'tcx>(
209            cx: &'a RustcPatCtxt<'_, 'tcx>,
210            iter: impl Iterator<Item = Ty<'tcx>>,
211        ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] {
212            cx.dropless_arena.alloc_from_iter(
213                iter.map(|ty| cx.reveal_opaque_ty(ty))
214                    .map(|ty| (ty, PrivateUninhabitedField(false))),
215            )
216        }
217        let cx = self;
218        let slice = match ctor {
219            Struct | Variant(_) | UnionField => match ty.kind() {
220                ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
221                ty::Adt(adt, args) => {
222                    if adt.is_box() {
223                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
224                        // patterns. If we're here we can assume this is a box pattern.
225                        reveal_and_alloc(cx, once(args.type_at(0)))
226                    } else {
227                        let variant =
228                            &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
229                        let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| {
230                            let is_visible =
231                                adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
232                            let is_uninhabited = cx.is_uninhabited(*ty);
233                            let is_unstable =
234                                cx.tcx.lookup_stability(field.did).is_some_and(|stab| {
235                                    stab.is_unstable() && stab.feature != sym::rustc_private
236                                });
237                            let skip = is_uninhabited && (!is_visible || is_unstable);
238                            (ty, PrivateUninhabitedField(skip))
239                        });
240                        cx.dropless_arena.alloc_from_iter(tys)
241                    }
242                }
243                _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
244            },
245            Ref => match ty.kind() {
246                ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
247                _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
248            },
249            Slice(slice) => match *ty.kind() {
250                ty::Slice(ty) | ty::Array(ty, _) => {
251                    let arity = slice.arity();
252                    reveal_and_alloc(cx, (0..arity).map(|_| ty))
253                }
254                _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
255            },
256            Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
257            | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
258            | PrivateUninhabited | Wildcard => &[],
259            Or => {
260                bug!("called `Fields::wildcards` on an `Or` ctor")
261            }
262        };
263        slice.iter().copied()
264    }
265
266    /// The number of fields for this constructor.
267    pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
268        match ctor {
269            Struct | Variant(_) | UnionField => match ty.kind() {
270                ty::Tuple(fs) => fs.len(),
271                ty::Adt(adt, ..) => {
272                    if adt.is_box() {
273                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
274                        // patterns. If we're here we can assume this is a box pattern.
275                        1
276                    } else {
277                        let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt);
278                        adt.variant(variant_idx).fields.len()
279                    }
280                }
281                _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
282            },
283            Ref => 1,
284            Slice(slice) => slice.arity(),
285            Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
286            | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
287            | PrivateUninhabited | Wildcard => 0,
288            Or => bug!("The `Or` constructor doesn't have a fixed arity"),
289        }
290    }
291
292    /// Creates a set that represents all the constructors of `ty`.
293    ///
294    /// See [`crate::constructor`] for considerations of emptiness.
295    pub fn ctors_for_ty(
296        &self,
297        ty: RevealedTy<'tcx>,
298    ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
299        let cx = self;
300        let make_uint_range = |start, end| {
301            IntRange::from_range(
302                MaybeInfiniteInt::new_finite_uint(start),
303                MaybeInfiniteInt::new_finite_uint(end),
304                RangeEnd::Included,
305            )
306        };
307        // Abort on type error.
308        ty.error_reported()?;
309        // This determines the set of all possible constructors for the type `ty`. For numbers,
310        // arrays and slices we use ranges and variable-length slices when appropriate.
311        Ok(match ty.kind() {
312            ty::Bool => ConstructorSet::Bool,
313            ty::Char => {
314                // The valid Unicode Scalar Value ranges.
315                ConstructorSet::Integers {
316                    range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128),
317                    range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)),
318                }
319            }
320            &ty::Int(ity) => {
321                let range = if ty.is_ptr_sized_integral() {
322                    // The min/max values of `isize` are not allowed to be observed.
323                    IntRange {
324                        lo: MaybeInfiniteInt::NegInfinity,
325                        hi: MaybeInfiniteInt::PosInfinity,
326                    }
327                } else {
328                    let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
329                    let min = 1u128 << (size - 1);
330                    let max = min - 1;
331                    let min = MaybeInfiniteInt::new_finite_int(min, size);
332                    let max = MaybeInfiniteInt::new_finite_int(max, size);
333                    IntRange::from_range(min, max, RangeEnd::Included)
334                };
335                ConstructorSet::Integers { range_1: range, range_2: None }
336            }
337            &ty::Uint(uty) => {
338                let range = if ty.is_ptr_sized_integral() {
339                    // The max value of `usize` is not allowed to be observed.
340                    let lo = MaybeInfiniteInt::new_finite_uint(0);
341                    IntRange { lo, hi: MaybeInfiniteInt::PosInfinity }
342                } else {
343                    let size = Integer::from_uint_ty(&cx.tcx, uty).size();
344                    let max = size.truncate(u128::MAX);
345                    make_uint_range(0, max)
346                };
347                ConstructorSet::Integers { range_1: range, range_2: None }
348            }
349            ty::Slice(sub_ty) => ConstructorSet::Slice {
350                array_len: None,
351                subtype_is_empty: cx.is_uninhabited(*sub_ty),
352            },
353            ty::Array(sub_ty, len) => {
354                // We treat arrays of a constant but unknown length like slices.
355                ConstructorSet::Slice {
356                    array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize),
357                    subtype_is_empty: cx.is_uninhabited(*sub_ty),
358                }
359            }
360            ty::Adt(def, args) if def.is_enum() => {
361                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
362                if def.variants().is_empty() && !is_declared_nonexhaustive {
363                    ConstructorSet::NoConstructors
364                } else {
365                    let mut variants =
366                        IndexVec::from_elem(VariantVisibility::Visible, def.variants());
367                    for (idx, v) in def.variants().iter_enumerated() {
368                        let variant_def_id = def.variant(idx).def_id;
369                        // Visibly uninhabited variants.
370                        let is_inhabited = v
371                            .inhabited_predicate(cx.tcx, *def)
372                            .instantiate(cx.tcx, args)
373                            .apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| {
374                                cx.reveal_opaque_key(key)
375                            });
376                        // Variants that depend on a disabled unstable feature.
377                        let is_unstable = matches!(
378                            cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
379                            EvalResult::Deny { .. }
380                        );
381                        // Foreign `#[doc(hidden)]` variants.
382                        let is_doc_hidden =
383                            cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
384                        let visibility = if !is_inhabited {
385                            // FIXME: handle empty+hidden
386                            VariantVisibility::Empty
387                        } else if is_unstable || is_doc_hidden {
388                            VariantVisibility::Hidden
389                        } else {
390                            VariantVisibility::Visible
391                        };
392                        variants[idx] = visibility;
393                    }
394
395                    ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
396                }
397            }
398            ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
399            ty::Adt(..) | ty::Tuple(..) => {
400                ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
401            }
402            ty::Ref(..) => ConstructorSet::Ref,
403            ty::Never => ConstructorSet::NoConstructors,
404            // This type is one for which we cannot list constructors, like `str` or `f64`.
405            // FIXME(Nadrieril): which of these are actually allowed?
406            ty::Float(_)
407            | ty::Str
408            | ty::Foreign(_)
409            | ty::RawPtr(_, _)
410            | ty::FnDef(_, _)
411            | ty::FnPtr(..)
412            | ty::Pat(_, _)
413            | ty::Dynamic(_, _, _)
414            | ty::Closure(..)
415            | ty::CoroutineClosure(..)
416            | ty::Coroutine(_, _)
417            | ty::UnsafeBinder(_)
418            | ty::Alias(_, _)
419            | ty::Param(_)
420            | ty::Error(_) => ConstructorSet::Unlistable,
421            ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
422                bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
423            }
424        })
425    }
426
427    pub(crate) fn lower_pat_range_bdy(
428        &self,
429        bdy: PatRangeBoundary<'tcx>,
430        ty: RevealedTy<'tcx>,
431    ) -> MaybeInfiniteInt {
432        match bdy {
433            PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
434            PatRangeBoundary::Finite(value) => {
435                let bits = value.eval_bits(self.tcx, self.typing_env);
436                match *ty.kind() {
437                    ty::Int(ity) => {
438                        let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
439                        MaybeInfiniteInt::new_finite_int(bits, size)
440                    }
441                    _ => MaybeInfiniteInt::new_finite_uint(bits),
442                }
443            }
444            PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity,
445        }
446    }
447
448    /// Note: the input patterns must have been lowered through
449    /// `rustc_mir_build::thir::pattern::check_match::MatchVisitor::lower_pattern`.
450    pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
451        let cx = self;
452        let ty = cx.reveal_opaque_ty(pat.ty);
453        let ctor;
454        let arity;
455        let fields: Vec<_>;
456        match &pat.kind {
457            PatKind::AscribeUserType { subpattern, .. }
458            | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern),
459            PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat),
460            PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
461                ctor = Wildcard;
462                fields = vec![];
463                arity = 0;
464            }
465            PatKind::Deref { subpattern } => {
466                fields = vec![self.lower_pat(subpattern).at_index(0)];
467                arity = 1;
468                ctor = match ty.kind() {
469                    // This is a box pattern.
470                    ty::Adt(adt, ..) if adt.is_box() => Struct,
471                    ty::Ref(..) => Ref,
472                    _ => span_bug!(
473                        pat.span,
474                        "pattern has unexpected type: pat: {:?}, ty: {:?}",
475                        pat.kind,
476                        ty.inner()
477                    ),
478                };
479            }
480            PatKind::DerefPattern { .. } => {
481                // FIXME(deref_patterns): At least detect that `box _` is irrefutable.
482                fields = vec![];
483                arity = 0;
484                ctor = Opaque(OpaqueId::new());
485            }
486            PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
487                match ty.kind() {
488                    ty::Tuple(fs) => {
489                        ctor = Struct;
490                        arity = fs.len();
491                        fields = subpatterns
492                            .iter()
493                            .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
494                            .collect();
495                    }
496                    ty::Adt(adt, _) if adt.is_box() => {
497                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
498                        // patterns. If we're here we can assume this is a box pattern.
499                        // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
500                        // _)` or a box pattern. As a hack to avoid an ICE with the former, we
501                        // ignore other fields than the first one. This will trigger an error later
502                        // anyway.
503                        // See https://github.com/rust-lang/rust/issues/82772,
504                        // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
505                        // The problem is that we can't know from the type whether we'll match
506                        // normally or through box-patterns. We'll have to figure out a proper
507                        // solution when we introduce generalized deref patterns. Also need to
508                        // prevent mixing of those two options.
509                        let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
510                        if let Some(pat) = pattern {
511                            fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
512                        } else {
513                            fields = vec![];
514                        }
515                        ctor = Struct;
516                        arity = 1;
517                    }
518                    ty::Adt(adt, _) => {
519                        ctor = match pat.kind {
520                            PatKind::Leaf { .. } if adt.is_union() => UnionField,
521                            PatKind::Leaf { .. } => Struct,
522                            PatKind::Variant { variant_index, .. } => Variant(variant_index),
523                            _ => bug!(),
524                        };
525                        let variant =
526                            &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));
527                        arity = variant.fields.len();
528                        fields = subpatterns
529                            .iter()
530                            .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
531                            .collect();
532                    }
533                    _ => span_bug!(
534                        pat.span,
535                        "pattern has unexpected type: pat: {:?}, ty: {}",
536                        pat.kind,
537                        ty.inner()
538                    ),
539                }
540            }
541            PatKind::Constant { value } => {
542                match ty.kind() {
543                    ty::Bool => {
544                        ctor = match value.try_eval_bool(cx.tcx, cx.typing_env) {
545                            Some(b) => Bool(b),
546                            None => Opaque(OpaqueId::new()),
547                        };
548                        fields = vec![];
549                        arity = 0;
550                    }
551                    ty::Char | ty::Int(_) | ty::Uint(_) => {
552                        ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
553                            Some(bits) => {
554                                let x = match *ty.kind() {
555                                    ty::Int(ity) => {
556                                        let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
557                                        MaybeInfiniteInt::new_finite_int(bits, size)
558                                    }
559                                    _ => MaybeInfiniteInt::new_finite_uint(bits),
560                                };
561                                IntRange(IntRange::from_singleton(x))
562                            }
563                            None => Opaque(OpaqueId::new()),
564                        };
565                        fields = vec![];
566                        arity = 0;
567                    }
568                    ty::Float(ty::FloatTy::F16) => {
569                        ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
570                            Some(bits) => {
571                                use rustc_apfloat::Float;
572                                let value = rustc_apfloat::ieee::Half::from_bits(bits);
573                                F16Range(value, value, RangeEnd::Included)
574                            }
575                            None => Opaque(OpaqueId::new()),
576                        };
577                        fields = vec![];
578                        arity = 0;
579                    }
580                    ty::Float(ty::FloatTy::F32) => {
581                        ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
582                            Some(bits) => {
583                                use rustc_apfloat::Float;
584                                let value = rustc_apfloat::ieee::Single::from_bits(bits);
585                                F32Range(value, value, RangeEnd::Included)
586                            }
587                            None => Opaque(OpaqueId::new()),
588                        };
589                        fields = vec![];
590                        arity = 0;
591                    }
592                    ty::Float(ty::FloatTy::F64) => {
593                        ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
594                            Some(bits) => {
595                                use rustc_apfloat::Float;
596                                let value = rustc_apfloat::ieee::Double::from_bits(bits);
597                                F64Range(value, value, RangeEnd::Included)
598                            }
599                            None => Opaque(OpaqueId::new()),
600                        };
601                        fields = vec![];
602                        arity = 0;
603                    }
604                    ty::Float(ty::FloatTy::F128) => {
605                        ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
606                            Some(bits) => {
607                                use rustc_apfloat::Float;
608                                let value = rustc_apfloat::ieee::Quad::from_bits(bits);
609                                F128Range(value, value, RangeEnd::Included)
610                            }
611                            None => Opaque(OpaqueId::new()),
612                        };
613                        fields = vec![];
614                        arity = 0;
615                    }
616                    ty::Ref(_, t, _) if t.is_str() => {
617                        // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
618                        // with other `Deref` patterns. This could have been done in `const_to_pat`,
619                        // but that causes issues with the rest of the matching code.
620                        // So here, the constructor for a `"foo"` pattern is `&` (represented by
621                        // `Ref`), and has one field. That field has constructor `Str(value)` and no
622                        // subfields.
623                        // Note: `t` is `str`, not `&str`.
624                        let ty = self.reveal_opaque_ty(*t);
625                        let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
626                        ctor = Ref;
627                        fields = vec![subpattern.at_index(0)];
628                        arity = 1;
629                    }
630                    // All constants that can be structurally matched have already been expanded
631                    // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
632                    // opaque.
633                    _ => {
634                        ctor = Opaque(OpaqueId::new());
635                        fields = vec![];
636                        arity = 0;
637                    }
638                }
639            }
640            PatKind::Range(patrange) => {
641                let PatRange { lo, hi, end, .. } = patrange.as_ref();
642                let end = match end {
643                    rustc_hir::RangeEnd::Included => RangeEnd::Included,
644                    rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
645                };
646                ctor = match ty.kind() {
647                    ty::Char | ty::Int(_) | ty::Uint(_) => {
648                        let lo = cx.lower_pat_range_bdy(*lo, ty);
649                        let hi = cx.lower_pat_range_bdy(*hi, ty);
650                        IntRange(IntRange::from_range(lo, hi, end))
651                    }
652                    ty::Float(fty) => {
653                        use rustc_apfloat::Float;
654                        let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
655                        let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
656                        match fty {
657                            ty::FloatTy::F16 => {
658                                use rustc_apfloat::ieee::Half;
659                                let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
660                                let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
661                                F16Range(lo, hi, end)
662                            }
663                            ty::FloatTy::F32 => {
664                                use rustc_apfloat::ieee::Single;
665                                let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
666                                let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
667                                F32Range(lo, hi, end)
668                            }
669                            ty::FloatTy::F64 => {
670                                use rustc_apfloat::ieee::Double;
671                                let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
672                                let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
673                                F64Range(lo, hi, end)
674                            }
675                            ty::FloatTy::F128 => {
676                                use rustc_apfloat::ieee::Quad;
677                                let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
678                                let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
679                                F128Range(lo, hi, end)
680                            }
681                        }
682                    }
683                    _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()),
684                };
685                fields = vec![];
686                arity = 0;
687            }
688            PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
689                let array_len = match ty.kind() {
690                    ty::Array(_, length) => Some(
691                        length
692                            .try_to_target_usize(cx.tcx)
693                            .expect("expected len of array pat to be definite")
694                            as usize,
695                    ),
696                    ty::Slice(_) => None,
697                    _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()),
698                };
699                let kind = if slice.is_some() {
700                    SliceKind::VarLen(prefix.len(), suffix.len())
701                } else {
702                    SliceKind::FixedLen(prefix.len() + suffix.len())
703                };
704                ctor = Slice(Slice::new(array_len, kind));
705                fields = prefix
706                    .iter()
707                    .chain(suffix.iter())
708                    .map(|p| self.lower_pat(&*p))
709                    .enumerate()
710                    .map(|(i, p)| p.at_index(i))
711                    .collect();
712                arity = kind.arity();
713            }
714            PatKind::Or { .. } => {
715                ctor = Or;
716                let pats = expand_or_pat(pat);
717                fields = pats
718                    .into_iter()
719                    .map(|p| self.lower_pat(p))
720                    .enumerate()
721                    .map(|(i, p)| p.at_index(i))
722                    .collect();
723                arity = fields.len();
724            }
725            PatKind::Never => {
726                // A never pattern matches all the values of its type (namely none). Moreover it
727                // must be compatible with other constructors, since we can use `!` on a type like
728                // `Result<!, !>` which has other constructors. Hence we lower it as a wildcard.
729                ctor = Wildcard;
730                fields = vec![];
731                arity = 0;
732            }
733            PatKind::Error(_) => {
734                ctor = Opaque(OpaqueId::new());
735                fields = vec![];
736                arity = 0;
737            }
738        }
739        DeconstructedPat::new(ctor, fields, arity, ty, pat)
740    }
741
742    /// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes.
743    /// Note: it is possible to get `isize/usize::MAX+1` here, as explained in the doc for
744    /// [`IntRange::split`]. This cannot be represented as a `Const`, so we represent it with
745    /// `PosInfinity`.
746    fn hoist_pat_range_bdy(
747        &self,
748        miint: MaybeInfiniteInt,
749        ty: RevealedTy<'tcx>,
750    ) -> PatRangeBoundary<'tcx> {
751        use MaybeInfiniteInt::*;
752        let tcx = self.tcx;
753        match miint {
754            NegInfinity => PatRangeBoundary::NegInfinity,
755            Finite(_) => {
756                let size = ty.primitive_size(tcx);
757                let bits = match *ty.kind() {
758                    ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(),
759                    _ => miint.as_finite_uint().unwrap(),
760                };
761                match ScalarInt::try_from_uint(bits, size) {
762                    Some(scalar) => {
763                        let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner());
764                        PatRangeBoundary::Finite(value)
765                    }
766                    // The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value
767                    // for a type, the problem isn't that the value is too small. So it must be too
768                    // large.
769                    None => PatRangeBoundary::PosInfinity,
770                }
771            }
772            PosInfinity => PatRangeBoundary::PosInfinity,
773        }
774    }
775
776    /// Prints an [`IntRange`] to a string for diagnostic purposes.
777    fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
778        use MaybeInfiniteInt::*;
779        let cx = self;
780        if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
781            "_".to_string()
782        } else if range.is_singleton() {
783            let lo = cx.hoist_pat_range_bdy(range.lo, ty);
784            let value = lo.as_finite().unwrap();
785            value.to_string()
786        } else {
787            // We convert to an inclusive range for diagnostics.
788            let mut end = rustc_hir::RangeEnd::Included;
789            let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
790            if matches!(lo, PatRangeBoundary::PosInfinity) {
791                // The only reason to get `PosInfinity` here is the special case where
792                // `hoist_pat_range_bdy` found `{u,i}size::MAX+1`. So the range denotes the
793                // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do
794                // this). We show this to the user as `usize::MAX..` which is slightly incorrect but
795                // probably clear enough.
796                lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap());
797            }
798            let hi = if let Some(hi) = range.hi.minus_one() {
799                hi
800            } else {
801                // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
802                end = rustc_hir::RangeEnd::Excluded;
803                range.hi
804            };
805            let hi = cx.hoist_pat_range_bdy(hi, ty);
806            PatRange { lo, hi, end, ty: ty.inner() }.to_string()
807        }
808    }
809
810    /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
811    ///
812    /// This panics for patterns that don't appear in diagnostics, like float ranges.
813    pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
814        let cx = self;
815        let print = |p| cx.print_witness_pat(p);
816        match pat.ctor() {
817            Bool(b) => b.to_string(),
818            Str(s) => s.to_string(),
819            IntRange(range) => return self.print_pat_range(range, *pat.ty()),
820            Struct if pat.ty().is_box() => {
821                // Outside of the `alloc` crate, the only way to create a struct pattern
822                // of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
823                format!("box {}", print(&pat.fields[0]))
824            }
825            Struct | Variant(_) | UnionField => {
826                let enum_info = match *pat.ty().kind() {
827                    ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
828                        adt_def,
829                        variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
830                    },
831                    ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
832                    _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
833                };
834
835                let subpatterns = pat
836                    .iter_fields()
837                    .enumerate()
838                    .map(|(i, pat)| print::FieldPat {
839                        field: FieldIdx::new(i),
840                        pattern: print(pat),
841                        is_wildcard: would_print_as_wildcard(cx.tcx, pat),
842                    })
843                    .collect::<Vec<_>>();
844
845                let mut s = String::new();
846                print::write_struct_like(
847                    &mut s,
848                    self.tcx,
849                    pat.ty().inner(),
850                    &enum_info,
851                    &subpatterns,
852                )
853                .unwrap();
854                s
855            }
856            Ref => {
857                let mut s = String::new();
858                print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
859                s
860            }
861            Slice(slice) => {
862                let (prefix_len, has_dot_dot) = match slice.kind {
863                    SliceKind::FixedLen(len) => (len, false),
864                    SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
865                };
866
867                let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
868
869                // If the pattern contains a `..`, but is applied to values of statically-known
870                // length (arrays), then we can slightly simplify diagnostics by merging any
871                // adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
872                // (This simplification isn't allowed for slice values, because in that case
873                // `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
874                if has_dot_dot && slice.array_len.is_some() {
875                    while let [rest @ .., last] = prefix
876                        && would_print_as_wildcard(cx.tcx, last)
877                    {
878                        prefix = rest;
879                    }
880                    while let [first, rest @ ..] = suffix
881                        && would_print_as_wildcard(cx.tcx, first)
882                    {
883                        suffix = rest;
884                    }
885                }
886
887                let prefix = prefix.iter().map(print).collect::<Vec<_>>();
888                let suffix = suffix.iter().map(print).collect::<Vec<_>>();
889
890                let mut s = String::new();
891                print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
892                s
893            }
894            Never if self.tcx.features().never_patterns() => "!".to_string(),
895            Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
896            Missing { .. } => bug!(
897                "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
898                `Missing` should have been processed in `apply_constructors`"
899            ),
900            F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
901                bug!("can't convert to pattern: {:?}", pat)
902            }
903        }
904    }
905}
906
907/// Returns `true` if the given pattern would be printed as a wildcard (`_`).
908fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
909    match p.ctor() {
910        Constructor::IntRange(IntRange {
911            lo: MaybeInfiniteInt::NegInfinity,
912            hi: MaybeInfiniteInt::PosInfinity,
913        })
914        | Constructor::Wildcard
915        | Constructor::NonExhaustive
916        | Constructor::Hidden
917        | Constructor::PrivateUninhabited => true,
918        Constructor::Never if !tcx.features().never_patterns() => true,
919        _ => false,
920    }
921}
922
923impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
924    type Ty = RevealedTy<'tcx>;
925    type Error = ErrorGuaranteed;
926    type VariantIdx = VariantIdx;
927    type StrLit = Const<'tcx>;
928    type ArmData = HirId;
929    type PatData = &'p Pat<'tcx>;
930
931    fn is_exhaustive_patterns_feature_on(&self) -> bool {
932        self.tcx.features().exhaustive_patterns()
933    }
934
935    fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
936        self.ctor_arity(ctor, *ty)
937    }
938    fn ctor_sub_tys(
939        &self,
940        ctor: &crate::constructor::Constructor<Self>,
941        ty: &Self::Ty,
942    ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator {
943        self.ctor_sub_tys(ctor, *ty)
944    }
945    fn ctors_for_ty(
946        &self,
947        ty: &Self::Ty,
948    ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
949        self.ctors_for_ty(*ty)
950    }
951
952    fn write_variant_name(
953        f: &mut fmt::Formatter<'_>,
954        ctor: &crate::constructor::Constructor<Self>,
955        ty: &Self::Ty,
956    ) -> fmt::Result {
957        if let ty::Adt(adt, _) = ty.kind() {
958            if adt.is_box() {
959                write!(f, "Box")?
960            } else {
961                let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt));
962                write!(f, "{}", variant.name)?;
963            }
964        }
965        Ok(())
966    }
967
968    fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error {
969        span_bug!(self.scrut_span, "{}", fmt)
970    }
971
972    fn lint_overlapping_range_endpoints(
973        &self,
974        pat: &crate::pat::DeconstructedPat<Self>,
975        overlaps_on: IntRange,
976        overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
977    ) {
978        let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
979        let overlaps: Vec<_> = overlaps_with
980            .iter()
981            .map(|pat| pat.data().span)
982            .map(|span| errors::Overlap { range: overlap_as_pat.to_string(), span })
983            .collect();
984        let pat_span = pat.data().span;
985        self.tcx.emit_node_span_lint(
986            lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
987            self.match_lint_level,
988            pat_span,
989            errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
990        );
991    }
992
993    fn complexity_exceeded(&self) -> Result<(), Self::Error> {
994        let span = self.whole_match_span.unwrap_or(self.scrut_span);
995        Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
996    }
997
998    fn lint_non_contiguous_range_endpoints(
999        &self,
1000        pat: &crate::pat::DeconstructedPat<Self>,
1001        gap: IntRange,
1002        gapped_with: &[&crate::pat::DeconstructedPat<Self>],
1003    ) {
1004        let &thir_pat = pat.data();
1005        let thir::PatKind::Range(range) = &thir_pat.kind else { return };
1006        // Only lint when the left range is an exclusive range.
1007        if range.end != rustc_hir::RangeEnd::Excluded {
1008            return;
1009        }
1010        // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with
1011        // `gap+1`.
1012        let suggested_range: String = {
1013            // Suggest `lo..=gap` instead.
1014            let mut suggested_range = PatRange::clone(range);
1015            suggested_range.end = rustc_hir::RangeEnd::Included;
1016            suggested_range.to_string()
1017        };
1018        let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
1019        if gapped_with.is_empty() {
1020            // If `gapped_with` is empty, `gap == T::MAX`.
1021            self.tcx.emit_node_span_lint(
1022                lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1023                self.match_lint_level,
1024                thir_pat.span,
1025                errors::ExclusiveRangeMissingMax {
1026                    // Point at this range.
1027                    first_range: thir_pat.span,
1028                    // That's the gap that isn't covered.
1029                    max: gap_as_pat,
1030                    // Suggest `lo..=max` instead.
1031                    suggestion: suggested_range,
1032                },
1033            );
1034        } else {
1035            self.tcx.emit_node_span_lint(
1036                lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
1037                self.match_lint_level,
1038                thir_pat.span,
1039                errors::ExclusiveRangeMissingGap {
1040                    // Point at this range.
1041                    first_range: thir_pat.span,
1042                    // That's the gap that isn't covered.
1043                    gap: gap_as_pat.to_string(),
1044                    // Suggest `lo..=gap` instead.
1045                    suggestion: suggested_range,
1046                    // All these ranges skipped over `gap` which we think is probably a
1047                    // mistake.
1048                    gap_with: gapped_with
1049                        .iter()
1050                        .map(|pat| errors::GappedRange {
1051                            span: pat.data().span,
1052                            gap: gap_as_pat.to_string(),
1053                            first_range: range.to_string(),
1054                        })
1055                        .collect(),
1056                },
1057            );
1058        }
1059    }
1060}
1061
1062/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
1063fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
1064    fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
1065        if let PatKind::Or { pats } = &pat.kind {
1066            for pat in pats.iter() {
1067                expand(pat, vec);
1068            }
1069        } else {
1070            vec.push(pat)
1071        }
1072    }
1073
1074    let mut pats = Vec::new();
1075    expand(pat, &mut pats);
1076    pats
1077}
1078
1079/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
1080/// useful, and runs some lints.
1081pub fn analyze_match<'p, 'tcx>(
1082    tycx: &RustcPatCtxt<'p, 'tcx>,
1083    arms: &[MatchArm<'p, 'tcx>],
1084    scrut_ty: Ty<'tcx>,
1085) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
1086    let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
1087    let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
1088    let report = compute_match_usefulness(
1089        tycx,
1090        arms,
1091        scrut_ty,
1092        scrut_validity,
1093        tycx.tcx.pattern_complexity_limit().0,
1094    )?;
1095
1096    // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
1097    // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
1098    if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
1099        let pat_column = PatternColumn::new(arms);
1100        lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?;
1101    }
1102
1103    Ok(report)
1104}