From b111b2e8390c4fe0b1a5c1c8a7e75f7ca7df9fcf Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 13:32:34 +0100 Subject: [PATCH 01/16] Split `Single` ctor into more specific variants --- .../src/thir/pattern/check_match.rs | 2 +- .../rustc_pattern_analysis/src/constructor.rs | 48 ++++++++---- compiler/rustc_pattern_analysis/src/cx.rs | 73 +++++++++++-------- .../rustc_pattern_analysis/src/usefulness.rs | 9 +-- 4 files changed, 80 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 792a443c908..20c3ed2ed42 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -874,7 +874,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, - Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Struct | Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 3bca7894a29..95ffece54f3 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -631,11 +631,16 @@ pub fn new() -> Self { /// `Fields`. #[derive(Clone, Debug, PartialEq)] pub enum Constructor<'tcx> { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns, - /// and references. Fixed-length arrays are treated separately with `Slice`. - Single, + /// Tuples and structs. + Struct, /// Enum variants. Variant(VariantIdx), + /// References + Ref, + /// Array and slice patterns. + Slice(Slice), + /// Union field accesses. + UnionField, /// Booleans Bool(bool), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). @@ -645,8 +650,6 @@ pub enum Constructor<'tcx> { F64Range(IeeeFloat, IeeeFloat, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. Str(Const<'tcx>), - /// Array and slice patterns. - Slice(Slice), /// Constants that must not be matched structurally. They are treated as black boxes for the /// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a /// match exhaustive. @@ -723,7 +726,9 @@ pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, other: &Self // Only a wildcard pattern can match these special constructors. (Missing { .. } | NonExhaustive | Hidden, _) => false, - (Single, Single) => true, + (Struct, Struct) => true, + (Ref, Ref) => true, + (UnionField, UnionField) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, (Bool(self_b), Bool(other_b)) => self_b == other_b, @@ -786,12 +791,15 @@ pub enum VariantVisibility { /// `exhaustive_patterns` feature. #[derive(Debug)] pub enum ConstructorSet { - /// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the - /// constructor is empty. - Single { empty: bool }, + /// The type is a tuple or struct. `empty` tracks whether the type is empty. + Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. Variants { variants: IndexVec, non_exhaustive: bool }, + /// The type is `&T`. + Ref, + /// The type is a union. + Union, /// Booleans. Bool, /// The type is spanned by integer values. The range or ranges give the set of allowed values. @@ -866,13 +874,27 @@ pub(crate) fn split<'a, 'tcx>( } match self { - ConstructorSet::Single { empty } => { + ConstructorSet::Struct { empty } => { if !seen.is_empty() { - present.push(Single); + present.push(Struct); } else if *empty { - missing_empty.push(Single); + missing_empty.push(Struct); } else { - missing.push(Single); + missing.push(Struct); + } + } + ConstructorSet::Ref => { + if !seen.is_empty() { + present.push(Ref); + } else { + missing.push(Ref); + } + } + ConstructorSet::Union => { + if !seen.is_empty() { + present.push(UnionField); + } else { + missing.push(UnionField); } } ConstructorSet::Variants { variants, non_exhaustive } => { diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/cx.rs index 8a4f39a1f4a..81c836b878e 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/cx.rs @@ -105,7 +105,7 @@ pub(crate) fn variant_index_for_adt( ) -> VariantIdx { match *ctor { Variant(idx) => idx, - Single => { + Struct | UnionField => { assert!(!adt.is_enum()); FIRST_VARIANT } @@ -123,9 +123,8 @@ pub(crate) fn ctor_wildcard_fields( ) -> &'p [DeconstructedPat<'p, 'tcx>] { let cx = self; match ctor { - Single | Variant(_) => match ty.kind() { + Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => cx.alloc_wildcard_slice(fs.iter()), - ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)), ty::Adt(adt, args) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box @@ -138,7 +137,11 @@ pub(crate) fn ctor_wildcard_fields( cx.alloc_wildcard_slice(tys) } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), + }, + Ref => match ty.kind() { + ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)), + _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"), }, Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { @@ -167,9 +170,8 @@ pub(crate) fn ctor_wildcard_fields( /// `Fields::wildcards`. pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize { match ctor { - Single | Variant(_) => match ty.kind() { + Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => fs.len(), - ty::Ref(..) => 1, ty::Adt(adt, ..) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box @@ -181,8 +183,9 @@ pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize self.list_variant_nonhidden_fields(ty, variant).count() } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), }, + Ref => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) @@ -298,9 +301,9 @@ pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet { ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive } } } - ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => { - ConstructorSet::Single { empty: cx.is_uninhabited(ty) } - } + ty::Adt(def, _) if def.is_union() => ConstructorSet::Union, + ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) }, + ty::Ref(..) => ConstructorSet::Ref, ty::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. // FIXME(Nadrieril): which of these are actually allowed? @@ -359,13 +362,18 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { fields = &[]; } PatKind::Deref { subpattern } => { - ctor = Single; fields = singleton(self.lower_pat(subpattern)); + ctor = match pat.ty.kind() { + // This is a box pattern. + ty::Adt(adt, ..) if adt.is_box() => Struct, + ty::Ref(..) => Ref, + _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), + }; } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match pat.ty.kind() { ty::Tuple(fs) => { - ctor = Single; + ctor = Struct; let mut wilds: SmallVec<[_; 2]> = fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); for pat in subpatterns { @@ -380,7 +388,7 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { // _)` or a box pattern. As a hack to avoid an ICE with the former, we // ignore other fields than the first one. This will trigger an error later // anyway. - // See https://github.com/rust-lang/rust/issues/82772 , + // See https://github.com/rust-lang/rust/issues/82772, // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 // The problem is that we can't know from the type whether we'll match // normally or through box-patterns. We'll have to figure out a proper @@ -392,12 +400,13 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { } else { DeconstructedPat::wildcard(args.type_at(0), pat.span) }; - ctor = Single; + ctor = Struct; fields = singleton(pat); } ty::Adt(adt, _) => { ctor = match pat.kind { - PatKind::Leaf { .. } => Single, + PatKind::Leaf { .. } if adt.is_union() => UnionField, + PatKind::Leaf { .. } => Struct, PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; @@ -477,11 +486,11 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { // with other `Deref` patterns. This could have been done in `const_to_pat`, // but that causes issues with the rest of the matching code. // So here, the constructor for a `"foo"` pattern is `&` (represented by - // `Single`), and has one field. That field has constructor `Str(value)` and no - // fields. + // `Ref`), and has one field. That field has constructor `Str(value)` and no + // subfields. // Note: `t` is `str`, not `&str`. let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat.span); - ctor = Single; + ctor = Ref; fields = singleton(subpattern) } // All constants that can be structurally matched have already been expanded @@ -657,7 +666,7 @@ pub fn hoist_witness_pat(&self, pat: &WitnessPat<'tcx>) -> Pat<'tcx> { let kind = match pat.ctor() { Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, IntRange(range) => return self.hoist_pat_range(range, pat.ty()), - Single | Variant(_) => match pat.ty().kind() { + Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .enumerate() @@ -686,13 +695,13 @@ pub fn hoist_witness_pat(&self, pat: &WitnessPat<'tcx>) -> Pat<'tcx> { PatKind::Leaf { subpatterns } } } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), }, + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, Slice(slice) => { match slice.kind { SliceKind::FixedLen(_) => PatKind::Slice { @@ -758,7 +767,7 @@ pub(crate) fn debug_pat( let mut start_or_comma = || start_or_continue(", "); match pat.ctor() { - Single | Variant(_) => match pat.ty().kind() { + Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Adt(def, _) if def.is_box() => { // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside // of `std`). So this branch is only reachable when the feature is enabled and @@ -789,15 +798,15 @@ pub(crate) fn debug_pat( } write!(f, ")") } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - ty::Ref(_, _, mutbl) => { - let subpattern = pat.iter_fields().next().unwrap(); - write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern) - } _ => write!(f, "_"), }, + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to detect strings here. However a string literal pattern will never + // be reported as a non-exhaustiveness witness, so we can ignore this issue. + Ref => { + let subpattern = pat.iter_fields().next().unwrap(); + write!(f, "&{:?}", subpattern) + } Slice(slice) => { let mut subpatterns = pat.iter_fields(); write!(f, "[")?; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index f268a551547..a2467ae6a39 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -629,12 +629,9 @@ fn allows_omitting_empty_arms(self) -> bool { /// /// Pending further opsem decisions, the current behavior is: validity is preserved, except /// inside `&` and union fields where validity is reset to `MaybeInvalid`. - fn specialize<'tcx>(self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self { + fn specialize(self, ctor: &Constructor<'_>) -> Self { // We preserve validity except when we go inside a reference or a union field. - if matches!(ctor, Constructor::Single) - && (matches!(pcx.ty.kind(), ty::Ref(..)) - || matches!(pcx.ty.kind(), ty::Adt(def, ..) if def.is_union())) - { + if matches!(ctor, Constructor::Ref | Constructor::UnionField) { // Validity of `x: &T` does not imply validity of `*x: T`. MaybeInvalid } else { @@ -902,7 +899,7 @@ fn specialize_constructor( ctor: &Constructor<'tcx>, ) -> Matrix<'p, 'tcx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); - let new_validity = self.place_validity[0].specialize(pcx, ctor); + let new_validity = self.place_validity[0].specialize(ctor); let new_place_validity = std::iter::repeat(new_validity) .take(ctor.arity(pcx)) .chain(self.place_validity[1..].iter().copied()) From 081c3dcf43e31ea2c5226ead3639a500b3ac3049 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 11:11:12 +0100 Subject: [PATCH 02/16] Remove all matching on `ty.kind()` outside `cx` --- compiler/rustc_pattern_analysis/src/cx.rs | 6 ++- compiler/rustc_pattern_analysis/src/lints.rs | 5 +- compiler/rustc_pattern_analysis/src/pat.rs | 49 ++++++++----------- .../rustc_pattern_analysis/src/usefulness.rs | 7 ++- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/cx.rs index 81c836b878e..e4acf317a0d 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/cx.rs @@ -49,10 +49,14 @@ pub struct MatchCheckCtxt<'p, 'tcx> { } impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { - pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { !ty.is_inhabited_from(self.tcx, self.module, self.param_env) } + pub(crate) fn is_opaque(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Alias(ty::Opaque, ..)) + } + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind() { diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 8ab559c9e7a..aaa859f33fa 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -53,12 +53,11 @@ fn head_ty(&self) -> Option> { } // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. - let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..)); let first_ty = self.patterns[0].ty(); - if is_opaque(first_ty) { + if MatchCheckCtxt::is_opaque(first_ty) { for pat in &self.patterns { let ty = pat.ty(); - if !is_opaque(ty) { + if !MatchCheckCtxt::is_opaque(ty) { return Some(ty); } } diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 404651124ad..7f33a1ab40c 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -6,13 +6,12 @@ use smallvec::{smallvec, SmallVec}; use rustc_data_structures::captures::Captures; -use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_middle::ty::Ty; +use rustc_span::Span; use self::Constructor::*; -use self::SliceKind::*; -use crate::constructor::{Constructor, SliceKind}; +use crate::constructor::{Constructor, Slice, SliceKind}; use crate::cx::MatchCheckCtxt; use crate::usefulness::PatCtxt; @@ -90,31 +89,25 @@ pub(crate) fn specialize<'a>( // We return a wildcard for each field of `other_ctor`. pcx.cx.ctor_wildcard_fields(other_ctor, pcx.ty).iter().collect() } - (Slice(self_slice), Slice(other_slice)) - if self_slice.arity() != other_slice.arity() => - { - // The only tricky case: two slices of different arity. Since `self_slice` covers - // `other_slice`, `self_slice` must be `VarLen`, i.e. of the form - // `[prefix, .., suffix]`. Moreover `other_slice` is guaranteed to have a larger - // arity. So we fill the middle part with enough wildcards to reach the length of - // the new, larger slice. - match self_slice.kind { - FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice), - VarLen(prefix, suffix) => { - let (ty::Slice(inner_ty) | ty::Array(inner_ty, _)) = *self.ty.kind() else { - bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty); - }; - let prefix = &self.fields[..prefix]; - let suffix = &self.fields[self_slice.arity() - suffix..]; - let wildcard: &_ = pcx - .cx - .pattern_arena - .alloc(DeconstructedPat::wildcard(inner_ty, DUMMY_SP)); - let extra_wildcards = other_slice.arity() - self_slice.arity(); - let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); - prefix.iter().chain(extra_wildcards).chain(suffix).collect() - } + ( + &Slice(self_slice @ Slice { kind: SliceKind::VarLen(prefix, suffix), .. }), + &Slice(other_slice), + ) if self_slice.arity() != other_slice.arity() => { + // The only non-trivial case: two slices of different arity. `other_slice` is + // guaranteed to have a larger arity, so we fill the middle part with enough + // wildcards to reach the length of the new, larger slice. + // Start with a slice of wildcards of the appropriate length. + let mut fields: SmallVec<[_; 2]> = + pcx.cx.ctor_wildcard_fields(other_ctor, pcx.ty).iter().collect(); + // Fill in the fields from both ends. + let new_arity = fields.len(); + for i in 0..prefix { + fields[i] = &self.fields[i]; } + for i in 0..suffix { + fields[new_arity - 1 - i] = &self.fields[self.fields.len() - 1 - i]; + } + fields } _ => self.fields.iter().collect(), } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index a2467ae6a39..d007e382000 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -556,7 +556,7 @@ use std::fmt; use rustc_data_structures::{captures::Captures, stack::ensure_sufficient_stack}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_span::{Span, DUMMY_SP}; use crate::constructor::{Constructor, ConstructorSet}; @@ -856,11 +856,10 @@ fn head_ty(&self) -> Option> { let mut ty = self.wildcard_row.head().ty(); // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. - let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..)); - if is_opaque(ty) { + if MatchCheckCtxt::is_opaque(ty) { for pat in self.heads() { let pat_ty = pat.ty(); - if !is_opaque(pat_ty) { + if !MatchCheckCtxt::is_opaque(pat_ty) { ty = pat_ty; break; } From 3ad76f93256c0869aafeb1404f494f00e6d5b5ae Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 17:57:53 +0100 Subject: [PATCH 03/16] Disentangle the arena from `MatchCheckCtxt` --- .../src/thir/pattern/check_match.rs | 6 +- compiler/rustc_pattern_analysis/src/cx.rs | 33 ++--- compiler/rustc_pattern_analysis/src/lib.rs | 8 +- compiler/rustc_pattern_analysis/src/lints.rs | 47 ++++--- compiler/rustc_pattern_analysis/src/pat.rs | 35 ++--- .../rustc_pattern_analysis/src/usefulness.rs | 128 ++++++++++-------- 6 files changed, 140 insertions(+), 117 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 20c3ed2ed42..109e9117a58 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,7 +7,7 @@ use crate::errors::*; -use rustc_arena::TypedArena; +use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -31,6 +31,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err let (thir, expr) = tcx.thir_body(def_id)?; let thir = thir.borrow(); let pattern_arena = TypedArena::default(); + let dropless_arena = DroplessArena::default(); let mut visitor = MatchVisitor { tcx, thir: &*thir, @@ -38,6 +39,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err lint_level: tcx.local_def_id_to_hir_id(def_id), let_source: LetSource::None, pattern_arena: &pattern_arena, + dropless_arena: &dropless_arena, error: Ok(()), }; visitor.visit_expr(&thir[expr]); @@ -82,6 +84,7 @@ struct MatchVisitor<'thir, 'p, 'tcx> { lint_level: HirId, let_source: LetSource, pattern_arena: &'p TypedArena>, + dropless_arena: &'p DroplessArena, /// Tracks if we encountered an error while checking this body. That the first function to /// report it stores it here. Some functions return `Result` to allow callers to short-circuit /// on error, but callers don't need to store it here again. @@ -382,6 +385,7 @@ fn new_cx( param_env: self.param_env, module: self.tcx.parent_module(self.lint_level).to_def_id(), pattern_arena: self.pattern_arena, + dropless_arena: self.dropless_arena, match_lint_level: self.lint_level, whole_match_span, scrut_span, diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/cx.rs index e4acf317a0d..a4bc99c8013 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/cx.rs @@ -1,15 +1,15 @@ use std::fmt; use std::iter::once; -use rustc_arena::TypedArena; +use rustc_arena::{DroplessArena, TypedArena}; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; use rustc_index::Idx; use rustc_index::IndexVec; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir; use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::{self}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; @@ -35,6 +35,7 @@ pub struct MatchCheckCtxt<'p, 'tcx> { pub module: DefId, pub param_env: ty::ParamEnv<'tcx>, pub pattern_arena: &'p TypedArena>, + pub dropless_arena: &'p DroplessArena, /// Lint level at the match. pub match_lint_level: HirId, /// The span of the whole match, if applicable. @@ -67,14 +68,6 @@ pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { } } - pub(crate) fn alloc_wildcard_slice( - &self, - tys: impl IntoIterator>, - ) -> &'p [DeconstructedPat<'p, 'tcx>] { - self.pattern_arena - .alloc_from_iter(tys.into_iter().map(|ty| DeconstructedPat::wildcard(ty, DUMMY_SP))) - } - // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. // This lists the fields we keep along with their types. @@ -117,40 +110,36 @@ pub(crate) fn variant_index_for_adt( } } - /// Creates a new list of wildcard fields for a given constructor. The result must have a length - /// of `ctor.arity()`. + /// Returns the types of the fields for a given constructor. The result must have a length of + /// `ctor.arity()`. #[instrument(level = "trace", skip(self))] - pub(crate) fn ctor_wildcard_fields( - &self, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ) -> &'p [DeconstructedPat<'p, 'tcx>] { + pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] { let cx = self; match ctor { Struct | Variant(_) | UnionField => match ty.kind() { - ty::Tuple(fs) => cx.alloc_wildcard_slice(fs.iter()), + ty::Tuple(fs) => cx.dropless_arena.alloc_from_iter(fs.iter()), ty::Adt(adt, args) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. - cx.alloc_wildcard_slice(once(args.type_at(0))) + cx.dropless_arena.alloc_from_iter(once(args.type_at(0))) } else { let variant = &adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); - cx.alloc_wildcard_slice(tys) + cx.dropless_arena.alloc_from_iter(tys) } } _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), }, Ref => match ty.kind() { - ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)), + ty::Ref(_, rty, _) => cx.dropless_arena.alloc_from_iter(once(*rty)), _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"), }, Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); - cx.alloc_wildcard_slice((0..arity).map(|_| ty)) + cx.dropless_arena.alloc_from_iter((0..arity).map(|_| ty)) } _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 07730aa49d3..f19dc7345fc 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -39,17 +39,19 @@ pub fn analyze_match<'p, 'tcx>( arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> UsefulnessReport<'p, 'tcx> { + // Arena to store the extra wildcards we construct during analysis. + let wildcard_arena = cx.pattern_arena; let pat_column = PatternColumn::new(arms); - let report = compute_match_usefulness(cx, arms, scrut_ty); + let report = compute_match_usefulness(cx, arms, scrut_ty, wildcard_arena); // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column); + lint_overlapping_range_endpoints(cx, &pat_column, wildcard_arena); // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. if cx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) + lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty, wildcard_arena) } report diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index aaa859f33fa..858e28ce897 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,3 +1,4 @@ +use rustc_arena::TypedArena; use smallvec::SmallVec; use rustc_data_structures::captures::Captures; @@ -27,11 +28,11 @@ /// /// This is not used in the main algorithm; only in lints. #[derive(Debug)] -pub(crate) struct PatternColumn<'p, 'tcx> { - patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>, +pub(crate) struct PatternColumn<'a, 'p, 'tcx> { + patterns: Vec<&'a DeconstructedPat<'p, 'tcx>>, } -impl<'p, 'tcx> PatternColumn<'p, 'tcx> { +impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> { pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self { let mut patterns = Vec::with_capacity(arms.len()); for arm in arms { @@ -71,7 +72,7 @@ fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors) } - fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { + fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.patterns.iter().copied() } @@ -80,7 +81,14 @@ fn iter<'a>(&'a self) -> impl Iterator> + /// This returns one column per field of the constructor. They usually all have the same length /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns /// which may change the lengths. - fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec { + fn specialize<'b>( + &self, + pcx: &'b PatCtxt<'_, 'p, 'tcx>, + ctor: &Constructor<'tcx>, + ) -> Vec> + where + 'a: 'b, + { let arity = ctor.arity(pcx); if arity == 0 { return Vec::new(); @@ -115,15 +123,16 @@ fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> V /// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned /// in a given column. -#[instrument(level = "debug", skip(cx), ret)] -fn collect_nonexhaustive_missing_variants<'p, 'tcx>( +#[instrument(level = "debug", skip(cx, wildcard_arena), ret)] +fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - column: &PatternColumn<'p, 'tcx>, + column: &PatternColumn<'a, 'p, 'tcx>, + wildcard_arena: &TypedArena>, ) -> Vec> { let Some(ty) = column.head_ty() else { return Vec::new(); }; - let pcx = &PatCtxt::new_dummy(cx, ty); + let pcx = &PatCtxt::new_dummy(cx, ty, wildcard_arena); let set = column.analyze_ctors(pcx); if set.present.is_empty() { @@ -150,7 +159,7 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor); for (i, col_i) in specialized_columns.iter().enumerate() { // Compute witnesses for each column. - let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i); + let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i, wildcard_arena); // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`, // adding enough wildcards to match `arity`. for wit in wits_for_col_i { @@ -163,17 +172,18 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( witnesses } -pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( +pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], - pat_column: &PatternColumn<'p, 'tcx>, + pat_column: &PatternColumn<'a, 'p, 'tcx>, scrut_ty: Ty<'tcx>, + wildcard_arena: &TypedArena>, ) { if !matches!( cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, cx.match_lint_level).0, rustc_session::lint::Level::Allow ) { - let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column); + let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column, wildcard_arena); if !witnesses.is_empty() { // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` // is not exhaustive enough. @@ -215,15 +225,16 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( } /// Traverse the patterns to warn the user about ranges that overlap on their endpoints. -#[instrument(level = "debug", skip(cx))] -pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>( +#[instrument(level = "debug", skip(cx, wildcard_arena))] +pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - column: &PatternColumn<'p, 'tcx>, + column: &PatternColumn<'a, 'p, 'tcx>, + wildcard_arena: &TypedArena>, ) { let Some(ty) = column.head_ty() else { return; }; - let pcx = &PatCtxt::new_dummy(cx, ty); + let pcx = &PatCtxt::new_dummy(cx, ty, wildcard_arena); let set = column.analyze_ctors(pcx); @@ -282,7 +293,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>( // Recurse into the fields. for ctor in set.present { for col in column.specialize(pcx, &ctor) { - lint_overlapping_range_endpoints(cx, &col); + lint_overlapping_range_endpoints(cx, &col, wildcard_arena); } } } diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 7f33a1ab40c..29230c390ef 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -53,7 +53,7 @@ pub(crate) fn is_or_pat(&self) -> bool { matches!(self.ctor, Or) } /// Expand this (possibly-nested) or-pattern into its alternatives. - pub(crate) fn flatten_or_pat(&'p self) -> SmallVec<[&'p Self; 1]> { + pub(crate) fn flatten_or_pat(&self) -> SmallVec<[&Self; 1]> { if self.is_or_pat() { self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect() } else { @@ -80,25 +80,29 @@ pub fn iter_fields<'a>( /// Specialize this pattern with a constructor. /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(crate) fn specialize<'a>( - &'a self, - pcx: &PatCtxt<'_, 'p, 'tcx>, + &self, + pcx: &PatCtxt<'a, 'p, 'tcx>, other_ctor: &Constructor<'tcx>, - ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { + ) -> SmallVec<[&'a DeconstructedPat<'p, 'tcx>; 2]> { + let wildcard_sub_tys = || { + let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty); + tys.iter() + .map(|ty| DeconstructedPat::wildcard(*ty, Span::default())) + .map(|pat| pcx.wildcard_arena.alloc(pat) as &_) + .collect() + }; match (&self.ctor, other_ctor) { - (Wildcard, _) => { - // We return a wildcard for each field of `other_ctor`. - pcx.cx.ctor_wildcard_fields(other_ctor, pcx.ty).iter().collect() - } + // Return a wildcard for each field of `other_ctor`. + (Wildcard, _) => wildcard_sub_tys(), + // The only non-trivial case: two slices of different arity. `other_slice` is + // guaranteed to have a larger arity, so we fill the middle part with enough + // wildcards to reach the length of the new, larger slice. ( &Slice(self_slice @ Slice { kind: SliceKind::VarLen(prefix, suffix), .. }), &Slice(other_slice), ) if self_slice.arity() != other_slice.arity() => { - // The only non-trivial case: two slices of different arity. `other_slice` is - // guaranteed to have a larger arity, so we fill the middle part with enough - // wildcards to reach the length of the new, larger slice. // Start with a slice of wildcards of the appropriate length. - let mut fields: SmallVec<[_; 2]> = - pcx.cx.ctor_wildcard_fields(other_ctor, pcx.ty).iter().collect(); + let mut fields: SmallVec<[_; 2]> = wildcard_sub_tys(); // Fill in the fields from both ends. let new_arity = fields.len(); for i in 0..prefix { @@ -179,9 +183,8 @@ pub(crate) fn wildcard(ty: Ty<'tcx>) -> Self { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let field_tys = - pcx.cx.ctor_wildcard_fields(&ctor, pcx.ty).iter().map(|deco_pat| deco_pat.ty()); - let fields = field_tys.map(|ty| Self::wildcard(ty)).collect(); + let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty); + let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); Self::new(ctor, fields, pcx.ty) } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index d007e382000..3300013805d 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -555,9 +555,10 @@ use smallvec::{smallvec, SmallVec}; use std::fmt; +use rustc_arena::TypedArena; use rustc_data_structures::{captures::Captures, stack::ensure_sufficient_stack}; use rustc_middle::ty::Ty; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use crate::constructor::{Constructor, ConstructorSet}; use crate::cx::MatchCheckCtxt; @@ -574,12 +575,18 @@ pub(crate) struct PatCtxt<'a, 'p, 'tcx> { /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a /// subpattern. pub(crate) is_top_level: bool, + /// An arena to store the wildcards we produce during analysis. + pub(crate) wildcard_arena: &'a TypedArena>, } impl<'a, 'p, 'tcx> PatCtxt<'a, 'p, 'tcx> { /// A `PatCtxt` when code other than `is_useful` needs one. - pub(crate) fn new_dummy(cx: &'a MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self { - PatCtxt { cx, ty, is_top_level: false } + pub(crate) fn new_dummy( + cx: &'a MatchCheckCtxt<'p, 'tcx>, + ty: Ty<'tcx>, + wildcard_arena: &'a TypedArena>, + ) -> Self { + PatCtxt { cx, ty, is_top_level: false, wildcard_arena } } } @@ -651,14 +658,18 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } /// Represents a pattern-tuple under investigation. +// The three lifetimes are: +// - 'a allocated by us +// - 'p coming from the input +// - 'tcx global compilation context #[derive(Clone)] -struct PatStack<'p, 'tcx> { +struct PatStack<'a, 'p, 'tcx> { // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well. - pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, + pats: SmallVec<[&'a DeconstructedPat<'p, 'tcx>; 2]>, } -impl<'p, 'tcx> PatStack<'p, 'tcx> { - fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self { +impl<'a, 'p, 'tcx> PatStack<'a, 'p, 'tcx> { + fn from_pattern(pat: &'a DeconstructedPat<'p, 'tcx>) -> Self { PatStack { pats: smallvec![pat] } } @@ -670,17 +681,17 @@ fn len(&self) -> usize { self.pats.len() } - fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'a DeconstructedPat<'p, 'tcx> { self.pats[0] } - fn iter(&self) -> impl Iterator> { + fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.iter().copied() } // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is // an or-pattern. Panics if `self` is empty. - fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { + fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.head().flatten_or_pat().into_iter().map(move |pat| { let mut new = self.clone(); new.pats[0] = pat; @@ -692,9 +703,9 @@ fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Cap /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'_, 'p, 'tcx>, + pcx: &PatCtxt<'a, 'p, 'tcx>, ctor: &Constructor<'tcx>, - ) -> PatStack<'p, 'tcx> { + ) -> PatStack<'a, 'p, 'tcx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. let mut new_pats = self.head().specialize(pcx, ctor); @@ -703,7 +714,7 @@ fn pop_head_constructor( } } -impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { +impl<'a, 'p, 'tcx> fmt::Debug for PatStack<'a, 'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // We pretty-print similarly to the `Debug` impl of `Matrix`. write!(f, "+")?; @@ -716,9 +727,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// A row of the matrix. #[derive(Clone)] -struct MatrixRow<'p, 'tcx> { +struct MatrixRow<'a, 'p, 'tcx> { // The patterns in the row. - pats: PatStack<'p, 'tcx>, + pats: PatStack<'a, 'p, 'tcx>, /// Whether the original arm had a guard. This is inherited when specializing. is_under_guard: bool, /// When we specialize, we remember which row of the original matrix produced a given row of the @@ -731,7 +742,7 @@ struct MatrixRow<'p, 'tcx> { useful: bool, } -impl<'p, 'tcx> MatrixRow<'p, 'tcx> { +impl<'a, 'p, 'tcx> MatrixRow<'a, 'p, 'tcx> { fn is_empty(&self) -> bool { self.pats.is_empty() } @@ -740,17 +751,17 @@ fn len(&self) -> usize { self.pats.len() } - fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'a DeconstructedPat<'p, 'tcx> { self.pats.head() } - fn iter(&self) -> impl Iterator> { + fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.iter() } // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is // an or-pattern. Panics if `self` is empty. - fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { + fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.expand_or_pat().map(|patstack| MatrixRow { pats: patstack, parent_row: self.parent_row, @@ -763,10 +774,10 @@ fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Ca /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'_, 'p, 'tcx>, + pcx: &PatCtxt<'a, 'p, 'tcx>, ctor: &Constructor<'tcx>, parent_row: usize, - ) -> MatrixRow<'p, 'tcx> { + ) -> MatrixRow<'a, 'p, 'tcx> { MatrixRow { pats: self.pats.pop_head_constructor(pcx, ctor), parent_row, @@ -776,7 +787,7 @@ fn pop_head_constructor( } } -impl<'p, 'tcx> fmt::Debug for MatrixRow<'p, 'tcx> { +impl<'a, 'p, 'tcx> fmt::Debug for MatrixRow<'a, 'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.pats.fmt(f) } @@ -793,22 +804,22 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// specializing `(,)` and `Some` on a pattern of type `(Option, bool)`, the first column of /// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`. #[derive(Clone)] -struct Matrix<'p, 'tcx> { +struct Matrix<'a, 'p, 'tcx> { /// Vector of rows. The rows must form a rectangular 2D array. Moreover, all the patterns of /// each column must have the same type. Each column corresponds to a place within the /// scrutinee. - rows: Vec>, + rows: Vec>, /// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of /// each column. This must obey the same invariants as the real rows. - wildcard_row: PatStack<'p, 'tcx>, + wildcard_row: PatStack<'a, 'p, 'tcx>, /// Track for each column/place whether it contains a known valid value. place_validity: SmallVec<[ValidityConstraint; 2]>, } -impl<'p, 'tcx> Matrix<'p, 'tcx> { +impl<'a, 'p, 'tcx> Matrix<'a, 'p, 'tcx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. Internal method, prefer [`Matrix::new`]. - fn expand_and_push(&mut self, row: MatrixRow<'p, 'tcx>) { + fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, 'tcx>) { if !row.is_empty() && row.head().is_or_pat() { // Expand nested or-patterns. for new_row in row.expand_or_pat() { @@ -820,16 +831,14 @@ fn expand_and_push(&mut self, row: MatrixRow<'p, 'tcx>) { } /// Build a new matrix from an iterator of `MatchArm`s. - fn new<'a>( - cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, 'tcx>], + fn new( + wildcard_arena: &'a TypedArena>, + arms: &'a [MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, scrut_validity: ValidityConstraint, - ) -> Self - where - 'p: 'a, - { - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP)); + ) -> Self { + let wild_pattern = + wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Span::default())); let wildcard_row = PatStack::from_pattern(wild_pattern); let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), @@ -871,32 +880,34 @@ fn column_count(&self) -> usize { self.wildcard_row.len() } - fn rows<'a>( - &'a self, - ) -> impl Iterator> + Clone + DoubleEndedIterator + ExactSizeIterator - { + fn rows<'b>( + &'b self, + ) -> impl Iterator> + + Clone + + DoubleEndedIterator + + ExactSizeIterator { self.rows.iter() } - fn rows_mut<'a>( - &'a mut self, - ) -> impl Iterator> + DoubleEndedIterator + ExactSizeIterator + fn rows_mut<'b>( + &'b mut self, + ) -> impl Iterator> + DoubleEndedIterator + ExactSizeIterator { self.rows.iter_mut() } /// Iterate over the first pattern of each row. - fn heads<'a>( - &'a self, - ) -> impl Iterator> + Clone + Captures<'a> { + fn heads<'b>( + &'b self, + ) -> impl Iterator> + Clone + Captures<'a> { self.rows().map(|r| r.head()) } /// This computes `specialize(ctor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: &PatCtxt<'_, 'p, 'tcx>, + pcx: &PatCtxt<'a, 'p, 'tcx>, ctor: &Constructor<'tcx>, - ) -> Matrix<'p, 'tcx> { + ) -> Matrix<'a, 'p, 'tcx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); let new_validity = self.place_validity[0].specialize(ctor); let new_place_validity = std::iter::repeat(new_validity) @@ -925,7 +936,7 @@ fn specialize_constructor( /// + _ + [_, _, tail @ ..] + /// | ✓ | ? | // column validity /// ``` -impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { +impl<'a, 'p, 'tcx> fmt::Debug for Matrix<'a, 'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -1156,10 +1167,11 @@ fn extend(&mut self, other: Self) { /// - unspecialization, where we lift the results from the previous step into results for this step /// (using `apply_constructor` and by updating `row.useful` for each parent row). /// This is all explained at the top of the file. -#[instrument(level = "debug", skip(cx, is_top_level), ret)] -fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - matrix: &mut Matrix<'p, 'tcx>, +#[instrument(level = "debug", skip(cx, is_top_level, wildcard_arena), ret)] +fn compute_exhaustiveness_and_usefulness<'a, 'p, 'tcx>( + cx: &'a MatchCheckCtxt<'p, 'tcx>, + matrix: &mut Matrix<'a, 'p, 'tcx>, + wildcard_arena: &'a TypedArena>, is_top_level: bool, ) -> WitnessMatrix<'tcx> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); @@ -1181,7 +1193,7 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( }; debug!("ty: {ty:?}"); - let pcx = &PatCtxt { cx, ty, is_top_level }; + let pcx = &PatCtxt { cx, ty, is_top_level, wildcard_arena }; // Whether the place/column we are inspecting is known to contain valid data. let place_validity = matrix.place_validity[0]; @@ -1224,7 +1236,7 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( // Dig into rows that match `ctor`. let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor); let mut witnesses = ensure_sufficient_stack(|| { - compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, false) + compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, wildcard_arena, false) }); let counts_for_exhaustiveness = match ctor { @@ -1286,15 +1298,17 @@ pub struct UsefulnessReport<'p, 'tcx> { } /// Computes whether a match is exhaustive and which of its arms are useful. -#[instrument(skip(cx, arms), level = "debug")] +#[instrument(skip(cx, arms, wildcard_arena), level = "debug")] pub(crate) fn compute_match_usefulness<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, + wildcard_arena: &TypedArena>, ) -> UsefulnessReport<'p, 'tcx> { let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee); - let mut matrix = Matrix::new(cx, arms, scrut_ty, scrut_validity); - let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true); + let mut matrix = Matrix::new(wildcard_arena, arms, scrut_ty, scrut_validity); + let non_exhaustiveness_witnesses = + compute_exhaustiveness_and_usefulness(cx, &mut matrix, wildcard_arena, true); let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); let arm_usefulness: Vec<_> = arms From 3d7c4df3260f80593c70f901029d696520234b64 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 20:01:02 +0100 Subject: [PATCH 04/16] Abstract `MatchCheckCtxt` into a trait --- .../src/thir/pattern/check_match.rs | 40 ++-- .../rustc_pattern_analysis/src/constructor.rs | 65 +++---- compiler/rustc_pattern_analysis/src/cx.rs | 114 ++++++++--- compiler/rustc_pattern_analysis/src/errors.rs | 6 +- compiler/rustc_pattern_analysis/src/lib.rs | 49 ++++- compiler/rustc_pattern_analysis/src/lints.rs | 25 +-- compiler/rustc_pattern_analysis/src/pat.rs | 76 ++++---- .../rustc_pattern_analysis/src/usefulness.rs | 177 +++++++++--------- 8 files changed, 313 insertions(+), 239 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 109e9117a58..c2b1fb77398 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,8 +1,7 @@ -use rustc_pattern_analysis::constructor::Constructor; -use rustc_pattern_analysis::cx::MatchCheckCtxt; +use rustc_pattern_analysis::cx::{ + Constructor, DeconstructedPat, MatchCheckCtxt, Usefulness, UsefulnessReport, WitnessPat, +}; use rustc_pattern_analysis::errors::Uncovered; -use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat}; -use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport}; use rustc_pattern_analysis::{analyze_match, MatchArm}; use crate::errors::*; @@ -851,14 +850,15 @@ fn report_arm_reachability<'p, 'tcx>( ); }; - use Usefulness::*; let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { match is_useful { - Redundant => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall), - Useful(redundant_spans) if redundant_spans.is_empty() => {} + Usefulness::Redundant => { + report_unreachable_pattern(*arm.pat.span(), arm.hir_id, catchall) + } + Usefulness::Useful(redundant_spans) if redundant_spans.is_empty() => {} // The arm is reachable, but contains redundant subpatterns (from or-patterns). - Useful(redundant_spans) => { + Usefulness::Useful(redundant_spans) => { let mut redundant_spans = redundant_spans.clone(); // Emit lints in the order in which they occur in the file. redundant_spans.sort_unstable(); @@ -868,17 +868,16 @@ fn report_arm_reachability<'p, 'tcx>( } } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { - catchall = Some(arm.pat.span()); + catchall = Some(*arm.pat.span()); } } } /// Checks for common cases of "catchall" patterns that may not be intended as such. fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { - use Constructor::*; match pat.ctor() { - Wildcard => true, - Struct | Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Constructor::Wildcard => true, + Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } @@ -889,7 +888,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( thir: &Thir<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - witnesses: Vec>, + witnesses: Vec>, arms: &[ArmId], expr_span: Span, ) -> ErrorGuaranteed { @@ -1086,10 +1085,10 @@ fn report_non_exhaustive_match<'p, 'tcx>( fn joined_uncovered_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: &[WitnessPat<'tcx>], + witnesses: &[WitnessPat<'p, 'tcx>], ) -> String { const LIMIT: usize = 3; - let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string(); + let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.hoist_witness_pat(pat).to_string(); match witnesses { [] => bug!(), [witness] => format!("`{}`", cx.hoist_witness_pat(witness)), @@ -1107,7 +1106,7 @@ fn joined_uncovered_patterns<'p, 'tcx>( fn collect_non_exhaustive_tys<'tcx>( cx: &MatchCheckCtxt<'_, 'tcx>, - pat: &WitnessPat<'tcx>, + pat: &WitnessPat<'_, 'tcx>, non_exhaustive_tys: &mut FxIndexSet>, ) { if matches!(pat.ctor(), Constructor::NonExhaustive) { @@ -1126,7 +1125,7 @@ fn collect_non_exhaustive_tys<'tcx>( fn report_adt_defined_here<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - witnesses: &[WitnessPat<'tcx>], + witnesses: &[WitnessPat<'_, 'tcx>], point_at_non_local_ty: bool, ) -> Option> { let ty = ty.peel_refs(); @@ -1148,15 +1147,14 @@ fn report_adt_defined_here<'tcx>( Some(AdtDefinedHere { adt_def_span, ty, variants }) } -fn maybe_point_at_variant<'a, 'tcx: 'a>( +fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>( tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, - patterns: impl Iterator>, + patterns: impl Iterator>, ) -> Vec { - use Constructor::*; let mut covered = vec![]; for pattern in patterns { - if let Variant(variant_index) = pattern.ctor() { + if let Constructor::Variant(variant_index) = pattern.ctor() { if let ty::Adt(this_def, _) = pattern.ty().kind() && this_def.did() != def.did() { diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 95ffece54f3..2082ecda44c 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -40,7 +40,7 @@ //! - That have no non-trivial intersection with any of the constructors in the column (i.e. they're //! each either disjoint with or covered by any given column constructor). //! -//! We compute this in two steps: first [`crate::cx::MatchCheckCtxt::ctors_for_ty`] determines the +//! We compute this in two steps: first [`MatchCx::ctors_for_ty`] determines the //! set of all possible constructors for the type. Then [`ConstructorSet::split`] looks at the //! column of constructors and splits the set into groups accordingly. The precise invariants of //! [`ConstructorSet::split`] is described in [`SplitConstructorSet`]. @@ -136,7 +136,7 @@ //! the algorithm can't distinguish them from a nonempty constructor. The only known case where this //! could happen is the `[..]` pattern on `[!; N]` with `N > 0` so we must take care to not emit it. //! -//! This is all handled by [`crate::cx::MatchCheckCtxt::ctors_for_ty`] and +//! This is all handled by [`MatchCx::ctors_for_ty`] and //! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest. //! //! @@ -158,14 +158,13 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::RangeEnd; use rustc_index::IndexVec; -use rustc_middle::mir::Const; -use rustc_target::abi::VariantIdx; use self::Constructor::*; use self::MaybeInfiniteInt::*; use self::SliceKind::*; use crate::usefulness::PatCtxt; +use crate::MatchCx; /// Whether we have seen a constructor in the column or not. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -630,11 +629,11 @@ pub fn new() -> Self { /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and /// `Fields`. #[derive(Clone, Debug, PartialEq)] -pub enum Constructor<'tcx> { +pub enum Constructor { /// Tuples and structs. Struct, /// Enum variants. - Variant(VariantIdx), + Variant(Cx::VariantIdx), /// References Ref, /// Array and slice patterns. @@ -649,7 +648,7 @@ pub enum Constructor<'tcx> { F32Range(IeeeFloat, IeeeFloat, RangeEnd), F64Range(IeeeFloat, IeeeFloat, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(Const<'tcx>), + Str(Cx::StrLit), /// Constants that must not be matched structurally. They are treated as black boxes for the /// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a /// match exhaustive. @@ -672,12 +671,12 @@ pub enum Constructor<'tcx> { Missing, } -impl<'tcx> Constructor<'tcx> { +impl Constructor { pub(crate) fn is_non_exhaustive(&self) -> bool { matches!(self, NonExhaustive) } - pub(crate) fn as_variant(&self) -> Option { + pub(crate) fn as_variant(&self) -> Option { match self { Variant(i) => Some(*i), _ => None, @@ -704,7 +703,7 @@ fn as_slice(&self) -> Option { /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. - pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> usize { + pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, Cx>) -> usize { pcx.cx.ctor_arity(self, pcx.ty) } @@ -713,14 +712,11 @@ pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> usize { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool { + pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, Cx>, other: &Self) -> bool { match (self, other) { - (Wildcard, _) => { - span_bug!( - pcx.cx.scrut_span, - "Constructor splitting should not have returned `Wildcard`" - ) - } + (Wildcard, _) => pcx + .cx + .bug(format_args!("Constructor splitting should not have returned `Wildcard`")), // Wildcards cover anything (_, Wildcard) => true, // Only a wildcard pattern can match these special constructors. @@ -761,12 +757,9 @@ pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, other: &Self (Opaque(self_id), Opaque(other_id)) => self_id == other_id, (Opaque(..), _) | (_, Opaque(..)) => false, - _ => span_bug!( - pcx.cx.scrut_span, - "trying to compare incompatible constructors {:?} and {:?}", - self, - other - ), + _ => pcx.cx.bug(format_args!( + "trying to compare incompatible constructors {self:?} and {other:?}" + )), } } } @@ -790,12 +783,12 @@ pub enum VariantVisibility { /// In terms of division of responsibility, [`ConstructorSet::split`] handles all of the /// `exhaustive_patterns` feature. #[derive(Debug)] -pub enum ConstructorSet { +pub enum ConstructorSet { /// The type is a tuple or struct. `empty` tracks whether the type is empty. Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. - Variants { variants: IndexVec, non_exhaustive: bool }, + Variants { variants: IndexVec, non_exhaustive: bool }, /// The type is `&T`. Ref, /// The type is a union. @@ -838,25 +831,25 @@ pub enum ConstructorSet { /// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be /// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4. #[derive(Debug)] -pub(crate) struct SplitConstructorSet<'tcx> { - pub(crate) present: SmallVec<[Constructor<'tcx>; 1]>, - pub(crate) missing: Vec>, - pub(crate) missing_empty: Vec>, +pub(crate) struct SplitConstructorSet { + pub(crate) present: SmallVec<[Constructor; 1]>, + pub(crate) missing: Vec>, + pub(crate) missing_empty: Vec>, } -impl ConstructorSet { +impl ConstructorSet { /// This analyzes a column of constructors to 1/ determine which constructors of the type (if /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation /// and its invariants. #[instrument(level = "debug", skip(self, pcx, ctors), ret)] - pub(crate) fn split<'a, 'tcx>( + pub(crate) fn split<'a>( &self, - pcx: &PatCtxt<'_, '_, 'tcx>, - ctors: impl Iterator> + Clone, - ) -> SplitConstructorSet<'tcx> + pcx: &PatCtxt<'_, '_, Cx>, + ctors: impl Iterator> + Clone, + ) -> SplitConstructorSet where - 'tcx: 'a, + Cx: 'a, { let mut present: SmallVec<[_; 1]> = SmallVec::new(); // Empty constructors found missing. @@ -997,7 +990,7 @@ pub(crate) fn split<'a, 'tcx>( // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty. // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. - if !pcx.cx.tcx.features().exhaustive_patterns + if !pcx.cx.is_exhaustive_patterns_feature_on() && !(pcx.is_top_level && matches!(self, Self::NoConstructors)) { // Treat all missing constructors as nonempty. diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/cx.rs index a4bc99c8013..de42764c9bc 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/cx.rs @@ -9,7 +9,7 @@ use rustc_index::IndexVec; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::{self}; +use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; @@ -18,13 +18,26 @@ use smallvec::SmallVec; use crate::constructor::{ - Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, OpaqueId, Slice, SliceKind, - VariantVisibility, + IntRange, MaybeInfiniteInt, OpaqueId, Slice, SliceKind, VariantVisibility, }; -use crate::pat::{DeconstructedPat, WitnessPat}; +use crate::MatchCx; -use Constructor::*; +use crate::constructor::Constructor::*; +pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; +pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; +pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, MatchCheckCtxt<'p, 'tcx>>; +pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, MatchCheckCtxt<'p, 'tcx>>; +pub(crate) type PatCtxt<'a, 'p, 'tcx> = + crate::usefulness::PatCtxt<'a, 'p, MatchCheckCtxt<'p, 'tcx>>; +pub(crate) type SplitConstructorSet<'p, 'tcx> = + crate::constructor::SplitConstructorSet>; +pub type Usefulness = crate::usefulness::Usefulness; +pub type UsefulnessReport<'p, 'tcx> = + crate::usefulness::UsefulnessReport<'p, MatchCheckCtxt<'p, 'tcx>>; +pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; + +#[derive(Clone)] pub struct MatchCheckCtxt<'p, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The module in which the match occurs. This is necessary for @@ -49,15 +62,17 @@ pub struct MatchCheckCtxt<'p, 'tcx> { pub known_valid_scrutinee: bool, } +impl<'p, 'tcx> fmt::Debug for MatchCheckCtxt<'p, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MatchCheckCtxt").finish() + } +} + impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { !ty.is_inhabited_from(self.tcx, self.module, self.param_env) } - pub(crate) fn is_opaque(ty: Ty<'tcx>) -> bool { - matches!(ty.kind(), ty::Alias(ty::Opaque, ..)) - } - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind() { @@ -68,6 +83,20 @@ pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { } } + /// Whether the range denotes the fictitious values before `isize::MIN` or after + /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist). + pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool { + ty.is_ptr_sized_integral() && { + // The two invalid ranges are `NegInfinity..isize::MIN` (represented as + // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy` + // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo` + // otherwise. + let lo = self.hoist_pat_range_bdy(range.lo, ty); + matches!(lo, PatRangeBoundary::PosInfinity) + || matches!(range.hi, MaybeInfiniteInt::Finite(0)) + } + } + // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. // This lists the fields we keep along with their types. @@ -97,7 +126,7 @@ pub(crate) fn list_variant_nonhidden_fields<'a>( } pub(crate) fn variant_index_for_adt( - ctor: &Constructor<'tcx>, + ctor: &Constructor<'p, 'tcx>, adt: ty::AdtDef<'tcx>, ) -> VariantIdx { match *ctor { @@ -113,7 +142,7 @@ pub(crate) fn variant_index_for_adt( /// Returns the types of the fields for a given constructor. The result must have a length of /// `ctor.arity()`. #[instrument(level = "trace", skip(self))] - pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] { + pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] { let cx = self; match ctor { Struct | Variant(_) | UnionField => match ty.kind() { @@ -159,9 +188,8 @@ pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> &[T } } - /// The number of fields for this constructor. This must be kept in sync with - /// `Fields::wildcards`. - pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize { + /// The number of fields for this constructor. + pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> usize { match ctor { Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => fs.len(), @@ -198,7 +226,7 @@ pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize /// /// See [`crate::constructor`] for considerations of emptiness. #[instrument(level = "debug", skip(self), ret)] - pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet { + pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet<'p, 'tcx> { let cx = self; let make_uint_range = |start, end| { IntRange::from_range( @@ -599,20 +627,6 @@ pub(crate) fn hoist_pat_range_bdy( } } - /// Whether the range denotes the fictitious values before `isize::MIN` or after - /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist). - pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool { - ty.is_ptr_sized_integral() && { - // The two invalid ranges are `NegInfinity..isize::MIN` (represented as - // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy` - // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo` - // otherwise. - let lo = self.hoist_pat_range_bdy(range.lo, ty); - matches!(lo, PatRangeBoundary::PosInfinity) - || matches!(range.hi, MaybeInfiniteInt::Finite(0)) - } - } - /// Convert back to a `thir::Pat` for diagnostic purposes. pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx> { use MaybeInfiniteInt::*; @@ -652,7 +666,7 @@ pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx } /// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't /// appear in diagnostics, like float ranges. - pub fn hoist_witness_pat(&self, pat: &WitnessPat<'tcx>) -> Pat<'tcx> { + pub fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> { let cx = self; let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild); let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); @@ -746,7 +760,7 @@ pub fn hoist_witness_pat(&self, pat: &WitnessPat<'tcx>) -> Pat<'tcx> { /// Best-effort `Debug` implementation. pub(crate) fn debug_pat( f: &mut fmt::Formatter<'_>, - pat: &DeconstructedPat<'p, 'tcx>, + pat: &crate::pat::DeconstructedPat<'_, Self>, ) -> fmt::Result { let mut first = true; let mut start_or_continue = |s| { @@ -840,6 +854,44 @@ pub(crate) fn debug_pat( } } +impl<'p, 'tcx> MatchCx for MatchCheckCtxt<'p, 'tcx> { + type Ty = Ty<'tcx>; + type Span = Span; + type VariantIdx = VariantIdx; + type StrLit = Const<'tcx>; + + fn is_exhaustive_patterns_feature_on(&self) -> bool { + self.tcx.features().exhaustive_patterns + } + fn is_opaque_ty(ty: Self::Ty) -> bool { + matches!(ty.kind(), ty::Alias(ty::Opaque, ..)) + } + + fn ctor_arity(&self, ctor: &crate::constructor::Constructor, ty: Self::Ty) -> usize { + self.ctor_arity(ctor, ty) + } + fn ctor_sub_tys( + &self, + ctor: &crate::constructor::Constructor, + ty: Self::Ty, + ) -> &[Self::Ty] { + self.ctor_sub_tys(ctor, ty) + } + fn ctors_for_ty(&self, ty: Self::Ty) -> crate::constructor::ConstructorSet { + self.ctors_for_ty(ty) + } + + fn debug_pat( + f: &mut fmt::Formatter<'_>, + pat: &crate::pat::DeconstructedPat<'_, Self>, + ) -> fmt::Result { + Self::debug_pat(f, pat) + } + fn bug(&self, fmt: fmt::Arguments<'_>) -> ! { + span_bug!(self.scrut_span, "{}", fmt) + } +} + /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 0efa8a0ec08..3483a2710d4 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,11 +1,11 @@ -use crate::{cx::MatchCheckCtxt, pat::WitnessPat}; - use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::Ty; use rustc_span::Span; +use crate::cx::{MatchCheckCtxt, WitnessPat}; + #[derive(Subdiagnostic)] #[label(pattern_analysis_uncovered)] pub struct Uncovered<'tcx> { @@ -22,7 +22,7 @@ impl<'tcx> Uncovered<'tcx> { pub fn new<'p>( span: Span, cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: Vec>, + witnesses: Vec>, ) -> Self { let witness_1 = cx.hoist_witness_pat(witnesses.get(0).unwrap()); Self { diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index f19dc7345fc..79c8eb394ad 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -14,36 +14,71 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +use std::fmt; + +use constructor::{Constructor, ConstructorSet}; use lints::PatternColumn; use rustc_hir::HirId; +use rustc_index::Idx; use rustc_middle::ty::Ty; -use usefulness::{compute_match_usefulness, UsefulnessReport}; +use usefulness::{compute_match_usefulness, UsefulnessReport, ValidityConstraint}; use crate::cx::MatchCheckCtxt; use crate::lints::{lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints}; use crate::pat::DeconstructedPat; +pub trait MatchCx: Sized + Clone + fmt::Debug { + type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy + type Span: Clone + Default; + type VariantIdx: Clone + Idx; + type StrLit: Clone + PartialEq + fmt::Debug; + + fn is_opaque_ty(ty: Self::Ty) -> bool; + fn is_exhaustive_patterns_feature_on(&self) -> bool; + + /// The number of fields for this constructor. + fn ctor_arity(&self, ctor: &Constructor, ty: Self::Ty) -> usize; + + /// The types of the fields for this constructor. The result must have a length of + /// `ctor_arity()`. + fn ctor_sub_tys(&self, ctor: &Constructor, ty: Self::Ty) -> &[Self::Ty]; + + /// The set of all the constructors for `ty`. + /// + /// This must follow the invariants of `ConstructorSet` + fn ctors_for_ty(&self, ty: Self::Ty) -> ConstructorSet; + + /// Best-effort `Debug` implementation. + fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result; + + /// Raise a bug. + fn bug(&self, fmt: fmt::Arguments<'_>) -> !; +} + /// The arm of a match expression. -#[derive(Clone, Copy, Debug)] -pub struct MatchArm<'p, 'tcx> { +#[derive(Clone, Debug)] +pub struct MatchArm<'p, Cx: MatchCx> { /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. - pub pat: &'p DeconstructedPat<'p, 'tcx>, + pub pat: &'p DeconstructedPat<'p, Cx>, pub hir_id: HirId, pub has_guard: bool, } +impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} + /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. pub fn analyze_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, 'tcx>], + arms: &[MatchArm<'p, MatchCheckCtxt<'p, 'tcx>>], scrut_ty: Ty<'tcx>, -) -> UsefulnessReport<'p, 'tcx> { +) -> UsefulnessReport<'p, MatchCheckCtxt<'p, 'tcx>> { // Arena to store the extra wildcards we construct during analysis. let wildcard_arena = cx.pattern_arena; let pat_column = PatternColumn::new(arms); - let report = compute_match_usefulness(cx, arms, scrut_ty, wildcard_arena); + let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee); + let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity, wildcard_arena); // Lint on ranges that overlap on their endpoints, which is likely a mistake. lint_overlapping_range_endpoints(cx, &pat_column, wildcard_arena); diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 858e28ce897..88967ab9010 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -7,15 +7,16 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::Span; -use crate::constructor::{Constructor, IntRange, MaybeInfiniteInt, SplitConstructorSet}; -use crate::cx::MatchCheckCtxt; +use crate::constructor::{IntRange, MaybeInfiniteInt}; +use crate::cx::{ + Constructor, DeconstructedPat, MatchArm, MatchCheckCtxt, PatCtxt, SplitConstructorSet, + WitnessPat, +}; use crate::errors::{ NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap, OverlappingRangeEndpoints, Uncovered, }; -use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::usefulness::PatCtxt; -use crate::MatchArm; +use crate::MatchCx; /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that /// inspect the same subvalue/place". @@ -55,10 +56,10 @@ fn head_ty(&self) -> Option> { // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. let first_ty = self.patterns[0].ty(); - if MatchCheckCtxt::is_opaque(first_ty) { + if MatchCheckCtxt::is_opaque_ty(first_ty) { for pat in &self.patterns { let ty = pat.ty(); - if !MatchCheckCtxt::is_opaque(ty) { + if !MatchCheckCtxt::is_opaque_ty(ty) { return Some(ty); } } @@ -67,7 +68,7 @@ fn head_ty(&self) -> Option> { } /// Do constructor splitting on the constructors of the column. - fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> { + fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors) } @@ -84,7 +85,7 @@ fn iter<'b>(&'b self) -> impl Iterator> + fn specialize<'b>( &self, pcx: &'b PatCtxt<'_, 'p, 'tcx>, - ctor: &Constructor<'tcx>, + ctor: &Constructor<'p, 'tcx>, ) -> Vec> where 'a: 'b, @@ -128,7 +129,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, wildcard_arena: &TypedArena>, -) -> Vec> { +) -> Vec> { let Some(ty) = column.head_ty() else { return Vec::new(); }; @@ -215,7 +216,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( }; use rustc_errors::DecorateLint; - let mut err = cx.tcx.sess.struct_span_warn(arm.pat.span(), ""); + let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.span(), ""); err.set_primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); @@ -265,7 +266,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let mut suffixes: SmallVec<[_; 1]> = Default::default(); // Iterate on patterns that contained `overlap`. for pat in column.iter() { - let this_span = pat.span(); + let this_span = *pat.span(); let Constructor::IntRange(this_range) = pat.ctor() else { continue }; if this_range.is_singleton() { // Don't lint when one of the ranges is a singleton. diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 29230c390ef..c1052c88514 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -6,14 +6,12 @@ use smallvec::{smallvec, SmallVec}; use rustc_data_structures::captures::Captures; -use rustc_middle::ty::Ty; -use rustc_span::Span; use self::Constructor::*; use crate::constructor::{Constructor, Slice, SliceKind}; -use crate::cx::MatchCheckCtxt; use crate::usefulness::PatCtxt; +use crate::MatchCx; /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. @@ -26,25 +24,25 @@ /// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't /// observe that it is uninhabited. In that case that field is not included in `fields`. Care must /// be taken when converting to/from `thir::Pat`. -pub struct DeconstructedPat<'p, 'tcx> { - ctor: Constructor<'tcx>, - fields: &'p [DeconstructedPat<'p, 'tcx>], - ty: Ty<'tcx>, - span: Span, +pub struct DeconstructedPat<'p, Cx: MatchCx> { + ctor: Constructor, + fields: &'p [DeconstructedPat<'p, Cx>], + ty: Cx::Ty, + span: Cx::Span, /// Whether removing this arm would change the behavior of the match expression. useful: Cell, } -impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { - pub fn wildcard(ty: Ty<'tcx>, span: Span) -> Self { +impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> { + pub fn wildcard(ty: Cx::Ty, span: Cx::Span) -> Self { Self::new(Wildcard, &[], ty, span) } pub fn new( - ctor: Constructor<'tcx>, - fields: &'p [DeconstructedPat<'p, 'tcx>], - ty: Ty<'tcx>, - span: Span, + ctor: Constructor, + fields: &'p [DeconstructedPat<'p, Cx>], + ty: Cx::Ty, + span: Cx::Span, ) -> Self { DeconstructedPat { ctor, fields, ty, span, useful: Cell::new(false) } } @@ -61,19 +59,19 @@ pub(crate) fn is_or_pat(&self) -> bool { } } - pub fn ctor(&self) -> &Constructor<'tcx> { + pub fn ctor(&self) -> &Constructor { &self.ctor } - pub fn ty(&self) -> Ty<'tcx> { + pub fn ty(&self) -> Cx::Ty { self.ty } - pub fn span(&self) -> Span { - self.span + pub fn span(&self) -> &Cx::Span { + &self.span } pub fn iter_fields<'a>( &'a self, - ) -> impl Iterator> + Captures<'a> { + ) -> impl Iterator> + Captures<'a> { self.fields.iter() } @@ -81,13 +79,13 @@ pub fn iter_fields<'a>( /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(crate) fn specialize<'a>( &self, - pcx: &PatCtxt<'a, 'p, 'tcx>, - other_ctor: &Constructor<'tcx>, - ) -> SmallVec<[&'a DeconstructedPat<'p, 'tcx>; 2]> { + pcx: &PatCtxt<'a, 'p, Cx>, + other_ctor: &Constructor, + ) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> { let wildcard_sub_tys = || { let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty); tys.iter() - .map(|ty| DeconstructedPat::wildcard(*ty, Span::default())) + .map(|ty| DeconstructedPat::wildcard(*ty, Cx::Span::default())) .map(|pat| pcx.wildcard_arena.alloc(pat) as &_) .collect() }; @@ -137,15 +135,15 @@ pub(crate) fn is_useful(&self) -> bool { } /// Report the spans of subpatterns that were not useful, if any. - pub(crate) fn redundant_spans(&self) -> Vec { + pub(crate) fn redundant_spans(&self) -> Vec { let mut spans = Vec::new(); self.collect_redundant_spans(&mut spans); spans } - fn collect_redundant_spans(&self, spans: &mut Vec) { + fn collect_redundant_spans(&self, spans: &mut Vec) { // We don't look at subpatterns if we already reported the whole pattern as redundant. if !self.is_useful() { - spans.push(self.span); + spans.push(self.span.clone()); } else { for p in self.iter_fields() { p.collect_redundant_spans(spans); @@ -156,46 +154,46 @@ fn collect_redundant_spans(&self, spans: &mut Vec) { /// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a /// `Display` impl. -impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { +impl<'p, Cx: MatchCx> fmt::Debug for DeconstructedPat<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - MatchCheckCtxt::debug_pat(f, self) + Cx::debug_pat(f, self) } } /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics /// purposes. As such they don't use interning and can be cloned. #[derive(Debug, Clone)] -pub struct WitnessPat<'tcx> { - ctor: Constructor<'tcx>, - pub(crate) fields: Vec>, - ty: Ty<'tcx>, +pub struct WitnessPat { + ctor: Constructor, + pub(crate) fields: Vec>, + ty: Cx::Ty, } -impl<'tcx> WitnessPat<'tcx> { - pub(crate) fn new(ctor: Constructor<'tcx>, fields: Vec, ty: Ty<'tcx>) -> Self { +impl WitnessPat { + pub(crate) fn new(ctor: Constructor, fields: Vec, ty: Cx::Ty) -> Self { Self { ctor, fields, ty } } - pub(crate) fn wildcard(ty: Ty<'tcx>) -> Self { + pub(crate) fn wildcard(ty: Cx::Ty) -> Self { Self::new(Wildcard, Vec::new(), ty) } /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. - pub(crate) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self { + pub(crate) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, Cx>, ctor: Constructor) -> Self { let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty); let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); Self::new(ctor, fields, pcx.ty) } - pub fn ctor(&self) -> &Constructor<'tcx> { + pub fn ctor(&self) -> &Constructor { &self.ctor } - pub fn ty(&self) -> Ty<'tcx> { + pub fn ty(&self) -> Cx::Ty { self.ty } - pub fn iter_fields<'a>(&'a self) -> impl Iterator> { + pub fn iter_fields<'a>(&'a self) -> impl Iterator> { self.fields.iter() } } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 3300013805d..d52d9358b94 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -242,7 +242,7 @@ //! Therefore `usefulness(tp_1, tp_2, tq)` returns the single witness-tuple `[Variant2(Some(true), 0)]`. //! //! -//! Computing the set of constructors for a type is done in [`MatchCheckCtxt::ctors_for_ty`]. See +//! Computing the set of constructors for a type is done in [`MatchCx::ctors_for_ty`]. See //! the following sections for more accurate versions of the algorithm and corresponding links. //! //! @@ -557,40 +557,39 @@ use rustc_arena::TypedArena; use rustc_data_structures::{captures::Captures, stack::ensure_sufficient_stack}; -use rustc_middle::ty::Ty; -use rustc_span::Span; use crate::constructor::{Constructor, ConstructorSet}; -use crate::cx::MatchCheckCtxt; use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::MatchArm; +use crate::{MatchArm, MatchCx}; use self::ValidityConstraint::*; -#[derive(Copy, Clone)] -pub(crate) struct PatCtxt<'a, 'p, 'tcx> { - pub(crate) cx: &'a MatchCheckCtxt<'p, 'tcx>, +#[derive(Clone)] +pub(crate) struct PatCtxt<'a, 'p, Cx: MatchCx> { + pub(crate) cx: &'a Cx, /// Type of the current column under investigation. - pub(crate) ty: Ty<'tcx>, + pub(crate) ty: Cx::Ty, /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a /// subpattern. pub(crate) is_top_level: bool, /// An arena to store the wildcards we produce during analysis. - pub(crate) wildcard_arena: &'a TypedArena>, + pub(crate) wildcard_arena: &'a TypedArena>, } -impl<'a, 'p, 'tcx> PatCtxt<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> PatCtxt<'a, 'p, Cx> { /// A `PatCtxt` when code other than `is_useful` needs one. pub(crate) fn new_dummy( - cx: &'a MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - wildcard_arena: &'a TypedArena>, + cx: &'a Cx, + ty: Cx::Ty, + wildcard_arena: &'a TypedArena>, ) -> Self { PatCtxt { cx, ty, is_top_level: false, wildcard_arena } } } -impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> Copy for PatCtxt<'a, 'p, Cx> {} + +impl<'a, 'p, Cx: MatchCx> fmt::Debug for PatCtxt<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PatCtxt").field("ty", &self.ty).finish() } @@ -602,7 +601,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// - in the matrix, track whether a given place (aka column) is known to contain a valid value or /// not. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum ValidityConstraint { +pub(crate) enum ValidityConstraint { ValidOnly, MaybeInvalid, /// Option for backwards compatibility: the place is not known to be valid but we allow omitting @@ -611,7 +610,7 @@ enum ValidityConstraint { } impl ValidityConstraint { - fn from_bool(is_valid_only: bool) -> Self { + pub(crate) fn from_bool(is_valid_only: bool) -> Self { if is_valid_only { ValidOnly } else { MaybeInvalid } } @@ -636,7 +635,7 @@ fn allows_omitting_empty_arms(self) -> bool { /// /// Pending further opsem decisions, the current behavior is: validity is preserved, except /// inside `&` and union fields where validity is reset to `MaybeInvalid`. - fn specialize(self, ctor: &Constructor<'_>) -> Self { + fn specialize(self, ctor: &Constructor) -> Self { // We preserve validity except when we go inside a reference or a union field. if matches!(ctor, Constructor::Ref | Constructor::UnionField) { // Validity of `x: &T` does not imply validity of `*x: T`. @@ -661,15 +660,15 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // The three lifetimes are: // - 'a allocated by us // - 'p coming from the input -// - 'tcx global compilation context +// - Cx global compilation context #[derive(Clone)] -struct PatStack<'a, 'p, 'tcx> { +struct PatStack<'a, 'p, Cx: MatchCx> { // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well. - pats: SmallVec<[&'a DeconstructedPat<'p, 'tcx>; 2]>, + pats: SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]>, } -impl<'a, 'p, 'tcx> PatStack<'a, 'p, 'tcx> { - fn from_pattern(pat: &'a DeconstructedPat<'p, 'tcx>) -> Self { +impl<'a, 'p, Cx: MatchCx> PatStack<'a, 'p, Cx> { + fn from_pattern(pat: &'a DeconstructedPat<'p, Cx>) -> Self { PatStack { pats: smallvec![pat] } } @@ -681,17 +680,17 @@ fn len(&self) -> usize { self.pats.len() } - fn head(&self) -> &'a DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'a DeconstructedPat<'p, Cx> { self.pats[0] } - fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { + fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.iter().copied() } // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is // an or-pattern. Panics if `self` is empty. - fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { + fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.head().flatten_or_pat().into_iter().map(move |pat| { let mut new = self.clone(); new.pats[0] = pat; @@ -703,9 +702,9 @@ fn expand_or_pat<'b>(&'b self) -> impl Iterator> + /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'a, 'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> PatStack<'a, 'p, 'tcx> { + pcx: &PatCtxt<'a, 'p, Cx>, + ctor: &Constructor, + ) -> PatStack<'a, 'p, Cx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. let mut new_pats = self.head().specialize(pcx, ctor); @@ -714,7 +713,7 @@ fn pop_head_constructor( } } -impl<'a, 'p, 'tcx> fmt::Debug for PatStack<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> fmt::Debug for PatStack<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // We pretty-print similarly to the `Debug` impl of `Matrix`. write!(f, "+")?; @@ -727,9 +726,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// A row of the matrix. #[derive(Clone)] -struct MatrixRow<'a, 'p, 'tcx> { +struct MatrixRow<'a, 'p, Cx: MatchCx> { // The patterns in the row. - pats: PatStack<'a, 'p, 'tcx>, + pats: PatStack<'a, 'p, Cx>, /// Whether the original arm had a guard. This is inherited when specializing. is_under_guard: bool, /// When we specialize, we remember which row of the original matrix produced a given row of the @@ -742,7 +741,7 @@ struct MatrixRow<'a, 'p, 'tcx> { useful: bool, } -impl<'a, 'p, 'tcx> MatrixRow<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> MatrixRow<'a, 'p, Cx> { fn is_empty(&self) -> bool { self.pats.is_empty() } @@ -751,17 +750,17 @@ fn len(&self) -> usize { self.pats.len() } - fn head(&self) -> &'a DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'a DeconstructedPat<'p, Cx> { self.pats.head() } - fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { + fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.iter() } // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is // an or-pattern. Panics if `self` is empty. - fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { + fn expand_or_pat<'b>(&'b self) -> impl Iterator> + Captures<'b> { self.pats.expand_or_pat().map(|patstack| MatrixRow { pats: patstack, parent_row: self.parent_row, @@ -774,10 +773,10 @@ fn expand_or_pat<'b>(&'b self) -> impl Iterator> /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'a, 'p, 'tcx>, - ctor: &Constructor<'tcx>, + pcx: &PatCtxt<'a, 'p, Cx>, + ctor: &Constructor, parent_row: usize, - ) -> MatrixRow<'a, 'p, 'tcx> { + ) -> MatrixRow<'a, 'p, Cx> { MatrixRow { pats: self.pats.pop_head_constructor(pcx, ctor), parent_row, @@ -787,7 +786,7 @@ fn pop_head_constructor( } } -impl<'a, 'p, 'tcx> fmt::Debug for MatrixRow<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> fmt::Debug for MatrixRow<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.pats.fmt(f) } @@ -804,22 +803,22 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// specializing `(,)` and `Some` on a pattern of type `(Option, bool)`, the first column of /// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`. #[derive(Clone)] -struct Matrix<'a, 'p, 'tcx> { +struct Matrix<'a, 'p, Cx: MatchCx> { /// Vector of rows. The rows must form a rectangular 2D array. Moreover, all the patterns of /// each column must have the same type. Each column corresponds to a place within the /// scrutinee. - rows: Vec>, + rows: Vec>, /// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of /// each column. This must obey the same invariants as the real rows. - wildcard_row: PatStack<'a, 'p, 'tcx>, + wildcard_row: PatStack<'a, 'p, Cx>, /// Track for each column/place whether it contains a known valid value. place_validity: SmallVec<[ValidityConstraint; 2]>, } -impl<'a, 'p, 'tcx> Matrix<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> Matrix<'a, 'p, Cx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. Internal method, prefer [`Matrix::new`]. - fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, 'tcx>) { + fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, Cx>) { if !row.is_empty() && row.head().is_or_pat() { // Expand nested or-patterns. for new_row in row.expand_or_pat() { @@ -832,13 +831,13 @@ fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, 'tcx>) { /// Build a new matrix from an iterator of `MatchArm`s. fn new( - wildcard_arena: &'a TypedArena>, - arms: &'a [MatchArm<'p, 'tcx>], - scrut_ty: Ty<'tcx>, + wildcard_arena: &'a TypedArena>, + arms: &'a [MatchArm<'p, Cx>], + scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Self { let wild_pattern = - wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Span::default())); + wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Cx::Span::default())); let wildcard_row = PatStack::from_pattern(wild_pattern); let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), @@ -857,7 +856,7 @@ fn new( matrix } - fn head_ty(&self) -> Option> { + fn head_ty(&self) -> Option { if self.column_count() == 0 { return None; } @@ -865,10 +864,10 @@ fn head_ty(&self) -> Option> { let mut ty = self.wildcard_row.head().ty(); // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. - if MatchCheckCtxt::is_opaque(ty) { + if Cx::is_opaque_ty(ty) { for pat in self.heads() { let pat_ty = pat.ty(); - if !MatchCheckCtxt::is_opaque(pat_ty) { + if !Cx::is_opaque_ty(pat_ty) { ty = pat_ty; break; } @@ -882,15 +881,13 @@ fn column_count(&self) -> usize { fn rows<'b>( &'b self, - ) -> impl Iterator> - + Clone - + DoubleEndedIterator - + ExactSizeIterator { + ) -> impl Iterator> + Clone + DoubleEndedIterator + ExactSizeIterator + { self.rows.iter() } fn rows_mut<'b>( &'b mut self, - ) -> impl Iterator> + DoubleEndedIterator + ExactSizeIterator + ) -> impl Iterator> + DoubleEndedIterator + ExactSizeIterator { self.rows.iter_mut() } @@ -898,16 +895,16 @@ fn rows_mut<'b>( /// Iterate over the first pattern of each row. fn heads<'b>( &'b self, - ) -> impl Iterator> + Clone + Captures<'a> { + ) -> impl Iterator> + Clone + Captures<'a> { self.rows().map(|r| r.head()) } /// This computes `specialize(ctor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: &PatCtxt<'a, 'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> Matrix<'a, 'p, 'tcx> { + pcx: &PatCtxt<'a, 'p, Cx>, + ctor: &Constructor, + ) -> Matrix<'a, 'p, Cx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); let new_validity = self.place_validity[0].specialize(ctor); let new_place_validity = std::iter::repeat(new_validity) @@ -936,7 +933,7 @@ fn specialize_constructor( /// + _ + [_, _, tail @ ..] + /// | ✓ | ? | // column validity /// ``` -impl<'a, 'p, 'tcx> fmt::Debug for Matrix<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: MatchCx> fmt::Debug for Matrix<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -1027,17 +1024,17 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// /// See the top of the file for more detailed explanations and examples. #[derive(Debug, Clone)] -struct WitnessStack<'tcx>(Vec>); +struct WitnessStack(Vec>); -impl<'tcx> WitnessStack<'tcx> { +impl WitnessStack { /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> WitnessPat<'tcx> { + fn single_pattern(self) -> WitnessPat { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } /// Reverses specialization by the `Missing` constructor by pushing a whole new pattern. - fn push_pattern(&mut self, pat: WitnessPat<'tcx>) { + fn push_pattern(&mut self, pat: WitnessPat) { self.0.push(pat); } @@ -1055,7 +1052,7 @@ fn push_pattern(&mut self, pat: WitnessPat<'tcx>) { /// pats: [(false, "foo"), _, true] /// result: [Enum::Variant { a: (false, "foo"), b: _ }, true] /// ``` - fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) { + fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, Cx>, ctor: &Constructor) { let len = self.0.len(); let arity = ctor.arity(pcx); let fields = self.0.drain((len - arity)..).rev().collect(); @@ -1074,9 +1071,9 @@ fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor< /// Just as the `Matrix` starts with a single column, by the end of the algorithm, this has a single /// column, which contains the patterns that are missing for the match to be exhaustive. #[derive(Debug, Clone)] -struct WitnessMatrix<'tcx>(Vec>); +struct WitnessMatrix(Vec>); -impl<'tcx> WitnessMatrix<'tcx> { +impl WitnessMatrix { /// New matrix with no witnesses. fn empty() -> Self { WitnessMatrix(vec![]) @@ -1091,12 +1088,12 @@ fn is_empty(&self) -> bool { self.0.is_empty() } /// Asserts that there is a single column and returns the patterns in it. - fn single_column(self) -> Vec> { + fn single_column(self) -> Vec> { self.0.into_iter().map(|w| w.single_pattern()).collect() } /// Reverses specialization by the `Missing` constructor by pushing a whole new pattern. - fn push_pattern(&mut self, pat: WitnessPat<'tcx>) { + fn push_pattern(&mut self, pat: WitnessPat) { for witness in self.0.iter_mut() { witness.push_pattern(pat.clone()) } @@ -1105,9 +1102,9 @@ fn push_pattern(&mut self, pat: WitnessPat<'tcx>) { /// Reverses specialization by `ctor`. See the section on `unspecialize` at the top of the file. fn apply_constructor( &mut self, - pcx: &PatCtxt<'_, '_, 'tcx>, - missing_ctors: &[Constructor<'tcx>], - ctor: &Constructor<'tcx>, + pcx: &PatCtxt<'_, '_, Cx>, + missing_ctors: &[Constructor], + ctor: &Constructor, report_individual_missing_ctors: bool, ) { if self.is_empty() { @@ -1168,12 +1165,12 @@ fn extend(&mut self, other: Self) { /// (using `apply_constructor` and by updating `row.useful` for each parent row). /// This is all explained at the top of the file. #[instrument(level = "debug", skip(cx, is_top_level, wildcard_arena), ret)] -fn compute_exhaustiveness_and_usefulness<'a, 'p, 'tcx>( - cx: &'a MatchCheckCtxt<'p, 'tcx>, - matrix: &mut Matrix<'a, 'p, 'tcx>, - wildcard_arena: &'a TypedArena>, +fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( + cx: &'a Cx, + matrix: &mut Matrix<'a, 'p, Cx>, + wildcard_arena: &'a TypedArena>, is_top_level: bool, -) -> WitnessMatrix<'tcx> { +) -> WitnessMatrix { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); let Some(ty) = matrix.head_ty() else { @@ -1278,7 +1275,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, 'tcx>( /// Indicates whether or not a given arm is useful. #[derive(Clone, Debug)] -pub enum Usefulness { +pub enum Usefulness { /// The arm is useful. This additionally carries a set of or-pattern branches that have been /// found to be redundant despite the overall arm being useful. Used only in the presence of /// or-patterns, otherwise it stays empty. @@ -1289,23 +1286,23 @@ pub enum Usefulness { } /// The output of checking a match for exhaustiveness and arm usefulness. -pub struct UsefulnessReport<'p, 'tcx> { +pub struct UsefulnessReport<'p, Cx: MatchCx> { /// For each arm of the input, whether that arm is useful after the arms above it. - pub arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness)>, + pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - pub non_exhaustiveness_witnesses: Vec>, + pub non_exhaustiveness_witnesses: Vec>, } /// Computes whether a match is exhaustive and which of its arms are useful. #[instrument(skip(cx, arms, wildcard_arena), level = "debug")] -pub(crate) fn compute_match_usefulness<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, 'tcx>], - scrut_ty: Ty<'tcx>, - wildcard_arena: &TypedArena>, -) -> UsefulnessReport<'p, 'tcx> { - let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee); +pub(crate) fn compute_match_usefulness<'p, Cx: MatchCx>( + cx: &Cx, + arms: &[MatchArm<'p, Cx>], + scrut_ty: Cx::Ty, + scrut_validity: ValidityConstraint, + wildcard_arena: &TypedArena>, +) -> UsefulnessReport<'p, Cx> { let mut matrix = Matrix::new(wildcard_arena, arms, scrut_ty, scrut_validity); let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, wildcard_arena, true); From cb622f3994a23d09bb24ef1add67a1569009d558 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 20:15:52 +0100 Subject: [PATCH 05/16] Name rustc-specific things "rustc" --- compiler/rustc_mir_build/src/errors.rs | 4 +- .../src/thir/pattern/check_match.rs | 7 +-- compiler/rustc_pattern_analysis/src/errors.rs | 4 +- compiler/rustc_pattern_analysis/src/lib.rs | 12 ++--- compiler/rustc_pattern_analysis/src/lints.rs | 17 ++++--- .../src/{cx.rs => rustc.rs} | 46 ++++++++----------- 6 files changed, 42 insertions(+), 48 deletions(-) rename compiler/rustc_pattern_analysis/src/{cx.rs => rustc.rs} (96%) diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 9baae706dff..6894b44dbd4 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -6,7 +6,7 @@ }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; -use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered}; +use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcCtxt}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -454,7 +454,7 @@ pub enum UnusedUnsafeEnclosing { } pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { - pub cx: &'m MatchCheckCtxt<'p, 'tcx>, + pub cx: &'m RustcCtxt<'p, 'tcx>, pub expr_span: Span, pub span: Span, pub ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index c2b1fb77398..e7f3eee16a4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,7 +1,8 @@ -use rustc_pattern_analysis::cx::{ - Constructor, DeconstructedPat, MatchCheckCtxt, Usefulness, UsefulnessReport, WitnessPat, -}; use rustc_pattern_analysis::errors::Uncovered; +use rustc_pattern_analysis::rustc::{ + Constructor, DeconstructedPat, RustcCtxt as MatchCheckCtxt, Usefulness, UsefulnessReport, + WitnessPat, +}; use rustc_pattern_analysis::{analyze_match, MatchArm}; use crate::errors::*; diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 3483a2710d4..8585e6da3cd 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::Ty; use rustc_span::Span; -use crate::cx::{MatchCheckCtxt, WitnessPat}; +use crate::rustc::{RustcCtxt, WitnessPat}; #[derive(Subdiagnostic)] #[label(pattern_analysis_uncovered)] @@ -21,7 +21,7 @@ pub struct Uncovered<'tcx> { impl<'tcx> Uncovered<'tcx> { pub fn new<'p>( span: Span, - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &RustcCtxt<'p, 'tcx>, witnesses: Vec>, ) -> Self { let witness_1 = cx.hoist_witness_pat(witnesses.get(0).unwrap()); diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 79c8eb394ad..192f35c6abf 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -1,10 +1,10 @@ //! Analysis of patterns, notably match exhaustiveness checking. pub mod constructor; -pub mod cx; pub mod errors; pub(crate) mod lints; pub mod pat; +pub mod rustc; pub mod usefulness; #[macro_use] @@ -21,11 +21,11 @@ use rustc_hir::HirId; use rustc_index::Idx; use rustc_middle::ty::Ty; -use usefulness::{compute_match_usefulness, UsefulnessReport, ValidityConstraint}; +use usefulness::{compute_match_usefulness, ValidityConstraint}; -use crate::cx::MatchCheckCtxt; use crate::lints::{lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints}; use crate::pat::DeconstructedPat; +use crate::rustc::RustcCtxt; pub trait MatchCx: Sized + Clone + fmt::Debug { type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy @@ -69,10 +69,10 @@ impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. pub fn analyze_match<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, MatchCheckCtxt<'p, 'tcx>>], + cx: &RustcCtxt<'p, 'tcx>, + arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, -) -> UsefulnessReport<'p, MatchCheckCtxt<'p, 'tcx>> { +) -> rustc::UsefulnessReport<'p, 'tcx> { // Arena to store the extra wildcards we construct during analysis. let wildcard_arena = cx.pattern_arena; let pat_column = PatternColumn::new(arms); diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 88967ab9010..89eb439c3f0 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -8,14 +8,13 @@ use rustc_span::Span; use crate::constructor::{IntRange, MaybeInfiniteInt}; -use crate::cx::{ - Constructor, DeconstructedPat, MatchArm, MatchCheckCtxt, PatCtxt, SplitConstructorSet, - WitnessPat, -}; use crate::errors::{ NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap, OverlappingRangeEndpoints, Uncovered, }; +use crate::rustc::{ + Constructor, DeconstructedPat, MatchArm, PatCtxt, RustcCtxt, SplitConstructorSet, WitnessPat, +}; use crate::MatchCx; /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that @@ -56,10 +55,10 @@ fn head_ty(&self) -> Option> { // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. let first_ty = self.patterns[0].ty(); - if MatchCheckCtxt::is_opaque_ty(first_ty) { + if RustcCtxt::is_opaque_ty(first_ty) { for pat in &self.patterns { let ty = pat.ty(); - if !MatchCheckCtxt::is_opaque_ty(ty) { + if !RustcCtxt::is_opaque_ty(ty) { return Some(ty); } } @@ -126,7 +125,7 @@ fn specialize<'b>( /// in a given column. #[instrument(level = "debug", skip(cx, wildcard_arena), ret)] fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &RustcCtxt<'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, wildcard_arena: &TypedArena>, ) -> Vec> { @@ -174,7 +173,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &RustcCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], pat_column: &PatternColumn<'a, 'p, 'tcx>, scrut_ty: Ty<'tcx>, @@ -228,7 +227,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( /// Traverse the patterns to warn the user about ranges that overlap on their endpoints. #[instrument(level = "debug", skip(cx, wildcard_arena))] pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &RustcCtxt<'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, wildcard_arena: &TypedArena>, ) { diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/rustc.rs similarity index 96% rename from compiler/rustc_pattern_analysis/src/cx.rs rename to compiler/rustc_pattern_analysis/src/rustc.rs index de42764c9bc..26c13332269 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -24,21 +24,19 @@ use crate::constructor::Constructor::*; -pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; -pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; -pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, MatchCheckCtxt<'p, 'tcx>>; -pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, MatchCheckCtxt<'p, 'tcx>>; -pub(crate) type PatCtxt<'a, 'p, 'tcx> = - crate::usefulness::PatCtxt<'a, 'p, MatchCheckCtxt<'p, 'tcx>>; +pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; +pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; +pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcCtxt<'p, 'tcx>>; +pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcCtxt<'p, 'tcx>>; +pub(crate) type PatCtxt<'a, 'p, 'tcx> = crate::usefulness::PatCtxt<'a, 'p, RustcCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = - crate::constructor::SplitConstructorSet>; + crate::constructor::SplitConstructorSet>; pub type Usefulness = crate::usefulness::Usefulness; -pub type UsefulnessReport<'p, 'tcx> = - crate::usefulness::UsefulnessReport<'p, MatchCheckCtxt<'p, 'tcx>>; -pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; +pub type UsefulnessReport<'p, 'tcx> = crate::usefulness::UsefulnessReport<'p, RustcCtxt<'p, 'tcx>>; +pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; #[derive(Clone)] -pub struct MatchCheckCtxt<'p, 'tcx> { +pub struct RustcCtxt<'p, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) @@ -62,13 +60,13 @@ pub struct MatchCheckCtxt<'p, 'tcx> { pub known_valid_scrutinee: bool, } -impl<'p, 'tcx> fmt::Debug for MatchCheckCtxt<'p, 'tcx> { +impl<'p, 'tcx> fmt::Debug for RustcCtxt<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MatchCheckCtxt").finish() + f.debug_struct("RustcCtxt").finish() } } -impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { +impl<'p, 'tcx> RustcCtxt<'p, 'tcx> { pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { !ty.is_inhabited_from(self.tcx, self.module, self.param_env) } @@ -153,8 +151,7 @@ pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> // patterns. If we're here we can assume this is a box pattern. cx.dropless_arena.alloc_from_iter(once(args.type_at(0))) } else { - let variant = - &adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); cx.dropless_arena.alloc_from_iter(tys) } @@ -199,8 +196,7 @@ pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> u // patterns. If we're here we can assume this is a box pattern. 1 } else { - let variant = - &adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); self.list_variant_nonhidden_fields(ty, variant).count() } } @@ -431,8 +427,7 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; - let variant = - &adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); @@ -687,8 +682,7 @@ pub fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> { PatKind::Deref { subpattern: subpatterns.next().unwrap() } } ty::Adt(adt_def, args) => { - let variant_index = - MatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); + let variant_index = RustcCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let variant = &adt_def.variant(variant_index); let subpatterns = cx .list_variant_nonhidden_fields(pat.ty(), variant) @@ -784,9 +778,9 @@ pub(crate) fn debug_pat( } ty::Adt(..) | ty::Tuple(..) => { let variant = match pat.ty().kind() { - ty::Adt(adt, _) => Some( - adt.variant(MatchCheckCtxt::variant_index_for_adt(pat.ctor(), *adt)), - ), + ty::Adt(adt, _) => { + Some(adt.variant(RustcCtxt::variant_index_for_adt(pat.ctor(), *adt))) + } ty::Tuple(_) => None, _ => unreachable!(), }; @@ -854,7 +848,7 @@ pub(crate) fn debug_pat( } } -impl<'p, 'tcx> MatchCx for MatchCheckCtxt<'p, 'tcx> { +impl<'p, 'tcx> MatchCx for RustcCtxt<'p, 'tcx> { type Ty = Ty<'tcx>; type Span = Span; type VariantIdx = VariantIdx; From 42f43938248d1272c0b3edc85385bde322a9c7fc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 20:45:34 +0100 Subject: [PATCH 06/16] Iron out last rustc-specific details --- .../src/thir/pattern/check_match.rs | 9 +++---- .../rustc_pattern_analysis/src/constructor.rs | 24 +++++++++++++++---- compiler/rustc_pattern_analysis/src/lib.rs | 6 ++++- compiler/rustc_pattern_analysis/src/lints.rs | 2 +- compiler/rustc_pattern_analysis/src/rustc.rs | 20 ++++++++++------ 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e7f3eee16a4..4eed289a24d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -429,7 +429,8 @@ fn check_match( let arm = &self.thir.arms[arm]; let got_error = self.with_lint_level(arm.lint_level, |this| { let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true }; - let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() }; + let arm = + MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() }; tarms.push(arm); false }); @@ -552,7 +553,7 @@ fn analyze_binding( ) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> { let cx = self.new_cx(refutability, None, scrut, pat.span); let pat = self.lower_pattern(&cx, pat)?; - let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }]; + let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }]; let report = analyze_match(&cx, &arms, pat.ty()); Ok((cx, report)) } @@ -855,7 +856,7 @@ fn report_arm_reachability<'p, 'tcx>( for (arm, is_useful) in report.arm_usefulness.iter() { match is_useful { Usefulness::Redundant => { - report_unreachable_pattern(*arm.pat.span(), arm.hir_id, catchall) + report_unreachable_pattern(*arm.pat.span(), arm.arm_data, catchall) } Usefulness::Useful(redundant_spans) if redundant_spans.is_empty() => {} // The arm is reachable, but contains redundant subpatterns (from or-patterns). @@ -864,7 +865,7 @@ fn report_arm_reachability<'p, 'tcx>( // Emit lints in the order in which they occur in the file. redundant_spans.sort_unstable(); for span in redundant_spans { - report_unreachable_pattern(span, arm.hir_id, None); + report_unreachable_pattern(span, arm.arm_data, None); } } } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 2082ecda44c..d7e216ddbc5 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -156,7 +156,6 @@ use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::RangeEnd; use rustc_index::IndexVec; use self::Constructor::*; @@ -173,6 +172,21 @@ enum Presence { Seen, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RangeEnd { + Included, + Excluded, +} + +impl fmt::Display for RangeEnd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + RangeEnd::Included => "..=", + RangeEnd::Excluded => "..", + }) + } +} + /// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the /// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as /// `255`. See `signed_bias` for details. @@ -220,7 +234,7 @@ pub fn minus_one(self) -> Self { match self { Finite(n) => match n.checked_sub(1) { Some(m) => Finite(m), - None => bug!(), + None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"), }, JustAfterMax => Finite(u128::MAX), x => x, @@ -233,7 +247,7 @@ pub fn plus_one(self) -> Self { Some(m) => Finite(m), None => JustAfterMax, }, - JustAfterMax => bug!(), + JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"), x => x, } } @@ -270,7 +284,7 @@ pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) } if lo >= hi { // This should have been caught earlier by E0030. - bug!("malformed range pattern: {lo:?}..{hi:?}"); + panic!("malformed range pattern: {lo:?}..{hi:?}"); } IntRange { lo, hi } } @@ -431,7 +445,7 @@ pub fn new(array_len: Option, kind: SliceKind) -> Self { let kind = match (array_len, kind) { // If the middle `..` has length 0, we effectively have a fixed-length pattern. (Some(len), VarLen(prefix, suffix)) if prefix + suffix == len => FixedLen(len), - (Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => bug!( + (Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => panic!( "Slice pattern of length {} longer than its array length {len}", prefix + suffix ), diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 192f35c6abf..d0eff55f5a1 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -30,8 +30,12 @@ pub trait MatchCx: Sized + Clone + fmt::Debug { type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy type Span: Clone + Default; + /// The index of an enum variant. type VariantIdx: Clone + Idx; + /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; + /// Extra data to store on a match arm. + type ArmData: Copy + Clone + fmt::Debug; fn is_opaque_ty(ty: Self::Ty) -> bool; fn is_exhaustive_patterns_feature_on(&self) -> bool; @@ -60,8 +64,8 @@ pub trait MatchCx: Sized + Clone + fmt::Debug { pub struct MatchArm<'p, Cx: MatchCx> { /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. pub pat: &'p DeconstructedPat<'p, Cx>, - pub hir_id: HirId, pub has_guard: bool, + pub arm_data: Cx::ArmData, } impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 89eb439c3f0..be12c8dbd7d 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -205,7 +205,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( // usage of the lint. for arm in arms { let (lint_level, lint_level_source) = - cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.hir_id); + cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(lint_level, rustc_session::lint::Level::Allow) { let decorator = NonExhaustiveOmittedPatternLintOnArm { lint_span: lint_level_source.span(), diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 26c13332269..4b13c11d29e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -4,7 +4,7 @@ use rustc_arena::{DroplessArena, TypedArena}; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; -use rustc_hir::{HirId, RangeEnd}; +use rustc_hir::HirId; use rustc_index::Idx; use rustc_index::IndexVec; use rustc_middle::middle::stability::EvalResult; @@ -18,12 +18,13 @@ use smallvec::SmallVec; use crate::constructor::{ - IntRange, MaybeInfiniteInt, OpaqueId, Slice, SliceKind, VariantVisibility, + IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; use crate::MatchCx; use crate::constructor::Constructor::*; +// Re-export rustc-specific versions of all these types. pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcCtxt<'p, 'tcx>>; @@ -520,12 +521,16 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { } PatKind::Range(patrange) => { let PatRange { lo, hi, end, .. } = patrange.as_ref(); + let end = match end { + rustc_hir::RangeEnd::Included => RangeEnd::Included, + rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded, + }; let ty = pat.ty; ctor = match ty.kind() { ty::Char | ty::Int(_) | ty::Uint(_) => { let lo = cx.lower_pat_range_bdy(*lo, ty); let hi = cx.lower_pat_range_bdy(*hi, ty); - IntRange(IntRange::from_range(lo, hi, *end)) + IntRange(IntRange::from_range(lo, hi, end)) } ty::Float(fty) => { use rustc_apfloat::Float; @@ -536,13 +541,13 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { use rustc_apfloat::ieee::Single; let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY); let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY); - F32Range(lo, hi, *end) + F32Range(lo, hi, end) } ty::FloatTy::F64 => { use rustc_apfloat::ieee::Double; let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY); let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY); - F64Range(lo, hi, *end) + F64Range(lo, hi, end) } } } @@ -634,7 +639,7 @@ pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx PatKind::Constant { value } } else { // We convert to an inclusive range for diagnostics. - let mut end = RangeEnd::Included; + let mut end = rustc_hir::RangeEnd::Included; let mut lo = cx.hoist_pat_range_bdy(range.lo, ty); if matches!(lo, PatRangeBoundary::PosInfinity) { // The only reason to get `PosInfinity` here is the special case where @@ -648,7 +653,7 @@ pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx } let hi = if matches!(range.hi, Finite(0)) { // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range. - end = RangeEnd::Excluded; + end = rustc_hir::RangeEnd::Excluded; range.hi } else { range.hi.minus_one() @@ -853,6 +858,7 @@ impl<'p, 'tcx> MatchCx for RustcCtxt<'p, 'tcx> { type Span = Span; type VariantIdx = VariantIdx; type StrLit = Const<'tcx>; + type ArmData = HirId; fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns From 16bd6ac3ed3e63cd6f2464b54026dbeb90ba6bbe Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 20:46:35 +0100 Subject: [PATCH 07/16] Gate rustc-specific code under a feature --- compiler/rustc_pattern_analysis/Cargo.toml | 29 ++++++++++++++----- .../rustc_pattern_analysis/src/constructor.rs | 1 + compiler/rustc_pattern_analysis/src/lib.rs | 20 +++++++++---- .../rustc_pattern_analysis/src/usefulness.rs | 7 +++-- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 0639944a45c..0ce1d1686f9 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -8,15 +8,28 @@ edition = "2021" rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_data_structures = { path = "../rustc_data_structures" } -rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } -rustc_hir = { path = "../rustc_hir" } +rustc_errors = { path = "../rustc_errors", optional = true } +rustc_fluent_macro = { path = "../rustc_fluent_macro", optional = true } +rustc_hir = { path = "../rustc_hir", optional = true } rustc_index = { path = "../rustc_index" } -rustc_macros = { path = "../rustc_macros" } -rustc_middle = { path = "../rustc_middle" } -rustc_session = { path = "../rustc_session" } -rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros", optional = true } +rustc_middle = { path = "../rustc_middle", optional = true } +rustc_session = { path = "../rustc_session", optional = true } +rustc_span = { path = "../rustc_span", optional = true } +rustc_target = { path = "../rustc_target", optional = true } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end + +[features] +default = ["rustc"] +rustc = [ + "dep:rustc_errors", + "dep:rustc_fluent_macro", + "dep:rustc_hir", + "dep:rustc_macros", + "dep:rustc_middle", + "dep:rustc_session", + "dep:rustc_span", + "dep:rustc_target", +] diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index d7e216ddbc5..a3af4eb6d21 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -266,6 +266,7 @@ pub struct IntRange { impl IntRange { /// Best effort; will not know that e.g. `255u8..` is a singleton. + #[cfg_attr(not(feature = "rustc"), allow(dead_code))] pub(crate) fn is_singleton(&self) -> bool { // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`. diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index d0eff55f5a1..8b7898af5b1 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -1,31 +1,40 @@ //! Analysis of patterns, notably match exhaustiveness checking. pub mod constructor; +#[cfg(feature = "rustc")] pub mod errors; +#[cfg(feature = "rustc")] pub(crate) mod lints; pub mod pat; +#[cfg(feature = "rustc")] pub mod rustc; pub mod usefulness; #[macro_use] extern crate tracing; +#[cfg(feature = "rustc")] #[macro_use] extern crate rustc_middle; +#[cfg(feature = "rustc")] rustc_fluent_macro::fluent_messages! { "../messages.ftl" } use std::fmt; -use constructor::{Constructor, ConstructorSet}; -use lints::PatternColumn; -use rustc_hir::HirId; use rustc_index::Idx; +#[cfg(feature = "rustc")] use rustc_middle::ty::Ty; -use usefulness::{compute_match_usefulness, ValidityConstraint}; -use crate::lints::{lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints}; +use crate::constructor::{Constructor, ConstructorSet}; +#[cfg(feature = "rustc")] +use crate::lints::{ + lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn, +}; use crate::pat::DeconstructedPat; +#[cfg(feature = "rustc")] use crate::rustc::RustcCtxt; +#[cfg(feature = "rustc")] +use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait MatchCx: Sized + Clone + fmt::Debug { type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy @@ -72,6 +81,7 @@ impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. +#[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( cx: &RustcCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index d52d9358b94..b1cc0f3c722 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -578,6 +578,7 @@ pub(crate) struct PatCtxt<'a, 'p, Cx: MatchCx> { impl<'a, 'p, Cx: MatchCx> PatCtxt<'a, 'p, Cx> { /// A `PatCtxt` when code other than `is_useful` needs one. + #[cfg_attr(not(feature = "rustc"), allow(dead_code))] pub(crate) fn new_dummy( cx: &'a Cx, ty: Cx::Ty, @@ -601,7 +602,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// - in the matrix, track whether a given place (aka column) is known to contain a valid value or /// not. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub(crate) enum ValidityConstraint { +pub enum ValidityConstraint { ValidOnly, MaybeInvalid, /// Option for backwards compatibility: the place is not known to be valid but we allow omitting @@ -610,7 +611,7 @@ pub(crate) enum ValidityConstraint { } impl ValidityConstraint { - pub(crate) fn from_bool(is_valid_only: bool) -> Self { + pub fn from_bool(is_valid_only: bool) -> Self { if is_valid_only { ValidOnly } else { MaybeInvalid } } @@ -1296,7 +1297,7 @@ pub struct UsefulnessReport<'p, Cx: MatchCx> { /// Computes whether a match is exhaustive and which of its arms are useful. #[instrument(skip(cx, arms, wildcard_arena), level = "debug")] -pub(crate) fn compute_match_usefulness<'p, Cx: MatchCx>( +pub fn compute_match_usefulness<'p, Cx: MatchCx>( cx: &Cx, arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty, From e646c9f7230becbe739d5c56fbb9952cb0d1b212 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 20:59:32 +0100 Subject: [PATCH 08/16] Make the `rustc_data_structures` dependency optional --- compiler/rustc_pattern_analysis/Cargo.toml | 3 ++- compiler/rustc_pattern_analysis/src/constructor.rs | 13 ++++++++----- compiler/rustc_pattern_analysis/src/lib.rs | 7 +++++++ compiler/rustc_pattern_analysis/src/pat.rs | 8 +++----- compiler/rustc_pattern_analysis/src/usefulness.rs | 10 ++++++++-- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 0ce1d1686f9..4c374056183 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # tidy-alphabetical-start rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } -rustc_data_structures = { path = "../rustc_data_structures" } +rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_errors = { path = "../rustc_errors", optional = true } rustc_fluent_macro = { path = "../rustc_fluent_macro", optional = true } rustc_hir = { path = "../rustc_hir", optional = true } @@ -24,6 +24,7 @@ tracing = "0.1" [features] default = ["rustc"] rustc = [ + "dep:rustc_data_structures", "dep:rustc_errors", "dep:rustc_fluent_macro", "dep:rustc_hir", diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index a3af4eb6d21..65312228801 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -155,7 +155,7 @@ use smallvec::SmallVec; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; -use rustc_data_structures::fx::FxHashSet; +use rustc_index::bit_set::{BitSet, GrowableBitSet}; use rustc_index::IndexVec; use self::Constructor::*; @@ -546,7 +546,7 @@ fn split( // therefore `Presence::Seen` in the column. let mut min_var_len = usize::MAX; // Tracks the fixed-length slices we've seen, to mark them as `Presence::Seen`. - let mut seen_fixed_lens = FxHashSet::default(); + let mut seen_fixed_lens = GrowableBitSet::new_empty(); match &mut max_slice { VarLen(max_prefix_len, max_suffix_len) => { // A length larger than any fixed-length slice encountered. @@ -614,7 +614,7 @@ fn split( smaller_lengths.map(FixedLen).chain(once(max_slice)).map(move |kind| { let arity = kind.arity(); - let seen = if min_var_len <= arity || seen_fixed_lens.contains(&arity) { + let seen = if min_var_len <= arity || seen_fixed_lens.contains(arity) { Presence::Seen } else { Presence::Unseen @@ -906,12 +906,15 @@ pub(crate) fn split<'a>( } } ConstructorSet::Variants { variants, non_exhaustive } => { - let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect(); + let mut seen_set: BitSet<_> = BitSet::new_empty(variants.len()); + for idx in seen.iter().map(|c| c.as_variant().unwrap()) { + seen_set.insert(idx); + } let mut skipped_a_hidden_variant = false; for (idx, visibility) in variants.iter_enumerated() { let ctor = Variant(idx); - if seen_set.contains(&idx) { + if seen_set.contains(idx) { present.push(ctor); } else { // We only put visible variants directly into `missing`. diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 8b7898af5b1..e54f8d90d0d 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -79,6 +79,13 @@ pub struct MatchArm<'p, Cx: MatchCx> { impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} +#[cfg(not(feature = "rustc"))] +pub trait Captures<'a> {} +#[cfg(not(feature = "rustc"))] +impl<'a, T: ?Sized> Captures<'a> for T {} +#[cfg(feature = "rustc")] +pub use rustc_data_structures::captures::Captures; + /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. #[cfg(feature = "rustc")] diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index c1052c88514..f719cc511fb 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -5,13 +5,11 @@ use smallvec::{smallvec, SmallVec}; -use rustc_data_structures::captures::Captures; - -use self::Constructor::*; - use crate::constructor::{Constructor, Slice, SliceKind}; use crate::usefulness::PatCtxt; -use crate::MatchCx; +use crate::{Captures, MatchCx}; + +use self::Constructor::*; /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index b1cc0f3c722..01f3962b31f 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -556,14 +556,20 @@ use std::fmt; use rustc_arena::TypedArena; -use rustc_data_structures::{captures::Captures, stack::ensure_sufficient_stack}; use crate::constructor::{Constructor, ConstructorSet}; use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::{MatchArm, MatchCx}; +use crate::{Captures, MatchArm, MatchCx}; use self::ValidityConstraint::*; +#[cfg(feature = "rustc")] +use rustc_data_structures::stack::ensure_sufficient_stack; +#[cfg(not(feature = "rustc"))] +pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + f() +} + #[derive(Clone)] pub(crate) struct PatCtxt<'a, 'p, Cx: MatchCx> { pub(crate) cx: &'a Cx, From f30c5956f5a755c532cc2a29f089a908bc0201fe Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 21:52:56 +0100 Subject: [PATCH 09/16] Make `rustc_index::bit_set` available on stable --- compiler/rustc_index/src/bit_set.rs | 48 ++++++++++++++++++++++------- compiler/rustc_index/src/lib.rs | 1 - 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index d0b4889b45f..3ea1a52ae28 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -9,6 +9,7 @@ use arrayvec::ArrayVec; use smallvec::{smallvec, SmallVec}; +#[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable}; use crate::{Idx, IndexVec}; @@ -111,7 +112,8 @@ pub fn intersect(&mut self, other: &Rhs) -> bool /// to or greater than the domain size. All operations that involve two bitsets /// will panic if the bitsets have differing domain sizes. /// -#[derive(Eq, PartialEq, Hash, Decodable, Encodable)] +#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[derive(Eq, PartialEq, Hash)] pub struct BitSet { domain_size: usize, words: SmallVec<[Word; 2]>, @@ -491,10 +493,21 @@ pub fn insert(&mut self, elem: T) -> bool { match *chunk { Zeros(chunk_domain_size) => { if chunk_domain_size > 1 { - // We take some effort to avoid copying the words. - let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed(); - // SAFETY: `words` can safely be all zeroes. - let mut words = unsafe { words.assume_init() }; + #[cfg(feature = "nightly")] + let mut words = { + // We take some effort to avoid copying the words. + let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed(); + // SAFETY: `words` can safely be all zeroes. + unsafe { words.assume_init() } + }; + #[cfg(not(feature = "nightly"))] + let mut words = { + let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); + // SAFETY: `words` can safely be all zeroes. + let words = unsafe { words.assume_init() }; + // Unfortunate possibly-large copy + Rc::new(words) + }; let words_ref = Rc::get_mut(&mut words).unwrap(); let (word_index, mask) = chunk_word_index_and_mask(elem); @@ -545,10 +558,21 @@ pub fn remove(&mut self, elem: T) -> bool { Zeros(_) => false, Ones(chunk_domain_size) => { if chunk_domain_size > 1 { - // We take some effort to avoid copying the words. - let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed(); - // SAFETY: `words` can safely be all zeroes. - let mut words = unsafe { words.assume_init() }; + #[cfg(feature = "nightly")] + let mut words = { + // We take some effort to avoid copying the words. + let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed(); + // SAFETY: `words` can safely be all zeroes. + unsafe { words.assume_init() } + }; + #[cfg(not(feature = "nightly"))] + let mut words = { + let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); + // SAFETY: `words` can safely be all zeroes. + let words = unsafe { words.assume_init() }; + // Unfortunate possibly-large copy + Rc::new(words) + }; let words_ref = Rc::get_mut(&mut words).unwrap(); // Set only the bits in use. @@ -1564,7 +1588,8 @@ fn from(bit_set: BitSet) -> Self { /// /// All operations that involve a row and/or column index will panic if the /// index exceeds the relevant bound. -#[derive(Clone, Eq, PartialEq, Hash, Decodable, Encodable)] +#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[derive(Clone, Eq, PartialEq, Hash)] pub struct BitMatrix { num_rows: usize, num_columns: usize, @@ -1993,7 +2018,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// A fixed-sized bitset type represented by an integer type. Indices outwith than the range /// representable by `T` are considered set. -#[derive(Copy, Clone, Eq, PartialEq, Decodable, Encodable)] +#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct FiniteBitSet(pub T); impl FiniteBitSet { diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index c5602392c53..185e0c7d698 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -14,7 +14,6 @@ )] #![cfg_attr(feature = "nightly", allow(internal_features))] -#[cfg(feature = "nightly")] pub mod bit_set; #[cfg(feature = "nightly")] pub mod interval; From 63c5b008e12355615a3dfe5b4c83b9a109b2bbc6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 11 Dec 2023 21:56:17 +0100 Subject: [PATCH 10/16] Make the crate compile on stable --- Cargo.lock | 7 +++++++ compiler/rustc_pattern_analysis/Cargo.toml | 15 ++++++++++++--- compiler/rustc_pattern_analysis/src/usefulness.rs | 5 +++++ src/tools/tidy/src/deps.rs | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98c030b3996..d7609eb35ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4348,6 +4348,7 @@ dependencies = [ "rustc_target", "smallvec", "tracing", + "typed-arena", ] [[package]] @@ -5683,6 +5684,12 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.16.0" diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index 4c374056183..908d00cf105 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -6,24 +6,28 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_apfloat = "0.2.0" -rustc_arena = { path = "../rustc_arena" } +rustc_arena = { path = "../rustc_arena", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_errors = { path = "../rustc_errors", optional = true } rustc_fluent_macro = { path = "../rustc_fluent_macro", optional = true } rustc_hir = { path = "../rustc_hir", optional = true } -rustc_index = { path = "../rustc_index" } +rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_middle = { path = "../rustc_middle", optional = true } rustc_session = { path = "../rustc_session", optional = true } rustc_span = { path = "../rustc_span", optional = true } rustc_target = { path = "../rustc_target", optional = true } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +smallvec = { version = "1.8.1", features = ["union"] } tracing = "0.1" +typed-arena = { version = "2.0.2", optional = true } # tidy-alphabetical-end [features] default = ["rustc"] +# It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so +# we use another feature instead. The crate won't compile if one of these isn't enabled. rustc = [ + "dep:rustc_arena", "dep:rustc_data_structures", "dep:rustc_errors", "dep:rustc_fluent_macro", @@ -33,4 +37,9 @@ rustc = [ "dep:rustc_session", "dep:rustc_span", "dep:rustc_target", + "smallvec/may_dangle", + "rustc_index/nightly", +] +stable = [ + "dep:typed-arena", ] diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 01f3962b31f..3141e992f33 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -555,7 +555,12 @@ use smallvec::{smallvec, SmallVec}; use std::fmt; +// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so +// we use another feature instead. The crate won't compile if one of these isn't enabled. +#[cfg(feature = "rustc")] use rustc_arena::TypedArena; +#[cfg(feature = "stable")] +use typed_arena::Arena as TypedArena; use crate::constructor::{Constructor, ConstructorSet}; use crate::pat::{DeconstructedPat, WitnessPat}; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 3bfe811b58e..3c00027b9fd 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -357,6 +357,7 @@ "tracing-tree", "twox-hash", "type-map", + "typed-arena", "typenum", "unic-langid", "unic-langid-impl", From e10b1657758e39668c0fd93c9a0d3f32d0a7c0a7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 14 Dec 2023 17:54:11 +0100 Subject: [PATCH 11/16] s/RustcCtxt/RustcMatchCheckCtxt/ --- compiler/rustc_mir_build/src/errors.rs | 4 +- .../src/thir/pattern/check_match.rs | 4 +- compiler/rustc_pattern_analysis/src/errors.rs | 4 +- compiler/rustc_pattern_analysis/src/lib.rs | 4 +- compiler/rustc_pattern_analysis/src/lints.rs | 13 +++-- compiler/rustc_pattern_analysis/src/rustc.rs | 57 +++++++++++-------- 6 files changed, 48 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 6894b44dbd4..6e5cfbd4652 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -6,7 +6,7 @@ }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; -use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcCtxt}; +use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcMatchCheckCtxt}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -454,7 +454,7 @@ pub enum UnusedUnsafeEnclosing { } pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { - pub cx: &'m RustcCtxt<'p, 'tcx>, + pub cx: &'m RustcMatchCheckCtxt<'p, 'tcx>, pub expr_span: Span, pub span: Span, pub ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 4eed289a24d..34fd10bf967 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,7 +1,7 @@ use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::{ - Constructor, DeconstructedPat, RustcCtxt as MatchCheckCtxt, Usefulness, UsefulnessReport, - WitnessPat, + Constructor, DeconstructedPat, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness, + UsefulnessReport, WitnessPat, }; use rustc_pattern_analysis::{analyze_match, MatchArm}; diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 8585e6da3cd..88770b0c43b 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::Ty; use rustc_span::Span; -use crate::rustc::{RustcCtxt, WitnessPat}; +use crate::rustc::{RustcMatchCheckCtxt, WitnessPat}; #[derive(Subdiagnostic)] #[label(pattern_analysis_uncovered)] @@ -21,7 +21,7 @@ pub struct Uncovered<'tcx> { impl<'tcx> Uncovered<'tcx> { pub fn new<'p>( span: Span, - cx: &RustcCtxt<'p, 'tcx>, + cx: &RustcMatchCheckCtxt<'p, 'tcx>, witnesses: Vec>, ) -> Self { let witness_1 = cx.hoist_witness_pat(witnesses.get(0).unwrap()); diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index e54f8d90d0d..3e2599858af 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -32,7 +32,7 @@ }; use crate::pat::DeconstructedPat; #[cfg(feature = "rustc")] -use crate::rustc::RustcCtxt; +use crate::rustc::RustcMatchCheckCtxt; #[cfg(feature = "rustc")] use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; @@ -90,7 +90,7 @@ impl<'a, T: ?Sized> Captures<'a> for T {} /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - cx: &RustcCtxt<'p, 'tcx>, + cx: &RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> rustc::UsefulnessReport<'p, 'tcx> { diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index be12c8dbd7d..38108b62d79 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -13,7 +13,8 @@ OverlappingRangeEndpoints, Uncovered, }; use crate::rustc::{ - Constructor, DeconstructedPat, MatchArm, PatCtxt, RustcCtxt, SplitConstructorSet, WitnessPat, + Constructor, DeconstructedPat, MatchArm, PatCtxt, RustcMatchCheckCtxt, SplitConstructorSet, + WitnessPat, }; use crate::MatchCx; @@ -55,10 +56,10 @@ fn head_ty(&self) -> Option> { // If the type is opaque and it is revealed anywhere in the column, we take the revealed // version. Otherwise we could encounter constructors for the revealed type and crash. let first_ty = self.patterns[0].ty(); - if RustcCtxt::is_opaque_ty(first_ty) { + if RustcMatchCheckCtxt::is_opaque_ty(first_ty) { for pat in &self.patterns { let ty = pat.ty(); - if !RustcCtxt::is_opaque_ty(ty) { + if !RustcMatchCheckCtxt::is_opaque_ty(ty) { return Some(ty); } } @@ -125,7 +126,7 @@ fn specialize<'b>( /// in a given column. #[instrument(level = "debug", skip(cx, wildcard_arena), ret)] fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &RustcCtxt<'p, 'tcx>, + cx: &RustcMatchCheckCtxt<'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, wildcard_arena: &TypedArena>, ) -> Vec> { @@ -173,7 +174,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &RustcCtxt<'p, 'tcx>, + cx: &RustcMatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], pat_column: &PatternColumn<'a, 'p, 'tcx>, scrut_ty: Ty<'tcx>, @@ -227,7 +228,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( /// Traverse the patterns to warn the user about ranges that overlap on their endpoints. #[instrument(level = "debug", skip(cx, wildcard_arena))] pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( - cx: &RustcCtxt<'p, 'tcx>, + cx: &RustcMatchCheckCtxt<'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, wildcard_arena: &TypedArena>, ) { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 4b13c11d29e..1d08d2efef3 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -25,19 +25,23 @@ use crate::constructor::Constructor::*; // Re-export rustc-specific versions of all these types. -pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; -pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; -pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcCtxt<'p, 'tcx>>; -pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcCtxt<'p, 'tcx>>; -pub(crate) type PatCtxt<'a, 'p, 'tcx> = crate::usefulness::PatCtxt<'a, 'p, RustcCtxt<'p, 'tcx>>; +pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; +pub type ConstructorSet<'p, 'tcx> = + crate::constructor::ConstructorSet>; +pub type DeconstructedPat<'p, 'tcx> = + crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub(crate) type PatCtxt<'a, 'p, 'tcx> = + crate::usefulness::PatCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = - crate::constructor::SplitConstructorSet>; + crate::constructor::SplitConstructorSet>; pub type Usefulness = crate::usefulness::Usefulness; -pub type UsefulnessReport<'p, 'tcx> = crate::usefulness::UsefulnessReport<'p, RustcCtxt<'p, 'tcx>>; -pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; +pub type UsefulnessReport<'p, 'tcx> = + crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; #[derive(Clone)] -pub struct RustcCtxt<'p, 'tcx> { +pub struct RustcMatchCheckCtxt<'p, 'tcx> { pub tcx: TyCtxt<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) @@ -61,13 +65,13 @@ pub struct RustcCtxt<'p, 'tcx> { pub known_valid_scrutinee: bool, } -impl<'p, 'tcx> fmt::Debug for RustcCtxt<'p, 'tcx> { +impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RustcCtxt").finish() + f.debug_struct("RustcMatchCheckCtxt").finish() } } -impl<'p, 'tcx> RustcCtxt<'p, 'tcx> { +impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { !ty.is_inhabited_from(self.tcx, self.module, self.param_env) } @@ -152,7 +156,8 @@ pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> // patterns. If we're here we can assume this is a box pattern. cx.dropless_arena.alloc_from_iter(once(args.type_at(0))) } else { - let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = + &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); cx.dropless_arena.alloc_from_iter(tys) } @@ -197,7 +202,8 @@ pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> u // patterns. If we're here we can assume this is a box pattern. 1 } else { - let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = + &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); self.list_variant_nonhidden_fields(ty, variant).count() } } @@ -428,7 +434,8 @@ pub fn lower_pat(&self, pat: &Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; - let variant = &adt.variant(RustcCtxt::variant_index_for_adt(&ctor, *adt)); + let variant = + &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); @@ -687,7 +694,8 @@ pub fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> { PatKind::Deref { subpattern: subpatterns.next().unwrap() } } ty::Adt(adt_def, args) => { - let variant_index = RustcCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); + let variant_index = + RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let variant = &adt_def.variant(variant_index); let subpatterns = cx .list_variant_nonhidden_fields(pat.ty(), variant) @@ -782,13 +790,14 @@ pub(crate) fn debug_pat( write!(f, "box {subpattern:?}") } ty::Adt(..) | ty::Tuple(..) => { - let variant = match pat.ty().kind() { - ty::Adt(adt, _) => { - Some(adt.variant(RustcCtxt::variant_index_for_adt(pat.ctor(), *adt))) - } - ty::Tuple(_) => None, - _ => unreachable!(), - }; + let variant = + match pat.ty().kind() { + ty::Adt(adt, _) => Some(adt.variant( + RustcMatchCheckCtxt::variant_index_for_adt(pat.ctor(), *adt), + )), + ty::Tuple(_) => None, + _ => unreachable!(), + }; if let Some(variant) = variant { write!(f, "{}", variant.name)?; @@ -853,7 +862,7 @@ pub(crate) fn debug_pat( } } -impl<'p, 'tcx> MatchCx for RustcCtxt<'p, 'tcx> { +impl<'p, 'tcx> MatchCx for RustcMatchCheckCtxt<'p, 'tcx> { type Ty = Ty<'tcx>; type Span = Span; type VariantIdx = VariantIdx; From 8c5e89907c1f6312c8613b5d6851381bbcde8052 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 14 Dec 2023 17:58:33 +0100 Subject: [PATCH 12/16] Address review comments --- compiler/rustc_pattern_analysis/src/constructor.rs | 3 +-- compiler/rustc_pattern_analysis/src/lib.rs | 4 ---- compiler/rustc_pattern_analysis/src/lints.rs | 9 +++------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 65312228801..2093c511032 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -266,8 +266,7 @@ pub struct IntRange { impl IntRange { /// Best effort; will not know that e.g. `255u8..` is a singleton. - #[cfg_attr(not(feature = "rustc"), allow(dead_code))] - pub(crate) fn is_singleton(&self) -> bool { + pub fn is_singleton(&self) -> bool { // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`. self.lo.plus_one() == self.hi diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 3e2599858af..36ec86eb5b3 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -79,12 +79,8 @@ pub struct MatchArm<'p, Cx: MatchCx> { impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} -#[cfg(not(feature = "rustc"))] pub trait Captures<'a> {} -#[cfg(not(feature = "rustc"))] impl<'a, T: ?Sized> Captures<'a> for T {} -#[cfg(feature = "rustc")] -pub use rustc_data_structures::captures::Captures; /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 38108b62d79..2391dd9096e 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -82,14 +82,11 @@ fn iter<'b>(&'b self) -> impl Iterator> + /// This returns one column per field of the constructor. They usually all have the same length /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns /// which may change the lengths. - fn specialize<'b>( + fn specialize( &self, - pcx: &'b PatCtxt<'_, 'p, 'tcx>, + pcx: &PatCtxt<'a, 'p, 'tcx>, ctor: &Constructor<'p, 'tcx>, - ) -> Vec> - where - 'a: 'b, - { + ) -> Vec> { let arity = ctor.arity(pcx); if arity == 0 { return Vec::new(); From 1e89a38423ee1b562e3b10278d0097d88bc48f5f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 15 Dec 2023 16:18:21 +0100 Subject: [PATCH 13/16] `pattern_analysis` doesn't need to know what spans are --- .../src/thir/pattern/check_match.rs | 16 ++++----- compiler/rustc_pattern_analysis/src/lib.rs | 5 ++- compiler/rustc_pattern_analysis/src/lints.rs | 4 +-- compiler/rustc_pattern_analysis/src/pat.rs | 36 +++++++++---------- compiler/rustc_pattern_analysis/src/rustc.rs | 4 +-- .../rustc_pattern_analysis/src/usefulness.rs | 10 +++--- 6 files changed, 39 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 34fd10bf967..c435f4023af 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -856,21 +856,21 @@ fn report_arm_reachability<'p, 'tcx>( for (arm, is_useful) in report.arm_usefulness.iter() { match is_useful { Usefulness::Redundant => { - report_unreachable_pattern(*arm.pat.span(), arm.arm_data, catchall) + report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall) } - Usefulness::Useful(redundant_spans) if redundant_spans.is_empty() => {} + Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {} // The arm is reachable, but contains redundant subpatterns (from or-patterns). - Usefulness::Useful(redundant_spans) => { - let mut redundant_spans = redundant_spans.clone(); + Usefulness::Useful(redundant_subpats) => { + let mut redundant_subpats = redundant_subpats.clone(); // Emit lints in the order in which they occur in the file. - redundant_spans.sort_unstable(); - for span in redundant_spans { - report_unreachable_pattern(span, arm.arm_data, None); + redundant_subpats.sort_unstable_by_key(|pat| pat.data()); + for pat in redundant_subpats { + report_unreachable_pattern(*pat.data(), arm.arm_data, None); } } } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { - catchall = Some(*arm.pat.span()); + catchall = Some(*arm.pat.data()); } } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 36ec86eb5b3..12d2e0fc18b 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -37,14 +37,17 @@ use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; pub trait MatchCx: Sized + Clone + fmt::Debug { + /// The type of a pattern. type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy - type Span: Clone + Default; /// The index of an enum variant. type VariantIdx: Clone + Idx; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store on a match arm. type ArmData: Copy + Clone + fmt::Debug; + /// Extra data to store on a pattern. `Default` needed when we create fictitious wildcard + /// patterns during analysis. + type PatData: Clone + Default; fn is_opaque_ty(ty: Self::Ty) -> bool; fn is_exhaustive_patterns_feature_on(&self) -> bool; diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 2391dd9096e..6c00a265b69 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -213,7 +213,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( }; use rustc_errors::DecorateLint; - let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.span(), ""); + let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.data(), ""); err.set_primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); @@ -263,7 +263,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let mut suffixes: SmallVec<[_; 1]> = Default::default(); // Iterate on patterns that contained `overlap`. for pat in column.iter() { - let this_span = *pat.span(); + let this_span = *pat.data(); let Constructor::IntRange(this_range) = pat.ctor() else { continue }; if this_range.is_singleton() { // Don't lint when one of the ranges is a singleton. diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index f719cc511fb..daaf62dca3d 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -26,23 +26,23 @@ pub struct DeconstructedPat<'p, Cx: MatchCx> { ctor: Constructor, fields: &'p [DeconstructedPat<'p, Cx>], ty: Cx::Ty, - span: Cx::Span, + data: Cx::PatData, /// Whether removing this arm would change the behavior of the match expression. useful: Cell, } impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> { - pub fn wildcard(ty: Cx::Ty, span: Cx::Span) -> Self { - Self::new(Wildcard, &[], ty, span) + pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self { + Self::new(Wildcard, &[], ty, data) } pub fn new( ctor: Constructor, fields: &'p [DeconstructedPat<'p, Cx>], ty: Cx::Ty, - span: Cx::Span, + data: Cx::PatData, ) -> Self { - DeconstructedPat { ctor, fields, ty, span, useful: Cell::new(false) } + DeconstructedPat { ctor, fields, ty, data, useful: Cell::new(false) } } pub(crate) fn is_or_pat(&self) -> bool { @@ -63,8 +63,8 @@ pub fn ctor(&self) -> &Constructor { pub fn ty(&self) -> Cx::Ty { self.ty } - pub fn span(&self) -> &Cx::Span { - &self.span + pub fn data(&self) -> &Cx::PatData { + &self.data } pub fn iter_fields<'a>( @@ -83,7 +83,7 @@ pub(crate) fn specialize<'a>( let wildcard_sub_tys = || { let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty); tys.iter() - .map(|ty| DeconstructedPat::wildcard(*ty, Cx::Span::default())) + .map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default())) .map(|pat| pcx.wildcard_arena.alloc(pat) as &_) .collect() }; @@ -113,8 +113,8 @@ pub(crate) fn specialize<'a>( } } - /// We keep track for each pattern if it was ever useful during the analysis. This is used - /// with `redundant_spans` to report redundant subpatterns arising from or patterns. + /// We keep track for each pattern if it was ever useful during the analysis. This is used with + /// `redundant_subpatterns` to report redundant subpatterns arising from or patterns. pub(crate) fn set_useful(&self) { self.useful.set(true) } @@ -132,19 +132,19 @@ pub(crate) fn is_useful(&self) -> bool { } } - /// Report the spans of subpatterns that were not useful, if any. - pub(crate) fn redundant_spans(&self) -> Vec { - let mut spans = Vec::new(); - self.collect_redundant_spans(&mut spans); - spans + /// Report the subpatterns that were not useful, if any. + pub(crate) fn redundant_subpatterns(&self) -> Vec<&Self> { + let mut subpats = Vec::new(); + self.collect_redundant_subpatterns(&mut subpats); + subpats } - fn collect_redundant_spans(&self, spans: &mut Vec) { + fn collect_redundant_subpatterns<'a>(&'a self, subpats: &mut Vec<&'a Self>) { // We don't look at subpatterns if we already reported the whole pattern as redundant. if !self.is_useful() { - spans.push(self.span.clone()); + subpats.push(self); } else { for p in self.iter_fields() { - p.collect_redundant_spans(spans); + p.collect_redundant_subpatterns(subpats); } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 1d08d2efef3..e5347ce6363 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -35,7 +35,7 @@ crate::usefulness::PatCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = crate::constructor::SplitConstructorSet>; -pub type Usefulness = crate::usefulness::Usefulness; +pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type UsefulnessReport<'p, 'tcx> = crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat>; @@ -864,10 +864,10 @@ pub(crate) fn debug_pat( impl<'p, 'tcx> MatchCx for RustcMatchCheckCtxt<'p, 'tcx> { type Ty = Ty<'tcx>; - type Span = Span; type VariantIdx = VariantIdx; type StrLit = Const<'tcx>; type ArmData = HirId; + type PatData = Span; fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 3141e992f33..88d28c8f483 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -849,7 +849,7 @@ fn new( scrut_validity: ValidityConstraint, ) -> Self { let wild_pattern = - wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Cx::Span::default())); + wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Default::default())); let wildcard_row = PatStack::from_pattern(wild_pattern); let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), @@ -1287,11 +1287,11 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( /// Indicates whether or not a given arm is useful. #[derive(Clone, Debug)] -pub enum Usefulness { +pub enum Usefulness<'p, Cx: MatchCx> { /// The arm is useful. This additionally carries a set of or-pattern branches that have been /// found to be redundant despite the overall arm being useful. Used only in the presence of /// or-patterns, otherwise it stays empty. - Useful(Vec), + Useful(Vec<&'p DeconstructedPat<'p, Cx>>), /// The arm is redundant and can be removed without changing the behavior of the match /// expression. Redundant, @@ -1300,7 +1300,7 @@ pub enum Usefulness { /// The output of checking a match for exhaustiveness and arm usefulness. pub struct UsefulnessReport<'p, Cx: MatchCx> { /// For each arm of the input, whether that arm is useful after the arms above it. - pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness)>, + pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<'p, Cx>)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. pub non_exhaustiveness_witnesses: Vec>, @@ -1327,7 +1327,7 @@ pub fn compute_match_usefulness<'p, Cx: MatchCx>( debug!(?arm); // We warn when a pattern is not useful. let usefulness = if arm.pat.is_useful() { - Usefulness::Useful(arm.pat.redundant_spans()) + Usefulness::Useful(arm.pat.redundant_subpatterns()) } else { Usefulness::Redundant }; From 60ea14bfaab32d2d1d5f956acfd08d72a2a79e1c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 15 Dec 2023 16:32:44 +0100 Subject: [PATCH 14/16] s/PatCtxt/PlaceCtxt/ --- .../rustc_pattern_analysis/src/constructor.rs | 10 +++--- compiler/rustc_pattern_analysis/src/lints.rs | 10 +++--- compiler/rustc_pattern_analysis/src/pat.rs | 6 ++-- compiler/rustc_pattern_analysis/src/rustc.rs | 4 +-- .../rustc_pattern_analysis/src/usefulness.rs | 36 +++++++++---------- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 2093c511032..ec1ddd56b1c 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -162,7 +162,7 @@ use self::MaybeInfiniteInt::*; use self::SliceKind::*; -use crate::usefulness::PatCtxt; +use crate::usefulness::PlaceCtxt; use crate::MatchCx; /// Whether we have seen a constructor in the column or not. @@ -717,7 +717,7 @@ fn as_slice(&self) -> Option { /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. - pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, Cx>) -> usize { + pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize { pcx.cx.ctor_arity(self, pcx.ty) } @@ -726,7 +726,7 @@ pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, Cx>) -> usize { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, Cx>, other: &Self) -> bool { + pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool { match (self, other) { (Wildcard, _) => pcx .cx @@ -859,7 +859,7 @@ impl ConstructorSet { #[instrument(level = "debug", skip(self, pcx, ctors), ret)] pub(crate) fn split<'a>( &self, - pcx: &PatCtxt<'_, '_, Cx>, + pcx: &PlaceCtxt<'_, '_, Cx>, ctors: impl Iterator> + Clone, ) -> SplitConstructorSet where @@ -1008,7 +1008,7 @@ pub(crate) fn split<'a>( // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. if !pcx.cx.is_exhaustive_patterns_feature_on() - && !(pcx.is_top_level && matches!(self, Self::NoConstructors)) + && !(pcx.is_scrutinee && matches!(self, Self::NoConstructors)) { // Treat all missing constructors as nonempty. // This clears `missing_empty`. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 6c00a265b69..2d4b06669b5 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -13,7 +13,7 @@ OverlappingRangeEndpoints, Uncovered, }; use crate::rustc::{ - Constructor, DeconstructedPat, MatchArm, PatCtxt, RustcMatchCheckCtxt, SplitConstructorSet, + Constructor, DeconstructedPat, MatchArm, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat, }; use crate::MatchCx; @@ -68,7 +68,7 @@ fn head_ty(&self) -> Option> { } /// Do constructor splitting on the constructors of the column. - fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> { + fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors) } @@ -84,7 +84,7 @@ fn iter<'b>(&'b self) -> impl Iterator> + /// which may change the lengths. fn specialize( &self, - pcx: &PatCtxt<'a, 'p, 'tcx>, + pcx: &PlaceCtxt<'a, 'p, 'tcx>, ctor: &Constructor<'p, 'tcx>, ) -> Vec> { let arity = ctor.arity(pcx); @@ -130,7 +130,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let Some(ty) = column.head_ty() else { return Vec::new(); }; - let pcx = &PatCtxt::new_dummy(cx, ty, wildcard_arena); + let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena); let set = column.analyze_ctors(pcx); if set.present.is_empty() { @@ -232,7 +232,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let Some(ty) = column.head_ty() else { return; }; - let pcx = &PatCtxt::new_dummy(cx, ty, wildcard_arena); + let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena); let set = column.analyze_ctors(pcx); diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index daaf62dca3d..4ddd5eb630a 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -6,7 +6,7 @@ use smallvec::{smallvec, SmallVec}; use crate::constructor::{Constructor, Slice, SliceKind}; -use crate::usefulness::PatCtxt; +use crate::usefulness::PlaceCtxt; use crate::{Captures, MatchCx}; use self::Constructor::*; @@ -77,7 +77,7 @@ pub fn iter_fields<'a>( /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(crate) fn specialize<'a>( &self, - pcx: &PatCtxt<'a, 'p, Cx>, + pcx: &PlaceCtxt<'a, 'p, Cx>, other_ctor: &Constructor, ) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> { let wildcard_sub_tys = || { @@ -178,7 +178,7 @@ pub(crate) fn wildcard(ty: Cx::Ty) -> Self { /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. - pub(crate) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, Cx>, ctor: Constructor) -> Self { + pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor) -> Self { let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty); let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); Self::new(ctor, fields, pcx.ty) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e5347ce6363..d9b8b3a39c0 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -31,8 +31,8 @@ pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>; -pub(crate) type PatCtxt<'a, 'p, 'tcx> = - crate::usefulness::PatCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub(crate) type PlaceCtxt<'a, 'p, 'tcx> = + crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = crate::constructor::SplitConstructorSet>; pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcMatchCheckCtxt<'p, 'tcx>>; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 88d28c8f483..9742507fef2 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -575,35 +575,35 @@ pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { f() } +/// Context that provides information local to a place under investigation. #[derive(Clone)] -pub(crate) struct PatCtxt<'a, 'p, Cx: MatchCx> { +pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> { pub(crate) cx: &'a Cx, - /// Type of the current column under investigation. - pub(crate) ty: Cx::Ty, - /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a - /// subpattern. - pub(crate) is_top_level: bool, /// An arena to store the wildcards we produce during analysis. pub(crate) wildcard_arena: &'a TypedArena>, + /// Type of the place under investigation. + pub(crate) ty: Cx::Ty, + /// Whether the place is the original scrutinee place, as opposed to a subplace of it. + pub(crate) is_scrutinee: bool, } -impl<'a, 'p, Cx: MatchCx> PatCtxt<'a, 'p, Cx> { - /// A `PatCtxt` when code other than `is_useful` needs one. +impl<'a, 'p, Cx: MatchCx> PlaceCtxt<'a, 'p, Cx> { + /// A `PlaceCtxt` when code other than `is_useful` needs one. #[cfg_attr(not(feature = "rustc"), allow(dead_code))] pub(crate) fn new_dummy( cx: &'a Cx, ty: Cx::Ty, wildcard_arena: &'a TypedArena>, ) -> Self { - PatCtxt { cx, ty, is_top_level: false, wildcard_arena } + PlaceCtxt { cx, ty, is_scrutinee: false, wildcard_arena } } } -impl<'a, 'p, Cx: MatchCx> Copy for PatCtxt<'a, 'p, Cx> {} +impl<'a, 'p, Cx: MatchCx> Copy for PlaceCtxt<'a, 'p, Cx> {} -impl<'a, 'p, Cx: MatchCx> fmt::Debug for PatCtxt<'a, 'p, Cx> { +impl<'a, 'p, Cx: MatchCx> fmt::Debug for PlaceCtxt<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PatCtxt").field("ty", &self.ty).finish() + f.debug_struct("PlaceCtxt").field("ty", &self.ty).finish() } } @@ -714,7 +714,7 @@ fn expand_or_pat<'b>(&'b self) -> impl Iterator> + C /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'a, 'p, Cx>, + pcx: &PlaceCtxt<'a, 'p, Cx>, ctor: &Constructor, ) -> PatStack<'a, 'p, Cx> { // We pop the head pattern and push the new fields extracted from the arguments of @@ -785,7 +785,7 @@ fn expand_or_pat<'b>(&'b self) -> impl Iterator> + /// Only call if `ctor.is_covered_by(self.head().ctor())` is true. fn pop_head_constructor( &self, - pcx: &PatCtxt<'a, 'p, Cx>, + pcx: &PlaceCtxt<'a, 'p, Cx>, ctor: &Constructor, parent_row: usize, ) -> MatrixRow<'a, 'p, Cx> { @@ -914,7 +914,7 @@ fn heads<'b>( /// This computes `specialize(ctor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: &PatCtxt<'a, 'p, Cx>, + pcx: &PlaceCtxt<'a, 'p, Cx>, ctor: &Constructor, ) -> Matrix<'a, 'p, Cx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); @@ -1064,7 +1064,7 @@ fn push_pattern(&mut self, pat: WitnessPat) { /// pats: [(false, "foo"), _, true] /// result: [Enum::Variant { a: (false, "foo"), b: _ }, true] /// ``` - fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, Cx>, ctor: &Constructor) { + fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, '_, Cx>, ctor: &Constructor) { let len = self.0.len(); let arity = ctor.arity(pcx); let fields = self.0.drain((len - arity)..).rev().collect(); @@ -1114,7 +1114,7 @@ fn push_pattern(&mut self, pat: WitnessPat) { /// Reverses specialization by `ctor`. See the section on `unspecialize` at the top of the file. fn apply_constructor( &mut self, - pcx: &PatCtxt<'_, '_, Cx>, + pcx: &PlaceCtxt<'_, '_, Cx>, missing_ctors: &[Constructor], ctor: &Constructor, report_individual_missing_ctors: bool, @@ -1202,7 +1202,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( }; debug!("ty: {ty:?}"); - let pcx = &PatCtxt { cx, ty, is_top_level, wildcard_arena }; + let pcx = &PlaceCtxt { cx, ty, is_scrutinee: is_top_level, wildcard_arena }; // Whether the place/column we are inspecting is known to contain valid data. let place_validity = matrix.place_validity[0]; From 4bcf66f875d77d0d5897c5b0b9d6bbd985d03a31 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 15 Dec 2023 16:53:29 +0100 Subject: [PATCH 15/16] Introduce `MatchCtxt` --- .../rustc_pattern_analysis/src/constructor.rs | 9 +-- compiler/rustc_pattern_analysis/src/lib.rs | 46 +++++++++++---- compiler/rustc_pattern_analysis/src/lints.rs | 56 +++++++++---------- compiler/rustc_pattern_analysis/src/pat.rs | 6 +- compiler/rustc_pattern_analysis/src/rustc.rs | 1 + .../rustc_pattern_analysis/src/usefulness.rs | 52 ++++++++--------- 6 files changed, 93 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index ec1ddd56b1c..99044e00aeb 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -718,7 +718,7 @@ fn as_slice(&self) -> Option { /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize { - pcx.cx.ctor_arity(self, pcx.ty) + pcx.ctor_arity(self) } /// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`. @@ -729,7 +729,8 @@ pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize { pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool { match (self, other) { (Wildcard, _) => pcx - .cx + .mcx + .tycx .bug(format_args!("Constructor splitting should not have returned `Wildcard`")), // Wildcards cover anything (_, Wildcard) => true, @@ -771,7 +772,7 @@ pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self (Opaque(self_id), Opaque(other_id)) => self_id == other_id, (Opaque(..), _) | (_, Opaque(..)) => false, - _ => pcx.cx.bug(format_args!( + _ => pcx.mcx.tycx.bug(format_args!( "trying to compare incompatible constructors {self:?} and {other:?}" )), } @@ -1007,7 +1008,7 @@ pub(crate) fn split<'a>( // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty. // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. - if !pcx.cx.is_exhaustive_patterns_feature_on() + if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on() && !(pcx.is_scrutinee && matches!(self, Self::NoConstructors)) { // Treat all missing constructors as nonempty. diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 12d2e0fc18b..44bc09d07fe 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -36,6 +36,19 @@ #[cfg(feature = "rustc")] use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; +// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so +// we use another feature instead. The crate won't compile if one of these isn't enabled. +#[cfg(feature = "rustc")] +pub(crate) use rustc_arena::TypedArena; +#[cfg(feature = "stable")] +pub(crate) use typed_arena::Arena as TypedArena; + +pub trait Captures<'a> {} +impl<'a, T: ?Sized> Captures<'a> for T {} + +/// Context that provides type information about constructors. +/// +/// Most of the crate is parameterized on a type that implements this trait. pub trait MatchCx: Sized + Clone + fmt::Debug { /// The type of a pattern. type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy @@ -71,10 +84,20 @@ pub trait MatchCx: Sized + Clone + fmt::Debug { fn bug(&self, fmt: fmt::Arguments<'_>) -> !; } +/// Context that provides information global to a match. +#[derive(Clone)] +pub struct MatchCtxt<'a, 'p, Cx: MatchCx> { + /// The context for type information. + pub tycx: &'a Cx, + /// An arena to store the wildcards we produce during analysis. + pub wildcard_arena: &'a TypedArena>, +} + +impl<'a, 'p, Cx: MatchCx> Copy for MatchCtxt<'a, 'p, Cx> {} + /// The arm of a match expression. #[derive(Clone, Debug)] pub struct MatchArm<'p, Cx: MatchCx> { - /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. pub pat: &'p DeconstructedPat<'p, Cx>, pub has_guard: bool, pub arm_data: Cx::ArmData, @@ -82,31 +105,30 @@ pub struct MatchArm<'p, Cx: MatchCx> { impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} -pub trait Captures<'a> {} -impl<'a, T: ?Sized> Captures<'a> for T {} - /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. #[cfg(feature = "rustc")] pub fn analyze_match<'p, 'tcx>( - cx: &RustcMatchCheckCtxt<'p, 'tcx>, + tycx: &RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> rustc::UsefulnessReport<'p, 'tcx> { // Arena to store the extra wildcards we construct during analysis. - let wildcard_arena = cx.pattern_arena; + let wildcard_arena = tycx.pattern_arena; + let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); + let cx = MatchCtxt { tycx, wildcard_arena }; + + let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); + let pat_column = PatternColumn::new(arms); - let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee); - let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity, wildcard_arena); - // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column, wildcard_arena); + lint_overlapping_range_endpoints(cx, &pat_column); // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. - if cx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty, wildcard_arena) + if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { + lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) } report diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 2d4b06669b5..f1287b1b4d2 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,4 +1,3 @@ -use rustc_arena::TypedArena; use smallvec::SmallVec; use rustc_data_structures::captures::Captures; @@ -13,8 +12,8 @@ OverlappingRangeEndpoints, Uncovered, }; use crate::rustc::{ - Constructor, DeconstructedPat, MatchArm, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet, - WitnessPat, + Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt, + SplitConstructorSet, WitnessPat, }; use crate::MatchCx; @@ -70,7 +69,7 @@ fn head_ty(&self) -> Option> { /// Do constructor splitting on the constructors of the column. fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); - pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors) + pcx.ctors_for_ty().split(pcx, column_ctors) } fn iter<'b>(&'b self) -> impl Iterator> + Captures<'b> { @@ -121,16 +120,15 @@ fn specialize( /// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned /// in a given column. -#[instrument(level = "debug", skip(cx, wildcard_arena), ret)] +#[instrument(level = "debug", skip(cx), ret)] fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &RustcMatchCheckCtxt<'p, 'tcx>, + cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, - wildcard_arena: &TypedArena>, ) -> Vec> { let Some(ty) = column.head_ty() else { return Vec::new(); }; - let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena); + let pcx = &PlaceCtxt::new_dummy(cx, ty); let set = column.analyze_ctors(pcx); if set.present.is_empty() { @@ -141,7 +139,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } let mut witnesses = Vec::new(); - if cx.is_foreign_non_exhaustive_enum(ty) { + if cx.tycx.is_foreign_non_exhaustive_enum(ty) { witnesses.extend( set.missing .into_iter() @@ -157,7 +155,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor); for (i, col_i) in specialized_columns.iter().enumerate() { // Compute witnesses for each column. - let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i, wildcard_arena); + let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i); // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`, // adding enough wildcards to match `arity`. for wit in wits_for_col_i { @@ -171,29 +169,29 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( - cx: &RustcMatchCheckCtxt<'p, 'tcx>, + cx: MatchCtxt<'a, 'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], pat_column: &PatternColumn<'a, 'p, 'tcx>, scrut_ty: Ty<'tcx>, - wildcard_arena: &TypedArena>, ) { + let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; if !matches!( - cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, cx.match_lint_level).0, + rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0, rustc_session::lint::Level::Allow ) { - let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column, wildcard_arena); + let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column); if !witnesses.is_empty() { // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` // is not exhaustive enough. // // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. - cx.tcx.emit_spanned_lint( + rcx.tcx.emit_spanned_lint( NON_EXHAUSTIVE_OMITTED_PATTERNS, - cx.match_lint_level, - cx.scrut_span, + rcx.match_lint_level, + rcx.scrut_span, NonExhaustiveOmittedPattern { scrut_ty, - uncovered: Uncovered::new(cx.scrut_span, cx, witnesses), + uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses), }, ); } @@ -203,17 +201,17 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( // usage of the lint. for arm in arms { let (lint_level, lint_level_source) = - cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); + rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(lint_level, rustc_session::lint::Level::Allow) { let decorator = NonExhaustiveOmittedPatternLintOnArm { lint_span: lint_level_source.span(), - suggest_lint_on_match: cx.whole_match_span.map(|span| span.shrink_to_lo()), + suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), lint_level: lint_level.as_str(), lint_name: "non_exhaustive_omitted_patterns", }; use rustc_errors::DecorateLint; - let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.data(), ""); + let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), ""); err.set_primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); @@ -223,30 +221,30 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } /// Traverse the patterns to warn the user about ranges that overlap on their endpoints. -#[instrument(level = "debug", skip(cx, wildcard_arena))] +#[instrument(level = "debug", skip(cx))] pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( - cx: &RustcMatchCheckCtxt<'p, 'tcx>, + cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'a, 'p, 'tcx>, - wildcard_arena: &TypedArena>, ) { let Some(ty) = column.head_ty() else { return; }; - let pcx = &PlaceCtxt::new_dummy(cx, ty, wildcard_arena); + let pcx = &PlaceCtxt::new_dummy(cx, ty); + let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; let set = column.analyze_ctors(pcx); if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) { let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { - let overlap_as_pat = cx.hoist_pat_range(overlap, ty); + let overlap_as_pat = rcx.hoist_pat_range(overlap, ty); let overlaps: Vec<_> = overlapped_spans .iter() .copied() .map(|span| Overlap { range: overlap_as_pat.clone(), span }) .collect(); - cx.tcx.emit_spanned_lint( + rcx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - cx.match_lint_level, + rcx.match_lint_level, this_span, OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, ); @@ -291,7 +289,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( // Recurse into the fields. for ctor in set.present { for col in column.specialize(pcx, &ctor) { - lint_overlapping_range_endpoints(cx, &col, wildcard_arena); + lint_overlapping_range_endpoints(cx, &col); } } } diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 4ddd5eb630a..defe95d3393 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -81,10 +81,10 @@ pub(crate) fn specialize<'a>( other_ctor: &Constructor, ) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> { let wildcard_sub_tys = || { - let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty); + let tys = pcx.ctor_sub_tys(other_ctor); tys.iter() .map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default())) - .map(|pat| pcx.wildcard_arena.alloc(pat) as &_) + .map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_) .collect() }; match (&self.ctor, other_ctor) { @@ -179,7 +179,7 @@ pub(crate) fn wildcard(ty: Cx::Ty) -> Self { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor) -> Self { - let field_tys = pcx.cx.ctor_sub_tys(&ctor, pcx.ty); + let field_tys = pcx.ctor_sub_tys(&ctor); let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect(); Self::new(ctor, fields, pcx.ty) } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d9b8b3a39c0..e5d43ff09a1 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -31,6 +31,7 @@ pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type PlaceCtxt<'a, 'p, 'tcx> = crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 9742507fef2..f9f6c7c637d 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -555,16 +555,9 @@ use smallvec::{smallvec, SmallVec}; use std::fmt; -// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so -// we use another feature instead. The crate won't compile if one of these isn't enabled. -#[cfg(feature = "rustc")] -use rustc_arena::TypedArena; -#[cfg(feature = "stable")] -use typed_arena::Arena as TypedArena; - use crate::constructor::{Constructor, ConstructorSet}; use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::{Captures, MatchArm, MatchCx}; +use crate::{Captures, MatchArm, MatchCtxt, MatchCx, TypedArena}; use self::ValidityConstraint::*; @@ -578,9 +571,7 @@ pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { /// Context that provides information local to a place under investigation. #[derive(Clone)] pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> { - pub(crate) cx: &'a Cx, - /// An arena to store the wildcards we produce during analysis. - pub(crate) wildcard_arena: &'a TypedArena>, + pub(crate) mcx: MatchCtxt<'a, 'p, Cx>, /// Type of the place under investigation. pub(crate) ty: Cx::Ty, /// Whether the place is the original scrutinee place, as opposed to a subplace of it. @@ -590,12 +581,18 @@ pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> { impl<'a, 'p, Cx: MatchCx> PlaceCtxt<'a, 'p, Cx> { /// A `PlaceCtxt` when code other than `is_useful` needs one. #[cfg_attr(not(feature = "rustc"), allow(dead_code))] - pub(crate) fn new_dummy( - cx: &'a Cx, - ty: Cx::Ty, - wildcard_arena: &'a TypedArena>, - ) -> Self { - PlaceCtxt { cx, ty, is_scrutinee: false, wildcard_arena } + pub(crate) fn new_dummy(mcx: MatchCtxt<'a, 'p, Cx>, ty: Cx::Ty) -> Self { + PlaceCtxt { mcx, ty, is_scrutinee: false } + } + + pub(crate) fn ctor_arity(&self, ctor: &Constructor) -> usize { + self.mcx.tycx.ctor_arity(ctor, self.ty) + } + pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor) -> &[Cx::Ty] { + self.mcx.tycx.ctor_sub_tys(ctor, self.ty) + } + pub(crate) fn ctors_for_ty(&self) -> ConstructorSet { + self.mcx.tycx.ctors_for_ty(self.ty) } } @@ -1176,11 +1173,10 @@ fn extend(&mut self, other: Self) { /// - unspecialization, where we lift the results from the previous step into results for this step /// (using `apply_constructor` and by updating `row.useful` for each parent row). /// This is all explained at the top of the file. -#[instrument(level = "debug", skip(cx, is_top_level, wildcard_arena), ret)] +#[instrument(level = "debug", skip(mcx, is_top_level), ret)] fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( - cx: &'a Cx, + mcx: MatchCtxt<'a, 'p, Cx>, matrix: &mut Matrix<'a, 'p, Cx>, - wildcard_arena: &'a TypedArena>, is_top_level: bool, ) -> WitnessMatrix { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); @@ -1202,7 +1198,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( }; debug!("ty: {ty:?}"); - let pcx = &PlaceCtxt { cx, ty, is_scrutinee: is_top_level, wildcard_arena }; + let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level }; // Whether the place/column we are inspecting is known to contain valid data. let place_validity = matrix.place_validity[0]; @@ -1211,7 +1207,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( // Analyze the constructors present in this column. let ctors = matrix.heads().map(|p| p.ctor()); - let ctors_for_ty = &cx.ctors_for_ty(ty); + let ctors_for_ty = pcx.ctors_for_ty(); let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. let split_set = ctors_for_ty.split(pcx, ctors); let all_missing = split_set.present.is_empty(); @@ -1245,7 +1241,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( // Dig into rows that match `ctor`. let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor); let mut witnesses = ensure_sufficient_stack(|| { - compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, wildcard_arena, false) + compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false) }); let counts_for_exhaustiveness = match ctor { @@ -1307,17 +1303,15 @@ pub struct UsefulnessReport<'p, Cx: MatchCx> { } /// Computes whether a match is exhaustive and which of its arms are useful. -#[instrument(skip(cx, arms, wildcard_arena), level = "debug")] +#[instrument(skip(cx, arms), level = "debug")] pub fn compute_match_usefulness<'p, Cx: MatchCx>( - cx: &Cx, + cx: MatchCtxt<'_, 'p, Cx>, arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, - wildcard_arena: &TypedArena>, ) -> UsefulnessReport<'p, Cx> { - let mut matrix = Matrix::new(wildcard_arena, arms, scrut_ty, scrut_validity); - let non_exhaustiveness_witnesses = - compute_exhaustiveness_and_usefulness(cx, &mut matrix, wildcard_arena, true); + let mut matrix = Matrix::new(cx.wildcard_arena, arms, scrut_ty, scrut_validity); + let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true); let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); let arm_usefulness: Vec<_> = arms From 3016c296287da816fba5c4a9c7e2c8cdebbe0126 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 15 Dec 2023 17:25:11 +0100 Subject: [PATCH 16/16] s/MatchCx/TypeCx/ --- .../rustc_pattern_analysis/src/constructor.rs | 16 +++---- compiler/rustc_pattern_analysis/src/lib.rs | 14 +++--- compiler/rustc_pattern_analysis/src/lints.rs | 2 +- compiler/rustc_pattern_analysis/src/pat.rs | 12 ++--- compiler/rustc_pattern_analysis/src/rustc.rs | 4 +- .../rustc_pattern_analysis/src/usefulness.rs | 48 +++++++++---------- 6 files changed, 48 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 99044e00aeb..d3dd81336e1 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -40,7 +40,7 @@ //! - That have no non-trivial intersection with any of the constructors in the column (i.e. they're //! each either disjoint with or covered by any given column constructor). //! -//! We compute this in two steps: first [`MatchCx::ctors_for_ty`] determines the +//! We compute this in two steps: first [`TypeCx::ctors_for_ty`] determines the //! set of all possible constructors for the type. Then [`ConstructorSet::split`] looks at the //! column of constructors and splits the set into groups accordingly. The precise invariants of //! [`ConstructorSet::split`] is described in [`SplitConstructorSet`]. @@ -136,7 +136,7 @@ //! the algorithm can't distinguish them from a nonempty constructor. The only known case where this //! could happen is the `[..]` pattern on `[!; N]` with `N > 0` so we must take care to not emit it. //! -//! This is all handled by [`MatchCx::ctors_for_ty`] and +//! This is all handled by [`TypeCx::ctors_for_ty`] and //! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest. //! //! @@ -163,7 +163,7 @@ use self::SliceKind::*; use crate::usefulness::PlaceCtxt; -use crate::MatchCx; +use crate::TypeCx; /// Whether we have seen a constructor in the column or not. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -643,7 +643,7 @@ pub fn new() -> Self { /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and /// `Fields`. #[derive(Clone, Debug, PartialEq)] -pub enum Constructor { +pub enum Constructor { /// Tuples and structs. Struct, /// Enum variants. @@ -685,7 +685,7 @@ pub enum Constructor { Missing, } -impl Constructor { +impl Constructor { pub(crate) fn is_non_exhaustive(&self) -> bool { matches!(self, NonExhaustive) } @@ -798,7 +798,7 @@ pub enum VariantVisibility { /// In terms of division of responsibility, [`ConstructorSet::split`] handles all of the /// `exhaustive_patterns` feature. #[derive(Debug)] -pub enum ConstructorSet { +pub enum ConstructorSet { /// The type is a tuple or struct. `empty` tracks whether the type is empty. Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and @@ -846,13 +846,13 @@ pub enum ConstructorSet { /// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be /// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4. #[derive(Debug)] -pub(crate) struct SplitConstructorSet { +pub(crate) struct SplitConstructorSet { pub(crate) present: SmallVec<[Constructor; 1]>, pub(crate) missing: Vec>, pub(crate) missing_empty: Vec>, } -impl ConstructorSet { +impl ConstructorSet { /// This analyzes a column of constructors to 1/ determine which constructors of the type (if /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 44bc09d07fe..785a60e9978 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -49,16 +49,16 @@ impl<'a, T: ?Sized> Captures<'a> for T {} /// Context that provides type information about constructors. /// /// Most of the crate is parameterized on a type that implements this trait. -pub trait MatchCx: Sized + Clone + fmt::Debug { +pub trait TypeCx: Sized + Clone + fmt::Debug { /// The type of a pattern. type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy /// The index of an enum variant. type VariantIdx: Clone + Idx; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; - /// Extra data to store on a match arm. + /// Extra data to store in a match arm. type ArmData: Copy + Clone + fmt::Debug; - /// Extra data to store on a pattern. `Default` needed when we create fictitious wildcard + /// Extra data to store in a pattern. `Default` needed when we create fictitious wildcard /// patterns during analysis. type PatData: Clone + Default; @@ -86,24 +86,24 @@ pub trait MatchCx: Sized + Clone + fmt::Debug { /// Context that provides information global to a match. #[derive(Clone)] -pub struct MatchCtxt<'a, 'p, Cx: MatchCx> { +pub struct MatchCtxt<'a, 'p, Cx: TypeCx> { /// The context for type information. pub tycx: &'a Cx, /// An arena to store the wildcards we produce during analysis. pub wildcard_arena: &'a TypedArena>, } -impl<'a, 'p, Cx: MatchCx> Copy for MatchCtxt<'a, 'p, Cx> {} +impl<'a, 'p, Cx: TypeCx> Copy for MatchCtxt<'a, 'p, Cx> {} /// The arm of a match expression. #[derive(Clone, Debug)] -pub struct MatchArm<'p, Cx: MatchCx> { +pub struct MatchArm<'p, Cx: TypeCx> { pub pat: &'p DeconstructedPat<'p, Cx>, pub has_guard: bool, pub arm_data: Cx::ArmData, } -impl<'p, Cx: MatchCx> Copy for MatchArm<'p, Cx> {} +impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {} /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are /// useful, and runs some lints. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index f1287b1b4d2..072ef4836a8 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -15,7 +15,7 @@ Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat, }; -use crate::MatchCx; +use crate::TypeCx; /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that /// inspect the same subvalue/place". diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index defe95d3393..0cc8477b7cd 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -7,7 +7,7 @@ use crate::constructor::{Constructor, Slice, SliceKind}; use crate::usefulness::PlaceCtxt; -use crate::{Captures, MatchCx}; +use crate::{Captures, TypeCx}; use self::Constructor::*; @@ -22,7 +22,7 @@ /// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't /// observe that it is uninhabited. In that case that field is not included in `fields`. Care must /// be taken when converting to/from `thir::Pat`. -pub struct DeconstructedPat<'p, Cx: MatchCx> { +pub struct DeconstructedPat<'p, Cx: TypeCx> { ctor: Constructor, fields: &'p [DeconstructedPat<'p, Cx>], ty: Cx::Ty, @@ -31,7 +31,7 @@ pub struct DeconstructedPat<'p, Cx: MatchCx> { useful: Cell, } -impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> { +impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> { pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self { Self::new(Wildcard, &[], ty, data) } @@ -152,7 +152,7 @@ fn collect_redundant_subpatterns<'a>(&'a self, subpats: &mut Vec<&'a Self>) { /// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a /// `Display` impl. -impl<'p, Cx: MatchCx> fmt::Debug for DeconstructedPat<'p, Cx> { +impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Cx::debug_pat(f, self) } @@ -161,13 +161,13 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics /// purposes. As such they don't use interning and can be cloned. #[derive(Debug, Clone)] -pub struct WitnessPat { +pub struct WitnessPat { ctor: Constructor, pub(crate) fields: Vec>, ty: Cx::Ty, } -impl WitnessPat { +impl WitnessPat { pub(crate) fn new(ctor: Constructor, fields: Vec, ty: Cx::Ty) -> Self { Self { ctor, fields, ty } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e5d43ff09a1..65c90aa9f1d 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -20,7 +20,7 @@ use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; -use crate::MatchCx; +use crate::TypeCx; use crate::constructor::Constructor::*; @@ -863,7 +863,7 @@ pub(crate) fn debug_pat( } } -impl<'p, 'tcx> MatchCx for RustcMatchCheckCtxt<'p, 'tcx> { +impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { type Ty = Ty<'tcx>; type VariantIdx = VariantIdx; type StrLit = Const<'tcx>; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index f9f6c7c637d..6b1de807797 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -242,7 +242,7 @@ //! Therefore `usefulness(tp_1, tp_2, tq)` returns the single witness-tuple `[Variant2(Some(true), 0)]`. //! //! -//! Computing the set of constructors for a type is done in [`MatchCx::ctors_for_ty`]. See +//! Computing the set of constructors for a type is done in [`TypeCx::ctors_for_ty`]. See //! the following sections for more accurate versions of the algorithm and corresponding links. //! //! @@ -557,7 +557,7 @@ use crate::constructor::{Constructor, ConstructorSet}; use crate::pat::{DeconstructedPat, WitnessPat}; -use crate::{Captures, MatchArm, MatchCtxt, MatchCx, TypedArena}; +use crate::{Captures, MatchArm, MatchCtxt, TypeCx, TypedArena}; use self::ValidityConstraint::*; @@ -570,7 +570,7 @@ pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { /// Context that provides information local to a place under investigation. #[derive(Clone)] -pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> { +pub(crate) struct PlaceCtxt<'a, 'p, Cx: TypeCx> { pub(crate) mcx: MatchCtxt<'a, 'p, Cx>, /// Type of the place under investigation. pub(crate) ty: Cx::Ty, @@ -578,7 +578,7 @@ pub(crate) struct PlaceCtxt<'a, 'p, Cx: MatchCx> { pub(crate) is_scrutinee: bool, } -impl<'a, 'p, Cx: MatchCx> PlaceCtxt<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> { /// A `PlaceCtxt` when code other than `is_useful` needs one. #[cfg_attr(not(feature = "rustc"), allow(dead_code))] pub(crate) fn new_dummy(mcx: MatchCtxt<'a, 'p, Cx>, ty: Cx::Ty) -> Self { @@ -596,9 +596,9 @@ pub(crate) fn ctors_for_ty(&self) -> ConstructorSet { } } -impl<'a, 'p, Cx: MatchCx> Copy for PlaceCtxt<'a, 'p, Cx> {} +impl<'a, 'p, Cx: TypeCx> Copy for PlaceCtxt<'a, 'p, Cx> {} -impl<'a, 'p, Cx: MatchCx> fmt::Debug for PlaceCtxt<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> fmt::Debug for PlaceCtxt<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PlaceCtxt").field("ty", &self.ty).finish() } @@ -644,7 +644,7 @@ fn allows_omitting_empty_arms(self) -> bool { /// /// Pending further opsem decisions, the current behavior is: validity is preserved, except /// inside `&` and union fields where validity is reset to `MaybeInvalid`. - fn specialize(self, ctor: &Constructor) -> Self { + fn specialize(self, ctor: &Constructor) -> Self { // We preserve validity except when we go inside a reference or a union field. if matches!(ctor, Constructor::Ref | Constructor::UnionField) { // Validity of `x: &T` does not imply validity of `*x: T`. @@ -671,12 +671,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // - 'p coming from the input // - Cx global compilation context #[derive(Clone)] -struct PatStack<'a, 'p, Cx: MatchCx> { +struct PatStack<'a, 'p, Cx: TypeCx> { // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well. pats: SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]>, } -impl<'a, 'p, Cx: MatchCx> PatStack<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> PatStack<'a, 'p, Cx> { fn from_pattern(pat: &'a DeconstructedPat<'p, Cx>) -> Self { PatStack { pats: smallvec![pat] } } @@ -722,7 +722,7 @@ fn pop_head_constructor( } } -impl<'a, 'p, Cx: MatchCx> fmt::Debug for PatStack<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> fmt::Debug for PatStack<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // We pretty-print similarly to the `Debug` impl of `Matrix`. write!(f, "+")?; @@ -735,7 +735,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// A row of the matrix. #[derive(Clone)] -struct MatrixRow<'a, 'p, Cx: MatchCx> { +struct MatrixRow<'a, 'p, Cx: TypeCx> { // The patterns in the row. pats: PatStack<'a, 'p, Cx>, /// Whether the original arm had a guard. This is inherited when specializing. @@ -750,7 +750,7 @@ struct MatrixRow<'a, 'p, Cx: MatchCx> { useful: bool, } -impl<'a, 'p, Cx: MatchCx> MatrixRow<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> MatrixRow<'a, 'p, Cx> { fn is_empty(&self) -> bool { self.pats.is_empty() } @@ -795,7 +795,7 @@ fn pop_head_constructor( } } -impl<'a, 'p, Cx: MatchCx> fmt::Debug for MatrixRow<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> fmt::Debug for MatrixRow<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.pats.fmt(f) } @@ -812,7 +812,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// specializing `(,)` and `Some` on a pattern of type `(Option, bool)`, the first column of /// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`. #[derive(Clone)] -struct Matrix<'a, 'p, Cx: MatchCx> { +struct Matrix<'a, 'p, Cx: TypeCx> { /// Vector of rows. The rows must form a rectangular 2D array. Moreover, all the patterns of /// each column must have the same type. Each column corresponds to a place within the /// scrutinee. @@ -824,7 +824,7 @@ struct Matrix<'a, 'p, Cx: MatchCx> { place_validity: SmallVec<[ValidityConstraint; 2]>, } -impl<'a, 'p, Cx: MatchCx> Matrix<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. Internal method, prefer [`Matrix::new`]. fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, Cx>) { @@ -942,7 +942,7 @@ fn specialize_constructor( /// + _ + [_, _, tail @ ..] + /// | ✓ | ? | // column validity /// ``` -impl<'a, 'p, Cx: MatchCx> fmt::Debug for Matrix<'a, 'p, Cx> { +impl<'a, 'p, Cx: TypeCx> fmt::Debug for Matrix<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -1033,9 +1033,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// /// See the top of the file for more detailed explanations and examples. #[derive(Debug, Clone)] -struct WitnessStack(Vec>); +struct WitnessStack(Vec>); -impl WitnessStack { +impl WitnessStack { /// Asserts that the witness contains a single pattern, and returns it. fn single_pattern(self) -> WitnessPat { assert_eq!(self.0.len(), 1); @@ -1080,9 +1080,9 @@ fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, '_, Cx>, ctor: &Constructor< /// Just as the `Matrix` starts with a single column, by the end of the algorithm, this has a single /// column, which contains the patterns that are missing for the match to be exhaustive. #[derive(Debug, Clone)] -struct WitnessMatrix(Vec>); +struct WitnessMatrix(Vec>); -impl WitnessMatrix { +impl WitnessMatrix { /// New matrix with no witnesses. fn empty() -> Self { WitnessMatrix(vec![]) @@ -1174,7 +1174,7 @@ fn extend(&mut self, other: Self) { /// (using `apply_constructor` and by updating `row.useful` for each parent row). /// This is all explained at the top of the file. #[instrument(level = "debug", skip(mcx, is_top_level), ret)] -fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( +fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( mcx: MatchCtxt<'a, 'p, Cx>, matrix: &mut Matrix<'a, 'p, Cx>, is_top_level: bool, @@ -1283,7 +1283,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>( /// Indicates whether or not a given arm is useful. #[derive(Clone, Debug)] -pub enum Usefulness<'p, Cx: MatchCx> { +pub enum Usefulness<'p, Cx: TypeCx> { /// The arm is useful. This additionally carries a set of or-pattern branches that have been /// found to be redundant despite the overall arm being useful. Used only in the presence of /// or-patterns, otherwise it stays empty. @@ -1294,7 +1294,7 @@ pub enum Usefulness<'p, Cx: MatchCx> { } /// The output of checking a match for exhaustiveness and arm usefulness. -pub struct UsefulnessReport<'p, Cx: MatchCx> { +pub struct UsefulnessReport<'p, Cx: TypeCx> { /// For each arm of the input, whether that arm is useful after the arms above it. pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<'p, Cx>)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of @@ -1304,7 +1304,7 @@ pub struct UsefulnessReport<'p, Cx: MatchCx> { /// Computes whether a match is exhaustive and which of its arms are useful. #[instrument(skip(cx, arms), level = "debug")] -pub fn compute_match_usefulness<'p, Cx: MatchCx>( +pub fn compute_match_usefulness<'p, Cx: TypeCx>( cx: MatchCtxt<'_, 'p, Cx>, arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty,