diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 2255220808ea7..186c77795e494 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -53,14 +53,13 @@ use smallvec::{smallvec, SmallVec}; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{HirId, RangeEnd}; +use rustc_hir::RangeEnd; use rustc_index::Idx; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; -use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; @@ -68,7 +67,6 @@ use self::Constructor::*; use self::SliceKind::*; use super::usefulness::{MatchCheckCtxt, PatCtxt}; -use crate::errors::{Overlap, OverlappingRangeEndpoints}; /// 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>> { @@ -111,15 +109,15 @@ pub(crate) struct IntRange { impl IntRange { #[inline] - fn is_integral(ty: Ty<'_>) -> bool { + pub(super) fn is_integral(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) } - fn is_singleton(&self) -> bool { + pub(super) fn is_singleton(&self) -> bool { self.range.start() == self.range.end() } - fn boundaries(&self) -> (u128, u128) { + pub(super) fn boundaries(&self) -> (u128, u128) { (*self.range.start(), *self.range.end()) } @@ -177,23 +175,6 @@ impl IntRange { } } - fn suspicious_intersection(&self, other: &Self) -> bool { - // `false` in the following cases: - // 1 ---- // 1 ---------- // 1 ---- // 1 ---- - // 2 ---------- // 2 ---- // 2 ---- // 2 ---- - // - // The following are currently `false`, but could be `true` in the future (#64007): - // 1 --------- // 1 --------- - // 2 ---------- // 2 ---------- - // - // `true` in the following cases: - // 1 ------- // 1 ------- - // 2 -------- // 2 ------- - let (lo, hi) = self.boundaries(); - let (other_lo, other_hi) = other.boundaries(); - (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton() - } - /// Partition a range of integers into disjoint subranges. This does constructor splitting for /// integer ranges as explained at the top of the file. /// @@ -293,7 +274,7 @@ impl IntRange { } /// Only used for displaying the range. - fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { + pub(super) fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { let (lo, hi) = self.boundaries(); let bias = IntRange::signed_bias(tcx, ty); @@ -315,51 +296,6 @@ impl IntRange { Pat { ty, span: DUMMY_SP, kind } } - - /// Lint on likely incorrect range patterns (#63987) - pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( - &self, - pcx: &PatCtxt<'_, 'p, 'tcx>, - pats: impl Iterator>, - column_count: usize, - lint_root: HirId, - ) { - if self.is_singleton() { - return; - } - - if column_count != 1 { - // FIXME: for now, only check for overlapping ranges on simple range - // patterns. Otherwise with the current logic the following is detected - // as overlapping: - // ``` - // match (0u8, true) { - // (0 ..= 125, false) => {} - // (125 ..= 255, true) => {} - // _ => {} - // } - // ``` - return; - } - - let overlap: Vec<_> = pats - .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) - .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| Overlap { - range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), - span, - }) - .collect(); - - if !overlap.is_empty() { - pcx.cx.tcx.emit_spanned_lint( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - lint_root, - pcx.span, - OverlappingRangeEndpoints { overlap, range: pcx.span }, - ); - } - } } /// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and @@ -644,7 +580,7 @@ impl<'tcx> Constructor<'tcx> { _ => None, } } - fn as_int_range(&self) -> Option<&IntRange> { + pub(super) fn as_int_range(&self) -> Option<&IntRange> { match self { IntRange(range) => Some(range), _ => None, diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 5218f772484fd..25e0f3ceaa473 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -307,8 +307,10 @@ use self::ArmType::*; use self::Usefulness::*; -use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat}; -use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; +use super::deconstruct_pat::{ + Constructor, ConstructorSet, DeconstructedPat, IntRange, SplitConstructorSet, WitnessPat, +}; +use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered}; use rustc_data_structures::captures::Captures; @@ -317,6 +319,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::lint; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::{Span, DUMMY_SP}; @@ -473,11 +476,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { Matrix { patterns: vec![] } } - /// Number of columns of this matrix. `None` is the matrix is empty. - pub(super) fn column_count(&self) -> Option { - self.patterns.get(0).map(|r| r.len()) - } - /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. fn push(&mut self, row: PatStack<'p, 'tcx>) { @@ -833,15 +831,6 @@ fn is_useful<'p, 'tcx>( let v_ctor = v.head().ctor(); debug!(?v_ctor); - if let Constructor::IntRange(ctor_range) = &v_ctor { - // Lint on likely incorrect range patterns (#63987) - ctor_range.lint_overlapping_range_endpoints( - pcx, - matrix.heads(), - matrix.column_count().unwrap_or(0), - lint_root, - ) - } // We split the head constructor of `v`. let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); // For each constructor, we compute whether there's a value that starts with it that would @@ -875,22 +864,102 @@ fn is_useful<'p, 'tcx>( ret } +/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that +/// inspect the same subvalue". +/// This is used to traverse patterns column-by-column for lints. Despite similarities with +/// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns, +/// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete). +#[derive(Debug)] +struct PatternColumn<'p, 'tcx> { + patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>, +} + +impl<'p, 'tcx> PatternColumn<'p, 'tcx> { + fn new(patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>) -> Self { + Self { patterns } + } + + fn is_empty(&self) -> bool { + self.patterns.is_empty() + } + fn head_ty(&self) -> Option> { + if self.patterns.len() == 0 { + return None; + } + // 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) { + for pat in &self.patterns { + let ty = pat.ty(); + if !is_opaque(ty) { + return Some(ty); + } + } + } + Some(first_ty) + } + + fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> { + let column_ctors = self.patterns.iter().map(|p| p.ctor()); + ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column_ctors) + } + fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { + self.patterns.iter().copied() + } + + /// Does specialization: given a constructor, this takes the patterns from the column that match + /// the constructor, and outputs their fields. + /// This returns one column per field of the constructor. The normally 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 { + let arity = ctor.arity(pcx); + if arity == 0 { + return Vec::new(); + } + + // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These + // columns may have different lengths in the presence of or-patterns (this is why we can't + // reuse `Matrix`). + let mut specialized_columns: Vec<_> = + (0..arity).map(|_| Self { patterns: Vec::new() }).collect(); + let relevant_patterns = + self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); + for pat in relevant_patterns { + let specialized = pat.specialize(pcx, &ctor); + for (subpat, column) in specialized.iter().zip(&mut specialized_columns) { + if subpat.is_or_pat() { + column.patterns.extend(subpat.iter_fields()) + } else { + column.patterns.push(subpat) + } + } + } + + assert!( + !specialized_columns[0].is_empty(), + "ctor {ctor:?} was listed as present but isn't; + there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`" + ); + specialized_columns + } +} + /// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned -/// in a given column. This traverses patterns column-by-column, where a column is the intuitive -/// notion of "subpatterns that inspect the same subvalue". -/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the -/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete). +/// in a given column. +#[instrument(level = "debug", skip(cx), ret)] fn collect_nonexhaustive_missing_variants<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, - column: &[&DeconstructedPat<'p, 'tcx>], + column: &PatternColumn<'p, 'tcx>, ) -> Vec> { - if column.is_empty() { + let Some(ty) = column.head_ty() else { return Vec::new(); - } - let ty = column[0].ty(); + }; let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false }; - let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor())); + let set = column.analyze_ctors(pcx); if set.present.is_empty() { // We can't consistently handle the case where no constructors are present (since this would // require digging deep through any type in case there's a non_exhaustive enum somewhere), @@ -911,35 +980,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( // Recurse into the fields. for ctor in set.present { - let arity = ctor.arity(pcx); - if arity == 0 { - continue; - } - - // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These - // columns may have different lengths in the presence of or-patterns (this is why we can't - // reuse `Matrix`). - let mut specialized_columns: Vec> = (0..arity).map(|_| Vec::new()).collect(); - let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor())); - for pat in relevant_patterns { - let specialized = pat.specialize(pcx, &ctor); - for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) { - if subpat.is_or_pat() { - sub_column.extend(subpat.iter_fields()) - } else { - sub_column.push(subpat) - } - } - } - debug_assert!( - !specialized_columns[0].is_empty(), - "ctor {ctor:?} was listed as present but isn't" - ); - + let specialized_columns = column.specialize(pcx, &ctor); 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.as_slice()); + 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 { @@ -952,6 +997,81 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>( witnesses } +/// Traverse the patterns to warn the user about ranges that overlap on their endpoints. +#[instrument(level = "debug", skip(cx, lint_root))] +fn lint_overlapping_range_endpoints<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + column: &PatternColumn<'p, 'tcx>, + lint_root: HirId, +) { + let Some(ty) = column.head_ty() else { + return; + }; + let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false }; + + let set = column.analyze_ctors(pcx); + + if IntRange::is_integral(ty) { + let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { + let overlap_as_pat = overlap.to_pat(cx.tcx, ty); + let overlaps: Vec<_> = overlapped_spans + .iter() + .copied() + .map(|span| Overlap { range: overlap_as_pat.clone(), span }) + .collect(); + cx.tcx.emit_spanned_lint( + lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, + lint_root, + this_span, + OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, + ); + }; + + // If two ranges overlapped, the split set will contain their intersection as a singleton. + let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range()); + for overlap_range in split_int_ranges.clone() { + if overlap_range.is_singleton() { + let overlap: u128 = overlap_range.boundaries().0; + // Spans of ranges that start or end with the overlap. + let mut prefixes: SmallVec<[_; 1]> = Default::default(); + let mut suffixes: SmallVec<[_; 1]> = Default::default(); + // Iterate on patterns that contained `overlap`. + for pat in column.iter() { + 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. + continue; + } + let (start, end) = this_range.boundaries(); + if start == overlap { + // `this_range` looks like `overlap..=end`; it overlaps with any ranges that + // look like `start..=overlap`. + if !prefixes.is_empty() { + emit_lint(overlap_range, this_span, &prefixes); + } + suffixes.push(this_span) + } else if end == overlap { + // `this_range` looks like `start..=overlap`; it overlaps with any ranges + // that look like `overlap..=end`. + if !suffixes.is_empty() { + emit_lint(overlap_range, this_span, &suffixes); + } + prefixes.push(this_span) + } + } + } + } + } else { + // Recurse into the fields. + for ctor in set.present { + for col in column.specialize(pcx, &ctor) { + lint_overlapping_range_endpoints(cx, &col, lint_root); + } + } + } +} + /// The arm of a match expression. #[derive(Clone, Copy, Debug)] pub(crate) struct MatchArm<'p, 'tcx> { @@ -1022,6 +1142,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( NoWitnesses { .. } => bug!(), }; + let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::>(); + let pat_column = PatternColumn::new(pat_column); + lint_overlapping_range_endpoints(cx, &pat_column, lint_root); + // 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 @@ -1031,9 +1155,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( rustc_session::lint::Level::Allow ) { - let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::>(); 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. diff --git a/tests/ui/mir/mir_match_test.rs b/tests/ui/mir/mir_match_test.rs index 1f96d6737e0af..d41a7f4a1db4a 100644 --- a/tests/ui/mir/mir_match_test.rs +++ b/tests/ui/mir/mir_match_test.rs @@ -1,4 +1,5 @@ #![feature(exclusive_range_pattern)] +#![allow(overlapping_range_endpoints)] // run-pass diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs index 5ea92b07081af..33c1dfd39d441 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs @@ -8,7 +8,7 @@ macro_rules! m { $t2 => {} _ => {} } - } + }; } fn main() { @@ -16,9 +16,9 @@ fn main() { m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20..=30, 31..=40); m!(0u8, 20..=30, 29..=40); - m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints - m!(0u8, 20.. 30, 28..=40); - m!(0u8, 20.. 30, 30..=40); + m!(0u8, 20..30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints + m!(0u8, 20..30, 28..=40); + m!(0u8, 20..30, 30..=40); m!(0u8, 20..=30, 30..=30); m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns overlap on their endpoints m!(0u8, 20..=30, 29..=30); @@ -28,7 +28,7 @@ fn main() { m!(0u8, 20..=30, 20); m!(0u8, 20..=30, 25); m!(0u8, 20..=30, 30); - m!(0u8, 20.. 30, 29); + m!(0u8, 20..30, 29); m!(0u8, 20, 20..=30); m!(0u8, 25, 20..=30); m!(0u8, 30, 20..=30); @@ -36,19 +36,21 @@ fn main() { match 0u8 { 0..=10 => {} 20..=30 => {} - 10..=20 => {} //~ ERROR multiple patterns overlap on their endpoints + 10..=20 => {} + //~^ ERROR multiple patterns overlap on their endpoints + //~| ERROR multiple patterns overlap on their endpoints _ => {} } match (0u8, true) { (0..=10, true) => {} - (10..20, true) => {} // not detected - (10..20, false) => {} + (10..20, true) => {} //~ ERROR multiple patterns overlap on their endpoints + (10..20, false) => {} //~ ERROR multiple patterns overlap on their endpoints _ => {} } match (true, 0u8) { (true, 0..=10) => {} (true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints - (false, 10..20) => {} + (false, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints _ => {} } match Some(0u8) { diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr index ea0e8f6e49e07..a87205d76d1c8 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr @@ -24,10 +24,10 @@ LL | m!(0u8, 30..=40, 20..=30); = note: you likely meant to write mutually exclusive ranges error: multiple patterns overlap on their endpoints - --> $DIR/overlapping_range_endpoints.rs:19:22 + --> $DIR/overlapping_range_endpoints.rs:19:21 | -LL | m!(0u8, 20.. 30, 29..=40); - | ------- ^^^^^^^ ... with this range +LL | m!(0u8, 20..30, 29..=40); + | ------ ^^^^^^^ ... with this range | | | this range overlaps on `29_u8`... | @@ -58,6 +58,15 @@ error: multiple patterns overlap on their endpoints | LL | 0..=10 => {} | ------ this range overlaps on `10_u8`... +LL | 20..=30 => {} +LL | 10..=20 => {} + | ^^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges + +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:39:9 + | LL | 20..=30 => {} | ------- this range overlaps on `20_u8`... LL | 10..=20 => {} @@ -66,7 +75,28 @@ LL | 10..=20 => {} = note: you likely meant to write mutually exclusive ranges error: multiple patterns overlap on their endpoints - --> $DIR/overlapping_range_endpoints.rs:50:16 + --> $DIR/overlapping_range_endpoints.rs:46:10 + | +LL | (0..=10, true) => {} + | ------ this range overlaps on `10_u8`... +LL | (10..20, true) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges + +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:47:10 + | +LL | (0..=10, true) => {} + | ------ this range overlaps on `10_u8`... +LL | (10..20, true) => {} +LL | (10..20, false) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges + +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:52:16 | LL | (true, 0..=10) => {} | ------ this range overlaps on `10_u8`... @@ -76,7 +106,18 @@ LL | (true, 10..20) => {} = note: you likely meant to write mutually exclusive ranges error: multiple patterns overlap on their endpoints - --> $DIR/overlapping_range_endpoints.rs:56:14 + --> $DIR/overlapping_range_endpoints.rs:53:17 + | +LL | (true, 0..=10) => {} + | ------ this range overlaps on `10_u8`... +LL | (true, 10..20) => {} +LL | (false, 10..20) => {} + | ^^^^^^ ... with this range + | + = note: you likely meant to write mutually exclusive ranges + +error: multiple patterns overlap on their endpoints + --> $DIR/overlapping_range_endpoints.rs:58:14 | LL | Some(0..=10) => {} | ------ this range overlaps on `10_u8`... @@ -85,5 +126,5 @@ LL | Some(10..20) => {} | = note: you likely meant to write mutually exclusive ranges -error: aborting due to 8 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs index fdd8fa65bd05f..5bd854be8c6ab 100644 --- a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -26,11 +26,9 @@ fn upvar() { fn enum_upvar() { type T = impl Copy; let foo: T = Some((1u32, 2u32)); - let x = move || { - match foo { - None => (), - Some((a, b)) => (), - } + let x = move || match foo { + None => (), + Some((a, b)) => (), }; } @@ -63,6 +61,19 @@ mod only_pattern { None => {} } } + + type V = impl Copy; + + fn baz(baz: Option) { + match baz { + _ => {} + Some((mut x, mut y)) => { + x = 42; + y = "foo"; + } + None => {} + } + } } mod only_pattern_rpit { @@ -71,11 +82,7 @@ mod only_pattern_rpit { let (mut x, mut y) = foo(false); x = 42; y = "foo"; - if b { - panic!() - } else { - foo(true) - } + if b { panic!() } else { foo(true) } } fn bar(b: bool) -> Option {