Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit bb218fa

Browse files
committed
Reimpl using a general CtorSet::split
1 parent fb32af5 commit bb218fa

File tree

2 files changed

+109
-166
lines changed

2 files changed

+109
-166
lines changed

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 73 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,32 @@ pub(super) enum ConstructorSet {
10911091
Uninhabited,
10921092
}
10931093

1094+
/// Describes the result of analyzing the constructors in a column of a match.
1095+
///
1096+
/// `present` is morally the set of constructors present in the column, and `missing` is the set of
1097+
/// constructors that exist in the type but are not present in the column.
1098+
///
1099+
/// More formally, they respect the following constraints:
1100+
/// - the union of `present` and `missing` covers the whole type
1101+
/// - `present` and `missing` are disjoint
1102+
/// - neither contains wildcards
1103+
/// - each constructor in `present` is covered by some non-wildcard constructor in the column
1104+
/// - together, the constructors in `present` cover all the non-wildcard constructor in the column
1105+
/// - non-wildcards in the column do no cover anything in `missing`
1106+
/// - constructors in `present` and `missing` are split for the column; in other words, they are
1107+
/// either fully included in or disjoint from each constructor in the column. This rules out
1108+
/// non-trivial intersections like between `0..10` and `5..15`.
1109+
///
1110+
/// FIXME(Nadrieril): examples?
1111+
struct SplitConstructorSet<'tcx> {
1112+
present: SmallVec<[Constructor<'tcx>; 1]>,
1113+
missing: Vec<Constructor<'tcx>>,
1114+
/// Whether there were any non-wildcard constructors in the column.
1115+
seen_any_non_wildcards: bool,
1116+
/// For the `non_exhaustive_omitted_patterns` lint.
1117+
nonexhaustive_enum_missing_real_variants: bool,
1118+
}
1119+
10941120
impl ConstructorSet {
10951121
pub(super) fn new<'p, 'tcx>(pcx: &PatCtxt<'_, 'p, 'tcx>) -> Self {
10961122
debug!("ConstructorSet::new({:?})", pcx.ty);
@@ -1203,36 +1229,38 @@ impl ConstructorSet {
12031229
}
12041230
}
12051231

1206-
pub(super) fn compute_split_wildcard<'a, 'tcx>(
1232+
/// This is the core logical operation of exhaustiveness checking.
1233+
/// This analyzes a column a constructors to 1/ determine which constructors of the type (if
1234+
/// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
1235+
/// or slices.
1236+
fn split<'a, 'tcx>(
12071237
&self,
12081238
pcx: &PatCtxt<'_, '_, 'tcx>,
12091239
ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
12101240
// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
12111241
// subpattern.
12121242
is_top_level: bool,
1213-
) -> SmallVec<[Constructor<'tcx>; 1]>
1243+
) -> SplitConstructorSet<'tcx>
12141244
where
12151245
'tcx: 'a,
12161246
{
12171247
let mut missing = Vec::new();
1218-
let mut any_missing = false;
12191248
let mut present: SmallVec<[_; 1]> = SmallVec::new();
1220-
// Constructors in `ctors`, except wildcards and opaques.
1249+
// Constructors in `ctors`, except wildcards.
12211250
let mut seen = Vec::new();
12221251
for ctor in ctors.cloned() {
12231252
match ctor {
12241253
// Wildcards in `ctors` are irrelevant to splitting
1225-
Wildcard => {}
1226-
Constructor::Opaque => {}
1254+
Opaque | Wildcard => {}
12271255
_ => {
12281256
seen.push(ctor);
12291257
}
12301258
}
12311259
}
1260+
let mut nonexhaustive_enum_missing_real_variants = false;
12321261
match self {
12331262
ConstructorSet::Single => {
12341263
if seen.is_empty() {
1235-
any_missing = true;
12361264
missing.push(Single);
12371265
} else {
12381266
present.push(Single);
@@ -1251,15 +1279,16 @@ impl ConstructorSet {
12511279
skipped_a_hidden_variant = true;
12521280
} else {
12531281
missing.push(ctor);
1254-
any_missing = true;
12551282
}
12561283
}
1284+
12571285
if *non_exhaustive {
1286+
nonexhaustive_enum_missing_real_variants = !missing.is_empty();
12581287
missing.push(NonExhaustive);
1259-
any_missing = true;
12601288
} else if skipped_a_hidden_variant {
1289+
// FIXME(Nadrieril): This represents the skipped variants, but isn't super
1290+
// clean. Using `NonExhaustive` breaks things elsewhere.
12611291
missing.push(Wildcard);
1262-
any_missing = true;
12631292
}
12641293
}
12651294
ConstructorSet::Integers { range_1, range_2, non_exhaustive } => {
@@ -1271,27 +1300,21 @@ impl ConstructorSet {
12711300
for (seen, splitted_range) in range.split(seen_ranges.cloned()) {
12721301
let ctor = IntRange(splitted_range);
12731302
match seen {
1274-
Presence::Unseen => {
1275-
missing.push(ctor);
1276-
any_missing = true;
1277-
}
1303+
Presence::Unseen => missing.push(ctor),
12781304
Presence::Seen => present.push(ctor),
12791305
}
12801306
}
1307+
12811308
if *non_exhaustive {
12821309
missing.push(NonExhaustive);
1283-
any_missing = true;
12841310
}
12851311
}
12861312
&ConstructorSet::Slice(array_len) => {
12871313
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap()).map(|s| s.kind);
12881314
for (seen, splitted_slice) in SplitVarLenSlice2::split(array_len, seen_slices) {
12891315
let ctor = Slice(splitted_slice);
12901316
match seen {
1291-
Presence::Unseen => {
1292-
missing.push(ctor);
1293-
any_missing = true;
1294-
}
1317+
Presence::Unseen => missing.push(ctor),
12951318
Presence::Seen => present.push(ctor),
12961319
}
12971320
}
@@ -1301,17 +1324,15 @@ impl ConstructorSet {
13011324
let slice = Slice(Slice::new(None, FixedLen(0)));
13021325
if seen.is_empty() {
13031326
missing.push(slice);
1304-
any_missing = true;
13051327
} else {
13061328
present.push(slice);
13071329
}
13081330
}
13091331
ConstructorSet::Unlistable => {
1310-
// Since we can't list constructors, we take the ones in the matrix. This might list
1332+
// Since we can't list constructors, we take the ones in the column. This might list
13111333
// some constructors several times but there's not much we can do.
13121334
present.extend(seen.iter().cloned());
13131335
missing.push(NonExhaustive);
1314-
any_missing = true;
13151336
}
13161337
// If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
13171338
// expose its emptiness. The exception is if the pattern is at the top level, because we
@@ -1320,14 +1341,33 @@ impl ConstructorSet {
13201341
if !pcx.cx.tcx.features().exhaustive_patterns && !is_top_level =>
13211342
{
13221343
missing.push(NonExhaustive);
1323-
any_missing = true;
13241344
}
13251345
ConstructorSet::Uninhabited => {}
13261346
}
13271347

1328-
if !missing.is_empty() {
1348+
SplitConstructorSet {
1349+
present,
1350+
missing,
1351+
seen_any_non_wildcards: !seen.is_empty(),
1352+
nonexhaustive_enum_missing_real_variants,
1353+
}
1354+
}
1355+
1356+
pub(super) fn compute_split_wildcard<'a, 'tcx>(
1357+
&self,
1358+
pcx: &PatCtxt<'_, '_, 'tcx>,
1359+
ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
1360+
// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
1361+
// subpattern.
1362+
is_top_level: bool,
1363+
) -> SmallVec<[Constructor<'tcx>; 1]>
1364+
where
1365+
'tcx: 'a,
1366+
{
1367+
let split_set = self.split(pcx, ctors, is_top_level);
1368+
if !split_set.missing.is_empty() {
13291369
// Some constructors are missing, thus we can specialize with the special `Missing`
1330-
// constructor, which stands for those constructors that are not seen in the matrix,
1370+
// constructor, which stands for those constructors that are not seen in the column,
13311371
// and matches the same rows as any of them (namely the wildcard rows). See the top of
13321372
// the file for details.
13331373
// However, when all constructors are missing we can also specialize with the full
@@ -1354,25 +1394,22 @@ impl ConstructorSet {
13541394
// The exception is: if we are at the top-level, for example in an empty match, we
13551395
// sometimes prefer reporting the list of constructors instead of just `_`.
13561396
let report_when_all_missing = is_top_level && !matches!(self, Self::Integers { .. });
1357-
let ctor = if !seen.is_empty() || report_when_all_missing {
1358-
if pcx.is_non_exhaustive {
1359-
Missing {
1360-
nonexhaustive_enum_missing_real_variants: missing
1361-
.iter()
1362-
.any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))),
1363-
}
1364-
} else {
1365-
Missing { nonexhaustive_enum_missing_real_variants: false }
1397+
let ctor = if split_set.seen_any_non_wildcards || report_when_all_missing {
1398+
Missing {
1399+
nonexhaustive_enum_missing_real_variants: split_set
1400+
.nonexhaustive_enum_missing_real_variants,
13661401
}
13671402
} else {
13681403
Wildcard
13691404
};
13701405
smallvec![ctor]
13711406
} else {
1372-
present
1407+
split_set.present
13731408
}
13741409
}
13751410

1411+
/// Compute the set of constructors missing from this column.
1412+
/// This is only used for reporting to the user.
13761413
pub(super) fn compute_missing<'a, 'tcx>(
13771414
&self,
13781415
pcx: &PatCtxt<'_, '_, 'tcx>,
@@ -1384,94 +1421,7 @@ impl ConstructorSet {
13841421
where
13851422
'tcx: 'a,
13861423
{
1387-
let mut missing = Vec::new();
1388-
// Constructors in `ctors`, except wildcards and opaques.
1389-
let mut seen = Vec::new();
1390-
for ctor in ctors.cloned() {
1391-
match ctor {
1392-
// Wildcards in `ctors` are irrelevant to splitting
1393-
Wildcard => {}
1394-
Constructor::Opaque => {}
1395-
_ => {
1396-
seen.push(ctor);
1397-
}
1398-
}
1399-
}
1400-
match self {
1401-
ConstructorSet::Single => {
1402-
if seen.is_empty() {
1403-
missing.push(Single);
1404-
}
1405-
}
1406-
ConstructorSet::Variants { variants, non_exhaustive } => {
1407-
let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
1408-
let mut skipped_a_hidden_variant = false;
1409-
for variant in variants {
1410-
let ctor = Variant(*variant);
1411-
if seen_set.contains(&variant) {
1412-
} else if ctor.is_doc_hidden_variant(pcx) || ctor.is_unstable_variant(pcx) {
1413-
// We don't want to mention any variants that are `doc(hidden)` or behind an
1414-
// unstable feature gate if they aren't present in the match.
1415-
skipped_a_hidden_variant = true;
1416-
} else {
1417-
missing.push(ctor);
1418-
}
1419-
}
1420-
if *non_exhaustive {
1421-
missing.push(NonExhaustive);
1422-
} else if skipped_a_hidden_variant {
1423-
missing.push(Wildcard);
1424-
}
1425-
}
1426-
ConstructorSet::Integers { range_1, range_2, non_exhaustive } => {
1427-
if *non_exhaustive {
1428-
missing.push(NonExhaustive);
1429-
} else {
1430-
let range = match range_2 {
1431-
None => SplitIntRange2::Single(range_1.clone()),
1432-
Some(range_2) => SplitIntRange2::Double([range_1.clone(), range_2.clone()]),
1433-
};
1434-
let seen_ranges = seen.iter().map(|ctor| ctor.as_int_range().unwrap());
1435-
for (seen, splitted_range) in range.split(seen_ranges.cloned()) {
1436-
let ctor = IntRange(splitted_range);
1437-
match seen {
1438-
Presence::Unseen => missing.push(ctor),
1439-
Presence::Seen => {}
1440-
}
1441-
}
1442-
}
1443-
}
1444-
&ConstructorSet::Slice(array_len) => {
1445-
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap()).map(|s| s.kind);
1446-
for (seen, splitted_slice) in SplitVarLenSlice2::split(array_len, seen_slices) {
1447-
let ctor = Slice(splitted_slice);
1448-
match seen {
1449-
Presence::Unseen => missing.push(ctor),
1450-
Presence::Seen => {}
1451-
}
1452-
}
1453-
}
1454-
ConstructorSet::SliceOfEmpty => {
1455-
if seen.is_empty() {
1456-
// Behaves essentially like `Single`.
1457-
let slice = Slice(Slice::new(None, FixedLen(0)));
1458-
missing.push(slice);
1459-
}
1460-
}
1461-
ConstructorSet::Unlistable => {
1462-
missing.push(NonExhaustive);
1463-
}
1464-
// If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
1465-
// expose its emptiness. The exception is if the pattern is at the top level, because we
1466-
// want empty matches to be considered exhaustive.
1467-
ConstructorSet::Uninhabited
1468-
if !pcx.cx.tcx.features().exhaustive_patterns && !is_top_level =>
1469-
{
1470-
missing.push(NonExhaustive);
1471-
}
1472-
ConstructorSet::Uninhabited => {}
1473-
}
1474-
missing
1424+
self.split(pcx, ctors, is_top_level).missing
14751425
}
14761426
}
14771427

0 commit comments

Comments
 (0)