Skip to content

Commit c511955

Browse files
committed
Factor out the two specialization steps
1 parent 6ad9f44 commit c511955

File tree

1 file changed

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

1 file changed

+107
-80
lines changed

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

Lines changed: 107 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,66 @@ impl<'tcx> Constructor<'tcx> {
11321132
}
11331133
}
11341134

1135+
/// Returns whether `self` is covered by `other`, ie whether `self` is a subset of `other`. For
1136+
/// the simple cases, this is simply checking for equality. For the "grouped" constructors,
1137+
/// this checks for inclusion.
1138+
fn is_covered_by<'p>(
1139+
&self,
1140+
cx: &MatchCheckCtxt<'p, 'tcx>,
1141+
other: &Constructor<'tcx>,
1142+
ty: Ty<'tcx>,
1143+
) -> bool {
1144+
match (self, other) {
1145+
(Single, Single) => true,
1146+
(Variant(self_id), Variant(other_id)) => self_id == other_id,
1147+
1148+
(IntRange(self_range), IntRange(other_range)) => {
1149+
if self_range.intersection(cx.tcx, other_range).is_some() {
1150+
// Constructor splitting should ensure that all intersections we encounter
1151+
// are actually inclusions.
1152+
assert!(self_range.is_subrange(other_range));
1153+
true
1154+
} else {
1155+
false
1156+
}
1157+
}
1158+
(
1159+
FloatRange(self_from, self_to, self_end),
1160+
FloatRange(other_from, other_to, other_end),
1161+
) => {
1162+
match (
1163+
compare_const_vals(cx.tcx, self_to, other_to, cx.param_env, ty),
1164+
compare_const_vals(cx.tcx, self_from, other_from, cx.param_env, ty),
1165+
) {
1166+
(Some(to), Some(from)) => {
1167+
(from == Ordering::Greater || from == Ordering::Equal)
1168+
&& (to == Ordering::Less
1169+
|| (other_end == self_end && to == Ordering::Equal))
1170+
}
1171+
_ => false,
1172+
}
1173+
}
1174+
(Str(self_val), Str(other_val)) => {
1175+
// FIXME: there's probably a more direct way of comparing for equality
1176+
match compare_const_vals(cx.tcx, self_val, other_val, cx.param_env, ty) {
1177+
Some(comparison) => comparison == Ordering::Equal,
1178+
None => false,
1179+
}
1180+
}
1181+
1182+
(Slice(self_slice), Slice(other_slice)) => {
1183+
other_slice.pattern_kind().covers_length(self_slice.arity())
1184+
}
1185+
1186+
// We are trying to inspect an opaque constant. Thus we skip the row.
1187+
(Opaque, _) | (_, Opaque) => false,
1188+
// Only a wildcard pattern can match the special extra constructor.
1189+
(NonExhaustive, _) => false,
1190+
1191+
_ => bug!("trying to compare incompatible constructors {:?} and {:?}", self, other),
1192+
}
1193+
}
1194+
11351195
/// Apply a constructor to a list of patterns, yielding a new pattern. `pats`
11361196
/// must have as many elements as this constructor's arity.
11371197
///
@@ -1461,6 +1521,41 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
14611521
}
14621522
}
14631523

1524+
/// Replaces contained fields with the arguments of the given pattern. Only use on a pattern
1525+
/// that is compatible with the constructor used to build `self`.
1526+
/// This is meant to be used on the result of `Fields::wildcards()`. The idea is that
1527+
/// `wildcards` constructs a list of fields where all entries are wildcards, and the pattern
1528+
/// provided to this function fills some of the fields with non-wildcards.
1529+
/// In the following example `Fields::wildcards` would return `[_, _, _, _]`. If we call
1530+
/// `replace_with_pattern_arguments` on it with the pattern, the result will be `[Some(0), _,
1531+
/// _, _]`.
1532+
/// ```rust
1533+
/// let x: [Option<u8>; 4] = foo();
1534+
/// match x {
1535+
/// [Some(0), ..] => {}
1536+
/// }
1537+
/// ```
1538+
fn replace_with_pattern_arguments(&self, pat: &'p Pat<'tcx>) -> Self {
1539+
match pat.kind.as_ref() {
1540+
PatKind::Deref { subpattern } => Self::from_single_pattern(subpattern),
1541+
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
1542+
self.replace_with_fieldpats(subpatterns)
1543+
}
1544+
PatKind::Array { prefix, suffix, .. } | PatKind::Slice { prefix, suffix, .. } => {
1545+
// Number of subpatterns for the constructor
1546+
let ctor_arity = self.len();
1547+
1548+
// Replace the prefix and the suffix with the given patterns, leaving wildcards in
1549+
// the middle if there was a subslice pattern `..`.
1550+
let prefix = prefix.iter().enumerate();
1551+
let suffix =
1552+
suffix.iter().enumerate().map(|(i, p)| (ctor_arity - suffix.len() + i, p));
1553+
self.replace_fields_indexed(prefix.chain(suffix))
1554+
}
1555+
_ => self.clone(),
1556+
}
1557+
}
1558+
14641559
fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> {
14651560
let pats: SmallVec<_> = match self {
14661561
Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(),
@@ -2535,89 +2630,21 @@ fn specialize_one_pattern<'p, 'tcx>(
25352630
return Some(ctor_wild_subpatterns.clone());
25362631
}
25372632

2538-
let ty = pat.ty;
2539-
// `unwrap` is safe because `pat` is not a wildcard.
2540-
let pat_ctor = pat_constructor(cx.tcx, cx.param_env, pat).unwrap();
2541-
2542-
let ctor_covered_by_pat = match (ctor, &pat_ctor) {
2543-
(Single, Single) => true,
2544-
(Variant(ctor_id), Variant(pat_id)) => ctor_id == pat_id,
2545-
2546-
(IntRange(ctor_range), IntRange(pat_range)) => {
2547-
if ctor_range.intersection(cx.tcx, pat_range).is_some() {
2548-
// Constructor splitting should ensure that all intersections we encounter
2549-
// are actually inclusions.
2550-
assert!(ctor_range.is_subrange(pat_range));
2551-
true
2552-
} else {
2553-
false
2554-
}
2633+
// We return `None` if `ctor` is not covered by `pat`. If `ctor` is known to be derived from
2634+
// `pat` then we don't need to check; otherwise, we compute the constructor of `pat` and check
2635+
// for constructor inclusion.
2636+
// Note that this shortcut is also necessary for correctness: a pattern should always be
2637+
// specializable with its own constructor, even in cases where we refuse to inspect values like
2638+
// opaque constants.
2639+
if !is_its_own_ctor {
2640+
// `unwrap` is safe because `pat` is not a wildcard.
2641+
let pat_ctor = pat_constructor(cx.tcx, cx.param_env, pat).unwrap();
2642+
if !ctor.is_covered_by(cx, &pat_ctor, pat.ty) {
2643+
return None;
25552644
}
2556-
(FloatRange(ctor_from, ctor_to, ctor_end), FloatRange(pat_from, pat_to, pat_end)) => {
2557-
let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?;
2558-
let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?;
2559-
(from == Ordering::Greater || from == Ordering::Equal)
2560-
&& (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal))
2561-
}
2562-
(Str(ctor_val), Str(pat_val)) => {
2563-
// FIXME: there's probably a more direct way of comparing for equality
2564-
let comparison = compare_const_vals(cx.tcx, ctor_val, pat_val, cx.param_env, ty)?;
2565-
comparison == Ordering::Equal
2566-
}
2567-
2568-
(Slice(ctor_slice), Slice(pat_slice)) => {
2569-
pat_slice.pattern_kind().covers_length(ctor_slice.arity())
2570-
}
2571-
2572-
// Only a wildcard pattern can match an opaque constant, unless we're specializing the
2573-
// value against its own constructor. That happens when we call
2574-
// `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`.
2575-
// For example, in the following match, when we are dealing with the third branch, we will
2576-
// specialize with an `Opaque` ctor. We want to ignore the second branch because opaque
2577-
// constants should not be inspected, but we don't want to ignore the current (third)
2578-
// branch, as that would cause us to always conclude that such a branch is unreachable.
2579-
// ```rust
2580-
// #[derive(PartialEq)]
2581-
// struct Foo(i32);
2582-
// impl Eq for Foo {}
2583-
// const FOO: Foo = Foo(42);
2584-
//
2585-
// match (Foo(0), true) {
2586-
// (_, true) => {}
2587-
// (FOO, true) => {}
2588-
// (FOO, false) => {}
2589-
// }
2590-
// ```
2591-
(Opaque, Opaque) if is_its_own_ctor => true,
2592-
// We are trying to inspect an opaque constant. Thus we skip the row.
2593-
(Opaque, _) | (_, Opaque) => false,
2594-
// Only a wildcard pattern can match the special extra constructor.
2595-
(NonExhaustive, _) => false,
2596-
2597-
_ => bug!("trying to specialize pattern {:?} with constructor {:?}", pat, ctor),
2598-
};
2599-
2600-
if !ctor_covered_by_pat {
2601-
return None;
26022645
}
26032646

2604-
let fields = match pat.kind.as_ref() {
2605-
PatKind::Deref { subpattern } => Fields::from_single_pattern(subpattern),
2606-
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
2607-
ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)
2608-
}
2609-
PatKind::Array { prefix, suffix, .. } | PatKind::Slice { prefix, suffix, .. } => {
2610-
// Number of subpatterns for the constructor
2611-
let ctor_arity = ctor_wild_subpatterns.len();
2612-
2613-
// Replace the prefix and the suffix with the given patterns, leaving wildcards in
2614-
// the middle if there was a subslice pattern `..`.
2615-
let prefix = prefix.iter().enumerate();
2616-
let suffix = suffix.iter().enumerate().map(|(i, p)| (ctor_arity - suffix.len() + i, p));
2617-
ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))
2618-
}
2619-
_ => ctor_wild_subpatterns.clone(),
2620-
};
2647+
let fields = ctor_wild_subpatterns.replace_with_pattern_arguments(pat);
26212648

26222649
debug!("specialize({:#?}, {:#?}, {:#?}) = {:#?}", pat, ctor, ctor_wild_subpatterns, fields);
26232650

0 commit comments

Comments
 (0)