Skip to content

Commit c417e27

Browse files
committed
Fix a ConstructorSet invariant
1 parent 39ed139 commit c417e27

File tree

1 file changed

+52
-57
lines changed

1 file changed

+52
-57
lines changed

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

Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ pub(super) enum ConstructorSet {
857857
Variants {
858858
visible_variants: Vec<VariantIdx>,
859859
hidden_variants: Vec<VariantIdx>,
860+
empty_variants: Vec<VariantIdx>,
860861
non_exhaustive: bool,
861862
},
862863
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
@@ -881,16 +882,18 @@ pub(super) enum ConstructorSet {
881882
/// `present` is morally the set of constructors present in the column, and `missing` is the set of
882883
/// constructors that exist in the type but are not present in the column.
883884
///
884-
/// More formally, they respect the following constraints:
885+
/// More formally, if we discard wildcards from the column, they respect the following constraints:
885886
/// - the union of `present` and `missing` covers the whole type
886-
/// - `present` and `missing` are disjoint
887-
/// - neither contains wildcards
888-
/// - each constructor in `present` is covered by some non-wildcard constructor in the column
889-
/// - together, the constructors in `present` cover all the non-wildcard constructor in the column
890-
/// - non-wildcards in the column do no cover anything in `missing`
887+
/// - each constructor in `present` is covered by something in the column
888+
/// - no constructor in `missing` is covered by anything in the column
889+
/// - each constructor in the column is equal to the union of one or more constructors in `present`
891890
/// - constructors in `present` and `missing` are split for the column; in other words, they are
892-
/// either fully included in or disjoint from each constructor in the column. This avoids
893-
/// non-trivial intersections like between `0..10` and `5..15`.
891+
/// either fully included in or disjoint from each constructor in the column. In other words,
892+
/// there are no non-trivial intersections like between `0..10` and `5..15`.
893+
///
894+
/// When the `exhaustive_patterns` feature is enabled, all ctors in `missing` must match at least
895+
/// one value of the corresponding type. E.g. if the type is `Option<!>`, `missing` will never
896+
/// contain `Some`. This restriction does not apply to `present`.
894897
#[derive(Debug)]
895898
pub(super) struct SplitConstructorSet<'tcx> {
896899
pub(super) present: SmallVec<[Constructor<'tcx>; 1]>,
@@ -904,12 +907,6 @@ impl ConstructorSet {
904907
|start, end| IntRange::from_range(cx.tcx, start, end, ty, RangeEnd::Included);
905908
// This determines the set of all possible constructors for the type `ty`. For numbers,
906909
// arrays and slices we use ranges and variable-length slices when appropriate.
907-
//
908-
// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
909-
// are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
910-
// returned list of constructors.
911-
// Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
912-
// `cx.is_uninhabited()`).
913910
match ty.kind() {
914911
ty::Bool => {
915912
Self::Integers { range_1: make_range(0, 1), range_2: None, non_exhaustive: false }
@@ -958,56 +955,42 @@ impl ConstructorSet {
958955
}
959956
}
960957
ty::Adt(def, args) if def.is_enum() => {
961-
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
962-
// additional "unknown" constructor.
963-
// There is no point in enumerating all possible variants, because the user can't
964-
// actually match against them all themselves. So we always return only the fictitious
965-
// constructor.
966-
// E.g., in an example like:
967-
//
968-
// ```
969-
// let err: io::ErrorKind = ...;
970-
// match err {
971-
// io::ErrorKind::NotFound => {},
972-
// }
973-
// ```
974-
//
975-
// we don't want to show every possible IO error, but instead have only `_` as the
976-
// witness.
977958
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
978-
979959
if def.variants().is_empty() && !is_declared_nonexhaustive {
980960
Self::Uninhabited
981961
} else {
982962
let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
983-
let (hidden_variants, visible_variants) = def
984-
.variants()
985-
.iter_enumerated()
986-
.filter(|(_, v)| {
987-
// If `exhaustive_patterns` is enabled, we exclude variants known to be
988-
// uninhabited.
989-
!is_exhaustive_pat_feature
990-
|| v.inhabited_predicate(cx.tcx, *def)
991-
.instantiate(cx.tcx, args)
992-
.apply(cx.tcx, cx.param_env, cx.module)
993-
})
994-
.map(|(idx, _)| idx)
995-
.partition(|idx| {
996-
let variant_def_id = def.variant(*idx).def_id;
997-
// Filter variants that depend on a disabled unstable feature.
998-
let is_unstable = matches!(
999-
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
1000-
EvalResult::Deny { .. }
1001-
);
1002-
// Filter foreign `#[doc(hidden)]` variants.
1003-
let is_doc_hidden =
1004-
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
1005-
is_unstable || is_doc_hidden
1006-
});
963+
let mut visible_variants = Vec::new();
964+
let mut hidden_variants = Vec::new();
965+
let mut empty_variants = Vec::new();
966+
for (idx, v) in def.variants().iter_enumerated() {
967+
let variant_def_id = def.variant(idx).def_id;
968+
// Visibly uninhabited variants.
969+
let is_inhabited = v
970+
.inhabited_predicate(cx.tcx, *def)
971+
.instantiate(cx.tcx, args)
972+
.apply(cx.tcx, cx.param_env, cx.module);
973+
// Variants that depend on a disabled unstable feature.
974+
let is_unstable = matches!(
975+
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
976+
EvalResult::Deny { .. }
977+
);
978+
// Foreign `#[doc(hidden)]` variants.
979+
let is_doc_hidden =
980+
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
981+
if is_exhaustive_pat_feature && !is_inhabited {
982+
empty_variants.push(idx);
983+
} else if is_unstable || is_doc_hidden {
984+
hidden_variants.push(idx);
985+
} else {
986+
visible_variants.push(idx);
987+
}
988+
}
1007989

1008990
Self::Variants {
1009991
visible_variants,
1010992
hidden_variants,
993+
empty_variants,
1011994
non_exhaustive: is_declared_nonexhaustive,
1012995
}
1013996
}
@@ -1044,7 +1027,12 @@ impl ConstructorSet {
10441027
present.push(Single);
10451028
}
10461029
}
1047-
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
1030+
ConstructorSet::Variants {
1031+
visible_variants,
1032+
hidden_variants,
1033+
empty_variants,
1034+
non_exhaustive,
1035+
} => {
10481036
let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect();
10491037
let mut skipped_a_hidden_variant = false;
10501038

@@ -1053,10 +1041,10 @@ impl ConstructorSet {
10531041
if seen_set.contains(&variant) {
10541042
present.push(ctor);
10551043
} else {
1044+
// We only put visible variants directly into `missing`.
10561045
missing.push(ctor);
10571046
}
10581047
}
1059-
10601048
for variant in hidden_variants {
10611049
let ctor = Variant(*variant);
10621050
if seen_set.contains(&variant) {
@@ -1065,6 +1053,13 @@ impl ConstructorSet {
10651053
skipped_a_hidden_variant = true;
10661054
}
10671055
}
1056+
for variant in empty_variants {
1057+
let ctor = Variant(*variant);
1058+
if seen_set.contains(&variant) {
1059+
present.push(ctor);
1060+
}
1061+
}
1062+
10681063
if skipped_a_hidden_variant {
10691064
missing.push(Hidden);
10701065
}

0 commit comments

Comments
 (0)