use std::fmt::Debug;
use std::hash::Hash;
use std::ops::{ControlFlow, Deref};
#[cfg(feature = "nightly")]
use rustc_macros::HashStable_NoContext;
use rustc_serialize::Decodable;
use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
use crate::inherent::*;
use crate::lift::Lift;
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use crate::{self as ty, Interner, SsoHashSet};
#[derive(derivative::Derivative)]
#[derivative(
Clone(bound = "T: Clone"),
Copy(bound = "T: Copy"),
Hash(bound = "T: Hash"),
PartialEq(bound = "T: PartialEq"),
Eq(bound = "T: Eq"),
Debug(bound = "T: Debug")
)]
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
pub struct Binder<I: Interner, T> {
value: T,
bound_vars: I::BoundVarKinds,
}
impl<I: Interner, U: Interner, T> Lift<U> for Binder<I, T>
where
T: Lift<U>,
I::BoundVarKinds: Lift<U, Lifted = U::BoundVarKinds>,
{
type Lifted = Binder<U, T::Lifted>;
fn lift_to_tcx(self, tcx: U) -> Option<Self::Lifted> {
Some(Binder {
value: self.value.lift_to_tcx(tcx)?,
bound_vars: self.bound_vars.lift_to_tcx(tcx)?,
})
}
}
macro_rules! impl_binder_encode_decode {
($($t:ty),+ $(,)?) => {
$(
impl<I: Interner, E: crate::TyEncoder<I = I>> rustc_serialize::Encodable<E> for ty::Binder<I, $t>
where
$t: rustc_serialize::Encodable<E>,
I::BoundVarKinds: rustc_serialize::Encodable<E>,
{
fn encode(&self, e: &mut E) {
self.bound_vars().encode(e);
self.as_ref().skip_binder().encode(e);
}
}
impl<I: Interner, D: crate::TyDecoder<I = I>> Decodable<D> for ty::Binder<I, $t>
where
$t: TypeVisitable<I> + rustc_serialize::Decodable<D>,
I::BoundVarKinds: rustc_serialize::Decodable<D>,
{
fn decode(decoder: &mut D) -> Self {
let bound_vars = Decodable::decode(decoder);
ty::Binder::bind_with_vars(<$t>::decode(decoder), bound_vars)
}
}
)*
}
}
impl_binder_encode_decode! {
ty::FnSig<I>,
ty::TraitPredicate<I>,
ty::ExistentialPredicate<I>,
ty::TraitRef<I>,
ty::ExistentialTraitRef<I>,
}
impl<I: Interner, T> Binder<I, T>
where
T: TypeVisitable<I>,
{
#[track_caller]
pub fn dummy(value: T) -> Binder<I, T> {
assert!(
!value.has_escaping_bound_vars(),
"`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder."
);
Binder { value, bound_vars: Default::default() }
}
pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder<I, T> {
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
Binder { value, bound_vars }
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
folder.try_fold_binder(self)
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
visitor.visit_binder(self)
}
}
impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> {
fn try_super_fold_with<F: FallibleTypeFolder<I>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
self.try_map_bound(|ty| ty.try_fold_with(folder))
}
}
impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
self.as_ref().skip_binder().visit_with(visitor)
}
}
impl<I: Interner, T> Binder<I, T> {
pub fn skip_binder(self) -> T {
self.value
}
pub fn bound_vars(&self) -> I::BoundVarKinds {
self.bound_vars
}
pub fn as_ref(&self) -> Binder<I, &T> {
Binder { value: &self.value, bound_vars: self.bound_vars }
}
pub fn as_deref(&self) -> Binder<I, &T::Target>
where
T: Deref,
{
Binder { value: &self.value, bound_vars: self.bound_vars }
}
pub fn map_bound_ref<F, U: TypeVisitable<I>>(&self, f: F) -> Binder<I, U>
where
F: FnOnce(&T) -> U,
{
self.as_ref().map_bound(f)
}
pub fn map_bound<F, U: TypeVisitable<I>>(self, f: F) -> Binder<I, U>
where
F: FnOnce(T) -> U,
{
let Binder { value, bound_vars } = self;
let value = f(value);
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
Binder { value, bound_vars }
}
pub fn try_map_bound<F, U: TypeVisitable<I>, E>(self, f: F) -> Result<Binder<I, U>, E>
where
F: FnOnce(T) -> Result<U, E>,
{
let Binder { value, bound_vars } = self;
let value = f(value)?;
if cfg!(debug_assertions) {
let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
Ok(Binder { value, bound_vars })
}
pub fn rebind<U>(&self, value: U) -> Binder<I, U>
where
U: TypeVisitable<I>,
{
Binder::bind_with_vars(value, self.bound_vars)
}
pub fn no_bound_vars(self) -> Option<T>
where
T: TypeVisitable<I>,
{
if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
}
pub fn split<U, V, F>(self, f: F) -> (Binder<I, U>, Binder<I, V>)
where
F: FnOnce(T) -> (U, V),
{
let Binder { value, bound_vars } = self;
let (u, v) = f(value);
(Binder { value: u, bound_vars }, Binder { value: v, bound_vars })
}
}
impl<I: Interner, T> Binder<I, Option<T>> {
pub fn transpose(self) -> Option<Binder<I, T>> {
let Binder { value, bound_vars } = self;
value.map(|value| Binder { value, bound_vars })
}
}
impl<I: Interner, T: IntoIterator> Binder<I, T> {
pub fn iter(self) -> impl Iterator<Item = Binder<I, T::Item>> {
let Binder { value, bound_vars } = self;
value.into_iter().map(move |value| Binder { value, bound_vars })
}
}
pub struct ValidateBoundVars<I: Interner> {
bound_vars: I::BoundVarKinds,
binder_index: ty::DebruijnIndex,
visited: SsoHashSet<(ty::DebruijnIndex, I::Ty)>,
}
impl<I: Interner> ValidateBoundVars<I> {
pub fn new(bound_vars: I::BoundVarKinds) -> Self {
ValidateBoundVars {
bound_vars,
binder_index: ty::INNERMOST,
visited: SsoHashSet::default(),
}
}
}
impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
type Result = ControlFlow<()>;
fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
self.binder_index.shift_in(1);
let result = t.super_visit_with(self);
self.binder_index.shift_out(1);
result
}
fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
if t.outer_exclusive_binder() < self.binder_index
|| !self.visited.insert((self.binder_index, t))
{
return ControlFlow::Break(());
}
match t.kind() {
ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
let idx = bound_ty.var().as_usize();
if self.bound_vars.len() <= idx {
panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars);
}
bound_ty.assert_eq(self.bound_vars[idx]);
}
_ => {}
};
t.super_visit_with(self)
}
fn visit_region(&mut self, r: I::Region) -> Self::Result {
match r.kind() {
ty::ReBound(index, br) if index == self.binder_index => {
let idx = br.var().as_usize();
if self.bound_vars.len() <= idx {
panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars);
}
br.assert_eq(self.bound_vars[idx]);
}
_ => (),
};
ControlFlow::Continue(())
}
}