diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 6fa8d7f0fcdd..f9f273acb7e6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1128,8 +1128,11 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut ImplTraitContext::Disallowed(ImplTraitPosition::Path), ); // Destructure like a tuple struct. - let tuple_struct_pat = - hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); + let tuple_struct_pat = hir::PatKind::TupleStruct( + qpath, + pats, + hir::DotDotPos::new(rest.map(|r| r.0)), + ); return self.pat_without_dbm(lhs.span, tuple_struct_pat); } } @@ -1184,13 +1187,13 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Tup(elements) => { let (pats, rest) = self.destructure_sequence(elements, "tuple", eq_sign_span, assignments); - let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0)); + let tuple_pat = hir::PatKind::Tuple(pats, hir::DotDotPos::new(rest.map(|r| r.0))); return self.pat_without_dbm(lhs.span, tuple_pat); } ExprKind::Paren(e) => { // We special-case `(..)` for consistency with patterns. if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { - let tuple_pat = hir::PatKind::Tuple(&[], Some(0)); + let tuple_pat = hir::PatKind::Tuple(&[], hir::DotDotPos::new(Some(0))); return self.pat_without_dbm(lhs.span, tuple_pat); } else { return self.destructure_assign_mut(e, eq_sign_span, assignments); diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 69e6c2c8ad0b..1ea76fdbfcbb 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -116,7 +116,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, pats: &[P], ctx: &str, - ) -> (&'hir [hir::Pat<'hir>], Option) { + ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) { let mut elems = Vec::with_capacity(pats.len()); let mut rest = None; @@ -160,7 +160,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos)) + (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos))) } /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 8eb0f6dd812d..cc5aed6cb548 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1059,6 +1059,35 @@ impl fmt::Display for RangeEnd { } } +// Equivalent to `Option`. That type takes up 16 bytes on 64-bit, but +// this type only takes up 4 bytes, at the cost of being restricted to a +// maximum value of `u32::MAX - 1`. In practice, this is more than enough. +#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable_Generic)] +pub struct DotDotPos(u32); + +impl DotDotPos { + // Panics if n >= u32::MAX. + pub fn new(n: Option) -> Self { + match n { + Some(n) => { + assert!(n < u32::MAX as usize); + Self(n as u32) + } + None => Self(u32::MAX), + } + } + + pub fn as_opt_usize(&self) -> Option { + if self.0 == u32::MAX { None } else { Some(self.0 as usize) } + } +} + +impl fmt::Debug for DotDotPos { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_opt_usize().fmt(f) + } +} + #[derive(Debug, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). @@ -1075,9 +1104,9 @@ pub enum PatKind<'hir> { Struct(QPath<'hir>, &'hir [PatField<'hir>], bool), /// A tuple struct/variant pattern `Variant(x, y, .., z)`. - /// If the `..` pattern fragment is present, then `Option` denotes its position. + /// If the `..` pattern fragment is present, then `DotDotPos` denotes its position. /// `0 <= position <= subpats.len()` - TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], Option), + TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], DotDotPos), /// An or-pattern `A | B | C`. /// Invariant: `pats.len() >= 2`. @@ -1089,7 +1118,7 @@ pub enum PatKind<'hir> { /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option` denotes its position. /// `0 <= position <= subpats.len()` - Tuple(&'hir [Pat<'hir>], Option), + Tuple(&'hir [Pat<'hir>], DotDotPos), /// A `box` pattern. Box(&'hir Pat<'hir>), @@ -3486,8 +3515,8 @@ mod size_asserts { static_assert_size!(ItemKind<'_>, 48); static_assert_size!(Local<'_>, 64); static_assert_size!(Param<'_>, 32); - static_assert_size!(Pat<'_>, 88); - static_assert_size!(PatKind<'_>, 64); + static_assert_size!(Pat<'_>, 72); + static_assert_size!(PatKind<'_>, 48); static_assert_size!(Path<'_>, 48); static_assert_size!(PathSegment<'_>, 56); static_assert_size!(QPath<'_>, 24); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 9baaf9390f2c..0c1819bb0c74 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -35,7 +35,7 @@ pub trait EnumerateAndAdjustIterator { fn enumerate_and_adjust( self, expected_len: usize, - gap_pos: Option, + gap_pos: hir::DotDotPos, ) -> EnumerateAndAdjust where Self: Sized; @@ -45,7 +45,7 @@ impl EnumerateAndAdjustIterator for T { fn enumerate_and_adjust( self, expected_len: usize, - gap_pos: Option, + gap_pos: hir::DotDotPos, ) -> EnumerateAndAdjust where Self: Sized, @@ -53,7 +53,7 @@ impl EnumerateAndAdjustIterator for T { let actual_len = self.len(); EnumerateAndAdjust { enumerate: self.enumerate(), - gap_pos: gap_pos.unwrap_or(expected_len), + gap_pos: gap_pos.as_opt_usize().unwrap_or(expected_len), gap_len: expected_len - actual_len, } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 1220755f44b3..35a58296e370 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1761,7 +1761,8 @@ impl<'a> State<'a> { PatKind::TupleStruct(ref qpath, elts, ddpos) => { self.print_qpath(qpath, true); self.popen(); - if let Some(ddpos) = ddpos { + if let Some(ddpos) = ddpos.as_opt_usize() { + let ddpos = ddpos as usize; self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); @@ -1804,7 +1805,7 @@ impl<'a> State<'a> { } PatKind::Tuple(elts, ddpos) => { self.popen(); - if let Some(ddpos) = ddpos { + if let Some(ddpos) = ddpos.as_opt_usize() { self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(p)); if ddpos != 0 { self.word_space(","); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index d2f93b679acc..9803cecd4459 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { &mut self, pats: &'tcx [hir::Pat<'tcx>], expected_len: usize, - gap_pos: Option, + gap_pos: hir::DotDotPos, ) -> Vec> { pats.iter() .enumerate_and_adjust(expected_len, gap_pos) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 625c854ea77a..a7ce0f312af8 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -226,19 +226,16 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { lhs: &hir::Pat<'_>, res: Res, pats: &[hir::Pat<'_>], - dotdot: Option, + dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), }; - let first_n = pats.iter().enumerate().take(dotdot.unwrap_or(pats.len())); + let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); + let first_n = pats.iter().enumerate().take(dotdot); let missing = variant.fields.len() - pats.len(); - let last_n = pats - .iter() - .enumerate() - .skip(dotdot.unwrap_or(pats.len())) - .map(|(idx, pat)| (idx + missing, pat)); + let last_n = pats.iter().enumerate().skip(dotdot).map(|(idx, pat)| (idx + missing, pat)); for (idx, pat) in first_n.chain(last_n) { if let PatKind::Wild = pat.kind { continue; diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 9096fc442d49..52948cbb4289 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -981,7 +981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, subpats: &'tcx [Pat<'tcx>], - ddpos: Option, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, @@ -1066,7 +1066,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check subpatterns. if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.is_some() + || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some() { let ty::Adt(_, substs) = pat_ty.kind() else { bug!("unexpected pattern type {:?}", pat_ty); @@ -1254,14 +1254,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, elements: &'tcx [Pat<'tcx>], - ddpos: Option, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); - if ddpos.is_some() { + if ddpos.as_opt_usize().is_some() { // Require known type only when `..` is present. if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() { expected_len = tys.len(); diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr index 24c281a0c995..8d9776065308 100644 --- a/src/test/ui/stats/hir-stats.stderr +++ b/src/test/ui/stats/hir-stats.stderr @@ -140,18 +140,18 @@ hir-stats - Expr 32 ( 0.3%) 1 hir-stats FnDecl 120 ( 1.2%) 3 40 hir-stats Attribute 128 ( 1.3%) 4 32 hir-stats GenericArgs 144 ( 1.5%) 3 48 -hir-stats Variant 160 ( 1.6%) 2 80 +hir-stats Variant 160 ( 1.7%) 2 80 hir-stats WherePredicate 168 ( 1.7%) 3 56 hir-stats - BoundPredicate 168 ( 1.7%) 3 hir-stats GenericBound 192 ( 2.0%) 4 48 hir-stats - Trait 192 ( 2.0%) 4 hir-stats Block 288 ( 3.0%) 6 48 +hir-stats Pat 360 ( 3.7%) 5 72 +hir-stats - Wild 72 ( 0.7%) 1 +hir-stats - Struct 72 ( 0.7%) 1 +hir-stats - Binding 216 ( 2.2%) 3 hir-stats GenericParam 400 ( 4.1%) 5 80 -hir-stats Pat 440 ( 4.5%) 5 88 -hir-stats - Wild 88 ( 0.9%) 1 -hir-stats - Struct 88 ( 0.9%) 1 -hir-stats - Binding 264 ( 2.7%) 3 -hir-stats Generics 560 ( 5.7%) 10 56 +hir-stats Generics 560 ( 5.8%) 10 56 hir-stats Ty 720 ( 7.4%) 15 48 hir-stats - Ptr 48 ( 0.5%) 1 hir-stats - Rptr 48 ( 0.5%) 1 @@ -162,17 +162,17 @@ hir-stats - Struct 64 ( 0.7%) 1 hir-stats - Match 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Lit 128 ( 1.3%) 2 -hir-stats - Block 384 ( 3.9%) 6 -hir-stats Item 960 ( 9.8%) 12 80 +hir-stats - Block 384 ( 4.0%) 6 +hir-stats Item 960 ( 9.9%) 12 80 hir-stats - Trait 80 ( 0.8%) 1 hir-stats - Enum 80 ( 0.8%) 1 hir-stats - ExternCrate 80 ( 0.8%) 1 hir-stats - ForeignMod 80 ( 0.8%) 1 hir-stats - Impl 80 ( 0.8%) 1 -hir-stats - Fn 160 ( 1.6%) 2 +hir-stats - Fn 160 ( 1.7%) 2 hir-stats - Use 400 ( 4.1%) 5 -hir-stats Path 1_536 (15.7%) 32 48 -hir-stats PathSegment 2_240 (23.0%) 40 56 +hir-stats Path 1_536 (15.9%) 32 48 +hir-stats PathSegment 2_240 (23.1%) 40 56 hir-stats ---------------------------------------------------------------- -hir-stats Total 9_760 +hir-stats Total 9_680 hir-stats diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index fdfb821ac789..bce49165e5b1 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -51,7 +51,9 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { false }, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), - PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a), + PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => { + !etc.as_opt_usize().is_some() && array_rec(a) + } PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index e32ef9933afe..93874b103b46 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -248,7 +248,7 @@ impl<'a> NormalizedPat<'a> { } else { (None, adt.non_enum_variant()) }; - let (front, back) = match wild_idx { + let (front, back) = match wild_idx.as_opt_usize() { Some(i) => pats.split_at(i), None => (pats, [].as_slice()), }; @@ -268,7 +268,7 @@ impl<'a> NormalizedPat<'a> { ty::Tuple(subs) => subs.len(), _ => return Self::Wild, }; - let (front, back) = match wild_idx { + let (front, back) = match wild_idx.as_opt_usize() { Some(i) => pats.split_at(i), None => (pats, [].as_slice()), }; diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 95478af45b4b..1bf1c4d10789 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -200,6 +200,8 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, // We don't actually know the position and the presence of the `..` (dotdot) operator // in the arms, so we need to evaluate the correct offsets here in order to iterate in // both arms at the same time. + let left_pos = left_pos.as_opt_usize(); + let right_pos = right_pos.as_opt_usize(); let len = max( left_in.len() + { if left_pos.is_some() { 1 } else { 0 } diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index f4f1fd336df7..569870ab2b7f 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -122,7 +122,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_chain! { if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); - if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind; + if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind; + if ddpos.as_opt_usize().is_none(); if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index 35824b03170a..ce9ebad8c89a 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -19,10 +19,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { && cx.typeck_results().pat_ty(local.pat).is_unit() { if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) - || matches!(local.pat.kind, PatKind::Tuple([], None))) + || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())) && expr_needs_inferred_result(cx, init) { - if !matches!(local.pat.kind, PatKind::Wild | PatKind::Tuple([], None)) { + if !matches!(local.pat.kind, PatKind::Wild) + && !matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()) + { span_lint_and_then( cx, LET_UNIT_VALUE, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index b27439cbec27..3cf043f22df5 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1552,7 +1552,8 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if_chain! { - if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind; + if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; + if ddpos.as_opt_usize().is_none(); if is_lang_ctor(cx, path, ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id);