use crate::*;
use std::convert::Infallible;
use std::fmt::Debug;
mod binder_impls;
mod boring_impls;
mod in_place;
pub mod shift;
mod subst;
pub use self::shift::Shift;
pub use self::subst::Subst;
pub trait FallibleTypeFolder<I: Interner> {
type Error;
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<I, Error = Self::Error>;
fn try_fold_ty(
&mut self,
ty: Ty<I>,
outer_binder: DebruijnIndex,
) -> Result<Ty<I>, Self::Error> {
ty.try_super_fold_with(self.as_dyn(), outer_binder)
}
fn try_fold_lifetime(
&mut self,
lifetime: Lifetime<I>,
outer_binder: DebruijnIndex,
) -> Result<Lifetime<I>, Self::Error> {
lifetime.try_super_fold_with(self.as_dyn(), outer_binder)
}
fn try_fold_const(
&mut self,
constant: Const<I>,
outer_binder: DebruijnIndex,
) -> Result<Const<I>, Self::Error> {
constant.try_super_fold_with(self.as_dyn(), outer_binder)
}
fn try_fold_program_clause(
&mut self,
clause: ProgramClause<I>,
outer_binder: DebruijnIndex,
) -> Result<ProgramClause<I>, Self::Error> {
clause.try_super_fold_with(self.as_dyn(), outer_binder)
}
fn try_fold_goal(
&mut self,
goal: Goal<I>,
outer_binder: DebruijnIndex,
) -> Result<Goal<I>, Self::Error> {
goal.try_super_fold_with(self.as_dyn(), outer_binder)
}
fn forbid_free_vars(&self) -> bool {
false
}
fn try_fold_free_var_ty(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Result<Ty<I>, Self::Error> {
if self.forbid_free_vars() {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
Ok(TyKind::<I>::BoundVar(bound_var).intern(self.interner()))
}
}
fn try_fold_free_var_lifetime(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Result<Lifetime<I>, Self::Error> {
if self.forbid_free_vars() {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
Ok(LifetimeData::<I>::BoundVar(bound_var).intern(self.interner()))
}
}
fn try_fold_free_var_const(
&mut self,
ty: Ty<I>,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Result<Const<I>, Self::Error> {
if self.forbid_free_vars() {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
Ok(ConstData {
ty: ty.try_fold_with(self.as_dyn(), outer_binder)?,
value: ConstValue::<I>::BoundVar(bound_var),
}
.intern(self.interner()))
}
}
fn forbid_free_placeholders(&self) -> bool {
false
}
#[allow(unused_variables)]
fn try_fold_free_placeholder_ty(
&mut self,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Result<Ty<I>, Self::Error> {
if self.forbid_free_placeholders() {
panic!("unexpected placeholder type `{:?}`", universe)
} else {
Ok(universe.to_ty::<I>(self.interner()))
}
}
#[allow(unused_variables)]
fn try_fold_free_placeholder_lifetime(
&mut self,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Result<Lifetime<I>, Self::Error> {
if self.forbid_free_placeholders() {
panic!("unexpected placeholder lifetime `{:?}`", universe)
} else {
Ok(universe.to_lifetime(self.interner()))
}
}
#[allow(unused_variables)]
fn try_fold_free_placeholder_const(
&mut self,
ty: Ty<I>,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Result<Const<I>, Self::Error> {
if self.forbid_free_placeholders() {
panic!("unexpected placeholder const `{:?}`", universe)
} else {
Ok(universe.to_const(
self.interner(),
ty.try_fold_with(self.as_dyn(), outer_binder)?,
))
}
}
fn forbid_inference_vars(&self) -> bool {
false
}
#[allow(unused_variables)]
fn try_fold_inference_ty(
&mut self,
var: InferenceVar,
kind: TyVariableKind,
outer_binder: DebruijnIndex,
) -> Result<Ty<I>, Self::Error> {
if self.forbid_inference_vars() {
panic!("unexpected inference type `{:?}`", var)
} else {
Ok(var.to_ty(self.interner(), kind))
}
}
#[allow(unused_variables)]
fn try_fold_inference_lifetime(
&mut self,
var: InferenceVar,
outer_binder: DebruijnIndex,
) -> Result<Lifetime<I>, Self::Error> {
if self.forbid_inference_vars() {
panic!("unexpected inference lifetime `'{:?}`", var)
} else {
Ok(var.to_lifetime(self.interner()))
}
}
#[allow(unused_variables)]
fn try_fold_inference_const(
&mut self,
ty: Ty<I>,
var: InferenceVar,
outer_binder: DebruijnIndex,
) -> Result<Const<I>, Self::Error> {
if self.forbid_inference_vars() {
panic!("unexpected inference const `{:?}`", var)
} else {
Ok(var.to_const(
self.interner(),
ty.try_fold_with(self.as_dyn(), outer_binder)?,
))
}
}
fn interner(&self) -> I;
}
pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Infallible> {
fn as_dyn(&mut self) -> &mut dyn TypeFolder<I>;
fn fold_ty(&mut self, ty: Ty<I>, outer_binder: DebruijnIndex) -> Ty<I> {
ty.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
}
fn fold_lifetime(&mut self, lifetime: Lifetime<I>, outer_binder: DebruijnIndex) -> Lifetime<I> {
lifetime.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
}
fn fold_const(&mut self, constant: Const<I>, outer_binder: DebruijnIndex) -> Const<I> {
constant.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
}
fn fold_program_clause(
&mut self,
clause: ProgramClause<I>,
outer_binder: DebruijnIndex,
) -> ProgramClause<I> {
clause.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
}
fn fold_goal(&mut self, goal: Goal<I>, outer_binder: DebruijnIndex) -> Goal<I> {
goal.super_fold_with(TypeFolder::as_dyn(self), outer_binder)
}
fn forbid_free_vars(&self) -> bool {
false
}
fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty<I> {
if TypeFolder::forbid_free_vars(self) {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
TyKind::<I>::BoundVar(bound_var).intern(TypeFolder::interner(self))
}
}
fn fold_free_var_lifetime(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Lifetime<I> {
if TypeFolder::forbid_free_vars(self) {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
LifetimeData::<I>::BoundVar(bound_var).intern(TypeFolder::interner(self))
}
}
fn fold_free_var_const(
&mut self,
ty: Ty<I>,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Const<I> {
if TypeFolder::forbid_free_vars(self) {
panic!(
"unexpected free variable with depth `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
let bound_var = bound_var.shifted_in_from(outer_binder);
ConstData {
ty: ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
value: ConstValue::<I>::BoundVar(bound_var),
}
.intern(TypeFolder::interner(self))
}
}
fn forbid_free_placeholders(&self) -> bool {
false
}
#[allow(unused_variables)]
fn fold_free_placeholder_ty(
&mut self,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Ty<I> {
if TypeFolder::forbid_free_placeholders(self) {
panic!("unexpected placeholder type `{:?}`", universe)
} else {
universe.to_ty::<I>(TypeFolder::interner(self))
}
}
#[allow(unused_variables)]
fn fold_free_placeholder_lifetime(
&mut self,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Lifetime<I> {
if TypeFolder::forbid_free_placeholders(self) {
panic!("unexpected placeholder lifetime `{:?}`", universe)
} else {
universe.to_lifetime(TypeFolder::interner(self))
}
}
#[allow(unused_variables)]
fn fold_free_placeholder_const(
&mut self,
ty: Ty<I>,
universe: PlaceholderIndex,
outer_binder: DebruijnIndex,
) -> Const<I> {
if TypeFolder::forbid_free_placeholders(self) {
panic!("unexpected placeholder const `{:?}`", universe)
} else {
universe.to_const(
TypeFolder::interner(self),
ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
)
}
}
fn forbid_inference_vars(&self) -> bool {
false
}
#[allow(unused_variables)]
fn fold_inference_ty(
&mut self,
var: InferenceVar,
kind: TyVariableKind,
outer_binder: DebruijnIndex,
) -> Ty<I> {
if TypeFolder::forbid_inference_vars(self) {
panic!("unexpected inference type `{:?}`", var)
} else {
var.to_ty(TypeFolder::interner(self), kind)
}
}
#[allow(unused_variables)]
fn fold_inference_lifetime(
&mut self,
var: InferenceVar,
outer_binder: DebruijnIndex,
) -> Lifetime<I> {
if TypeFolder::forbid_inference_vars(self) {
panic!("unexpected inference lifetime `'{:?}`", var)
} else {
var.to_lifetime(TypeFolder::interner(self))
}
}
#[allow(unused_variables)]
fn fold_inference_const(
&mut self,
ty: Ty<I>,
var: InferenceVar,
outer_binder: DebruijnIndex,
) -> Const<I> {
if TypeFolder::forbid_inference_vars(self) {
panic!("unexpected inference const `{:?}`", var)
} else {
var.to_const(
TypeFolder::interner(self),
ty.fold_with(TypeFolder::as_dyn(self), outer_binder),
)
}
}
fn interner(&self) -> I;
}
pub trait TypeFoldable<I: Interner>: Debug + Sized {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E>;
fn fold_with(self, folder: &mut dyn TypeFolder<I>, outer_binder: DebruijnIndex) -> Self {
self.try_fold_with(FallibleTypeFolder::as_dyn(folder), outer_binder)
.unwrap()
}
}
pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> {
fn try_super_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E>;
fn super_fold_with(self, folder: &mut dyn TypeFolder<I>, outer_binder: DebruijnIndex) -> Self {
self.try_super_fold_with(FallibleTypeFolder::as_dyn(folder), outer_binder)
.unwrap()
}
}
impl<I: Interner> TypeFoldable<I> for Ty<I> {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
folder.try_fold_ty(self, outer_binder)
}
}
impl<I> TypeSuperFoldable<I> for Ty<I>
where
I: Interner,
{
fn try_super_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Ty<I>, E> {
let interner = folder.interner();
Ok(match self.kind(interner) {
TyKind::BoundVar(bound_var) => {
if let Some(bound_var1) = bound_var.shifted_out_to(outer_binder) {
folder.try_fold_free_var_ty(bound_var1, outer_binder)?
} else {
self
}
}
TyKind::Dyn(clauses) => {
TyKind::Dyn(clauses.clone().try_fold_with(folder, outer_binder)?)
.intern(folder.interner())
}
TyKind::InferenceVar(var, kind) => {
folder.try_fold_inference_ty(*var, *kind, outer_binder)?
}
TyKind::Placeholder(ui) => folder.try_fold_free_placeholder_ty(*ui, outer_binder)?,
TyKind::Alias(proj) => TyKind::Alias(proj.clone().try_fold_with(folder, outer_binder)?)
.intern(folder.interner()),
TyKind::Function(fun) => {
TyKind::Function(fun.clone().try_fold_with(folder, outer_binder)?)
.intern(folder.interner())
}
TyKind::Adt(id, substitution) => TyKind::Adt(
id.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::AssociatedType(assoc_ty, substitution) => TyKind::AssociatedType(
assoc_ty.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Scalar(scalar) => TyKind::Scalar(scalar.try_fold_with(folder, outer_binder)?)
.intern(folder.interner()),
TyKind::Str => TyKind::Str.intern(folder.interner()),
TyKind::Tuple(arity, substitution) => TyKind::Tuple(
*arity,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::OpaqueType(opaque_ty, substitution) => TyKind::OpaqueType(
opaque_ty.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Slice(substitution) => {
TyKind::Slice(substitution.clone().try_fold_with(folder, outer_binder)?)
.intern(folder.interner())
}
TyKind::FnDef(fn_def, substitution) => TyKind::FnDef(
fn_def.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Ref(mutability, lifetime, ty) => TyKind::Ref(
mutability.try_fold_with(folder, outer_binder)?,
lifetime.clone().try_fold_with(folder, outer_binder)?,
ty.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Raw(mutability, ty) => TyKind::Raw(
mutability.try_fold_with(folder, outer_binder)?,
ty.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Never => TyKind::Never.intern(folder.interner()),
TyKind::Array(ty, const_) => TyKind::Array(
ty.clone().try_fold_with(folder, outer_binder)?,
const_.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Closure(id, substitution) => TyKind::Closure(
id.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Coroutine(id, substitution) => TyKind::Coroutine(
id.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::CoroutineWitness(id, substitution) => TyKind::CoroutineWitness(
id.try_fold_with(folder, outer_binder)?,
substitution.clone().try_fold_with(folder, outer_binder)?,
)
.intern(folder.interner()),
TyKind::Foreign(id) => {
TyKind::Foreign(id.try_fold_with(folder, outer_binder)?).intern(folder.interner())
}
TyKind::Error => TyKind::Error.intern(folder.interner()),
})
}
}
impl<I: Interner> TypeFoldable<I> for Lifetime<I> {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
folder.try_fold_lifetime(self, outer_binder)
}
}
impl<I> TypeSuperFoldable<I> for Lifetime<I>
where
I: Interner,
{
fn try_super_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Lifetime<I>, E> {
let interner = folder.interner();
match self.data(interner) {
LifetimeData::BoundVar(bound_var) => {
if let Some(bound_var1) = bound_var.shifted_out_to(outer_binder) {
folder.try_fold_free_var_lifetime(bound_var1, outer_binder)
} else {
Ok(self)
}
}
LifetimeData::InferenceVar(var) => {
folder.try_fold_inference_lifetime(*var, outer_binder)
}
LifetimeData::Placeholder(universe) => {
folder.try_fold_free_placeholder_lifetime(*universe, outer_binder)
}
LifetimeData::Static => Ok(LifetimeData::<I>::Static.intern(folder.interner())),
LifetimeData::Erased => Ok(LifetimeData::<I>::Erased.intern(folder.interner())),
LifetimeData::Error => Ok(LifetimeData::<I>::Error.intern(folder.interner())),
LifetimeData::Phantom(void, ..) => match *void {},
}
}
}
impl<I: Interner> TypeFoldable<I> for Const<I> {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
folder.try_fold_const(self, outer_binder)
}
}
impl<I> TypeSuperFoldable<I> for Const<I>
where
I: Interner,
{
fn try_super_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Const<I>, E> {
let interner = folder.interner();
let ConstData { ref ty, ref value } = self.data(interner);
let mut fold_ty = || ty.clone().try_fold_with(folder, outer_binder);
match value {
ConstValue::BoundVar(bound_var) => {
if let Some(bound_var1) = bound_var.shifted_out_to(outer_binder) {
folder.try_fold_free_var_const(ty.clone(), bound_var1, outer_binder)
} else {
Ok(self)
}
}
ConstValue::InferenceVar(var) => {
folder.try_fold_inference_const(ty.clone(), *var, outer_binder)
}
ConstValue::Placeholder(universe) => {
folder.try_fold_free_placeholder_const(ty.clone(), *universe, outer_binder)
}
ConstValue::Concrete(ev) => Ok(ConstData {
ty: fold_ty()?,
value: ConstValue::Concrete(ConcreteConst {
interned: ev.interned.clone(),
}),
}
.intern(folder.interner())),
}
}
}
impl<I: Interner> TypeFoldable<I> for Goal<I> {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
folder.try_fold_goal(self, outer_binder)
}
}
impl<I: Interner> TypeSuperFoldable<I> for Goal<I> {
fn try_super_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
let interner = folder.interner();
Ok(Goal::new(
interner,
self.data(interner)
.clone()
.try_fold_with(folder, outer_binder)?,
))
}
}
impl<I: Interner> TypeFoldable<I> for ProgramClause<I> {
fn try_fold_with<E>(
self,
folder: &mut dyn FallibleTypeFolder<I, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
folder.try_fold_program_clause(self, outer_binder)
}
}