Skip to content

Commit 2f4cab4

Browse files
committed
Clarify handling of hidden variants
1 parent c1800ef commit 2f4cab4

File tree

2 files changed

+76
-74
lines changed

2 files changed

+76
-74
lines changed

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

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -609,19 +609,23 @@ pub(super) enum Constructor<'tcx> {
609609
/// boxes for the purposes of exhaustiveness: we must not inspect them, and they
610610
/// don't count towards making a match exhaustive.
611611
Opaque,
612+
/// Or-pattern.
613+
Or,
614+
/// Wildcard pattern.
615+
Wildcard,
612616
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
613617
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
614618
NonExhaustive,
615-
/// Stands for constructors that are not seen in the matrix, as explained in the code for
616-
/// [`Constructor::split`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns`
617-
/// lint.
619+
/// Fake extra constructor for variants that should not be mentioned in diagnostics.
620+
/// We use this for variants behind an unstable gate as well as
621+
/// `#[doc(hidden)]` ones.
622+
Hidden,
623+
/// Fake extra constructor for constructors that are not seen in the matrix, as explained in the
624+
/// code for [`Constructor::split`]. The carried `bool` is used for the
625+
/// `non_exhaustive_omitted_patterns` lint.
618626
Missing {
619-
nonexhaustive_enum_missing_real_variants: bool,
627+
nonexhaustive_enum_missing_visible_variants: bool,
620628
},
621-
/// Wildcard pattern.
622-
Wildcard,
623-
/// Or-pattern.
624-
Or,
625629
}
626630

627631
impl<'tcx> Constructor<'tcx> {
@@ -652,32 +656,6 @@ impl<'tcx> Constructor<'tcx> {
652656
}
653657
}
654658

655-
/// Checks if the `Constructor` is a variant and `TyCtxt::eval_stability` returns
656-
/// `EvalResult::Deny { .. }`.
657-
///
658-
/// This means that the variant has a stdlib unstable feature marking it.
659-
pub(super) fn is_unstable_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
660-
if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
661-
let variant_def_id = adt.variant(*idx).def_id;
662-
// Filter variants that depend on a disabled unstable feature.
663-
return matches!(
664-
pcx.cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
665-
EvalResult::Deny { .. }
666-
);
667-
}
668-
false
669-
}
670-
671-
/// Checks if the `Constructor` is a `Constructor::Variant` with a `#[doc(hidden)]`
672-
/// attribute from a type not local to the current crate.
673-
pub(super) fn is_doc_hidden_variant(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> bool {
674-
if let Constructor::Variant(idx) = self && let ty::Adt(adt, _) = pcx.ty.kind() {
675-
let variant_def_id = adt.variants()[*idx].def_id;
676-
return pcx.cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
677-
}
678-
false
679-
}
680-
681659
fn variant_index_for_adt(&self, adt: ty::AdtDef<'tcx>) -> VariantIdx {
682660
match *self {
683661
Variant(idx) => idx,
@@ -713,8 +691,9 @@ impl<'tcx> Constructor<'tcx> {
713691
| F32Range(..)
714692
| F64Range(..)
715693
| IntRange(..)
716-
| NonExhaustive
717694
| Opaque
695+
| NonExhaustive
696+
| Hidden
718697
| Missing { .. }
719698
| Wildcard => 0,
720699
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
@@ -795,8 +774,8 @@ impl<'tcx> Constructor<'tcx> {
795774
Wildcard
796775
} else {
797776
Missing {
798-
nonexhaustive_enum_missing_real_variants: split_set
799-
.nonexhaustive_enum_missing_real_variants,
777+
nonexhaustive_enum_missing_visible_variants: split_set
778+
.nonexhaustive_enum_missing_visible_variants,
800779
}
801780
};
802781
smallvec![ctor]
@@ -828,8 +807,8 @@ impl<'tcx> Constructor<'tcx> {
828807
match (self, other) {
829808
// Wildcards cover anything
830809
(_, Wildcard) => true,
831-
// The missing ctors are not covered by anything in the matrix except wildcards.
832-
(Missing { .. } | Wildcard, _) => false,
810+
// Only a wildcard pattern can match these special constructors.
811+
(Wildcard | Missing { .. } | NonExhaustive | Hidden, _) => false,
833812

834813
(Single, Single) => true,
835814
(Variant(self_id), Variant(other_id)) => self_id == other_id,
@@ -860,8 +839,6 @@ impl<'tcx> Constructor<'tcx> {
860839

861840
// We are trying to inspect an opaque constant. Thus we skip the row.
862841
(Opaque, _) | (_, Opaque) => false,
863-
// Only a wildcard pattern can match the special extra constructor.
864-
(NonExhaustive, _) => false,
865842

866843
_ => span_bug!(
867844
pcx.span,
@@ -878,7 +855,14 @@ pub(super) enum ConstructorSet {
878855
/// The type has a single constructor, e.g. `&T` or a struct.
879856
Single,
880857
/// This type has the following list of constructors.
881-
Variants { variants: Vec<VariantIdx>, non_exhaustive: bool },
858+
/// Some variants are hidden, which means they won't be mentioned in diagnostics unless the user
859+
/// mentioned them first. We use this for variants behind an unstable gate as well as
860+
/// `#[doc(hidden)]` ones.
861+
Variants {
862+
visible_variants: Vec<VariantIdx>,
863+
hidden_variants: Vec<VariantIdx>,
864+
non_exhaustive: bool,
865+
},
882866
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
883867
/// The second range is only useful for `char`.
884868
/// This is reused for bool. FIXME: don't.
@@ -915,7 +899,7 @@ struct SplitConstructorSet<'tcx> {
915899
present: SmallVec<[Constructor<'tcx>; 1]>,
916900
missing: Vec<Constructor<'tcx>>,
917901
/// For the `non_exhaustive_omitted_patterns` lint.
918-
nonexhaustive_enum_missing_real_variants: bool,
902+
nonexhaustive_enum_missing_visible_variants: bool,
919903
}
920904

921905
impl ConstructorSet {
@@ -1001,7 +985,7 @@ impl ConstructorSet {
1001985
Self::Uninhabited
1002986
} else {
1003987
let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
1004-
let variants: Vec<_> = def
988+
let (hidden_variants, visible_variants) = def
1005989
.variants()
1006990
.iter_enumerated()
1007991
.filter(|(_, v)| {
@@ -1013,9 +997,24 @@ impl ConstructorSet {
1013997
.apply(cx.tcx, cx.param_env, cx.module)
1014998
})
1015999
.map(|(idx, _)| idx)
1016-
.collect();
1017-
1018-
Self::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
1000+
.partition(|idx| {
1001+
let variant_def_id = def.variant(*idx).def_id;
1002+
// Filter variants that depend on a disabled unstable feature.
1003+
let is_unstable = matches!(
1004+
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
1005+
EvalResult::Deny { .. }
1006+
);
1007+
// Filter foreign `#[doc(hidden)]` variants.
1008+
let is_doc_hidden =
1009+
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
1010+
is_unstable || is_doc_hidden
1011+
});
1012+
1013+
Self::Variants {
1014+
visible_variants,
1015+
hidden_variants,
1016+
non_exhaustive: is_declared_nonexhaustive,
1017+
}
10191018
}
10201019
}
10211020
ty::Never => Self::Uninhabited,
@@ -1050,7 +1049,7 @@ impl ConstructorSet {
10501049
}
10511050
}
10521051
}
1053-
let mut nonexhaustive_enum_missing_real_variants = false;
1052+
let mut nonexhaustive_enum_missing_visible_variants = false;
10541053
match self {
10551054
ConstructorSet::Single => {
10561055
if seen.is_empty() {
@@ -1059,29 +1058,34 @@ impl ConstructorSet {
10591058
present.push(Single);
10601059
}
10611060
}
1062-
ConstructorSet::Variants { variants, non_exhaustive } => {
1061+
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
10631062
let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
10641063
let mut skipped_a_hidden_variant = false;
1065-
for variant in variants {
1064+
for variant in visible_variants {
10661065
let ctor = Variant(*variant);
10671066
if seen_set.contains(&variant) {
10681067
present.push(ctor);
1069-
} else if ctor.is_doc_hidden_variant(pcx) || ctor.is_unstable_variant(pcx) {
1070-
// We don't want to mention any variants that are `doc(hidden)` or behind an
1071-
// unstable feature gate if they aren't present in the match.
1072-
skipped_a_hidden_variant = true;
10731068
} else {
10741069
missing.push(ctor);
10751070
}
10761071
}
1072+
nonexhaustive_enum_missing_visible_variants =
1073+
*non_exhaustive && !missing.is_empty();
1074+
1075+
for variant in hidden_variants {
1076+
let ctor = Variant(*variant);
1077+
if seen_set.contains(&variant) {
1078+
present.push(ctor);
1079+
} else {
1080+
skipped_a_hidden_variant = true;
1081+
}
1082+
}
1083+
if skipped_a_hidden_variant {
1084+
missing.push(Hidden);
1085+
}
10771086

10781087
if *non_exhaustive {
1079-
nonexhaustive_enum_missing_real_variants = !missing.is_empty();
10801088
missing.push(NonExhaustive);
1081-
} else if skipped_a_hidden_variant {
1082-
// FIXME(Nadrieril): This represents the skipped variants, but isn't super
1083-
// clean. Using `NonExhaustive` breaks things elsewhere.
1084-
missing.push(Wildcard);
10851089
}
10861090
}
10871091
ConstructorSet::Integers { range_1, range_2, non_exhaustive } => {
@@ -1142,7 +1146,7 @@ impl ConstructorSet {
11421146
ConstructorSet::Uninhabited => {}
11431147
}
11441148

1145-
SplitConstructorSet { present, missing, nonexhaustive_enum_missing_real_variants }
1149+
SplitConstructorSet { present, missing, nonexhaustive_enum_missing_visible_variants }
11461150
}
11471151

11481152
/// Compute the set of constructors missing from this column.
@@ -1272,8 +1276,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12721276
| F32Range(..)
12731277
| F64Range(..)
12741278
| IntRange(..)
1275-
| NonExhaustive
12761279
| Opaque
1280+
| NonExhaustive
1281+
| Hidden
12771282
| Missing { .. }
12781283
| Wildcard => Fields::empty(),
12791284
Or => {
@@ -1587,7 +1592,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
15871592
}
15881593
&Str(value) => PatKind::Constant { value },
15891594
IntRange(range) => return range.to_pat(cx.tcx, self.ty),
1590-
Wildcard | NonExhaustive => PatKind::Wild,
1595+
Wildcard | NonExhaustive | Hidden => PatKind::Wild,
15911596
Missing { .. } => bug!(
15921597
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
15931598
`Missing` should have been processed in `apply_constructors`"
@@ -1770,15 +1775,15 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
17701775
F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
17711776
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
17721777
IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0`
1773-
Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
1778+
Str(value) => write!(f, "{value}"),
1779+
Opaque => write!(f, "<constant pattern>"),
17741780
Or => {
17751781
for pat in self.iter_fields() {
17761782
write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
17771783
}
17781784
Ok(())
17791785
}
1780-
Str(value) => write!(f, "{value}"),
1781-
Opaque => write!(f, "<constant pattern>"),
1786+
Wildcard | Missing { .. } | NonExhaustive | Hidden => write!(f, "_ : {:?}", self.ty),
17821787
}
17831788
}
17841789
}

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -872,22 +872,19 @@ fn is_useful<'p, 'tcx>(
872872
&& usefulness.is_useful() && matches!(witness_preference, RealArm)
873873
&& matches!(
874874
&ctor,
875-
Constructor::Missing { nonexhaustive_enum_missing_real_variants: true }
875+
Constructor::Missing { nonexhaustive_enum_missing_visible_variants: true }
876876
)
877877
{
878878
let missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
879879
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
880-
// Construct for each missing constructor a "wild" version of this
881-
// constructor, that matches everything that can be built with
882-
// it. For example, if `ctor` is a `Constructor::Variant` for
883-
// `Option::Some`, we get the pattern `Some(_)`.
880+
// Construct for each missing constructor a "wild" version of this constructor, that
881+
// matches everything that can be built with it. For example, if `ctor` is a
882+
// `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`.
884883
let patterns = missing
885884
.into_iter()
886-
// Filter out the `NonExhaustive` because we want to list only real
887-
// variants. Also remove any unstable feature gated variants.
888-
// Because of how we computed `nonexhaustive_enum_missing_real_variants`,
885+
// Because of how we computed `nonexhaustive_enum_missing_visible_variants`,
889886
// this will not return an empty `Vec`.
890-
.filter(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx)))
887+
.filter(|c| !(matches!(c, Constructor::NonExhaustive | Constructor::Hidden)))
891888
.map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
892889
.collect::<Vec<_>>();
893890

0 commit comments

Comments
 (0)