use std::fmt;
use std::hash::Hash;
use std::ops::Index;
use derive_where::derive_where;
#[cfg(feature = "nightly")]
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
use crate::inherent::*;
use crate::{self as ty, Interner, UniverseIndex};
#[derive_where(Clone; I: Interner, V: Clone)]
#[derive_where(Hash; I: Interner, V: Hash)]
#[derive_where(PartialEq; I: Interner, V: PartialEq)]
#[derive_where(Eq; I: Interner, V: Eq)]
#[derive_where(Debug; I: Interner, V: fmt::Debug)]
#[derive_where(Copy; I: Interner, V: Copy)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
pub struct Canonical<I: Interner, V> {
pub value: V,
pub max_universe: UniverseIndex,
pub defining_opaque_types: I::DefiningOpaqueTypes,
pub variables: I::CanonicalVars,
}
impl<I: Interner, V> Canonical<I, V> {
pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<I, W> {
let Canonical { defining_opaque_types, max_universe, variables, value } = self;
Canonical { defining_opaque_types, max_universe, variables, value: map_op(value) }
}
pub fn unchecked_rebind<W>(self, value: W) -> Canonical<I, W> {
let Canonical { defining_opaque_types, max_universe, variables, value: _ } = self;
Canonical { defining_opaque_types, max_universe, variables, 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, variables, defining_opaque_types } = self;
write!(
f,
"Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?}, defining_opaque_types: {defining_opaque_types:?} }}",
)
}
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub struct CanonicalVarInfo<I: Interner> {
pub kind: CanonicalVarKind<I>,
}
impl<I: Interner> CanonicalVarInfo<I> {
pub fn universe(self) -> UniverseIndex {
self.kind.universe()
}
#[must_use]
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarInfo<I> {
CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) }
}
pub fn is_existential(&self) -> bool {
match self.kind {
CanonicalVarKind::Ty(_) => true,
CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true,
CanonicalVarKind::PlaceholderConst(_) => false,
CanonicalVarKind::Effect => true,
}
}
pub fn is_region(&self) -> bool {
match self.kind {
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true,
CanonicalVarKind::Ty(_)
| CanonicalVarKind::PlaceholderTy(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::PlaceholderConst(_)
| CanonicalVarKind::Effect => false,
}
}
pub fn expect_placeholder_index(self) -> usize {
match self.kind {
CanonicalVarKind::Ty(_)
| CanonicalVarKind::Region(_)
| CanonicalVarKind::Const(_)
| CanonicalVarKind::Effect => 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, Eq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub enum CanonicalVarKind<I: Interner> {
Ty(CanonicalTyVarKind),
PlaceholderTy(I::PlaceholderTy),
Region(UniverseIndex),
PlaceholderRegion(I::PlaceholderRegion),
Const(UniverseIndex),
Effect,
PlaceholderConst(I::PlaceholderConst),
}
impl<I: Interner> CanonicalVarKind<I> {
pub fn universe(self) -> UniverseIndex {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => 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::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => {
UniverseIndex::ROOT
}
CanonicalVarKind::Effect => UniverseIndex::ROOT,
}
}
pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind<I> {
match self {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui))
}
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::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float)
| CanonicalVarKind::Effect => {
assert_eq!(ui, UniverseIndex::ROOT);
self
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
pub enum CanonicalTyVarKind {
General(UniverseIndex),
Int,
Float,
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq, Debug; I: Interner)]
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
pub struct CanonicalVarValues<I: Interner> {
pub var_values: I::GenericArgs,
}
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::INNERMOST, br) if br.var().as_usize() == bv)
}
ty::GenericArgKind::Type(ty) => {
matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var().as_usize() == bv)
}
ty::GenericArgKind::Const(ct) => {
matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, 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::INNERMOST, br) if var == br.var()) {
var = var + 1;
} else {
}
}
ty::GenericArgKind::Type(ty) => {
if matches!(ty.kind(), ty::Bound(ty::INNERMOST, bt) if var == bt.var()) {
var = var + 1;
} else {
return false;
}
}
ty::GenericArgKind::Const(ct) => {
if matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if var == bc.var())
{
var = var + 1;
} else {
return false;
}
}
}
}
true
}
pub fn make_identity(cx: I, infos: I::CanonicalVars) -> CanonicalVarValues<I> {
CanonicalVarValues {
var_values: cx.mk_args_from_iter(infos.iter().enumerate().map(
|(i, info)| -> I::GenericArg {
match info.kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
Region::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
CanonicalVarKind::Effect => {
Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => {
Const::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i))
.into()
}
}
},
)),
}
}
pub fn dummy() -> CanonicalVarValues<I> {
CanonicalVarValues { var_values: Default::default() }
}
#[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()]
}
}