use std::cmp::{self, Ordering, max, min};
use std::fmt;
use std::iter::once;
use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
use rustc_index::IndexVec;
use rustc_index::bit_set::{DenseBitSet, GrowableBitSet};
use smallvec::SmallVec;
use self::Constructor::*;
use self::MaybeInfiniteInt::*;
use self::SliceKind::*;
use crate::PatCx;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum Presence {
Unseen,
Seen,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RangeEnd {
Included,
Excluded,
}
impl fmt::Display for RangeEnd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
RangeEnd::Included => "..=",
RangeEnd::Excluded => "..",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum MaybeInfiniteInt {
NegInfinity,
#[non_exhaustive]
Finite(u128),
PosInfinity,
}
impl MaybeInfiniteInt {
pub fn new_finite_uint(bits: u128) -> Self {
Finite(bits)
}
pub fn new_finite_int(bits: u128, size: u64) -> Self {
let bias = 1u128 << (size - 1);
Finite(bits ^ bias)
}
pub fn as_finite_uint(self) -> Option<u128> {
match self {
Finite(bits) => Some(bits),
_ => None,
}
}
pub fn as_finite_int(self, size: u64) -> Option<u128> {
match self {
Finite(bits) => {
let bias = 1u128 << (size - 1);
Some(bits ^ bias)
}
_ => None,
}
}
pub fn minus_one(self) -> Option<Self> {
match self {
Finite(n) => n.checked_sub(1).map(Finite),
x => Some(x),
}
}
pub fn plus_one(self) -> Option<Self> {
match self {
Finite(n) => match n.checked_add(1) {
Some(m) => Some(Finite(m)),
None => Some(PosInfinity),
},
x => Some(x),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct IntRange {
pub lo: MaybeInfiniteInt, pub hi: MaybeInfiniteInt, }
impl IntRange {
pub fn is_singleton(&self) -> bool {
self.lo.plus_one() == Some(self.hi)
}
#[inline]
pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange {
IntRange { lo: x, hi: x.plus_one().unwrap() }
}
#[inline]
pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange {
if end == RangeEnd::Included {
hi = hi.plus_one().unwrap();
}
if lo >= hi {
panic!("malformed range pattern: {lo:?}..{hi:?}");
}
IntRange { lo, hi }
}
#[inline]
pub fn is_subrange(&self, other: &Self) -> bool {
other.lo <= self.lo && self.hi <= other.hi
}
fn intersection(&self, other: &Self) -> Option<Self> {
if self.lo < other.hi && other.lo < self.hi {
Some(IntRange { lo: max(self.lo, other.lo), hi: min(self.hi, other.hi) })
} else {
None
}
}
fn split(
&self,
column_ranges: impl Iterator<Item = IntRange>,
) -> impl Iterator<Item = (Presence, IntRange)> {
let mut boundaries: Vec<(MaybeInfiniteInt, isize)> = column_ranges
.filter_map(|r| self.intersection(&r))
.flat_map(|r| [(r.lo, 1), (r.hi, -1)])
.collect();
boundaries.sort_unstable();
let mut paren_counter = 0isize;
let mut prev_bdy = self.lo;
boundaries
.into_iter()
.chain(once((self.hi, 0)))
.map(move |(bdy, delta)| {
let ret = (prev_bdy, paren_counter, bdy);
prev_bdy = bdy;
paren_counter += delta;
ret
})
.filter(|&(prev_bdy, _, bdy)| prev_bdy != bdy)
.map(move |(prev_bdy, paren_count, bdy)| {
use Presence::*;
let presence = if paren_count > 0 { Seen } else { Unseen };
let range = IntRange { lo: prev_bdy, hi: bdy };
(presence, range)
})
}
}
impl fmt::Debug for IntRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_singleton() {
let Finite(lo) = self.lo else { unreachable!() };
write!(f, "{lo}")?;
} else {
if let Finite(lo) = self.lo {
write!(f, "{lo}")?;
}
write!(f, "{}", RangeEnd::Excluded)?;
if let Finite(hi) = self.hi {
write!(f, "{hi}")?;
}
}
Ok(())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SliceKind {
FixedLen(usize),
VarLen(usize, usize),
}
impl SliceKind {
pub fn arity(self) -> usize {
match self {
FixedLen(length) => length,
VarLen(prefix, suffix) => prefix + suffix,
}
}
fn covers_length(self, other_len: usize) -> bool {
match self {
FixedLen(len) => len == other_len,
VarLen(prefix, suffix) => prefix + suffix <= other_len,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Slice {
pub(crate) array_len: Option<usize>,
pub(crate) kind: SliceKind,
}
impl Slice {
pub fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
let kind = match (array_len, kind) {
(Some(len), VarLen(prefix, suffix)) if prefix + suffix == len => FixedLen(len),
(Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => panic!(
"Slice pattern of length {} longer than its array length {len}",
prefix + suffix
),
_ => kind,
};
Slice { array_len, kind }
}
pub fn arity(self) -> usize {
self.kind.arity()
}
fn is_covered_by(self, other: Self) -> bool {
other.kind.covers_length(self.arity())
}
pub fn array_len(self) -> Option<usize> {
self.array_len
}
pub fn kind(self) -> SliceKind {
self.kind
}
fn split(
self,
column_slices: impl Iterator<Item = Slice>,
) -> impl Iterator<Item = (Presence, Slice)> {
let smaller_lengths;
let arity = self.arity();
let mut max_slice = self.kind;
let mut min_var_len = usize::MAX;
let mut seen_fixed_lens = GrowableBitSet::new_empty();
match &mut max_slice {
VarLen(max_prefix_len, max_suffix_len) => {
let mut fixed_len_upper_bound = 1;
for slice in column_slices {
match slice.kind {
FixedLen(len) => {
fixed_len_upper_bound = cmp::max(fixed_len_upper_bound, len + 1);
seen_fixed_lens.insert(len);
}
VarLen(prefix, suffix) => {
*max_prefix_len = cmp::max(*max_prefix_len, prefix);
*max_suffix_len = cmp::max(*max_suffix_len, suffix);
min_var_len = cmp::min(min_var_len, prefix + suffix);
}
}
}
if let Some(delta) =
fixed_len_upper_bound.checked_sub(*max_prefix_len + *max_suffix_len)
{
*max_prefix_len += delta
}
match self.array_len {
Some(len) if max_slice.arity() >= len => max_slice = FixedLen(len),
_ => {}
}
smaller_lengths = match self.array_len {
Some(_) => 0..0, None => self.arity()..max_slice.arity(),
};
}
FixedLen(_) => {
for slice in column_slices {
match slice.kind {
FixedLen(len) => {
if len == arity {
seen_fixed_lens.insert(len);
}
}
VarLen(prefix, suffix) => {
min_var_len = cmp::min(min_var_len, prefix + suffix);
}
}
}
smaller_lengths = 0..0;
}
};
smaller_lengths.map(FixedLen).chain(once(max_slice)).map(move |kind| {
let arity = kind.arity();
let seen = if min_var_len <= arity || seen_fixed_lens.contains(arity) {
Presence::Seen
} else {
Presence::Unseen
};
(seen, Slice::new(self.array_len, kind))
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpaqueId(u32);
impl OpaqueId {
pub fn new() -> Self {
use std::sync::atomic::{AtomicU32, Ordering};
static OPAQUE_ID: AtomicU32 = AtomicU32::new(0);
OpaqueId(OPAQUE_ID.fetch_add(1, Ordering::SeqCst))
}
}
#[derive(Debug)]
pub enum Constructor<Cx: PatCx> {
Struct,
Variant(Cx::VariantIdx),
Ref,
Slice(Slice),
UnionField,
Bool(bool),
IntRange(IntRange),
F16Range(IeeeFloat<HalfS>, IeeeFloat<HalfS>, RangeEnd),
F32Range(IeeeFloat<SingleS>, IeeeFloat<SingleS>, RangeEnd),
F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd),
F128Range(IeeeFloat<QuadS>, IeeeFloat<QuadS>, RangeEnd),
Str(Cx::StrLit),
DerefPattern(Cx::Ty),
Opaque(OpaqueId),
Or,
Wildcard,
Never,
NonExhaustive,
Hidden,
Missing,
PrivateUninhabited,
}
impl<Cx: PatCx> Clone for Constructor<Cx> {
fn clone(&self) -> Self {
match self {
Constructor::Struct => Constructor::Struct,
Constructor::Variant(idx) => Constructor::Variant(*idx),
Constructor::Ref => Constructor::Ref,
Constructor::Slice(slice) => Constructor::Slice(*slice),
Constructor::UnionField => Constructor::UnionField,
Constructor::Bool(b) => Constructor::Bool(*b),
Constructor::IntRange(range) => Constructor::IntRange(*range),
Constructor::F16Range(lo, hi, end) => Constructor::F16Range(*lo, *hi, *end),
Constructor::F32Range(lo, hi, end) => Constructor::F32Range(*lo, *hi, *end),
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end),
Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end),
Constructor::Str(value) => Constructor::Str(value.clone()),
Constructor::DerefPattern(ty) => Constructor::DerefPattern(ty.clone()),
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
Constructor::Or => Constructor::Or,
Constructor::Never => Constructor::Never,
Constructor::Wildcard => Constructor::Wildcard,
Constructor::NonExhaustive => Constructor::NonExhaustive,
Constructor::Hidden => Constructor::Hidden,
Constructor::Missing => Constructor::Missing,
Constructor::PrivateUninhabited => Constructor::PrivateUninhabited,
}
}
}
impl<Cx: PatCx> Constructor<Cx> {
pub(crate) fn is_non_exhaustive(&self) -> bool {
matches!(self, NonExhaustive)
}
pub(crate) fn as_variant(&self) -> Option<Cx::VariantIdx> {
match self {
Variant(i) => Some(*i),
_ => None,
}
}
fn as_bool(&self) -> Option<bool> {
match self {
Bool(b) => Some(*b),
_ => None,
}
}
pub(crate) fn as_int_range(&self) -> Option<&IntRange> {
match self {
IntRange(range) => Some(range),
_ => None,
}
}
fn as_slice(&self) -> Option<Slice> {
match self {
Slice(slice) => Some(*slice),
_ => None,
}
}
pub(crate) fn arity(&self, cx: &Cx, ty: &Cx::Ty) -> usize {
cx.ctor_arity(self, ty)
}
#[inline]
pub(crate) fn is_covered_by(&self, cx: &Cx, other: &Self) -> Result<bool, Cx::Error> {
Ok(match (self, other) {
(Wildcard, _) => {
return Err(cx.bug(format_args!(
"Constructor splitting should not have returned `Wildcard`"
)));
}
(_, Wildcard) => true,
(PrivateUninhabited, _) => true,
(Missing { .. } | NonExhaustive | Hidden, _) => false,
(Struct, Struct) => true,
(Ref, Ref) => true,
(UnionField, UnionField) => true,
(Variant(self_id), Variant(other_id)) => self_id == other_id,
(Bool(self_b), Bool(other_b)) => self_b == other_b,
(IntRange(self_range), IntRange(other_range)) => self_range.is_subrange(other_range),
(F16Range(self_from, self_to, self_end), F16Range(other_from, other_to, other_end)) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(F64Range(self_from, self_to, self_end), F64Range(other_from, other_to, other_end)) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(
F128Range(self_from, self_to, self_end),
F128Range(other_from, other_to, other_end),
) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(Str(self_val), Str(other_val)) => {
self_val == other_val
}
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
(DerefPattern(_), DerefPattern(_)) => true,
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
(Opaque(..), _) | (_, Opaque(..)) => false,
_ => {
return Err(cx.bug(format_args!(
"trying to compare incompatible constructors {self:?} and {other:?}"
)));
}
})
}
pub(crate) fn fmt_fields(
&self,
f: &mut fmt::Formatter<'_>,
ty: &Cx::Ty,
mut fields: impl Iterator<Item = impl fmt::Debug>,
) -> fmt::Result {
let mut first = true;
let mut start_or_continue = |s| {
if first {
first = false;
""
} else {
s
}
};
let mut start_or_comma = || start_or_continue(", ");
match self {
Struct | Variant(_) | UnionField => {
Cx::write_variant_name(f, self, ty)?;
write!(f, "(")?;
for p in fields {
write!(f, "{}{:?}", start_or_comma(), p)?;
}
write!(f, ")")?;
}
Ref => {
write!(f, "&{:?}", fields.next().unwrap())?;
}
Slice(slice) => {
write!(f, "[")?;
match slice.kind {
SliceKind::FixedLen(_) => {
for p in fields {
write!(f, "{}{:?}", start_or_comma(), p)?;
}
}
SliceKind::VarLen(prefix_len, _) => {
for p in fields.by_ref().take(prefix_len) {
write!(f, "{}{:?}", start_or_comma(), p)?;
}
write!(f, "{}..", start_or_comma())?;
for p in fields {
write!(f, "{}{:?}", start_or_comma(), p)?;
}
}
}
write!(f, "]")?;
}
Bool(b) => write!(f, "{b}")?,
IntRange(range) => write!(f, "{range:?}")?,
F16Range(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}")?,
F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
Str(value) => write!(f, "{value:?}")?,
DerefPattern(_) => write!(f, "deref!({:?})", fields.next().unwrap())?,
Opaque(..) => write!(f, "<constant pattern>")?,
Or => {
for pat in fields {
write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
}
}
Never => write!(f, "!")?,
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?,
}
Ok(())
}
}
#[derive(Debug, Clone, Copy)]
pub enum VariantVisibility {
Visible,
Hidden,
Empty,
}
#[derive(Debug)]
pub enum ConstructorSet<Cx: PatCx> {
Struct { empty: bool },
Variants { variants: IndexVec<Cx::VariantIdx, VariantVisibility>, non_exhaustive: bool },
Ref,
Union,
Bool,
Integers { range_1: IntRange, range_2: Option<IntRange> },
Slice { array_len: Option<usize>, subtype_is_empty: bool },
Unlistable,
NoConstructors,
}
#[derive(Debug)]
pub struct SplitConstructorSet<Cx: PatCx> {
pub present: SmallVec<[Constructor<Cx>; 1]>,
pub missing: Vec<Constructor<Cx>>,
pub missing_empty: Vec<Constructor<Cx>>,
}
impl<Cx: PatCx> ConstructorSet<Cx> {
pub fn split<'a>(
&self,
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
) -> SplitConstructorSet<Cx>
where
Cx: 'a,
{
let mut present: SmallVec<[_; 1]> = SmallVec::new();
let mut missing_empty = Vec::new();
let mut missing = Vec::new();
let mut seen = Vec::new();
let mut deref_pat_present = false;
for ctor in ctors.cloned() {
match ctor {
DerefPattern(..) => {
if !deref_pat_present {
deref_pat_present = true;
present.push(ctor);
}
}
Opaque(..) => present.push(ctor),
Wildcard => {} _ => seen.push(ctor),
}
}
match self {
_ if deref_pat_present => {
}
ConstructorSet::Struct { empty } => {
if !seen.is_empty() {
present.push(Struct);
} else if *empty {
missing_empty.push(Struct);
} else {
missing.push(Struct);
}
}
ConstructorSet::Ref => {
if !seen.is_empty() {
present.push(Ref);
} else {
missing.push(Ref);
}
}
ConstructorSet::Union => {
if !seen.is_empty() {
present.push(UnionField);
} else {
missing.push(UnionField);
}
}
ConstructorSet::Variants { variants, non_exhaustive } => {
let mut seen_set = DenseBitSet::new_empty(variants.len());
for idx in seen.iter().filter_map(|c| c.as_variant()) {
seen_set.insert(idx);
}
let mut skipped_a_hidden_variant = false;
for (idx, visibility) in variants.iter_enumerated() {
let ctor = Variant(idx);
if seen_set.contains(idx) {
present.push(ctor);
} else {
match visibility {
VariantVisibility::Visible => missing.push(ctor),
VariantVisibility::Hidden => skipped_a_hidden_variant = true,
VariantVisibility::Empty => missing_empty.push(ctor),
}
}
}
if skipped_a_hidden_variant {
missing.push(Hidden);
}
if *non_exhaustive {
missing.push(NonExhaustive);
}
}
ConstructorSet::Bool => {
let mut seen_false = false;
let mut seen_true = false;
for b in seen.iter().filter_map(|ctor| ctor.as_bool()) {
if b {
seen_true = true;
} else {
seen_false = true;
}
}
if seen_true {
present.push(Bool(true));
} else {
missing.push(Bool(true));
}
if seen_false {
present.push(Bool(false));
} else {
missing.push(Bool(false));
}
}
ConstructorSet::Integers { range_1, range_2 } => {
let seen_ranges: Vec<_> =
seen.iter().filter_map(|ctor| ctor.as_int_range()).copied().collect();
for (seen, splitted_range) in range_1.split(seen_ranges.iter().cloned()) {
match seen {
Presence::Unseen => missing.push(IntRange(splitted_range)),
Presence::Seen => present.push(IntRange(splitted_range)),
}
}
if let Some(range_2) = range_2 {
for (seen, splitted_range) in range_2.split(seen_ranges.into_iter()) {
match seen {
Presence::Unseen => missing.push(IntRange(splitted_range)),
Presence::Seen => present.push(IntRange(splitted_range)),
}
}
}
}
ConstructorSet::Slice { array_len, subtype_is_empty } => {
let seen_slices = seen.iter().filter_map(|c| c.as_slice());
let base_slice = Slice::new(*array_len, VarLen(0, 0));
for (seen, splitted_slice) in base_slice.split(seen_slices) {
let ctor = Slice(splitted_slice);
match seen {
Presence::Seen => present.push(ctor),
Presence::Unseen => {
if *subtype_is_empty && splitted_slice.arity() != 0 {
missing_empty.push(ctor);
} else {
missing.push(ctor);
}
}
}
}
}
ConstructorSet::Unlistable => {
present.extend(seen);
missing.push(NonExhaustive);
}
ConstructorSet::NoConstructors => {
missing_empty.push(Never);
}
}
SplitConstructorSet { present, missing, missing_empty }
}
pub(crate) fn all_empty(&self) -> bool {
match self {
ConstructorSet::Bool
| ConstructorSet::Integers { .. }
| ConstructorSet::Ref
| ConstructorSet::Union
| ConstructorSet::Unlistable => false,
ConstructorSet::NoConstructors => true,
ConstructorSet::Struct { empty } => *empty,
ConstructorSet::Variants { variants, non_exhaustive } => {
!*non_exhaustive
&& variants
.iter()
.all(|visibility| matches!(visibility, VariantVisibility::Empty))
}
ConstructorSet::Slice { array_len, subtype_is_empty } => {
*subtype_is_empty && matches!(array_len, Some(1..))
}
}
}
}