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 2685bae4d09c8..f395bb44f5745 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -420,9 +420,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { { let mut redundant_subpats = redundant_subpats.clone(); // Emit lints in the order in which they occur in the file. - redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span); + redundant_subpats.sort_unstable_by_key(|pat| pat.data().span); for pat in redundant_subpats { - report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None) + report_unreachable_pattern(cx, arm.arm_data, pat.data().span, None) } } } @@ -905,10 +905,10 @@ fn report_arm_reachability<'p, 'tcx>( let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { if matches!(is_useful, Usefulness::Redundant) { - report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall) + report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().span, catchall) } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { - catchall = Some(arm.pat.data().unwrap().span); + catchall = Some(arm.pat.data().span); } } } @@ -917,7 +917,9 @@ fn report_arm_reachability<'p, 'tcx>( fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { match pat.ctor() { Constructor::Wildcard => true, - Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Constructor::Struct | Constructor::Ref => { + pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat)) + } _ => false, } } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 69e294e47a561..2d55785cd0692 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -423,7 +423,7 @@ pub enum SliceKind { } impl SliceKind { - fn arity(self) -> usize { + pub fn arity(self) -> usize { match self { FixedLen(length) => length, VarLen(prefix, suffix) => prefix + suffix, @@ -462,7 +462,7 @@ impl Slice { Slice { array_len, kind } } - pub(crate) fn arity(self) -> usize { + pub fn arity(self) -> usize { self.kind.arity() } diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 16530960656fe..072a8e4bfe5a0 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -98,7 +98,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( }; use rustc_errors::LintDiagnostic; - let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().unwrap().span, ""); + let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().span, ""); err.primary_message(decorator.msg()); decorator.decorate_lint(&mut err); err.emit(); diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index decbfa5c0cf4d..cefc1d8e3b3a1 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -20,32 +20,42 @@ impl PatId { } } +/// A pattern with an index denoting which field it corresponds to. +pub struct IndexedPat { + pub idx: usize, + pub pat: DeconstructedPat, +} + /// Values and patterns can be represented as a constructor applied to some fields. This represents /// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only /// exception are some `Wildcard`s introduced during pattern lowering. pub struct DeconstructedPat { ctor: Constructor, - fields: Vec>, + fields: Vec>, + /// The number of fields in this pattern. E.g. if the pattern is `SomeStruct { field12: true, .. + /// }` this would be the total number of fields of the struct. + /// This is also the same as `self.ctor.arity(self.ty)`. + arity: usize, ty: Cx::Ty, - /// Extra data to store in a pattern. `None` if the pattern is a wildcard that does not - /// correspond to a user-supplied pattern. - data: Option, + /// Extra data to store in a pattern. + data: Cx::PatData, /// Globally-unique id used to track usefulness at the level of subpatterns. pub(crate) uid: PatId, } impl DeconstructedPat { - pub fn wildcard(ty: Cx::Ty) -> Self { - DeconstructedPat { ctor: Wildcard, fields: Vec::new(), ty, data: None, uid: PatId::new() } - } - pub fn new( ctor: Constructor, - fields: Vec>, + fields: Vec>, + arity: usize, ty: Cx::Ty, data: Cx::PatData, ) -> Self { - DeconstructedPat { ctor, fields, ty, data: Some(data), uid: PatId::new() } + DeconstructedPat { ctor, fields, arity, ty, data, uid: PatId::new() } + } + + pub fn at_index(self, idx: usize) -> IndexedPat { + IndexedPat { idx, pat: self } } pub(crate) fn is_or_pat(&self) -> bool { @@ -58,13 +68,15 @@ impl DeconstructedPat { pub fn ty(&self) -> &Cx::Ty { &self.ty } - /// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that - /// does not correspond to a user-supplied pattern. - pub fn data(&self) -> Option<&Cx::PatData> { - self.data.as_ref() + /// Returns the extra data stored in a pattern. + pub fn data(&self) -> &Cx::PatData { + &self.data + } + pub fn arity(&self) -> usize { + self.arity } - pub fn iter_fields<'a>(&'a self) -> impl Iterator> { + pub fn iter_fields<'a>(&'a self) -> impl Iterator> { self.fields.iter() } @@ -73,36 +85,40 @@ impl DeconstructedPat { pub(crate) fn specialize<'a>( &'a self, other_ctor: &Constructor, - ctor_arity: usize, + other_ctor_arity: usize, ) -> SmallVec<[PatOrWild<'a, Cx>; 2]> { - let wildcard_sub_tys = || (0..ctor_arity).map(|_| PatOrWild::Wild).collect(); - match (&self.ctor, other_ctor) { - // Return a wildcard for each field of `other_ctor`. - (Wildcard, _) => wildcard_sub_tys(), + if matches!(other_ctor, PrivateUninhabited) { // Skip this column. - (_, PrivateUninhabited) => smallvec![], - // 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() => { - // Start with a slice of wildcards of the appropriate length. - 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 { - fields[i] = PatOrWild::Pat(&self.fields[i]); + return smallvec![]; + } + + // Start with a slice of wildcards of the appropriate length. + let mut fields: SmallVec<[_; 2]> = (0..other_ctor_arity).map(|_| PatOrWild::Wild).collect(); + // Fill `fields` with our fields. The arities are known to be compatible. + match self.ctor { + // The only non-trivial case: two slices of different arity. `other_ctor` is guaranteed + // to have a larger arity, so we adjust the indices of the patterns in the suffix so + // that they are correctly positioned in the larger slice. + Slice(Slice { kind: SliceKind::VarLen(prefix, _), .. }) + if self.arity != other_ctor_arity => + { + for ipat in &self.fields { + let new_idx = if ipat.idx < prefix { + ipat.idx + } else { + // Adjust the indices in the suffix. + ipat.idx + other_ctor_arity - self.arity + }; + fields[new_idx] = PatOrWild::Pat(&ipat.pat); } - for i in 0..suffix { - fields[new_arity - 1 - i] = - PatOrWild::Pat(&self.fields[self.fields.len() - 1 - i]); + } + _ => { + for ipat in &self.fields { + fields[ipat.idx] = PatOrWild::Pat(&ipat.pat); } - fields } - _ => self.fields.iter().map(PatOrWild::Pat).collect(), } + fields } /// Walk top-down and call `it` in each place where a pattern occurs @@ -114,7 +130,7 @@ impl DeconstructedPat { } for p in self.iter_fields() { - p.walk(it) + p.pat.walk(it) } } } @@ -134,6 +150,11 @@ impl fmt::Debug for DeconstructedPat { }; let mut start_or_comma = || start_or_continue(", "); + let mut fields: Vec<_> = (0..self.arity).map(|_| PatOrWild::Wild).collect(); + for ipat in self.iter_fields() { + fields[ipat.idx] = PatOrWild::Pat(&ipat.pat); + } + match pat.ctor() { Struct | Variant(_) | UnionField => { Cx::write_variant_name(f, pat)?; @@ -141,7 +162,7 @@ impl fmt::Debug for DeconstructedPat { // get the names of the fields. Instead we just display everything as a tuple // struct, which should be good enough. write!(f, "(")?; - for p in pat.iter_fields() { + for p in fields { write!(f, "{}", start_or_comma())?; write!(f, "{p:?}")?; } @@ -151,25 +172,23 @@ impl fmt::Debug for DeconstructedPat { // 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) + write!(f, "&{:?}", &fields[0]) } Slice(slice) => { - let mut subpatterns = pat.iter_fields(); write!(f, "[")?; match slice.kind { SliceKind::FixedLen(_) => { - for p in subpatterns { + for p in fields { write!(f, "{}{:?}", start_or_comma(), p)?; } } SliceKind::VarLen(prefix_len, _) => { - for p in subpatterns.by_ref().take(prefix_len) { + for p in &fields[..prefix_len] { write!(f, "{}{:?}", start_or_comma(), p)?; } write!(f, "{}", start_or_comma())?; write!(f, "..")?; - for p in subpatterns { + for p in &fields[prefix_len..] { write!(f, "{}{:?}", start_or_comma(), p)?; } } @@ -184,7 +203,7 @@ impl fmt::Debug for DeconstructedPat { Str(value) => write!(f, "{value:?}"), Opaque(..) => write!(f, ""), Or => { - for pat in pat.iter_fields() { + for pat in fields { write!(f, "{}{:?}", start_or_continue(" | "), pat)?; } Ok(()) @@ -242,9 +261,10 @@ impl<'p, Cx: TypeCx> PatOrWild<'p, Cx> { /// Expand this (possibly-nested) or-pattern into its alternatives. pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> { match self { - PatOrWild::Pat(pat) if pat.is_or_pat() => { - pat.iter_fields().flat_map(|p| PatOrWild::Pat(p).flatten_or_pat()).collect() - } + PatOrWild::Pat(pat) if pat.is_or_pat() => pat + .iter_fields() + .flat_map(|ipat| PatOrWild::Pat(&ipat.pat).flatten_or_pat()) + .collect(), _ => smallvec![self], } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 0085f0ab6566b..53a32d3237e6c 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -445,7 +445,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { let cx = self; let ty = cx.reveal_opaque_ty(pat.ty); let ctor; - let mut fields: Vec<_>; + let arity; + let fields: Vec<_>; match &pat.kind { PatKind::AscribeUserType { subpattern, .. } | PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern), @@ -453,9 +454,11 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = vec![]; + arity = 0; } PatKind::Deref { subpattern } => { - fields = vec![self.lower_pat(subpattern)]; + fields = vec![self.lower_pat(subpattern).at_index(0)]; + arity = 1; ctor = match ty.kind() { // This is a box pattern. ty::Adt(adt, ..) if adt.is_box() => Struct, @@ -467,16 +470,13 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { match ty.kind() { ty::Tuple(fs) => { ctor = Struct; - fields = fs + arity = fs.len(); + fields = subpatterns .iter() - .map(|ty| cx.reveal_opaque_ty(ty)) - .map(|ty| DeconstructedPat::wildcard(ty)) + .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) .collect(); - for pat in subpatterns { - fields[pat.field.index()] = self.lower_pat(&pat.pattern); - } } - ty::Adt(adt, args) if adt.is_box() => { + ty::Adt(adt, _) 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. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -490,13 +490,13 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // solution when we introduce generalized deref patterns. Also need to // prevent mixing of those two options. let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0); - let pat = if let Some(pat) = pattern { - self.lower_pat(&pat.pattern) + if let Some(pat) = pattern { + fields = vec![self.lower_pat(&pat.pattern).at_index(0)]; } else { - DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0))) - }; + fields = vec![]; + } ctor = Struct; - fields = vec![pat]; + arity = 1; } ty::Adt(adt, _) => { ctor = match pat.kind { @@ -507,13 +507,11 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { }; let variant = &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt)); - fields = cx - .variant_sub_tys(ty, variant) - .map(|(_, ty)| DeconstructedPat::wildcard(ty)) + arity = variant.fields.len(); + fields = subpatterns + .iter() + .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) .collect(); - for pat in subpatterns { - fields[pat.field.index()] = self.lower_pat(&pat.pattern); - } } _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), } @@ -526,6 +524,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { None => Opaque(OpaqueId::new()), }; fields = vec![]; + arity = 0; } ty::Char | ty::Int(_) | ty::Uint(_) => { ctor = match value.try_eval_bits(cx.tcx, cx.param_env) { @@ -542,6 +541,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { None => Opaque(OpaqueId::new()), }; fields = vec![]; + arity = 0; } ty::Float(ty::FloatTy::F32) => { ctor = match value.try_eval_bits(cx.tcx, cx.param_env) { @@ -553,6 +553,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { None => Opaque(OpaqueId::new()), }; fields = vec![]; + arity = 0; } ty::Float(ty::FloatTy::F64) => { ctor = match value.try_eval_bits(cx.tcx, cx.param_env) { @@ -564,6 +565,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { None => Opaque(OpaqueId::new()), }; fields = vec![]; + arity = 0; } ty::Ref(_, t, _) if t.is_str() => { // We want a `&str` constant to behave like a `Deref` pattern, to be compatible @@ -574,9 +576,10 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // subfields. // Note: `t` is `str`, not `&str`. let ty = self.reveal_opaque_ty(*t); - let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), ty, pat); + let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat); ctor = Ref; - fields = vec![subpattern] + fields = vec![subpattern.at_index(0)]; + arity = 1; } // All constants that can be structurally matched have already been expanded // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are @@ -584,6 +587,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { _ => { ctor = Opaque(OpaqueId::new()); fields = vec![]; + arity = 0; } } } @@ -623,6 +627,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { _ => bug!("invalid type for range pattern: {}", ty.inner()), }; fields = vec![]; + arity = 0; } PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { let array_len = match ty.kind() { @@ -638,12 +643,25 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { SliceKind::FixedLen(prefix.len() + suffix.len()) }; ctor = Slice(Slice::new(array_len, kind)); - fields = prefix.iter().chain(suffix.iter()).map(|p| self.lower_pat(&*p)).collect(); + fields = prefix + .iter() + .chain(suffix.iter()) + .map(|p| self.lower_pat(&*p)) + .enumerate() + .map(|(i, p)| p.at_index(i)) + .collect(); + arity = kind.arity(); } PatKind::Or { .. } => { ctor = Or; let pats = expand_or_pat(pat); - fields = pats.into_iter().map(|p| self.lower_pat(p)).collect(); + fields = pats + .into_iter() + .map(|p| self.lower_pat(p)) + .enumerate() + .map(|(i, p)| p.at_index(i)) + .collect(); + arity = fields.len(); } PatKind::Never => { // A never pattern matches all the values of its type (namely none). Moreover it @@ -651,13 +669,15 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> { // `Result` which has other constructors. Hence we lower it as a wildcard. ctor = Wildcard; fields = vec![]; + arity = 0; } PatKind::Error(_) => { ctor = Opaque(OpaqueId::new()); fields = vec![]; + arity = 0; } } - DeconstructedPat::new(ctor, fields, ty, pat) + DeconstructedPat::new(ctor, fields, arity, ty, pat) } /// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes. @@ -884,10 +904,10 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty()); let overlaps: Vec<_> = overlaps_with .iter() - .map(|pat| pat.data().unwrap().span) + .map(|pat| pat.data().span) .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) .collect(); - let pat_span = pat.data().unwrap().span; + let pat_span = pat.data().span; self.tcx.emit_node_span_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, self.match_lint_level, @@ -907,7 +927,7 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { gap: IntRange, gapped_with: &[&crate::pat::DeconstructedPat], ) { - let Some(&thir_pat) = pat.data() else { return }; + let &thir_pat = pat.data(); let thir::PatKind::Range(range) = &thir_pat.kind else { return }; // Only lint when the left range is an exclusive range. if range.end != rustc_hir::RangeEnd::Excluded { @@ -955,7 +975,7 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { gap_with: gapped_with .iter() .map(|pat| errors::GappedRange { - span: pat.data().unwrap().span, + span: pat.data().span, gap: gap_as_pat.clone(), first_range: thir_pat.clone(), }) diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index a067bf1f0c23e..0834d08106f3b 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1006,15 +1006,17 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> { ctor_arity: usize, ctor_is_relevant: bool, ) -> Result, Cx::Error> { - // We pop the head pattern and push the new fields extracted from the arguments of - // `self.head()`. - let mut new_pats = self.head().specialize(ctor, ctor_arity); - if new_pats.len() != ctor_arity { + let head_pat = self.head(); + if head_pat.as_pat().is_some_and(|pat| pat.arity() > ctor_arity) { + // Arity can be smaller in case of variable-length slices, but mustn't be larger. return Err(cx.bug(format_args!( - "uncaught type error: pattern {:?} has inconsistent arity (expected arity {ctor_arity})", - self.head().as_pat().unwrap() + "uncaught type error: pattern {:?} has inconsistent arity (expected arity <= {ctor_arity})", + head_pat.as_pat().unwrap() ))); } + // We pop the head pattern and push the new fields extracted from the arguments of + // `self.head()`. + let mut new_pats = head_pat.specialize(ctor, ctor_arity); new_pats.extend_from_slice(&self.pats[1..]); // `ctor` is relevant for this row if it is the actual constructor of this row, or if the // row has a wildcard and `ctor` is relevant for wildcards. @@ -1706,7 +1708,8 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>( ) -> bool { if useful_subpatterns.contains(&pat.uid) { true - } else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, f)) + } else if pat.is_or_pat() + && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, &f.pat)) { // We always expand or patterns in the matrix, so we will never see the actual // or-pattern (the one with constructor `Or`) in the column. As such, it will not be