blob: 2dee118100d9e9e3c8790c4aa42d7151d2aad863 [file] [log] [blame]
//! Traits for visiting bits of IR.
use std::fmt::Debug;
use std::ops::ControlFlow;
use crate::{
BoundVar, Const, ConstValue, DebruijnIndex, DomainGoal, Goal, InferenceVar, Interner, Lifetime,
LifetimeData, PlaceholderIndex, ProgramClause, Ty, TyKind, WhereClause,
};
mod binder_impls;
mod boring_impls;
pub mod visitors;
pub use visitors::VisitExt;
/// Unwraps a `ControlFlow` or propagates its `Break` value.
/// This replaces the `Try` implementation that would be used
/// with `std::ops::ControlFlow`.
#[macro_export]
macro_rules! try_break {
($expr:expr) => {
match $expr {
std::ops::ControlFlow::Continue(c) => c,
std::ops::ControlFlow::Break(b) => return std::ops::ControlFlow::Break(b),
}
};
}
/// A "visitor" recursively folds some term -- that is, some bit of IR,
/// such as a `Goal`, and computes a value as a result.
///
///
/// To **apply** a visitor, use the `TypeVisitable::visit_with` method, like so
///
/// ```rust,ignore
/// let result = x.visit_with(&mut visitor, 0);
/// ```
pub trait TypeVisitor<I: Interner> {
/// The "break type" of the visitor, often `()`. It represents the result
/// the visitor yields when it stops visiting.
type BreakTy;
/// Creates a `dyn` value from this visitor. Unfortunately, this
/// must be added manually to each impl of visitor; it permits the
/// default implements below to create a `&mut dyn TypeVisitor` from
/// `Self` without knowing what `Self` is (by invoking this
/// method). Effectively, this limits impls of `visitor` to types
/// for which we are able to create a dyn value (i.e., not `[T]`
/// types).
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy>;
/// Top-level callback: invoked for each `Ty<I>` that is
/// encountered when visiting. By default, invokes
/// `super_visit_with`, which will in turn invoke the more
/// specialized visiting methods below, like `visit_free_var`.
fn visit_ty(&mut self, ty: &Ty<I>, outer_binder: DebruijnIndex) -> ControlFlow<Self::BreakTy> {
ty.super_visit_with(self.as_dyn(), outer_binder)
}
/// Top-level callback: invoked for each `Lifetime<I>` that is
/// encountered when visiting. By default, invokes
/// `super_visit_with`, which will in turn invoke the more
/// specialized visiting methods below, like `visit_free_var`.
fn visit_lifetime(
&mut self,
lifetime: &Lifetime<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
lifetime.super_visit_with(self.as_dyn(), outer_binder)
}
/// Top-level callback: invoked for each `Const<I>` that is
/// encountered when visiting. By default, invokes
/// `super_visit_with`, which will in turn invoke the more
/// specialized visiting methods below, like `visit_free_var`.
fn visit_const(
&mut self,
constant: &Const<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
constant.super_visit_with(self.as_dyn(), outer_binder)
}
/// Invoked for every program clause. By default, recursively visits the goals contents.
fn visit_program_clause(
&mut self,
clause: &ProgramClause<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
clause.super_visit_with(self.as_dyn(), outer_binder)
}
/// Invoked for every goal. By default, recursively visits the goals contents.
fn visit_goal(
&mut self,
goal: &Goal<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
goal.super_visit_with(self.as_dyn(), outer_binder)
}
/// Invoked for each domain goal.
fn visit_domain_goal(
&mut self,
domain_goal: &DomainGoal<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
domain_goal.super_visit_with(self.as_dyn(), outer_binder)
}
/// If overridden to return true, then visiting will panic if a
/// free variable is encountered. This should be done if free
/// type/lifetime/const variables are not expected.
fn forbid_free_vars(&self) -> bool {
false
}
/// Invoked for `BoundVar` instances that are not bound
/// within the type being visited over:
fn visit_free_var(
&mut self,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
if self.forbid_free_vars() {
panic!(
"unexpected free variable `{:?}` with outer binder {:?}",
bound_var, outer_binder
)
} else {
ControlFlow::Continue(())
}
}
/// If overridden to return true, we will panic when a free
/// placeholder type/lifetime 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.
fn visit_free_placeholder(
&mut self,
universe: PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
if self.forbid_free_placeholders() {
panic!("unexpected placeholder type `{:?}`", universe)
} else {
ControlFlow::Continue(())
}
}
/// Invoked for each where clause.
fn visit_where_clause(
&mut self,
where_clause: &WhereClause<I>,
outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
where_clause.super_visit_with(self.as_dyn(), outer_binder)
}
/// If overridden to return true, inference variables will trigger
/// panics when visited. 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.
fn visit_inference_var(
&mut self,
var: InferenceVar,
_outer_binder: DebruijnIndex,
) -> ControlFlow<Self::BreakTy> {
if self.forbid_inference_vars() {
panic!("unexpected inference type `{:?}`", var)
} else {
ControlFlow::Continue(())
}
}
/// Gets the visitor's interner.
fn interner(&self) -> I;
}
/// Applies the given `visitor` to a value, producing a visited result
/// of type `TypeVisitor::Result`.
pub trait TypeVisitable<I: Interner>: Debug {
/// Apply the given visitor `visitor` to `self`; `binders` is the
/// number of binders that are in scope when beginning the
/// visitor. Typically `binders` starts as 0, but is adjusted when
/// we encounter `Binders<T>` in the IR or other similar
/// constructs.
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B>;
}
/// For types where "visit" invokes a callback on the `visitor`, the
/// `TypeSuperVisitable` trait captures the recursive behavior that visits all
/// the contents of the type.
pub trait TypeSuperVisitable<I: Interner>: TypeVisitable<I> {
/// Recursively visits the type contents.
fn super_visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B>;
}
/// "visiting" a type invokes the `visit_ty` method on the visitor; this
/// usually (in turn) invokes `super_visit_ty` to visit the individual
/// parts.
impl<I: Interner> TypeVisitable<I> for Ty<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_ty(self, outer_binder)
}
}
/// "Super visit" for a type invokes the more detailed callbacks on the type
impl<I> TypeSuperVisitable<I> for Ty<I>
where
I: Interner,
{
fn super_visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
let interner = visitor.interner();
match self.kind(interner) {
TyKind::BoundVar(bound_var) => {
if bound_var.shifted_out_to(outer_binder).is_some() {
visitor.visit_free_var(*bound_var, outer_binder)
} else {
ControlFlow::Continue(())
}
}
TyKind::Dyn(clauses) => clauses.visit_with(visitor, outer_binder),
TyKind::InferenceVar(var, _) => visitor.visit_inference_var(*var, outer_binder),
TyKind::Placeholder(ui) => visitor.visit_free_placeholder(*ui, outer_binder),
TyKind::Alias(proj) => proj.visit_with(visitor, outer_binder),
TyKind::Function(fun) => fun.visit_with(visitor, outer_binder),
TyKind::Adt(_id, substitution) => substitution.visit_with(visitor, outer_binder),
TyKind::AssociatedType(_assoc_ty, substitution) => {
substitution.visit_with(visitor, outer_binder)
}
TyKind::Scalar(scalar) => scalar.visit_with(visitor, outer_binder),
TyKind::Str => ControlFlow::Continue(()),
TyKind::Tuple(arity, substitution) => {
try_break!(arity.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::OpaqueType(opaque_ty, substitution) => {
try_break!(opaque_ty.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::Slice(substitution) => substitution.visit_with(visitor, outer_binder),
TyKind::FnDef(fn_def, substitution) => {
try_break!(fn_def.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::Ref(mutability, lifetime, ty) => {
try_break!(mutability.visit_with(visitor, outer_binder));
try_break!(lifetime.visit_with(visitor, outer_binder));
ty.visit_with(visitor, outer_binder)
}
TyKind::Raw(mutability, ty) => {
try_break!(mutability.visit_with(visitor, outer_binder));
ty.visit_with(visitor, outer_binder)
}
TyKind::Never => ControlFlow::Continue(()),
TyKind::Array(ty, const_) => {
try_break!(ty.visit_with(visitor, outer_binder));
const_.visit_with(visitor, outer_binder)
}
TyKind::Closure(id, substitution) => {
try_break!(id.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::Coroutine(coroutine, substitution) => {
try_break!(coroutine.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::CoroutineWitness(witness, substitution) => {
try_break!(witness.visit_with(visitor, outer_binder));
substitution.visit_with(visitor, outer_binder)
}
TyKind::Foreign(foreign_ty) => foreign_ty.visit_with(visitor, outer_binder),
TyKind::Error => ControlFlow::Continue(()),
}
}
}
impl<I: Interner> TypeVisitable<I> for Lifetime<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_lifetime(self, outer_binder)
}
}
impl<I: Interner> TypeSuperVisitable<I> for Lifetime<I> {
fn super_visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
let interner = visitor.interner();
match self.data(interner) {
LifetimeData::BoundVar(bound_var) => {
if bound_var.shifted_out_to(outer_binder).is_some() {
visitor.visit_free_var(*bound_var, outer_binder)
} else {
ControlFlow::Continue(())
}
}
LifetimeData::InferenceVar(var) => visitor.visit_inference_var(*var, outer_binder),
LifetimeData::Placeholder(universe) => {
visitor.visit_free_placeholder(*universe, outer_binder)
}
LifetimeData::Static | LifetimeData::Erased => ControlFlow::Continue(()),
LifetimeData::Phantom(void, ..) => match *void {},
}
}
}
impl<I: Interner> TypeVisitable<I> for Const<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_const(self, outer_binder)
}
}
impl<I: Interner> TypeSuperVisitable<I> for Const<I> {
fn super_visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
let interner = visitor.interner();
match &self.data(interner).value {
ConstValue::BoundVar(bound_var) => {
if bound_var.shifted_out_to(outer_binder).is_some() {
visitor.visit_free_var(*bound_var, outer_binder)
} else {
ControlFlow::Continue(())
}
}
ConstValue::InferenceVar(var) => visitor.visit_inference_var(*var, outer_binder),
ConstValue::Placeholder(universe) => {
visitor.visit_free_placeholder(*universe, outer_binder)
}
ConstValue::Concrete(_) => ControlFlow::Continue(()),
}
}
}
impl<I: Interner> TypeVisitable<I> for Goal<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_goal(self, outer_binder)
}
}
impl<I: Interner> TypeSuperVisitable<I> for Goal<I> {
fn super_visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
let interner = visitor.interner();
self.data(interner).visit_with(visitor, outer_binder)
}
}
impl<I: Interner> TypeVisitable<I> for ProgramClause<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_program_clause(self, outer_binder)
}
}
impl<I: Interner> TypeVisitable<I> for WhereClause<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_where_clause(self, outer_binder)
}
}
impl<I: Interner> TypeVisitable<I> for DomainGoal<I> {
fn visit_with<B>(
&self,
visitor: &mut dyn TypeVisitor<I, BreakTy = B>,
outer_binder: DebruijnIndex,
) -> ControlFlow<B> {
visitor.visit_domain_goal(self, outer_binder)
}
}