Skip to content

Commit b111b2e

Browse files
committed
Split Single ctor into more specific variants
1 parent 4d1bd0d commit b111b2e

File tree

4 files changed

+80
-52
lines changed

4 files changed

+80
-52
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
874874
use Constructor::*;
875875
match pat.ctor() {
876876
Wildcard => true,
877-
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
877+
Struct | Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
878878
_ => false,
879879
}
880880
}

compiler/rustc_pattern_analysis/src/constructor.rs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -631,11 +631,16 @@ impl OpaqueId {
631631
/// `Fields`.
632632
#[derive(Clone, Debug, PartialEq)]
633633
pub enum Constructor<'tcx> {
634-
/// The constructor for patterns that have a single constructor, like tuples, struct patterns,
635-
/// and references. Fixed-length arrays are treated separately with `Slice`.
636-
Single,
634+
/// Tuples and structs.
635+
Struct,
637636
/// Enum variants.
638637
Variant(VariantIdx),
638+
/// References
639+
Ref,
640+
/// Array and slice patterns.
641+
Slice(Slice),
642+
/// Union field accesses.
643+
UnionField,
639644
/// Booleans
640645
Bool(bool),
641646
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
@@ -645,8 +650,6 @@ pub enum Constructor<'tcx> {
645650
F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd),
646651
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
647652
Str(Const<'tcx>),
648-
/// Array and slice patterns.
649-
Slice(Slice),
650653
/// Constants that must not be matched structurally. They are treated as black boxes for the
651654
/// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a
652655
/// match exhaustive.
@@ -723,7 +726,9 @@ impl<'tcx> Constructor<'tcx> {
723726
// Only a wildcard pattern can match these special constructors.
724727
(Missing { .. } | NonExhaustive | Hidden, _) => false,
725728

726-
(Single, Single) => true,
729+
(Struct, Struct) => true,
730+
(Ref, Ref) => true,
731+
(UnionField, UnionField) => true,
727732
(Variant(self_id), Variant(other_id)) => self_id == other_id,
728733
(Bool(self_b), Bool(other_b)) => self_b == other_b,
729734

@@ -786,12 +791,15 @@ pub enum VariantVisibility {
786791
/// `exhaustive_patterns` feature.
787792
#[derive(Debug)]
788793
pub enum ConstructorSet {
789-
/// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the
790-
/// constructor is empty.
791-
Single { empty: bool },
794+
/// The type is a tuple or struct. `empty` tracks whether the type is empty.
795+
Struct { empty: bool },
792796
/// This type has the following list of constructors. If `variants` is empty and
793797
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
794798
Variants { variants: IndexVec<VariantIdx, VariantVisibility>, non_exhaustive: bool },
799+
/// The type is `&T`.
800+
Ref,
801+
/// The type is a union.
802+
Union,
795803
/// Booleans.
796804
Bool,
797805
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
@@ -866,13 +874,27 @@ impl ConstructorSet {
866874
}
867875

868876
match self {
869-
ConstructorSet::Single { empty } => {
877+
ConstructorSet::Struct { empty } => {
870878
if !seen.is_empty() {
871-
present.push(Single);
879+
present.push(Struct);
872880
} else if *empty {
873-
missing_empty.push(Single);
881+
missing_empty.push(Struct);
882+
} else {
883+
missing.push(Struct);
884+
}
885+
}
886+
ConstructorSet::Ref => {
887+
if !seen.is_empty() {
888+
present.push(Ref);
889+
} else {
890+
missing.push(Ref);
891+
}
892+
}
893+
ConstructorSet::Union => {
894+
if !seen.is_empty() {
895+
present.push(UnionField);
874896
} else {
875-
missing.push(Single);
897+
missing.push(UnionField);
876898
}
877899
}
878900
ConstructorSet::Variants { variants, non_exhaustive } => {

compiler/rustc_pattern_analysis/src/cx.rs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
105105
) -> VariantIdx {
106106
match *ctor {
107107
Variant(idx) => idx,
108-
Single => {
108+
Struct | UnionField => {
109109
assert!(!adt.is_enum());
110110
FIRST_VARIANT
111111
}
@@ -123,9 +123,8 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
123123
) -> &'p [DeconstructedPat<'p, 'tcx>] {
124124
let cx = self;
125125
match ctor {
126-
Single | Variant(_) => match ty.kind() {
126+
Struct | Variant(_) | UnionField => match ty.kind() {
127127
ty::Tuple(fs) => cx.alloc_wildcard_slice(fs.iter()),
128-
ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)),
129128
ty::Adt(adt, args) => {
130129
if adt.is_box() {
131130
// The only legal patterns of type `Box` (outside `std`) are `_` and box
@@ -138,7 +137,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
138137
cx.alloc_wildcard_slice(tys)
139138
}
140139
}
141-
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
140+
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
141+
},
142+
Ref => match ty.kind() {
143+
ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)),
144+
_ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
142145
},
143146
Slice(slice) => match *ty.kind() {
144147
ty::Slice(ty) | ty::Array(ty, _) => {
@@ -167,9 +170,8 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
167170
/// `Fields::wildcards`.
168171
pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize {
169172
match ctor {
170-
Single | Variant(_) => match ty.kind() {
173+
Struct | Variant(_) | UnionField => match ty.kind() {
171174
ty::Tuple(fs) => fs.len(),
172-
ty::Ref(..) => 1,
173175
ty::Adt(adt, ..) => {
174176
if adt.is_box() {
175177
// The only legal patterns of type `Box` (outside `std`) are `_` and box
@@ -181,8 +183,9 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
181183
self.list_variant_nonhidden_fields(ty, variant).count()
182184
}
183185
}
184-
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
186+
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
185187
},
188+
Ref => 1,
186189
Slice(slice) => slice.arity(),
187190
Bool(..)
188191
| IntRange(..)
@@ -298,9 +301,9 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
298301
ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
299302
}
300303
}
301-
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => {
302-
ConstructorSet::Single { empty: cx.is_uninhabited(ty) }
303-
}
304+
ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
305+
ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) },
306+
ty::Ref(..) => ConstructorSet::Ref,
304307
ty::Never => ConstructorSet::NoConstructors,
305308
// This type is one for which we cannot list constructors, like `str` or `f64`.
306309
// FIXME(Nadrieril): which of these are actually allowed?
@@ -359,13 +362,18 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
359362
fields = &[];
360363
}
361364
PatKind::Deref { subpattern } => {
362-
ctor = Single;
363365
fields = singleton(self.lower_pat(subpattern));
366+
ctor = match pat.ty.kind() {
367+
// This is a box pattern.
368+
ty::Adt(adt, ..) if adt.is_box() => Struct,
369+
ty::Ref(..) => Ref,
370+
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
371+
};
364372
}
365373
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
366374
match pat.ty.kind() {
367375
ty::Tuple(fs) => {
368-
ctor = Single;
376+
ctor = Struct;
369377
let mut wilds: SmallVec<[_; 2]> =
370378
fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect();
371379
for pat in subpatterns {
@@ -380,7 +388,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
380388
// _)` or a box pattern. As a hack to avoid an ICE with the former, we
381389
// ignore other fields than the first one. This will trigger an error later
382390
// anyway.
383-
// See https://github.com/rust-lang/rust/issues/82772 ,
391+
// See https://github.com/rust-lang/rust/issues/82772,
384392
// explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
385393
// The problem is that we can't know from the type whether we'll match
386394
// normally or through box-patterns. We'll have to figure out a proper
@@ -392,12 +400,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
392400
} else {
393401
DeconstructedPat::wildcard(args.type_at(0), pat.span)
394402
};
395-
ctor = Single;
403+
ctor = Struct;
396404
fields = singleton(pat);
397405
}
398406
ty::Adt(adt, _) => {
399407
ctor = match pat.kind {
400-
PatKind::Leaf { .. } => Single,
408+
PatKind::Leaf { .. } if adt.is_union() => UnionField,
409+
PatKind::Leaf { .. } => Struct,
401410
PatKind::Variant { variant_index, .. } => Variant(variant_index),
402411
_ => bug!(),
403412
};
@@ -477,11 +486,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
477486
// with other `Deref` patterns. This could have been done in `const_to_pat`,
478487
// but that causes issues with the rest of the matching code.
479488
// So here, the constructor for a `"foo"` pattern is `&` (represented by
480-
// `Single`), and has one field. That field has constructor `Str(value)` and no
481-
// fields.
489+
// `Ref`), and has one field. That field has constructor `Str(value)` and no
490+
// subfields.
482491
// Note: `t` is `str`, not `&str`.
483492
let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat.span);
484-
ctor = Single;
493+
ctor = Ref;
485494
fields = singleton(subpattern)
486495
}
487496
// All constants that can be structurally matched have already been expanded
@@ -657,7 +666,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
657666
let kind = match pat.ctor() {
658667
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
659668
IntRange(range) => return self.hoist_pat_range(range, pat.ty()),
660-
Single | Variant(_) => match pat.ty().kind() {
669+
Struct | Variant(_) | UnionField => match pat.ty().kind() {
661670
ty::Tuple(..) => PatKind::Leaf {
662671
subpatterns: subpatterns
663672
.enumerate()
@@ -686,13 +695,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
686695
PatKind::Leaf { subpatterns }
687696
}
688697
}
689-
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
690-
// be careful to reconstruct the correct constant pattern here. However a string
691-
// literal pattern will never be reported as a non-exhaustiveness witness, so we
692-
// ignore this issue.
693-
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
694698
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()),
695699
},
700+
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
701+
// be careful to reconstruct the correct constant pattern here. However a string
702+
// literal pattern will never be reported as a non-exhaustiveness witness, so we
703+
// ignore this issue.
704+
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
696705
Slice(slice) => {
697706
match slice.kind {
698707
SliceKind::FixedLen(_) => PatKind::Slice {
@@ -758,7 +767,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
758767
let mut start_or_comma = || start_or_continue(", ");
759768

760769
match pat.ctor() {
761-
Single | Variant(_) => match pat.ty().kind() {
770+
Struct | Variant(_) | UnionField => match pat.ty().kind() {
762771
ty::Adt(def, _) if def.is_box() => {
763772
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
764773
// of `std`). So this branch is only reachable when the feature is enabled and
@@ -789,15 +798,15 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
789798
}
790799
write!(f, ")")
791800
}
792-
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
793-
// be careful to detect strings here. However a string literal pattern will never
794-
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
795-
ty::Ref(_, _, mutbl) => {
796-
let subpattern = pat.iter_fields().next().unwrap();
797-
write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern)
798-
}
799801
_ => write!(f, "_"),
800802
},
803+
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
804+
// be careful to detect strings here. However a string literal pattern will never
805+
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
806+
Ref => {
807+
let subpattern = pat.iter_fields().next().unwrap();
808+
write!(f, "&{:?}", subpattern)
809+
}
801810
Slice(slice) => {
802811
let mut subpatterns = pat.iter_fields();
803812
write!(f, "[")?;

compiler/rustc_pattern_analysis/src/usefulness.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -629,12 +629,9 @@ impl ValidityConstraint {
629629
///
630630
/// Pending further opsem decisions, the current behavior is: validity is preserved, except
631631
/// inside `&` and union fields where validity is reset to `MaybeInvalid`.
632-
fn specialize<'tcx>(self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
632+
fn specialize(self, ctor: &Constructor<'_>) -> Self {
633633
// We preserve validity except when we go inside a reference or a union field.
634-
if matches!(ctor, Constructor::Single)
635-
&& (matches!(pcx.ty.kind(), ty::Ref(..))
636-
|| matches!(pcx.ty.kind(), ty::Adt(def, ..) if def.is_union()))
637-
{
634+
if matches!(ctor, Constructor::Ref | Constructor::UnionField) {
638635
// Validity of `x: &T` does not imply validity of `*x: T`.
639636
MaybeInvalid
640637
} else {
@@ -902,7 +899,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
902899
ctor: &Constructor<'tcx>,
903900
) -> Matrix<'p, 'tcx> {
904901
let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
905-
let new_validity = self.place_validity[0].specialize(pcx, ctor);
902+
let new_validity = self.place_validity[0].specialize(ctor);
906903
let new_place_validity = std::iter::repeat(new_validity)
907904
.take(ctor.arity(pcx))
908905
.chain(self.place_validity[1..].iter().copied())

0 commit comments

Comments
 (0)