Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename folding/visiting traits #767

Merged
merged 6 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
- [Application types](./types/rust_types/application_ty.md)
- [Rust lifetimes](./types/rust_lifetimes.md)
- [Operations](./types/operations.md)
- [Fold and the Folder trait](./types/operations/fold.md)
- [TypeFoldable and the TypeFolder trait](./types/operations/fold.md)
- [Lowering Rust IR to logic](./clauses.md)
- [Goals and clauses](./clauses/goals_and_clauses.md)
- [Type equality and unification](./clauses/type_equality.md)
Expand Down
66 changes: 33 additions & 33 deletions book/src/types/operations/fold.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
# Fold and the Folder trait
# TypeFoldable and the TypeFolder trait

The [`Fold`] trait permits one to traverse a type or other term in the
The [`TypeFoldable`] trait permits one to traverse a type or other term in the
chalk-ir and make a copy of it, possibly making small substitutions or
alterations along the way. Folding also allows copying a term from one
interner to another.

[`Fold`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html
[`TypeFoldable`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.TypeFoldable.html

To use the [`Fold`] trait, one invokes the [`Fold::fold_with`] method, supplying some
To use the [`TypeFoldable`] trait, one invokes the [`TypeFoldable::fold_with`] method, supplying some
"folder" as well as the number of "in scope binders" for that term (typically `0`
to start):

```rust,ignore
let output_ty = input_ty.fold_with(&mut folder, 0);
```

[`Fold::fold_with`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#tymethod.fold_with
[`TypeFoldable::fold_with`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.TypeFoldable.html#tymethod.fold_with

The folder is some instance of the [`Folder`] trait. This trait
The folder is some instance of the [`TypeFolder`] trait. This trait
defines a few key callbacks that allow you to substitute different
values as the fold proceeds. For example, when a type is folded, the
folder can substitute a new type in its place.

[`Folder`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.Folder.html
[`TypeFolder`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.TypeFolder.html

## Uses for folders

A common use for `Fold` is to permit a substitution -- that is,
A common use for `TypeFoldable` is to permit a substitution -- that is,
replacing generic type parameters with their values.

## From Fold to Folder to SuperFold and back again
## From TypeFoldable to TypeFolder to TypeSuperFoldable and back again

The overall flow of folding is like this.

1. [`Fold::fold_with`] is invoked on the outermost term. It recursively
1. [`TypeFoldable::fold_with`] is invoked on the outermost term. It recursively
walks the term.
2. For those sorts of terms (types, lifetimes, goals, program clauses) that have
callbacks in the [`Folder`] trait, invoking [`Fold::fold_with`] will in turn
invoke the corresponding method on the [`Folder`] trait, such as `Folder::fold_ty`.
3. The default implementation of `Folder::fold_ty`, in turn, invokes
`SuperFold::super_fold_with`. This will recursively fold the
callbacks in the [`TypeFolder`] trait, invoking [`TypeFoldable::fold_with`] will in turn
invoke the corresponding method on the [`TypeFolder`] trait, such as `TypeFolder::fold_ty`.
3. The default implementation of `TypeFolder::fold_ty`, in turn, invokes
`TypeSuperFoldable::super_fold_with`. This will recursively fold the
contents of the type. In some cases, the `super_fold_with`
implementation invokes more specialized methods on [`Folder`], such
as [`Folder::fold_free_var_ty`], which makes it easier to write
implementation invokes more specialized methods on [`TypeFolder`], such
as [`TypeFolder::fold_free_var_ty`], which makes it easier to write
folders that just intercept *certain* types.

[`Folder::fold_free_var_ty`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.Folder.html#method.fold_free_var_ty
[`TypeFolder::fold_free_var_ty`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.TypeFolder.html#method.fold_free_var_ty

Thus, as a user, you can customize folding by:

* Defining your own `Folder` type
* Defining your own `TypeFolder` type
* Implementing the appropriate methods to "intercept" types/lifetimes/etc at the right level of
detail
* In those methods, if you find a case where you would prefer not to
substitute a new value, then invoke `SuperFold::super_fold_with` to
substitute a new value, then invoke `TypeSuperFoldable::super_fold_with` to
return to the default behavior.

## The `binders` argument

Each callback in the [`Folder`] trait takes a `binders` argument. This indicates
Each callback in the [`TypeFolder`] trait takes a `binders` argument. This indicates
the number of binders that we have traversed during folding, which is relevant for De Bruijn indices.
So e.g. a bound variable with depth 1, if invoked with a `binders` value of 1, indicates something that was bound to something external to the fold.

Expand All @@ -70,32 +70,32 @@ Foo<'a>: for<'b> Bar<'b>

In this case, `Foo<'a>` gets visited with depth 0 and `Bar<'b>` gets visited with depth 1.

## The `Fold::Result` associated type
## The `TypeFoldable::Result` associated type

The `Fold` trait defines a [`Result`] associated type, indicating the
The `TypeFoldable` trait defines a [`Result`] associated type, indicating the
type that will result from folding.

[`Result`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.Fold.html#associatedtype.Result
[`Result`]: https://rust-lang.github.io/chalk/chalk_ir/fold/trait.TypeFoldable.html#associatedtype.Result

## When to implement the Fold and SuperFold traits
## When to implement the TypeFoldable and TypeSuperFoldable traits

Any piece of IR that represents a kind of "term" (e.g., a type, part
of a type, or a goal, etc) in the logic should implement `Fold`. We
also implement `Fold` for common collection types like `Vec` as well
of a type, or a goal, etc) in the logic should implement `TypeFoldable`. We
also implement `TypeFoldable` for common collection types like `Vec` as well
as tuples, references, etc.

The `SuperFold` trait should only be implemented for those types that
have a callback defined on the `Folder` trait (e.g., types and
The `TypeSuperFoldable` trait should only be implemented for those types that
have a callback defined on the `TypeFolder` trait (e.g., types and
lifetimes).

## Derives

Using the `chalk-derive` crate, you can auto-derive the `Fold` trait.
There isn't presently a derive for `SuperFold` since it is very rare
to require it. The derive for `Fold` is a bit cludgy and requires:
Using the `chalk-derive` crate, you can auto-derive the `TypeFoldable` trait.
There isn't presently a derive for `TypeSuperFoldable` since it is very rare
to require it. The derive for `TypeFoldable` is a bit cludgy and requires:

* You must import `Fold` into scope.
* The type you are deriving `Fold` on must have either:
* You must import `TypeFoldable` into scope.
* The type you are deriving `TypeFoldable` on must have either:
* A type parameter that has a `Interner` bound, like `I: Interner`
* A type parameter that has a `HasInterner` bound, like `I: HasInterner`
* The `has_interner(XXX)` attribute.
Expand Down
22 changes: 11 additions & 11 deletions book/src/what_is_chalk/crates.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The following crate is an implementation detail, used internally by `chalk-solve

* The `chalk-engine` crate, which defines the actual engine that solves logical predicate. This
engine is quite general and not really specific to Rust.
* The `chalk-derive` crate defines custom derives for the `chalk_ir::fold::Fold` trait and other
* The `chalk-derive` crate defines custom derives for the `chalk_ir::fold::TypeFoldable` trait and other
such things.

## Crates for standalone REPL and testing
Expand All @@ -37,11 +37,11 @@ define a kind of "minimal embedding" of chalk.

## The chalk-solve crate

| The `chalk-solve` crate | |
|---|--- |
| Purpose: | to solve a given goal |
| Depends on IR: | chalk-ir and rust-ir |
| Context required: | `RustIrDatabase` |
| The `chalk-solve` crate | |
| ----------------------- | --------------------- |
| Purpose: | to solve a given goal |
| Depends on IR: | chalk-ir and rust-ir |
| Context required: | `RustIrDatabase` |

The `chalk-solve` crate exposes a key type called `Solver`. This is a
solver that, given a goal (expressed in chalk-ir) will solve the goal
Expand All @@ -60,11 +60,11 @@ provide needed context for the solver -- notably, the solver can ask:

## The chalk-engine crate

| The `chalk-engine` crate | |
|---|--- |
| Purpose: | define the base solving strategy |
| IR: | none |
| Context required: | `Context` trait |
| The `chalk-engine` crate | |
| ------------------------ | -------------------------------- |
| Purpose: | define the base solving strategy |
| IR: | none |
| Context required: | `Context` trait |

For the purposes of chalk, the `chalk-engine` crate is effectively
encapsulated by `chalk-solve`. It defines the base SLG engine. It is
Expand Down
44 changes: 24 additions & 20 deletions chalk-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ enum DeriveKind {
}

decl_derive!([HasInterner, attributes(has_interner)] => derive_has_interner);
decl_derive!([Visit, attributes(has_interner)] => derive_visit);
decl_derive!([SuperVisit, attributes(has_interner)] => derive_super_visit);
decl_derive!([Fold, attributes(has_interner)] => derive_fold);
decl_derive!([TypeVisitable, attributes(has_interner)] => derive_type_visitable);
decl_derive!([TypeSuperVisitable, attributes(has_interner)] => derive_type_super_visitable);
decl_derive!([TypeFoldable, attributes(has_interner)] => derive_type_foldable);
decl_derive!([Zip, attributes(has_interner)] => derive_zip);

fn derive_has_interner(mut s: synstructure::Structure) -> TokenStream {
Expand All @@ -136,24 +136,28 @@ fn derive_has_interner(mut s: synstructure::Structure) -> TokenStream {
)
}

/// Derives Visit for structs and enums for which one of the following is true:
/// Derives TypeVisitable for structs and enums for which one of the following is true:
/// - It has a `#[has_interner(TheInterner)]` attribute
/// - There is a single parameter `T: HasInterner` (does not have to be named `T`)
/// - There is a single parameter `I: Interner` (does not have to be named `I`)
fn derive_visit(s: synstructure::Structure) -> TokenStream {
derive_any_visit(s, parse_quote! { Visit }, parse_quote! { visit_with })
fn derive_type_visitable(s: synstructure::Structure) -> TokenStream {
derive_any_type_visitable(
s,
parse_quote! { TypeVisitable },
parse_quote! { visit_with },
)
}

/// Same as Visit, but derives SuperVisit instead
fn derive_super_visit(s: synstructure::Structure) -> TokenStream {
derive_any_visit(
/// Same as TypeVisitable, but derives TypeSuperVisitable instead
fn derive_type_super_visitable(s: synstructure::Structure) -> TokenStream {
derive_any_type_visitable(
s,
parse_quote! { SuperVisit },
parse_quote! { TypeSuperVisitable },
parse_quote! { super_visit_with },
)
}

fn derive_any_visit(
fn derive_any_type_visitable(
mut s: synstructure::Structure,
trait_name: Ident,
method_name: Ident,
Expand All @@ -164,13 +168,13 @@ fn derive_any_visit(

let body = s.each(|bi| {
quote! {
::chalk_ir::try_break!(::chalk_ir::visit::Visit::visit_with(#bi, visitor, outer_binder));
::chalk_ir::try_break!(::chalk_ir::visit::TypeVisitable::visit_with(#bi, visitor, outer_binder));
}
});

if kind == DeriveKind::FromHasInterner {
let param = get_generic_param_name(input).unwrap();
s.add_where_predicate(parse_quote! { #param: ::chalk_ir::visit::Visit<#interner> });
s.add_where_predicate(parse_quote! { #param: ::chalk_ir::visit::TypeVisitable<#interner> });
}

s.add_bounds(synstructure::AddBounds::None);
Expand All @@ -179,7 +183,7 @@ fn derive_any_visit(
quote! {
fn #method_name <B>(
&self,
visitor: &mut dyn ::chalk_ir::visit::Visitor < #interner, BreakTy = B >,
visitor: &mut dyn ::chalk_ir::visit::TypeVisitor < #interner, BreakTy = B >,
outer_binder: ::chalk_ir::DebruijnIndex,
) -> std::ops::ControlFlow<B> {
match *self {
Expand Down Expand Up @@ -250,11 +254,11 @@ fn derive_zip(mut s: synstructure::Structure) -> TokenStream {
)
}

/// Derives Fold for structs and enums for which one of the following is true:
/// Derives TypeFoldable for structs and enums for which one of the following is true:
/// - It has a `#[has_interner(TheInterner)]` attribute
/// - There is a single parameter `T: HasInterner` (does not have to be named `T`)
/// - There is a single parameter `I: Interner` (does not have to be named `I`)
fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
fn derive_type_foldable(mut s: synstructure::Structure) -> TokenStream {
s.underscore_const(true);
s.bind_with(|_| synstructure::BindStyle::Move);

Expand All @@ -265,7 +269,7 @@ fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
vi.construct(|_, index| {
let bind = &bindings[index];
quote! {
::chalk_ir::fold::Fold::fold_with(#bind, folder, outer_binder)?
::chalk_ir::fold::TypeFoldable::fold_with(#bind, folder, outer_binder)?
}
})
});
Expand All @@ -277,7 +281,7 @@ fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
let param = get_generic_param_name(input).unwrap();
s.add_impl_generic(parse_quote! { _U })
.add_where_predicate(
parse_quote! { #param: ::chalk_ir::fold::Fold<#interner, Result = _U> },
parse_quote! { #param: ::chalk_ir::fold::TypeFoldable<#interner, Result = _U> },
)
.add_where_predicate(
parse_quote! { _U: ::chalk_ir::interner::HasInterner<Interner = #interner> },
Expand All @@ -289,13 +293,13 @@ fn derive_fold(mut s: synstructure::Structure) -> TokenStream {

s.add_bounds(synstructure::AddBounds::None);
s.bound_impl(
quote!(::chalk_ir::fold::Fold<#interner>),
quote!(::chalk_ir::fold::TypeFoldable<#interner>),
quote! {
type Result = #result;

fn fold_with<E>(
self,
folder: &mut dyn ::chalk_ir::fold::Folder < #interner, Error = E >,
folder: &mut dyn ::chalk_ir::fold::TypeFolder < #interner, Error = E >,
outer_binder: ::chalk_ir::DebruijnIndex,
) -> ::std::result::Result<Self::Result, E> {
Ok(match self { #body })
Expand Down
10 changes: 5 additions & 5 deletions chalk-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
use std::cmp::min;
use std::usize;

use chalk_derive::{Fold, HasInterner, Visit};
use chalk_derive::{HasInterner, TypeFoldable, TypeVisitable};
use chalk_ir::interner::Interner;
use chalk_ir::{
AnswerSubst, Canonical, ConstrainedSubst, Constraint, DebruijnIndex, Goal, InEnvironment,
Expand All @@ -78,13 +78,13 @@ mod table;
mod tables;

index_struct! {
pub struct TableIndex { // FIXME: pub b/c Fold
pub struct TableIndex { // FIXME: pub b/c TypeFoldable
value: usize,
}
}

/// The paper describes these as `A :- D | G`.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable, HasInterner)]
pub struct ExClause<I: Interner> {
/// The substitution which, applied to the goal of our table,
/// would yield A.
Expand Down Expand Up @@ -168,7 +168,7 @@ impl TimeStamp {
///
/// trying to solve `?T: Foo` would immediately require solving `?T:
/// Sized`, and hence would flounder.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
pub struct FlounderedSubgoal<I: Interner> {
/// Literal that floundered.
pub floundered_literal: Literal<I>,
Expand Down Expand Up @@ -209,7 +209,7 @@ pub struct CompleteAnswer<I: Interner> {
}

/// Either `A` or `~A`, where `A` is a `Env |- Goal`.
#[derive(Clone, Debug, Fold, Visit)]
#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
pub enum Literal<I: Interner> {
// FIXME: pub b/c fold
Positive(InEnvironment<Goal<I>>),
Expand Down
8 changes: 4 additions & 4 deletions chalk-engine/src/normalize_deep.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use chalk_ir::fold::shift::Shift;
use chalk_ir::fold::{Fold, Folder};
use chalk_ir::fold::{TypeFoldable, TypeFolder};
use chalk_ir::interner::Interner;
use chalk_ir::*;
use chalk_solve::infer::InferenceTable;
Expand All @@ -21,7 +21,7 @@ impl<I: Interner> DeepNormalizer<'_, I> {
/// See also `InferenceTable::canonicalize`, which -- during real
/// processing -- is often used to capture the "current state" of
/// variables.
pub fn normalize_deep<T: Fold<I>>(
pub fn normalize_deep<T: TypeFoldable<I>>(
table: &mut InferenceTable<I>,
interner: I,
value: T,
Expand All @@ -35,10 +35,10 @@ impl<I: Interner> DeepNormalizer<'_, I> {
}
}

impl<I: Interner> Folder<I> for DeepNormalizer<'_, I> {
impl<I: Interner> TypeFolder<I> for DeepNormalizer<'_, I> {
type Error = NoSolution;

fn as_dyn(&mut self) -> &mut dyn Folder<I, Error = Self::Error> {
fn as_dyn(&mut self) -> &mut dyn TypeFolder<I, Error = Self::Error> {
self
}

Expand Down
4 changes: 2 additions & 2 deletions chalk-engine/src/slg/resolvent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::slg::ResolventOps;
use crate::{ExClause, Literal, TimeStamp};
use chalk_ir::cast::Caster;
use chalk_ir::fold::shift::Shift;
use chalk_ir::fold::Fold;
use chalk_ir::fold::TypeFoldable;
use chalk_ir::interner::{HasInterner, Interner};
use chalk_ir::zip::{Zip, Zipper};
use chalk_ir::*;
Expand Down Expand Up @@ -708,7 +708,7 @@ impl<'i, I: Interner> Zipper<I> for AnswerSubstitutor<'i, I> {
pending: &Binders<T>,
) -> Fallible<()>
where
T: HasInterner<Interner = I> + Zip<I> + Fold<I, Result = T>,
T: HasInterner<Interner = I> + Zip<I> + TypeFoldable<I, Result = T>,
{
self.outer_binder.shift_in();
Zip::zip_with(
Expand Down
Loading