use std::fmt;
use std::ops::Index;
use arrayvec::ArrayVec;
use derive_where::derive_where;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext};
use rustc_type_ir_macros::{
GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
};
use crate::data_structures::HashMap;
use crate::inherent::*;
use crate::{self as ty, Interner, TypingModeEqWrapper, UniverseIndex};
#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, V)]
#[derive_where(Copy; I: Interner, V: Copy)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
)]
pub struct CanonicalQueryInput<I: Interner, V> {
pub canonical: Canonical<I, V>,
pub typing_mode: TypingModeEqWrapper<I>,
}
impl<I: Interner, V: Eq> Eq for CanonicalQueryInput<I, V> {}
#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, V)]
#[derive_where(Copy; I: Interner, V: Copy)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
)]
pub struct Canonical<I: Interner, V> {
pub value: V,
pub max_universe: UniverseIndex,
pub var_kinds: I::CanonicalVarKinds,
}
impl<I: Interner, V: Eq> Eq for Canonical<I, V> {}
impl<I: Interner, V> Canonical<I, V> {
pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<I, W> {
let Canonical { max_universe, var_kinds, value } = self;
Canonical { max_universe, var_kinds, value: map_op(value) }
}
}
impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { value, max_universe, var_kinds } = self;
write!(
f,
"Canonical {{ value: {value}, max_universe: {max_universe:?}, var_kinds: {var_kinds:?} }}",
)
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
#[derive(GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
)]
pub enum CanonicalVarKind<I: Interner> {
Ty { ui: UniverseIndex, sub_root: ty::BoundVar },
Int,
Float,
PlaceholderTy(ty::PlaceholderType<I>),
Region(UniverseIndex),
PlaceholderRegion(ty::PlaceholderRegion<I>),
Const(UniverseIndex),
PlaceholderConst(ty::PlaceholderConst<I>),
}
impl<I: Interner> Eq for CanonicalVarKind<I> {}
impl<I: Interner> CanonicalVarKind<I> {
pub fn universe(self) -> UniverseIndex {
match self {
CanonicalVarKind::Ty { ui, sub_root: _ } => ui,
CanonicalVarKind::Region(ui) => ui,
CanonicalVarKind::Const(ui) => ui,
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(),
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe(),
CanonicalVarKind::Float | CanonicalVarKind::Int => UniverseIndex::ROOT,
}
}
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind<I> {
match self {
CanonicalVarKind::Ty { ui: _, sub_root } => CanonicalVarKind::Ty { ui, sub_root },
CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui),
CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui))
}
CanonicalVarKind::Int | CanonicalVarKind::Float => {
assert_eq!(ui, UniverseIndex::ROOT);
self
}
}
}
pub fn is_existential(self) -> bool {
match self {
CanonicalVarKind::Ty { .. }
| CanonicalVarKind::Int
| CanonicalVarKind::Float
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::PlaceholderRegion(..)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn is_region(self) -> bool {
match self {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty { .. }
| CanonicalVarKind::Int
| CanonicalVarKind::Float
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_) => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self {
CanonicalVarKind::Ty { .. }
| CanonicalVarKind::Int
| CanonicalVarKind::Float
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_) => {
panic!("expected placeholder: {self:?}")
}
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.var().as_usize(),
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.var().as_usize(),
}
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
pub struct CanonicalVarValues<I: Interner> {
pub var_values: I::GenericArgs,
}
impl<I: Interner> Eq for CanonicalVarValues<I> {}
impl<I: Interner> CanonicalVarValues<I> {
pub fn is_identity(&self) -> bool {
self.var_values.iter().enumerate().all(|(bv, arg)| match arg.kind() {
ty::GenericArgKind::Lifetime(r) => {
matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if br.var().as_usize() == bv)
}
ty::GenericArgKind::Type(ty) => {
matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if bt.var().as_usize() == bv)
}
ty::GenericArgKind::Const(ct) => {
matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if bc.var().as_usize() == bv)
}
})
}
pub fn is_identity_modulo_regions(&self) -> bool {
let mut var = ty::BoundVar::ZERO;
for arg in self.var_values.iter() {
match arg.kind() {
ty::GenericArgKind::Lifetime(r) => {
if matches!(r.kind(), ty::ReBound(ty::BoundVarIndexKind::Canonical, br) if var == br.var())
{
var = var + 1;
} else {
}
}
ty::GenericArgKind::Type(ty) => {
if matches!(ty.kind(), ty::Bound(ty::BoundVarIndexKind::Canonical, bt) if var == bt.var())
{
var = var + 1;
} else {
return false;
}
}
ty::GenericArgKind::Const(ct) => {
if matches!(ct.kind(), ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, bc) if var == bc.var())
{
var = var + 1;
} else {
return false;
}
}
}
}
true
}
pub fn make_identity(cx: I, infos: I::CanonicalVarKinds) -> CanonicalVarValues<I> {
CanonicalVarValues {
var_values: cx.mk_args_from_iter(infos.iter().enumerate().map(
|(i, kind)| -> I::GenericArg {
match kind {
CanonicalVarKind::Ty { .. }
| CanonicalVarKind::Int
| CanonicalVarKind::Float
| CanonicalVarKind::PlaceholderTy(_) => {
Ty::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
}
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
Region::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
}
CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => {
Const::new_canonical_bound(cx, ty::BoundVar::from_usize(i)).into()
}
}
},
)),
}
}
pub fn dummy() -> CanonicalVarValues<I> {
CanonicalVarValues { var_values: Default::default() }
}
pub fn instantiate(
cx: I,
var_kinds: I::CanonicalVarKinds,
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
) -> CanonicalVarValues<I> {
if var_kinds.len() <= 4 {
let mut var_values = ArrayVec::<_, 4>::new();
for info in var_kinds.iter() {
var_values.push(f(&var_values, info));
}
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
} else {
CanonicalVarValues::instantiate_cold(cx, var_kinds, f)
}
}
#[cold]
fn instantiate_cold(
cx: I,
var_kinds: I::CanonicalVarKinds,
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
) -> CanonicalVarValues<I> {
let mut var_values = Vec::with_capacity(var_kinds.len());
for info in var_kinds.iter() {
var_values.push(f(&var_values, info));
}
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
}
#[inline]
pub fn len(&self) -> usize {
self.var_values.len()
}
}
impl<'a, I: Interner> IntoIterator for &'a CanonicalVarValues<I> {
type Item = I::GenericArg;
type IntoIter = <I::GenericArgs as SliceLike>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.var_values.iter()
}
}
impl<I: Interner> Index<ty::BoundVar> for CanonicalVarValues<I> {
type Output = I::GenericArg;
fn index(&self, value: ty::BoundVar) -> &I::GenericArg {
&self.var_values.as_slice()[value.as_usize()]
}
}
#[derive_where(Clone, Debug; I: Interner)]
pub struct CanonicalParamEnvCacheEntry<I: Interner> {
pub param_env: I::ParamEnv,
pub variables: Vec<I::GenericArg>,
pub variable_lookup_table: HashMap<I::GenericArg, usize>,
pub var_kinds: Vec<CanonicalVarKind<I>>,
}