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

Commit 7cd4726

Browse files
committed
Fix a ConstructorSet invariant
1 parent c79e9b4 commit 7cd4726

File tree

1 file changed

+51
-57
lines changed

1 file changed

+51
-57
lines changed

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

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,7 @@ pub(super) enum ConstructorSet {
793793
Variants {
794794
visible_variants: Vec<VariantIdx>,
795795
hidden_variants: Vec<VariantIdx>,
796+
empty_variants: Vec<VariantIdx>,
796797
non_exhaustive: bool,
797798
},
798799
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
@@ -817,16 +818,18 @@ pub(super) enum ConstructorSet {
817818
/// `present` is morally the set of constructors present in the column, and `missing` is the set of
818819
/// constructors that exist in the type but are not present in the column.
819820
///
820-
/// More formally, they respect the following constraints:
821+
/// More formally, if we discard wildcards from the column, they respect the following constraints:
821822
/// - the union of `present` and `missing` covers the whole type
822-
/// - `present` and `missing` are disjoint
823-
/// - neither contains wildcards
824-
/// - each constructor in `present` is covered by some non-wildcard constructor in the column
825-
/// - together, the constructors in `present` cover all the non-wildcard constructor in the column
826-
/// - non-wildcards in the column do no cover anything in `missing`
823+
/// - each constructor in `present` is covered by something in the column
824+
/// - no constructor in `missing` is covered by anything in the column
825+
/// - each constructor in the column is equal to the union of one or more constructors in `present`
827826
/// - constructors in `present` and `missing` are split for the column; in other words, they are
828-
/// either fully included in or disjoint from each constructor in the column. This avoids
829-
/// non-trivial intersections like between `0..10` and `5..15`.
827+
/// either fully included in or disjoint from each constructor in the column. In other words,
828+
/// there are no non-trivial intersections like between `0..10` and `5..15`.
829+
///
830+
/// When the `exhaustive_patterns` feature is enabled, all ctors in `missing` must match at least
831+
/// one value of the corresponding type. E.g. if the type is `Option<!>`, `missing` will never
832+
/// contain `Some`. This restriction does not apply to `present`.
830833
#[derive(Debug)]
831834
pub(super) struct SplitConstructorSet<'tcx> {
832835
pub(super) present: SmallVec<[Constructor<'tcx>; 1]>,
@@ -840,12 +843,6 @@ impl ConstructorSet {
840843
|start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
841844
// This determines the set of all possible constructors for the type `ty`. For numbers,
842845
// arrays and slices we use ranges and variable-length slices when appropriate.
843-
//
844-
// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
845-
// are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
846-
// returned list of constructors.
847-
// Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
848-
// `cx.is_uninhabited()`).
849846
match ty.kind() {
850847
ty::Bool => {
851848
Self::Integers { range_1: make_range(0, 1), range_2: None, non_exhaustive: false }
@@ -894,56 +891,42 @@ impl ConstructorSet {
894891
}
895892
}
896893
ty::Adt(def, args) if def.is_enum() => {
897-
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
898-
// additional "unknown" constructor.
899-
// There is no point in enumerating all possible variants, because the user can't
900-
// actually match against them all themselves. So we always return only the fictitious
901-
// constructor.
902-
// E.g., in an example like:
903-
//
904-
// ```
905-
// let err: io::ErrorKind = ...;
906-
// match err {
907-
// io::ErrorKind::NotFound => {},
908-
// }
909-
// ```
910-
//
911-
// we don't want to show every possible IO error, but instead have only `_` as the
912-
// witness.
913894
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
914-
915895
if def.variants().is_empty() && !is_declared_nonexhaustive {
916896
Self::Uninhabited
917897
} else {
918898
let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
919-
let (hidden_variants, visible_variants) = def
920-
.variants()
921-
.iter_enumerated()
922-
.filter(|(_, v)| {
923-
// If `exhaustive_patterns` is enabled, we exclude variants known to be
924-
// uninhabited.
925-
!is_exhaustive_pat_feature
926-
|| v.inhabited_predicate(cx.tcx, *def)
927-
.instantiate(cx.tcx, args)
928-
.apply(cx.tcx, cx.param_env, cx.module)
929-
})
930-
.map(|(idx, _)| idx)
931-
.partition(|idx| {
932-
let variant_def_id = def.variant(*idx).def_id;
933-
// Filter variants that depend on a disabled unstable feature.
934-
let is_unstable = matches!(
935-
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
936-
EvalResult::Deny { .. }
937-
);
938-
// Filter foreign `#[doc(hidden)]` variants.
939-
let is_doc_hidden =
940-
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
941-
is_unstable || is_doc_hidden
942-
});
899+
let mut visible_variants = Vec::new();
900+
let mut hidden_variants = Vec::new();
901+
let mut empty_variants = Vec::new();
902+
for (idx, v) in def.variants().iter_enumerated() {
903+
let variant_def_id = def.variant(idx).def_id;
904+
// Visibly uninhabited variants.
905+
let is_inhabited = v
906+
.inhabited_predicate(cx.tcx, *def)
907+
.instantiate(cx.tcx, args)
908+
.apply(cx.tcx, cx.param_env, cx.module);
909+
// Variants that depend on a disabled unstable feature.
910+
let is_unstable = matches!(
911+
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
912+
EvalResult::Deny { .. }
913+
);
914+
// Foreign `#[doc(hidden)]` variants.
915+
let is_doc_hidden =
916+
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
917+
if is_exhaustive_pat_feature && !is_inhabited {
918+
empty_variants.push(idx);
919+
} else if is_unstable || is_doc_hidden {
920+
hidden_variants.push(idx);
921+
} else {
922+
visible_variants.push(idx);
923+
}
924+
}
943925

944926
Self::Variants {
945927
visible_variants,
946928
hidden_variants,
929+
empty_variants,
947930
non_exhaustive: is_declared_nonexhaustive,
948931
}
949932
}
@@ -980,7 +963,12 @@ impl ConstructorSet {
980963
present.push(Single);
981964
}
982965
}
983-
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
966+
ConstructorSet::Variants {
967+
visible_variants,
968+
hidden_variants,
969+
empty_variants,
970+
non_exhaustive,
971+
} => {
984972
let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect();
985973
let mut skipped_a_hidden_variant = false;
986974

@@ -992,7 +980,6 @@ impl ConstructorSet {
992980
missing.push(ctor);
993981
}
994982
}
995-
996983
for variant in hidden_variants {
997984
let ctor = Variant(*variant);
998985
if seen_set.contains(&variant) {
@@ -1001,6 +988,13 @@ impl ConstructorSet {
1001988
skipped_a_hidden_variant = true;
1002989
}
1003990
}
991+
for variant in empty_variants {
992+
let ctor = Variant(*variant);
993+
if seen_set.contains(&variant) {
994+
present.push(ctor);
995+
}
996+
}
997+
1004998
if skipped_a_hidden_variant {
1005999
missing.push(Hidden);
10061000
}

0 commit comments

Comments
 (0)