Skip to content

Commit 833089f

Browse files
committed
Unify the two kinds of specialization by adding a Wildcard ctor
1 parent 41e7ca4 commit 833089f

File tree

1 file changed

+80
-97
lines changed
  • compiler/rustc_mir_build/src/thir/pattern

1 file changed

+80
-97
lines changed

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

Lines changed: 80 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@
137137
//! S(c, (r_1, p_2, .., p_n))
138138
//! S(c, (r_2, p_2, .., p_n))
139139
//!
140-
//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
141-
//! a pattern-stack.
140+
//! 2. We can pop a wildcard off the top of the stack. This is called `S(_, p)`, where `p` is
141+
//! a pattern-stack. Note: the paper calls this `D(p)`.
142142
//! This is used when we know there are missing constructor cases, but there might be
143143
//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
144144
//! all its *other* components.
@@ -150,8 +150,8 @@
150150
//! p_2, .., p_n
151151
//! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
152152
//! stack.
153-
//! D((r_1, p_2, .., p_n))
154-
//! D((r_2, p_2, .., p_n))
153+
//! S(_, (r_1, p_2, .., p_n))
154+
//! S(_, (r_2, p_2, .., p_n))
155155
//!
156156
//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
157157
//! exhaustive integer matching rules, so they're written here for posterity.
@@ -205,7 +205,7 @@
205205
//! That's almost correct, but only works if there were no wildcards in those first
206206
//! components. So we need to check that `p` is useful with respect to the rows that
207207
//! start with a wildcard, if there are any. This is where `D` comes in:
208-
//! `U(P, p) := U(D(P), D(p))`
208+
//! `U(P, p) := U(S(_, P), S(_, p))`
209209
//!
210210
//! For example, if `P` is:
211211
//!
@@ -358,10 +358,6 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
358358
PatStack(vec)
359359
}
360360

361-
fn from_slice(s: &[&'p Pat<'tcx>]) -> Self {
362-
PatStack(SmallVec::from_slice(s))
363-
}
364-
365361
fn is_empty(&self) -> bool {
366362
self.0.is_empty()
367363
}
@@ -374,10 +370,6 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
374370
self.0[0]
375371
}
376372

377-
fn to_tail(&self) -> Self {
378-
PatStack::from_slice(&self.0[1..])
379-
}
380-
381373
fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
382374
self.0.iter().copied()
383375
}
@@ -401,11 +393,6 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
401393
}
402394
}
403395

404-
/// This computes `D(self)`. See top of the file for explanations.
405-
fn specialize_wildcard(&self) -> Option<Self> {
406-
if self.head().is_wildcard() { Some(self.to_tail()) } else { None }
407-
}
408-
409396
/// This computes `S(constructor, self)`. See top of the file for explanations.
410397
///
411398
/// This is the main specialization step. It expands the pattern
@@ -427,15 +414,13 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
427414
is_my_head_ctor: bool,
428415
) -> Option<PatStack<'p, 'tcx>> {
429416
// We return `None` if `ctor` is not covered by `self.head()`. If `ctor` is known to be
430-
// derived from `self.head()`, or if `self.head()` is a wildcard, then we don't need to
431-
// check; otherwise, we compute the constructor of `self.head()` and check for constructor
432-
// inclusion.
417+
// derived from `self.head()`, then we don't need to check; otherwise, we compute the
418+
// constructor of `self.head()` and check for constructor inclusion.
433419
// Note that this shortcut is also necessary for correctness: a pattern should always be
434420
// specializable with its own constructor, even in cases where we refuse to inspect values like
435421
// opaque constants.
436-
if !self.head().is_wildcard() && !is_my_head_ctor {
437-
// `unwrap` is safe because `pat` is not a wildcard.
438-
let head_ctor = pat_constructor(cx.tcx, cx.param_env, self.head()).unwrap();
422+
if !is_my_head_ctor {
423+
let head_ctor = pat_constructor(cx.tcx, cx.param_env, self.head());
439424
if !ctor.is_covered_by(cx, &head_ctor, self.head().ty) {
440425
return None;
441426
}
@@ -480,8 +465,8 @@ enum SpecializationCache {
480465
/// so it is possible to precompute the result of `Matrix::specialize_constructor` at a
481466
/// lower computational complexity.
482467
/// `lookup` is responsible for holding the precomputed result of
483-
/// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is
484-
/// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a
468+
/// specialization, while `wilds` is used for two purposes: the first one is
469+
/// the precomputed result of specialization with a wildcard, and the second is to be used as a
485470
/// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that
486471
/// has not been seen in the `Matrix`. See `update_cache` for further explanations.
487472
Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> },
@@ -553,9 +538,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
553538
v.push(idx);
554539
}
555540
// Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns
556-
// are included in the result of `specialize_wildcard`.
541+
// are included in the result of specialization with a wildcard.
557542
// What we do here is to track the wildcards we have seen; so in addition to
558-
// acting as the precomputed result of `specialize_wildcard`, `wilds` also
543+
// acting as the precomputed result of specialization with a wildcard, `wilds` also
559544
// serves as the default value of `specialize_constructor` for constructors
560545
// that are not in `lookup`.
561546
wilds.push(idx);
@@ -585,30 +570,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
585570
self.patterns.iter().map(|r| r.head())
586571
}
587572

588-
/// This computes `D(self)`. See top of the file for explanations.
589-
fn specialize_wildcard(&self) -> Self {
590-
match &self.cache {
591-
SpecializationCache::Variants { wilds, .. } => {
592-
let result =
593-
wilds.iter().filter_map(|&i| self.patterns[i].specialize_wildcard()).collect();
594-
// When debug assertions are enabled, check the results against the "slow path"
595-
// result.
596-
debug_assert_eq!(
597-
result,
598-
Self {
599-
patterns: self.patterns.clone(),
600-
cache: SpecializationCache::Incompatible
601-
}
602-
.specialize_wildcard()
603-
);
604-
result
605-
}
606-
SpecializationCache::Incompatible => {
607-
self.patterns.iter().filter_map(|r| r.specialize_wildcard()).collect()
608-
}
609-
}
610-
}
611-
612573
/// This computes `S(constructor, self)`. See top of the file for explanations.
613574
fn specialize_constructor(
614575
&self,
@@ -618,24 +579,30 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
618579
) -> Matrix<'p, 'tcx> {
619580
match &self.cache {
620581
SpecializationCache::Variants { lookup, wilds } => {
621-
let result: Self = if let Constructor::Variant(id) = constructor {
582+
let cached = if let Constructor::Variant(id) = constructor {
622583
lookup
623584
.get(id)
624585
// Default to `wilds` for absent keys. See `update_cache` for an explanation.
625586
.unwrap_or(&wilds)
626-
.iter()
627-
.filter_map(|&i| {
628-
self.patterns[i].specialize_constructor(
629-
cx,
630-
constructor,
631-
ctor_wild_subpatterns,
632-
false,
633-
)
634-
})
635-
.collect()
587+
} else if let Wildcard = constructor {
588+
&wilds
636589
} else {
637-
unreachable!()
590+
bug!(
591+
"unexpected constructor encountered while dealing with matrix cache: {:?}",
592+
constructor
593+
);
638594
};
595+
let result: Self = cached
596+
.iter()
597+
.filter_map(|&i| {
598+
self.patterns[i].specialize_constructor(
599+
cx,
600+
constructor,
601+
ctor_wild_subpatterns,
602+
false,
603+
)
604+
})
605+
.collect();
639606
// When debug assertions are enabled, check the results against the "slow path"
640607
// result.
641608
debug_assert_eq!(
@@ -939,8 +906,10 @@ impl Slice {
939906
_ => return smallvec![Slice(self)],
940907
};
941908

942-
let head_ctors =
943-
matrix.heads().filter_map(|pat| pat_constructor(cx.tcx, cx.param_env, pat));
909+
let head_ctors = matrix
910+
.heads()
911+
.map(|p| pat_constructor(cx.tcx, cx.param_env, p))
912+
.filter(|c| !c.is_wildcard());
944913

945914
let mut max_prefix_len = self_prefix;
946915
let mut max_suffix_len = self_suffix;
@@ -1026,9 +995,18 @@ enum Constructor<'tcx> {
1026995
Opaque,
1027996
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
1028997
NonExhaustive,
998+
/// Wildcard pattern.
999+
Wildcard,
10291000
}
10301001

10311002
impl<'tcx> Constructor<'tcx> {
1003+
fn is_wildcard(&self) -> bool {
1004+
match self {
1005+
Wildcard => true,
1006+
_ => false,
1007+
}
1008+
}
1009+
10321010
fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
10331011
match *self {
10341012
Variant(id) => adt.variant_index_with_id(id),
@@ -1120,7 +1098,8 @@ impl<'tcx> Constructor<'tcx> {
11201098
}
11211099
// This constructor is never covered by anything else
11221100
NonExhaustive => vec![NonExhaustive],
1123-
Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self),
1101+
Opaque => bug!("found unexpected opaque ctor in all_ctors"),
1102+
Wildcard => bug!("found unexpected wildcard ctor in all_ctors"),
11241103
}
11251104
}
11261105

@@ -1173,6 +1152,11 @@ impl<'tcx> Constructor<'tcx> {
11731152
ty: Ty<'tcx>,
11741153
) -> bool {
11751154
match (self, other) {
1155+
// Wildcards cover anything
1156+
(_, Wildcard) => true,
1157+
// Wildcards are only covered by wildcards
1158+
(Wildcard, _) => false,
1159+
11761160
(Single, Single) => true,
11771161
(Variant(self_id), Variant(other_id)) => self_id == other_id,
11781162

@@ -1302,7 +1286,8 @@ impl<'tcx> Constructor<'tcx> {
13021286
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
13031287
IntRange(range) => return range.to_pat(cx.tcx),
13041288
NonExhaustive => PatKind::Wild,
1305-
Opaque => bug!("we should not try to apply an opaque constructor {:?}", self),
1289+
Opaque => bug!("we should not try to apply an opaque constructor"),
1290+
Wildcard => bug!("we should not try to apply a wildcard constructor"),
13061291
};
13071292

13081293
Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
@@ -1454,7 +1439,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
14541439
}
14551440
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
14561441
},
1457-
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(),
1442+
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => {
1443+
Fields::empty()
1444+
}
14581445
};
14591446
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
14601447
ret
@@ -2011,19 +1998,7 @@ impl<'tcx> IntRange<'tcx> {
20111998
) -> Option<IntRange<'tcx>> {
20121999
// This MUST be kept in sync with `pat_constructor`.
20132000
match *pat.kind {
2014-
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
2015-
PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
2016-
2017-
PatKind::Binding { .. }
2018-
| PatKind::Wild
2019-
| PatKind::Leaf { .. }
2020-
| PatKind::Deref { .. }
2021-
| PatKind::Variant { .. }
2022-
| PatKind::Array { .. }
2023-
| PatKind::Slice { .. } => None,
2024-
20252001
PatKind::Constant { value } => Self::from_const(tcx, param_env, value, pat.span),
2026-
20272002
PatKind::Range(PatRange { lo, hi, end }) => {
20282003
let ty = lo.ty;
20292004
Self::from_range(
@@ -2035,6 +2010,7 @@ impl<'tcx> IntRange<'tcx> {
20352010
pat.span,
20362011
)
20372012
}
2013+
_ => None,
20382014
}
20392015
}
20402016

@@ -2436,7 +2412,8 @@ crate fn is_useful<'p, 'tcx>(
24362412

24372413
debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head());
24382414

2439-
let ret = if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) {
2415+
let constructor = pat_constructor(cx.tcx, cx.param_env, v.head());
2416+
let ret = if !constructor.is_wildcard() {
24402417
debug!("is_useful - expanding constructor: {:#?}", constructor);
24412418
constructor
24422419
.split(cx, pcx, matrix, Some(hir_id))
@@ -2458,8 +2435,11 @@ crate fn is_useful<'p, 'tcx>(
24582435
} else {
24592436
debug!("is_useful - expanding wildcard");
24602437

2461-
let used_ctors: Vec<Constructor<'_>> =
2462-
matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect();
2438+
let used_ctors: Vec<Constructor<'_>> = matrix
2439+
.heads()
2440+
.map(|p| pat_constructor(cx.tcx, cx.param_env, p))
2441+
.filter(|c| !c.is_wildcard())
2442+
.collect();
24632443
debug!("is_useful_used_ctors = {:#?}", used_ctors);
24642444
// `all_ctors` are all the constructors for the given type, which
24652445
// should all be represented (or caught with the wild pattern `_`).
@@ -2501,8 +2481,11 @@ crate fn is_useful<'p, 'tcx>(
25012481
.find(|result| result.is_useful())
25022482
.unwrap_or(NotUseful)
25032483
} else {
2504-
let matrix = matrix.specialize_wildcard();
2505-
let v = v.to_tail();
2484+
let ctor_wild_subpatterns = Fields::empty();
2485+
let matrix = matrix.specialize_constructor(cx, &constructor, &ctor_wild_subpatterns);
2486+
// Unwrap is ok: v can always be specialized with its own constructor.
2487+
let v =
2488+
v.specialize_constructor(cx, &constructor, &ctor_wild_subpatterns, true).unwrap();
25062489
let usefulness =
25072490
is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
25082491

@@ -2584,26 +2567,26 @@ fn pat_constructor<'tcx>(
25842567
tcx: TyCtxt<'tcx>,
25852568
param_env: ty::ParamEnv<'tcx>,
25862569
pat: &Pat<'tcx>,
2587-
) -> Option<Constructor<'tcx>> {
2570+
) -> Constructor<'tcx> {
25882571
// This MUST be kept in sync with `IntRange::from_pat`.
25892572
match *pat.kind {
25902573
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
2591-
PatKind::Binding { .. } | PatKind::Wild => None,
2592-
PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single),
2574+
PatKind::Binding { .. } | PatKind::Wild => Wildcard,
2575+
PatKind::Leaf { .. } | PatKind::Deref { .. } => Single,
25932576
PatKind::Variant { adt_def, variant_index, .. } => {
2594-
Some(Variant(adt_def.variants[variant_index].def_id))
2577+
Variant(adt_def.variants[variant_index].def_id)
25952578
}
25962579
PatKind::Constant { value } => {
25972580
if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
2598-
Some(IntRange(int_range))
2581+
IntRange(int_range)
25992582
} else {
26002583
match value.ty.kind() {
2601-
ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),
2602-
ty::Ref(_, t, _) if t.is_str() => Some(Str(value)),
2584+
ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
2585+
ty::Ref(_, t, _) if t.is_str() => Str(value),
26032586
// All constants that can be structurally matched have already been expanded
26042587
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
26052588
// opaque.
2606-
_ => Some(Opaque),
2589+
_ => Opaque,
26072590
}
26082591
}
26092592
}
@@ -2617,9 +2600,9 @@ fn pat_constructor<'tcx>(
26172600
&end,
26182601
pat.span,
26192602
) {
2620-
Some(IntRange(int_range))
2603+
IntRange(int_range)
26212604
} else {
2622-
Some(FloatRange(lo, hi, end))
2605+
FloatRange(lo, hi, end)
26232606
}
26242607
}
26252608
PatKind::Array { ref prefix, ref slice, ref suffix }
@@ -2633,7 +2616,7 @@ fn pat_constructor<'tcx>(
26332616
let suffix = suffix.len() as u64;
26342617
let kind =
26352618
if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) };
2636-
Some(Slice(Slice { array_len, kind }))
2619+
Slice(Slice { array_len, kind })
26372620
}
26382621
PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
26392622
}

0 commit comments

Comments
 (0)