| //! Traits for transforming bits of IR. |
| |
| 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; |
| |
| /// A "folder" is a transformer that can be used to make a copy of |
| /// some term -- that is, some bit of IR, such as a `Goal` -- with |
| /// certain changes applied. The idea is that it contains methods that |
| /// let you swap types/lifetimes for new types/lifetimes; meanwhile, |
| /// each bit of IR implements the `TypeFoldable` trait which, given a |
| /// `FallibleTypeFolder`, will reconstruct itself, invoking the folder's |
| /// methods to transform each of the types/lifetimes embedded within. |
| /// |
| /// As the name suggests, folds performed by `FallibleTypeFolder` can |
| /// fail (with type `Error`); if the folder cannot fail, consider |
| /// implementing `TypeFolder` instead (which is an infallible, but |
| /// otherwise equivalent, trait). |
| /// |
| /// # Usage patterns |
| /// |
| /// ## Substituting for free variables |
| /// |
| /// Most of the time, though, we are not interested in adjust |
| /// arbitrary types/lifetimes, but rather just free variables (even |
| /// more often, just free existential variables) that appear within |
| /// the term. |
| /// |
| /// For this reason, the `FallibleTypeFolder` trait extends two other |
| /// traits that contain methods that are invoked when just those particular |
| /// |
| /// In particular, folders can intercept references to free variables |
| /// (either existentially or universally quantified) and replace them |
| /// with other types/lifetimes as appropriate. |
| /// |
| /// To create a folder `F`, one never implements `FallibleTypeFolder` |
| /// directly, but instead implements one of each of these three sub-traits: |
| /// |
| /// - `FreeVarFolder` -- folds `BoundVar` instances that appear free |
| /// in the term being folded (use `DefaultFreeVarFolder` to |
| /// ignore/forbid these altogether) |
| /// - `InferenceFolder` -- folds existential `InferenceVar` instances |
| /// that appear in the term being folded (use |
| /// `DefaultInferenceFolder` to ignore/forbid these altogether) |
| /// - `PlaceholderFolder` -- folds universal `Placeholder` instances |
| /// that appear in the term being folded (use |
| /// `DefaultPlaceholderFolder` to ignore/forbid these altogether) |
| /// |
| /// To **apply** a folder, use the `TypeFoldable::try_fold_with` method, |
| /// like so |
| /// |
| /// ```rust,ignore |
| /// let x = x.try_fold_with(&mut folder, 0); |
| /// ``` |
| pub trait FallibleTypeFolder<I: Interner> { |
| /// The type this folder returns when folding fails. This is |
| /// commonly [`NoSolution`]. |
| type Error; |
| |
| /// Creates a `dyn` value from this folder. Unfortunately, this |
| /// must be added manually to each impl of FallibleTypeFolder; it |
| /// permits the default implements below to create a |
| /// `&mut dyn FallibleTypeFolder` from `Self` without knowing what |
| /// `Self` is (by invoking this method). Effectively, this limits |
| /// impls of `FallibleTypeFolder` to types for which we are able to |
| /// create a dyn value (i.e., not `[T]` types). |
| fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<I, Error = Self::Error>; |
| |
| /// Top-level callback: invoked for each `Ty<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `try_super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `try_fold_free_var_ty`. |
| 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) |
| } |
| |
| /// Top-level callback: invoked for each `Lifetime<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `try_super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `try_fold_free_var_lifetime`. |
| 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) |
| } |
| |
| /// Top-level callback: invoked for each `Const<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `try_super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `try_fold_free_var_const`. |
| 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) |
| } |
| |
| /// Invoked for every program clause. By default, recursively folds the goals contents. |
| 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) |
| } |
| |
| /// Invoked for every goal. By default, recursively folds the goals contents. |
| 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) |
| } |
| |
| /// If overridden to return true, then folding will panic if a |
| /// free variable is encountered. This should be done if free |
| /// type/lifetime variables are not expected. |
| fn forbid_free_vars(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for `TyKind::BoundVar` instances that are not bound |
| /// within the type being folded over: |
| /// |
| /// - `depth` is the depth of the `TyKind::BoundVar`; this has |
| /// been adjusted to account for binders in scope. |
| /// - `binders` is the number of binders in scope. |
| /// |
| /// This should return a type suitable for a context with |
| /// `binders` in scope. |
| 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())) |
| } |
| } |
| |
| /// As `try_fold_free_var_ty`, but for lifetimes. |
| 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())) |
| } |
| } |
| |
| /// As `try_fold_free_var_ty`, but for constants. |
| 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())) |
| } |
| } |
| |
| /// If overridden to return true, we will panic when a free |
| /// placeholder type/lifetime/const is encountered. |
| fn forbid_free_placeholders(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for each occurrence of a placeholder type; these are |
| /// used when we instantiate binders universally. Returns a type |
| /// to use instead, which should be suitably shifted to account |
| /// for `binders`. |
| /// |
| /// - `universe` is the universe of the `TypeName::ForAll` that was found |
| /// - `binders` is the number of binders in scope |
| #[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())) |
| } |
| } |
| |
| /// As with `try_fold_free_placeholder_ty`, but for lifetimes. |
| #[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())) |
| } |
| } |
| |
| /// As with `try_fold_free_placeholder_ty`, but for constants. |
| #[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)?, |
| )) |
| } |
| } |
| |
| /// If overridden to return true, inference variables will trigger |
| /// panics when folded. Used when inference variables are |
| /// unexpected. |
| fn forbid_inference_vars(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for each occurrence of a inference type; these are |
| /// used when we instantiate binders universally. Returns a type |
| /// to use instead, which should be suitably shifted to account |
| /// for `binders`. |
| /// |
| /// - `universe` is the universe of the `TypeName::ForAll` that was found |
| /// - `binders` is the number of binders in scope |
| #[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)) |
| } |
| } |
| |
| /// As with `try_fold_inference_ty`, but for lifetimes. |
| #[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())) |
| } |
| } |
| |
| /// As with `try_fold_inference_ty`, but for constants. |
| #[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)?, |
| )) |
| } |
| } |
| |
| /// Gets the interner that is being folded from. |
| fn interner(&self) -> I; |
| } |
| |
| /// A "folder" is a transformer that can be used to make a copy of |
| /// some term -- that is, some bit of IR, such as a `Goal` -- with |
| /// certain changes applied. The idea is that it contains methods that |
| /// let you swap types/lifetimes for new types/lifetimes; meanwhile, |
| /// each bit of IR implements the `TypeFoldable` trait which, given a |
| /// `TypeFolder`, will reconstruct itself, invoking the folder's methods |
| /// to transform each of the types/lifetimes embedded within. |
| /// |
| /// Folds performed by `TypeFolder` cannot fail. If folds might fail, |
| /// consider implementing `FallibleTypeFolder` instead (which is a |
| /// fallible, but otherwise equivalent, trait). |
| /// |
| /// # Usage patterns |
| /// |
| /// ## Substituting for free variables |
| /// |
| /// Most of the time, though, we are not interested in adjust |
| /// arbitrary types/lifetimes, but rather just free variables (even |
| /// more often, just free existential variables) that appear within |
| /// the term. |
| /// |
| /// For this reason, the `TypeFolder` trait extends two other traits that |
| /// contain methods that are invoked when just those particular |
| /// |
| /// In particular, folders can intercept references to free variables |
| /// (either existentially or universally quantified) and replace them |
| /// with other types/lifetimes as appropriate. |
| /// |
| /// To create a folder `F`, one never implements `TypeFolder` directly, but instead |
| /// implements one of each of these three sub-traits: |
| /// |
| /// - `FreeVarFolder` -- folds `BoundVar` instances that appear free |
| /// in the term being folded (use `DefaultFreeVarFolder` to |
| /// ignore/forbid these altogether) |
| /// - `InferenceFolder` -- folds existential `InferenceVar` instances |
| /// that appear in the term being folded (use |
| /// `DefaultInferenceFolder` to ignore/forbid these altogether) |
| /// - `PlaceholderFolder` -- folds universal `Placeholder` instances |
| /// that appear in the term being folded (use |
| /// `DefaultPlaceholderFolder` to ignore/forbid these altogether) |
| /// |
| /// To **apply** a folder, use the `TypeFoldable::fold_with` method, like so |
| /// |
| /// ```rust,ignore |
| /// let x = x.fold_with(&mut folder, 0); |
| /// ``` |
| pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Infallible> { |
| /// Creates a `dyn` value from this folder. Unfortunately, this |
| /// must be added manually to each impl of TypeFolder; it permits the |
| /// default implements below to create a `&mut dyn TypeFolder` from |
| /// `Self` without knowing what `Self` is (by invoking this |
| /// method). Effectively, this limits impls of `TypeFolder` to types |
| /// for which we are able to create a dyn value (i.e., not `[T]` |
| /// types). |
| fn as_dyn(&mut self) -> &mut dyn TypeFolder<I>; |
| |
| /// Top-level callback: invoked for each `Ty<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `fold_free_var_ty`. |
| fn fold_ty(&mut self, ty: Ty<I>, outer_binder: DebruijnIndex) -> Ty<I> { |
| ty.super_fold_with(TypeFolder::as_dyn(self), outer_binder) |
| } |
| |
| /// Top-level callback: invoked for each `Lifetime<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `fold_free_var_lifetime`. |
| fn fold_lifetime(&mut self, lifetime: Lifetime<I>, outer_binder: DebruijnIndex) -> Lifetime<I> { |
| lifetime.super_fold_with(TypeFolder::as_dyn(self), outer_binder) |
| } |
| |
| /// Top-level callback: invoked for each `Const<I>` that is |
| /// encountered when folding. By default, invokes |
| /// `super_fold_with`, which will in turn invoke the more |
| /// specialized folding methods below, like `fold_free_var_const`. |
| fn fold_const(&mut self, constant: Const<I>, outer_binder: DebruijnIndex) -> Const<I> { |
| constant.super_fold_with(TypeFolder::as_dyn(self), outer_binder) |
| } |
| |
| /// Invoked for every program clause. By default, recursively folds the goals contents. |
| fn fold_program_clause( |
| &mut self, |
| clause: ProgramClause<I>, |
| outer_binder: DebruijnIndex, |
| ) -> ProgramClause<I> { |
| clause.super_fold_with(TypeFolder::as_dyn(self), outer_binder) |
| } |
| |
| /// Invoked for every goal. By default, recursively folds the goals contents. |
| fn fold_goal(&mut self, goal: Goal<I>, outer_binder: DebruijnIndex) -> Goal<I> { |
| goal.super_fold_with(TypeFolder::as_dyn(self), outer_binder) |
| } |
| |
| /// If overridden to return true, then folding will panic if a |
| /// free variable is encountered. This should be done if free |
| /// type/lifetime variables are not expected. |
| fn forbid_free_vars(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for `TyKind::BoundVar` instances that are not bound |
| /// within the type being folded over: |
| /// |
| /// - `depth` is the depth of the `TyKind::BoundVar`; this has |
| /// been adjusted to account for binders in scope. |
| /// - `binders` is the number of binders in scope. |
| /// |
| /// This should return a type suitable for a context with |
| /// `binders` in scope. |
| 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)) |
| } |
| } |
| |
| /// As `fold_free_var_ty`, but for lifetimes. |
| 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)) |
| } |
| } |
| |
| /// As `fold_free_var_ty`, but for constants. |
| 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)) |
| } |
| } |
| |
| /// If overridden to return true, we will panic when a free |
| /// placeholder type/lifetime/const is encountered. |
| fn forbid_free_placeholders(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for each occurrence of a placeholder type; these are |
| /// used when we instantiate binders universally. Returns a type |
| /// to use instead, which should be suitably shifted to account |
| /// for `binders`. |
| /// |
| /// - `universe` is the universe of the `TypeName::ForAll` that was found |
| /// - `binders` is the number of binders in scope |
| #[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)) |
| } |
| } |
| |
| /// As with `fold_free_placeholder_ty`, but for lifetimes. |
| #[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)) |
| } |
| } |
| |
| /// As with `fold_free_placeholder_ty`, but for constants. |
| #[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), |
| ) |
| } |
| } |
| |
| /// If overridden to return true, inference variables will trigger |
| /// panics when folded. Used when inference variables are |
| /// unexpected. |
| fn forbid_inference_vars(&self) -> bool { |
| false |
| } |
| |
| /// Invoked for each occurrence of a inference type; these are |
| /// used when we instantiate binders universally. Returns a type |
| /// to use instead, which should be suitably shifted to account |
| /// for `binders`. |
| /// |
| /// - `universe` is the universe of the `TypeName::ForAll` that was found |
| /// - `binders` is the number of binders in scope |
| #[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) |
| } |
| } |
| |
| /// As with `fold_inference_ty`, but for lifetimes. |
| #[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)) |
| } |
| } |
| |
| /// As with `fold_inference_ty`, but for constants. |
| #[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), |
| ) |
| } |
| } |
| |
| /// Gets the interner that is being folded from. |
| fn interner(&self) -> I; |
| } |
| |
| /// Applies the given `TypeFolder` to a value, producing a folded result |
| /// of type `Self::Result`. The result type is typically the same as |
| /// the source type, but in some cases we convert from borrowed |
| /// to owned as well (e.g., the folder for `&T` will fold to a fresh |
| /// `T`; well, actually `T::Result`). |
| pub trait TypeFoldable<I: Interner>: Debug + Sized { |
| /// Apply the given folder `folder` to `self`; `binders` is the |
| /// number of binders that are in scope when beginning the |
| /// folder. Typically `binders` starts as 0, but is adjusted when |
| /// we encounter `Binders<T>` in the IR or other similar |
| /// constructs. |
| fn try_fold_with<E>( |
| self, |
| folder: &mut dyn FallibleTypeFolder<I, Error = E>, |
| outer_binder: DebruijnIndex, |
| ) -> Result<Self, E>; |
| |
| /// A convenient alternative to `try_fold_with` for use with infallible |
| /// folders. Do not override this method, to ensure coherence with |
| /// `try_fold_with`. |
| fn fold_with(self, folder: &mut dyn TypeFolder<I>, outer_binder: DebruijnIndex) -> Self { |
| self.try_fold_with(FallibleTypeFolder::as_dyn(folder), outer_binder) |
| .unwrap() |
| } |
| } |
| |
| /// For types where "fold" invokes a callback on the `TypeFolder`, the |
| /// `TypeSuperFoldable` trait captures the recursive behavior that folds all |
| /// the contents of the type. |
| pub trait TypeSuperFoldable<I: Interner>: TypeFoldable<I> { |
| /// Recursively folds the value. |
| fn try_super_fold_with<E>( |
| self, |
| folder: &mut dyn FallibleTypeFolder<I, Error = E>, |
| outer_binder: DebruijnIndex, |
| ) -> Result<Self, E>; |
| |
| /// A convenient alternative to `try_super_fold_with` for use with |
| /// infallible folders. Do not override this method, to ensure coherence |
| /// with `try_super_fold_with`. |
| 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() |
| } |
| } |
| |
| /// "Folding" a type invokes the `try_fold_ty` method on the folder; this |
| /// usually (in turn) invokes `try_super_fold_ty` to fold the individual |
| /// parts. |
| 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) |
| } |
| } |
| |
| /// "Super fold" for a type invokes te more detailed callbacks on the type |
| 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) { |
| // This variable was bound outside of the binders |
| // that we have traversed during folding; |
| // therefore, it is free. Let the folder have a |
| // crack at it. |
| folder.try_fold_free_var_ty(bound_var1, outer_binder)? |
| } else { |
| // This variable was bound within the binders that |
| // we folded over, so just return a bound |
| // variable. |
| 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()), |
| }) |
| } |
| } |
| |
| /// "Folding" a lifetime invokes the `fold_lifetime` method on the folder; this |
| /// usually (in turn) invokes `super_fold_lifetime` to fold the individual |
| /// parts. |
| 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) { |
| // This variable was bound outside of the binders |
| // that we have traversed during folding; |
| // therefore, it is free. Let the folder have a |
| // crack at it. |
| folder.try_fold_free_var_lifetime(bound_var1, outer_binder) |
| } else { |
| // This variable was bound within the binders that |
| // we folded over, so just return a bound |
| // variable. |
| 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 {}, |
| } |
| } |
| } |
| |
| /// "Folding" a const invokes the `fold_const` method on the folder; this |
| /// usually (in turn) invokes `super_fold_const` to fold the individual |
| /// parts. |
| 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())), |
| } |
| } |
| } |
| |
| /// Folding a goal invokes the `fold_goal` callback (which will, by |
| /// default, invoke super-fold). |
| 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) |
| } |
| } |
| |
| /// Superfold folds recursively. |
| 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)?, |
| )) |
| } |
| } |
| |
| /// Folding a program clause invokes the `fold_program_clause` |
| /// callback on the folder (which will, by default, invoke the |
| /// `super_fold_with` method on the program clause). |
| 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) |
| } |
| } |