1
Fork 0

Respect split invariants for Opaques

This commit is contained in:
Nadrieril 2023-10-31 00:40:41 +01:00
parent 25696cc0e9
commit 6ee51426a9

View file

@ -41,6 +41,13 @@
//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting //! or-patterns; instead we just try the alternatives one-by-one. For details on splitting
//! wildcards, see [`Constructor::split`]; for integer ranges, see //! wildcards, see [`Constructor::split`]; for integer ranges, see
//! [`IntRange::split`]; for slices, see [`Slice::split`]. //! [`IntRange::split`]; for slices, see [`Slice::split`].
//!
//! ## Opaque patterns
//!
//! Some patterns, such as TODO, cannot be inspected, which we handle with `Constructor::Opaque`.
//! Since we know nothing of these patterns, we assume they never cover each other. In order to
//! respect the invariants of [`SplitConstructorSet`], we give each `Opaque` constructor a unique id
//! so we can recognize it.
use std::cell::Cell; use std::cell::Cell;
use std::cmp::{self, max, min, Ordering}; use std::cmp::{self, max, min, Ordering};
@ -617,6 +624,18 @@ impl Slice {
} }
} }
/// A globally unique id to distinguish `Opaque` patterns.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct OpaqueId(u32);
impl OpaqueId {
fn new() -> Self {
use std::sync::atomic::{AtomicU32, Ordering};
static OPAQUE_ID: AtomicU32 = AtomicU32::new(0);
OpaqueId(OPAQUE_ID.fetch_add(1, Ordering::SeqCst))
}
}
/// A value can be decomposed into a constructor applied to some fields. This struct represents /// A value can be decomposed into a constructor applied to some fields. This struct represents
/// the constructor. See also `Fields`. /// the constructor. See also `Fields`.
/// ///
@ -642,10 +661,12 @@ pub(super) enum Constructor<'tcx> {
Str(mir::Const<'tcx>), Str(mir::Const<'tcx>),
/// Array and slice patterns. /// Array and slice patterns.
Slice(Slice), Slice(Slice),
/// Constants that must not be matched structurally. They are treated as black /// Constants that must not be matched structurally. They are treated as black boxes for the
/// boxes for the purposes of exhaustiveness: we must not inspect them, and they /// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a
/// don't count towards making a match exhaustive. /// match exhaustive.
Opaque, /// Carries an id that must be unique within a match. We need this to ensure the invariants of
/// [`SplitConstructorSet`].
Opaque(OpaqueId),
/// Or-pattern. /// Or-pattern.
Or, Or,
/// Wildcard pattern. /// Wildcard pattern.
@ -663,6 +684,9 @@ pub(super) enum Constructor<'tcx> {
} }
impl<'tcx> Constructor<'tcx> { impl<'tcx> Constructor<'tcx> {
pub(super) fn is_wildcard(&self) -> bool {
matches!(self, Wildcard)
}
pub(super) fn is_non_exhaustive(&self) -> bool { pub(super) fn is_non_exhaustive(&self) -> bool {
matches!(self, NonExhaustive) matches!(self, NonExhaustive)
} }
@ -728,7 +752,7 @@ impl<'tcx> Constructor<'tcx> {
| F32Range(..) | F32Range(..)
| F64Range(..) | F64Range(..)
| Str(..) | Str(..)
| Opaque | Opaque(..)
| NonExhaustive | NonExhaustive
| Hidden | Hidden
| Missing { .. } | Missing { .. }
@ -869,8 +893,10 @@ impl<'tcx> Constructor<'tcx> {
} }
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
// We are trying to inspect an opaque constant. Thus we skip the row. // Opaque constructors don't interact with anything unless they come from the
(Opaque, _) | (_, Opaque) => false, // syntactically identical pattern.
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
(Opaque(..), _) | (_, Opaque(..)) => false,
_ => span_bug!( _ => span_bug!(
pcx.span, pcx.span,
@ -1083,18 +1109,26 @@ impl ConstructorSet {
{ {
let mut present: SmallVec<[_; 1]> = SmallVec::new(); let mut present: SmallVec<[_; 1]> = SmallVec::new();
let mut missing = Vec::new(); let mut missing = Vec::new();
// Constructors in `ctors`, except wildcards. // Constructors in `ctors`, except wildcards and opaques.
let mut seen = ctors.filter(|c| !(matches!(c, Opaque | Wildcard))); let mut seen = Vec::new();
for ctor in ctors.cloned() {
if let Constructor::Opaque(..) = ctor {
present.push(ctor);
} else if !ctor.is_wildcard() {
seen.push(ctor);
}
}
match self { match self {
ConstructorSet::Single => { ConstructorSet::Single => {
if seen.next().is_none() { if seen.is_empty() {
missing.push(Single); missing.push(Single);
} else { } else {
present.push(Single); present.push(Single);
} }
} }
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => { ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
let seen_set: FxHashSet<_> = seen.map(|c| c.as_variant().unwrap()).collect(); let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
let mut skipped_a_hidden_variant = false; let mut skipped_a_hidden_variant = false;
for variant in visible_variants { for variant in visible_variants {
@ -1125,7 +1159,7 @@ impl ConstructorSet {
ConstructorSet::Bool => { ConstructorSet::Bool => {
let mut seen_false = false; let mut seen_false = false;
let mut seen_true = false; let mut seen_true = false;
for b in seen.map(|ctor| ctor.as_bool().unwrap()) { for b in seen.iter().map(|ctor| ctor.as_bool().unwrap()) {
if b { if b {
seen_true = true; seen_true = true;
} else { } else {
@ -1145,7 +1179,7 @@ impl ConstructorSet {
} }
ConstructorSet::Integers { range_1, range_2 } => { ConstructorSet::Integers { range_1, range_2 } => {
let seen_ranges: Vec<_> = let seen_ranges: Vec<_> =
seen.map(|ctor| ctor.as_int_range().unwrap().clone()).collect(); seen.iter().map(|ctor| ctor.as_int_range().unwrap().clone()).collect();
for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) { for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
match seen { match seen {
Presence::Unseen => missing.push(IntRange(splitted_range)), Presence::Unseen => missing.push(IntRange(splitted_range)),
@ -1162,7 +1196,7 @@ impl ConstructorSet {
} }
} }
&ConstructorSet::Slice(array_len) => { &ConstructorSet::Slice(array_len) => {
let seen_slices = seen.map(|c| c.as_slice().unwrap()); let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
let base_slice = Slice::new(array_len, VarLen(0, 0)); let base_slice = Slice::new(array_len, VarLen(0, 0));
for (seen, splitted_slice) in base_slice.split(seen_slices) { for (seen, splitted_slice) in base_slice.split(seen_slices) {
let ctor = Slice(splitted_slice); let ctor = Slice(splitted_slice);
@ -1178,7 +1212,7 @@ impl ConstructorSet {
// unreachable if length != 0. // unreachable if length != 0.
// We still gather the seen constructors in `present`, but the only slice that can // We still gather the seen constructors in `present`, but the only slice that can
// go in `missing` is `[]`. // go in `missing` is `[]`.
let seen_slices = seen.map(|c| c.as_slice().unwrap()); let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
let base_slice = Slice::new(None, VarLen(0, 0)); let base_slice = Slice::new(None, VarLen(0, 0));
for (seen, splitted_slice) in base_slice.split(seen_slices) { for (seen, splitted_slice) in base_slice.split(seen_slices) {
let ctor = Slice(splitted_slice); let ctor = Slice(splitted_slice);
@ -1194,7 +1228,7 @@ impl ConstructorSet {
ConstructorSet::Unlistable => { ConstructorSet::Unlistable => {
// Since we can't list constructors, we take the ones in the column. This might list // Since we can't list constructors, we take the ones in the column. This might list
// some constructors several times but there's not much we can do. // some constructors several times but there's not much we can do.
present.extend(seen.cloned()); present.extend(seen);
missing.push(NonExhaustive); missing.push(NonExhaustive);
} }
// If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot // If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
@ -1339,7 +1373,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
| F32Range(..) | F32Range(..)
| F64Range(..) | F64Range(..)
| Str(..) | Str(..)
| Opaque | Opaque(..)
| NonExhaustive | NonExhaustive
| Hidden | Hidden
| Missing { .. } | Missing { .. }
@ -1470,14 +1504,14 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
ty::Bool => { ty::Bool => {
ctor = match value.try_eval_bool(cx.tcx, cx.param_env) { ctor = match value.try_eval_bool(cx.tcx, cx.param_env) {
Some(b) => Bool(b), Some(b) => Bool(b),
None => Opaque, None => Opaque(OpaqueId::new()),
}; };
fields = Fields::empty(); fields = Fields::empty();
} }
ty::Char | ty::Int(_) | ty::Uint(_) => { ty::Char | ty::Int(_) | ty::Uint(_) => {
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) { ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)), Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)),
None => Opaque, None => Opaque(OpaqueId::new()),
}; };
fields = Fields::empty(); fields = Fields::empty();
} }
@ -1488,7 +1522,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
let value = rustc_apfloat::ieee::Single::from_bits(bits); let value = rustc_apfloat::ieee::Single::from_bits(bits);
F32Range(value, value, RangeEnd::Included) F32Range(value, value, RangeEnd::Included)
} }
None => Opaque, None => Opaque(OpaqueId::new()),
}; };
fields = Fields::empty(); fields = Fields::empty();
} }
@ -1499,7 +1533,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
let value = rustc_apfloat::ieee::Double::from_bits(bits); let value = rustc_apfloat::ieee::Double::from_bits(bits);
F64Range(value, value, RangeEnd::Included) F64Range(value, value, RangeEnd::Included)
} }
None => Opaque, None => Opaque(OpaqueId::new()),
}; };
fields = Fields::empty(); fields = Fields::empty();
} }
@ -1520,7 +1554,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
// opaque. // opaque.
_ => { _ => {
ctor = Opaque; ctor = Opaque(OpaqueId::new());
fields = Fields::empty(); fields = Fields::empty();
} }
} }
@ -1581,7 +1615,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
fields = Fields::from_iter(cx, pats.into_iter().map(mkpat)); fields = Fields::from_iter(cx, pats.into_iter().map(mkpat));
} }
PatKind::Error(_) => { PatKind::Error(_) => {
ctor = Opaque; ctor = Opaque(OpaqueId::new());
fields = Fields::empty(); fields = Fields::empty();
} }
} }
@ -1768,7 +1802,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
Str(value) => write!(f, "{value}"), Str(value) => write!(f, "{value}"),
Opaque => write!(f, "<constant pattern>"), Opaque(..) => write!(f, "<constant pattern>"),
Or => { Or => {
for pat in self.iter_fields() { for pat in self.iter_fields() {
write!(f, "{}{:?}", start_or_continue(" | "), pat)?; write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
@ -1898,7 +1932,7 @@ impl<'tcx> WitnessPat<'tcx> {
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`" `Missing` should have been processed in `apply_constructors`"
), ),
F32Range(..) | F64Range(..) | Opaque | Or => { F32Range(..) | F64Range(..) | Opaque(..) | Or => {
bug!("can't convert to pattern: {:?}", self) bug!("can't convert to pattern: {:?}", self)
} }
}; };