Skip to content

Commit b30d696

Browse files
committed
pattern_analysis: add option to get a full set of witnesses
1 parent 6dec76f commit b30d696

File tree

7 files changed

+116
-16
lines changed

7 files changed

+116
-16
lines changed

compiler/rustc_pattern_analysis/src/constructor.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -950,9 +950,7 @@ impl<Cx: PatCx> Constructor<Cx> {
950950
}
951951
}
952952
Never => write!(f, "!")?,
953-
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
954-
write!(f, "_ : {:?}", ty)?
955-
}
953+
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?,
956954
}
957955
Ok(())
958956
}

compiler/rustc_pattern_analysis/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ pub trait PatCx: Sized + fmt::Debug {
5656

5757
fn is_exhaustive_patterns_feature_on(&self) -> bool;
5858

59+
/// Whether to ensure the non-exhaustiveness witnesses we report for a complete set. This is
60+
/// `false` by default to avoid some exponential blowup cases such as
61+
/// https://github.com/rust-lang/rust/issues/118437.
62+
fn exhaustive_witnesses(&self) -> bool {
63+
false
64+
}
65+
5966
/// The number of fields for this constructor.
6067
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize;
6168

compiler/rustc_pattern_analysis/src/usefulness.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1747,7 +1747,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
17471747
// `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
17481748
// strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
17491749
// details.
1750-
let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty();
1750+
let ctor_is_relevant = matches!(ctor, Constructor::Missing)
1751+
|| missing_ctors.is_empty()
1752+
|| mcx.tycx.exhaustive_witnesses();
17511753
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant)?;
17521754
let mut witnesses = ensure_sufficient_stack(|| {
17531755
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix)

compiler/rustc_pattern_analysis/tests/common/mod.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ pub(super) fn compute_match_usefulness<'p>(
125125
ty: Ty,
126126
scrut_validity: PlaceValidity,
127127
complexity_limit: usize,
128+
exhaustive_witnesses: bool,
128129
) -> Result<UsefulnessReport<'p, Cx>, ()> {
129130
init_tracing();
130131
rustc_pattern_analysis::usefulness::compute_match_usefulness(
131-
&Cx,
132+
&Cx { exhaustive_witnesses },
132133
arms,
133134
ty,
134135
scrut_validity,
@@ -137,7 +138,9 @@ pub(super) fn compute_match_usefulness<'p>(
137138
}
138139

139140
#[derive(Debug)]
140-
pub(super) struct Cx;
141+
pub(super) struct Cx {
142+
exhaustive_witnesses: bool,
143+
}
141144

142145
/// The context for pattern analysis. Forwards anything interesting to `Ty` methods.
143146
impl PatCx for Cx {
@@ -152,6 +155,10 @@ impl PatCx for Cx {
152155
false
153156
}
154157

158+
fn exhaustive_witnesses(&self) -> bool {
159+
self.exhaustive_witnesses
160+
}
161+
155162
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
156163
ty.sub_tys(ctor).len()
157164
}
@@ -210,16 +217,18 @@ macro_rules! pats {
210217
// Entrypoint
211218
// Parse `type; ..`
212219
($ty:expr; $($rest:tt)*) => {{
213-
#[allow(unused_imports)]
220+
#[allow(unused)]
214221
use rustc_pattern_analysis::{
215222
constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd},
216-
pat::DeconstructedPat,
223+
pat::{DeconstructedPat, IndexedPat},
217224
};
218225
let ty = $ty;
219226
// The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around
220227
// that.
228+
#[allow(unused)]
221229
let sub_tys = ::std::iter::repeat(&ty);
222-
let mut vec = Vec::new();
230+
#[allow(unused)]
231+
let mut vec: Vec<IndexedPat<_>> = Vec::new();
223232
pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*);
224233
vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>()
225234
}};
@@ -254,6 +263,8 @@ macro_rules! pats {
254263
let ctor = Constructor::Wildcard;
255264
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
256265
}};
266+
// Nothing
267+
(@ctor($($args:tt)*)) => {};
257268

258269
// Integers and int ranges
259270
(@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{

compiler/rustc_pattern_analysis/tests/complexity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(
1616
let ty = *patterns[0].ty();
1717
let arms: Vec<_> =
1818
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
19-
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit)
19+
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit, false)
2020
.map(|_report| ())
2121
}
2222

compiler/rustc_pattern_analysis/tests/exhaustiveness.rs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,30 @@ use rustc_pattern_analysis::usefulness::PlaceValidity;
1111
mod common;
1212

1313
/// Analyze a match made of these patterns.
14-
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
15-
let ty = *patterns[0].ty();
14+
fn run(
15+
ty: Ty,
16+
patterns: Vec<DeconstructedPat<Cx>>,
17+
exhaustive_witnesses: bool,
18+
) -> Vec<WitnessPat<Cx>> {
1619
let arms: Vec<_> =
1720
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
18-
let report =
19-
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
20-
.unwrap();
21+
let report = compute_match_usefulness(
22+
arms.as_slice(),
23+
ty,
24+
PlaceValidity::ValidOnly,
25+
usize::MAX,
26+
exhaustive_witnesses,
27+
)
28+
.unwrap();
2129
report.non_exhaustiveness_witnesses
2230
}
2331

32+
/// Analyze a match made of these patterns. Panics if there are no patterns
33+
fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
34+
let ty = *patterns[0].ty();
35+
run(ty, patterns, true)
36+
}
37+
2438
#[track_caller]
2539
fn assert_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
2640
let witnesses = check(patterns);
@@ -35,6 +49,26 @@ fn assert_non_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) {
3549
assert!(!witnesses.is_empty())
3650
}
3751

52+
use WhichWitnesses::*;
53+
enum WhichWitnesses {
54+
AllOfThem,
55+
OnlySome,
56+
}
57+
58+
#[track_caller]
59+
/// We take the type as input to support empty matches.
60+
fn assert_witnesses(
61+
which: WhichWitnesses,
62+
ty: Ty,
63+
patterns: Vec<DeconstructedPat<Cx>>,
64+
expected: Vec<&str>,
65+
) {
66+
let exhaustive_wit = matches!(which, AllOfThem);
67+
let witnesses = run(ty, patterns, exhaustive_wit);
68+
let witnesses: Vec<_> = witnesses.iter().map(|w| format!("{w:?}")).collect();
69+
assert_eq!(witnesses, expected)
70+
}
71+
3872
#[test]
3973
fn test_int_ranges() {
4074
let ty = Ty::U8;
@@ -59,6 +93,8 @@ fn test_int_ranges() {
5993

6094
#[test]
6195
fn test_nested() {
96+
// enum E { A(bool), B(bool) }
97+
// ty = (E, E)
6298
let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } };
6399
assert_non_exhaustive(pats!(ty;
64100
Struct(Variant.0, _),
@@ -78,6 +114,52 @@ fn test_nested() {
78114
));
79115
}
80116

117+
#[test]
118+
fn test_witnesses() {
119+
// TY = Option<bool>
120+
const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Tuple(&[])]);
121+
// ty = (Option<bool>, Option<bool>)
122+
let ty = Ty::Tuple(&[TY, TY]);
123+
assert_witnesses(AllOfThem, ty, vec![], vec!["(_, _)"]);
124+
assert_witnesses(
125+
OnlySome,
126+
ty,
127+
pats!(ty;
128+
(Variant.0(false), Variant.0(false)),
129+
),
130+
vec!["(Enum::Variant1(_), _)"],
131+
);
132+
assert_witnesses(
133+
AllOfThem,
134+
ty,
135+
pats!(ty;
136+
(Variant.0(false), Variant.0(false)),
137+
),
138+
vec![
139+
"(Enum::Variant0(false), Enum::Variant0(true))",
140+
"(Enum::Variant0(false), Enum::Variant1(_))",
141+
"(Enum::Variant0(true), _)",
142+
"(Enum::Variant1(_), _)",
143+
],
144+
);
145+
assert_witnesses(
146+
OnlySome,
147+
ty,
148+
pats!(ty;
149+
(_, Variant.0(false)),
150+
),
151+
vec!["(_, Enum::Variant1(_))"],
152+
);
153+
assert_witnesses(
154+
AllOfThem,
155+
ty,
156+
pats!(ty;
157+
(_, Variant.0(false)),
158+
),
159+
vec!["(_, Enum::Variant0(true))", "(_, Enum::Variant1(_))"],
160+
);
161+
}
162+
81163
#[test]
82164
fn test_empty() {
83165
// `TY = Result<bool, !>`

compiler/rustc_pattern_analysis/tests/intersection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> {
1616
let arms: Vec<_> =
1717
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
1818
let report =
19-
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
19+
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX, false)
2020
.unwrap();
2121
report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect()
2222
}

0 commit comments

Comments
 (0)