use super::Fold;
use crate::*;
pub trait Shift<I: Interner>: Fold<I> {
fn shifted_in(self, interner: I) -> Self::Result;
fn shifted_in_from(self, interner: I, source_binder: DebruijnIndex) -> Self::Result;
fn shifted_out(self, interner: I) -> Fallible<Self::Result>;
fn shifted_out_to(self, interner: I, target_binder: DebruijnIndex) -> Fallible<Self::Result>;
}
impl<T: Fold<I>, I: Interner> Shift<I> for T {
fn shifted_in(self, interner: I) -> Self::Result {
self.shifted_in_from(interner, DebruijnIndex::ONE)
}
fn shifted_in_from(self, interner: I, source_binder: DebruijnIndex) -> T::Result {
self.fold_with(
&mut Shifter {
source_binder,
interner,
},
DebruijnIndex::INNERMOST,
)
.unwrap()
}
fn shifted_out_to(self, interner: I, target_binder: DebruijnIndex) -> Fallible<T::Result> {
self.fold_with(
&mut DownShifter {
target_binder,
interner,
},
DebruijnIndex::INNERMOST,
)
}
fn shifted_out(self, interner: I) -> Fallible<Self::Result> {
self.shifted_out_to(interner, DebruijnIndex::ONE)
}
}
struct Shifter<I> {
source_binder: DebruijnIndex,
interner: I,
}
impl<I> Shifter<I> {
fn adjust(&self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> BoundVar {
bound_var
.shifted_in_from(self.source_binder)
.shifted_in_from(outer_binder)
}
}
impl<I: Interner> Folder<I> for Shifter<I> {
type Error = NoSolution;
fn as_dyn(&mut self) -> &mut dyn Folder<I, Error = Self::Error> {
self
}
fn fold_free_var_ty(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Ty<I>> {
Ok(TyKind::<I>::BoundVar(self.adjust(bound_var, outer_binder)).intern(self.interner()))
}
fn fold_free_var_lifetime(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Lifetime<I>> {
Ok(
LifetimeData::<I>::BoundVar(self.adjust(bound_var, outer_binder))
.intern(self.interner()),
)
}
fn fold_free_var_const(
&mut self,
ty: Ty<I>,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Const<I>> {
Ok(self
.adjust(bound_var, outer_binder)
.to_const(self.interner(), ty))
}
fn interner(&self) -> I {
self.interner
}
}
struct DownShifter<I> {
target_binder: DebruijnIndex,
interner: I,
}
impl<I> DownShifter<I> {
fn adjust(&self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Fallible<BoundVar> {
match bound_var.shifted_out_to(self.target_binder) {
Some(bound_var1) => Ok(bound_var1.shifted_in_from(outer_binder)),
None => Err(NoSolution),
}
}
}
impl<I: Interner> Folder<I> for DownShifter<I> {
type Error = NoSolution;
fn as_dyn(&mut self) -> &mut dyn Folder<I, Error = Self::Error> {
self
}
fn fold_free_var_ty(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Ty<I>> {
Ok(TyKind::<I>::BoundVar(self.adjust(bound_var, outer_binder)?).intern(self.interner()))
}
fn fold_free_var_lifetime(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Lifetime<I>> {
Ok(
LifetimeData::<I>::BoundVar(self.adjust(bound_var, outer_binder)?)
.intern(self.interner()),
)
}
fn fold_free_var_const(
&mut self,
ty: Ty<I>,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> Fallible<Const<I>> {
Ok(self
.adjust(bound_var, outer_binder)?
.to_const(self.interner(), ty))
}
fn interner(&self) -> I {
self.interner
}
}