#![allow(dead_code, unreachable_pub)]
use rustc_pattern_analysis::constructor::{
Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility,
};
use rustc_pattern_analysis::pat::DeconstructedPat;
use rustc_pattern_analysis::usefulness::{PlaceValidity, UsefulnessReport};
use rustc_pattern_analysis::{MatchArm, PatCx, PrivateUninhabitedField};
fn init_tracing() {
use tracing_subscriber::Layer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
let _ = tracing_tree::HierarchicalLayer::default()
.with_writer(std::io::stderr)
.with_ansi(true)
.with_targets(true)
.with_indent_amount(2)
.with_subscriber(
tracing_subscriber::Registry::default()
.with(tracing_subscriber::EnvFilter::from_default_env()),
)
.try_init();
}
pub(super) const UNIT: Ty = Ty::Tuple(&[]);
pub(super) const NEVER: Ty = Ty::Enum(&[]);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum Ty {
Bool,
U8,
Tuple(&'static [Ty]),
Enum(&'static [Ty]),
BigStruct { arity: usize, ty: &'static Ty },
BigEnum { arity: usize, ty: &'static Ty },
NonExhaustiveEnum(&'static [Ty]),
}
impl Ty {
pub(super) fn sub_tys(&self, ctor: &Constructor<Cx>) -> Vec<Self> {
use Constructor::*;
match (ctor, *self) {
(Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(),
(Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(),
(Variant(i), Ty::Enum(tys) | Ty::NonExhaustiveEnum(tys)) => vec![tys[*i]],
(Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty],
(Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![],
_ => panic!("Unexpected ctor {ctor:?} for type {self:?}"),
}
}
fn is_empty(&self) -> bool {
match *self {
Ty::Bool | Ty::U8 => false,
Ty::Tuple(tys) => tys.iter().any(|ty| ty.is_empty()),
Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()),
Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(),
Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(),
Ty::NonExhaustiveEnum(..) => false,
}
}
fn ctor_set(&self) -> ConstructorSet<Cx> {
match *self {
Ty::Bool => ConstructorSet::Bool,
Ty::U8 => ConstructorSet::Integers {
range_1: IntRange::from_range(
MaybeInfiniteInt::new_finite_uint(0),
MaybeInfiniteInt::new_finite_uint(255),
RangeEnd::Included,
),
range_2: None,
},
Ty::Tuple(..) | Ty::BigStruct { .. } => ConstructorSet::Struct { empty: false },
Ty::Enum(tys) if tys.is_empty() => ConstructorSet::NoConstructors,
Ty::Enum(tys) => ConstructorSet::Variants {
variants: tys
.iter()
.map(|ty| {
if ty.is_empty() {
VariantVisibility::Empty
} else {
VariantVisibility::Visible
}
})
.collect(),
non_exhaustive: false,
},
Ty::NonExhaustiveEnum(tys) => ConstructorSet::Variants {
variants: tys
.iter()
.map(|ty| {
if ty.is_empty() {
VariantVisibility::Empty
} else {
VariantVisibility::Visible
}
})
.collect(),
non_exhaustive: true,
},
Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors,
Ty::BigEnum { arity, ty } => {
let vis = if ty.is_empty() {
VariantVisibility::Empty
} else {
VariantVisibility::Visible
};
ConstructorSet::Variants {
variants: (0..arity).map(|_| vis).collect(),
non_exhaustive: false,
}
}
}
}
fn write_variant_name(
&self,
f: &mut std::fmt::Formatter<'_>,
ctor: &Constructor<Cx>,
) -> std::fmt::Result {
match (*self, ctor) {
(Ty::Tuple(..), _) => Ok(()),
(Ty::BigStruct { .. }, _) => write!(f, "BigStruct"),
(Ty::Enum(..) | Ty::NonExhaustiveEnum(..), Constructor::Variant(i)) => {
write!(f, "Enum::Variant{i}")
}
(Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"),
_ => write!(f, "{:?}::{:?}", self, ctor),
}
}
}
pub(super) fn compute_match_usefulness<'p>(
arms: &[MatchArm<'p, Cx>],
ty: Ty,
scrut_validity: PlaceValidity,
complexity_limit: usize,
exhaustive_witnesses: bool,
) -> Result<UsefulnessReport<'p, Cx>, ()> {
init_tracing();
rustc_pattern_analysis::usefulness::compute_match_usefulness(
&Cx { exhaustive_witnesses },
arms,
ty,
scrut_validity,
complexity_limit,
)
}
#[derive(Debug)]
pub(super) struct Cx {
exhaustive_witnesses: bool,
}
impl PatCx for Cx {
type Ty = Ty;
type Error = ();
type VariantIdx = usize;
type StrLit = ();
type ArmData = ();
type PatData = ();
fn is_exhaustive_patterns_feature_on(&self) -> bool {
false
}
fn exhaustive_witnesses(&self) -> bool {
self.exhaustive_witnesses
}
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize {
ty.sub_tys(ctor).len()
}
fn ctor_sub_tys(
&self,
ctor: &Constructor<Self>,
ty: &Self::Ty,
) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator {
ty.sub_tys(ctor).into_iter().map(|ty| (ty, PrivateUninhabitedField(false)))
}
fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet<Self>, Self::Error> {
Ok(ty.ctor_set())
}
fn write_variant_name(
f: &mut std::fmt::Formatter<'_>,
ctor: &Constructor<Self>,
ty: &Self::Ty,
) -> std::fmt::Result {
ty.write_variant_name(f, ctor)
}
fn bug(&self, fmt: std::fmt::Arguments<'_>) -> Self::Error {
panic!("{}", fmt)
}
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
Err(())
}
fn report_mixed_deref_pat_ctors(
&self,
_deref_pat: &DeconstructedPat<Self>,
_normal_pat: &DeconstructedPat<Self>,
) -> Self::Error {
panic!("`rustc_pattern_analysis::tests` currently doesn't test deref pattern errors")
}
}
#[allow(unused_macros)]
macro_rules! pat {
($($rest:tt)*) => {{
let mut vec = pats!($($rest)*);
vec.pop().unwrap()
}};
}
macro_rules! pats {
($ty:expr; $($rest:tt)*) => {{
#[allow(unused)]
use rustc_pattern_analysis::{
constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd},
pat::{DeconstructedPat, IndexedPat},
};
let ty = $ty;
#[allow(unused)]
let sub_tys = ::std::iter::repeat(&ty);
#[allow(unused)]
let mut vec: Vec<IndexedPat<_>> = Vec::new();
pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*);
vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>()
}};
(@ctor($($args:tt)*) true $($rest:tt)*) => {{
let ctor = Constructor::Bool(true);
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) false $($rest:tt)*) => {{
let ctor = Constructor::Bool(false);
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) Struct $($rest:tt)*) => {{
let ctor = Constructor::Struct;
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) ( $($fields:tt)* ) $($rest:tt)*) => {{
let ctor = Constructor::Struct; pats!(@pat($($args)*, ctor:ctor) ( $($fields)* ) $($rest)*)
}};
(@ctor($($args:tt)*) Variant.$variant:ident $($rest:tt)*) => {{
let ctor = Constructor::Variant($variant);
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) Variant.$variant:literal $($rest:tt)*) => {{
let ctor = Constructor::Variant($variant);
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) _ $($rest:tt)*) => {{
let ctor = Constructor::Wildcard;
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*)) => {};
(@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{
let ctor = Constructor::IntRange(IntRange::from_range(
pats!(@rangeboundary- $($start)?),
pats!(@rangeboundary+ $end),
RangeEnd::Excluded,
));
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) $($start:literal)?.. $($rest:tt)*) => {{
let ctor = Constructor::IntRange(IntRange::from_range(
pats!(@rangeboundary- $($start)?),
pats!(@rangeboundary+),
RangeEnd::Excluded,
));
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) $($start:literal)?..=$end:literal $($rest:tt)*) => {{
let ctor = Constructor::IntRange(IntRange::from_range(
pats!(@rangeboundary- $($start)?),
pats!(@rangeboundary+ $end),
RangeEnd::Included,
));
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@ctor($($args:tt)*) $int:literal $($rest:tt)*) => {{
let ctor = Constructor::IntRange(IntRange::from_range(
pats!(@rangeboundary- $int),
pats!(@rangeboundary+ $int),
RangeEnd::Included,
));
pats!(@pat($($args)*, ctor:ctor) $($rest)*)
}};
(@rangeboundary $sign:tt $int:literal) => { MaybeInfiniteInt::new_finite_uint($int) };
(@rangeboundary -) => { MaybeInfiniteInt::NegInfinity };
(@rangeboundary +) => { MaybeInfiniteInt::PosInfinity };
(@pat($($args:tt)*) $(,)?) => {
pats!(@pat($($args)*) {})
};
(@pat($($args:tt)*) , $($rest:tt)*) => {
pats!(@pat($($args)*) {}, $($rest)*)
};
(@pat($($args:tt)*) ( $($subpat:tt)* ) $($rest:tt)*) => {{
pats!(@pat($($args)*) { $($subpat)* } $($rest)*)
}};
(@pat(vec:$vec:expr, sub_tys:$sub_tys:expr, idx:$idx:expr, ctor:$ctor:expr) { $($fields:tt)* } $($rest:tt)*) => {{
let sub_tys = $sub_tys;
let index = $idx;
let ty = *(&sub_tys).clone().into_iter().nth(index).unwrap();
let ctor = $ctor;
let ctor_sub_tys = &ty.sub_tys(&ctor);
#[allow(unused_mut)]
let mut fields = Vec::new();
pats!(@fields(idx:0, vec:fields, sub_tys:ctor_sub_tys) ,$($fields)*);
let arity = ctor_sub_tys.len();
let pat = DeconstructedPat::new(ctor, fields, arity, ty, ()).at_index(index);
$vec.push(pat);
pats!(@fields(idx:index+1, vec:$vec, sub_tys:sub_tys) $($rest)*);
}};
(@fields($($args:tt)*) $(,)?) => {};
(@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:literal : $($rest:tt)*) => {{
pats!(@ctor($($args)*, idx:$idx) $($rest)*);
}};
(@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:ident : $($rest:tt)*) => {{
pats!(@ctor($($args)*, idx:$idx) $($rest)*);
}};
(@fields(idx:$idx:expr, $($args:tt)*) , $($rest:tt)*) => {{
pats!(@ctor($($args)*, idx:$idx) $($rest)*);
}};
}