Skip to content

Commit

Permalink
Merge pull request #420 from zaharidichev/zd/derive-zip
Browse files Browse the repository at this point in the history
Derive Zip
  • Loading branch information
nikomatsakis authored Apr 29, 2020
2 parents 7e7b624 + 104c1ff commit bc9f4de
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 146 deletions.
64 changes: 63 additions & 1 deletion chalk-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
extern crate proc_macro;

use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use quote::ToTokens;
use syn::{parse_quote, DeriveInput, GenericParam, Ident, TypeParamBound};

use synstructure::decl_derive;
Expand Down Expand Up @@ -120,6 +121,7 @@ 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!([Zip, attributes(has_interner)] => derive_zip);

fn derive_has_interner(mut s: synstructure::Structure) -> TokenStream {
let (interner, _) = find_interner(&mut s);
Expand Down Expand Up @@ -194,6 +196,66 @@ fn derive_any_visit(
)
}

fn each_variant_pair<F, R>(
a: &mut synstructure::Structure,
b: &mut synstructure::Structure,
mut f: F,
) -> TokenStream
where
F: FnMut(&synstructure::VariantInfo<'_>, &synstructure::VariantInfo<'_>) -> R,
R: ToTokens,
{
let mut t = TokenStream::new();
for (v_a, v_b) in a.variants_mut().iter_mut().zip(b.variants_mut().iter_mut()) {
v_a.binding_name(|_, i| Ident::new(&format!("a_{}", i), Span::call_site()));
v_b.binding_name(|_, i| Ident::new(&format!("b_{}", i), Span::call_site()));

let pat_a = v_a.pat();
let pat_b = v_b.pat();
let body = f(v_a, v_b);

quote!((#pat_a, #pat_b) => {#body}).to_tokens(&mut t);
}
t
}

fn derive_zip(mut s: synstructure::Structure) -> TokenStream {
let (interner, _) = find_interner(&mut s);

let mut a = s.clone();
let mut b = s.clone();

let mut body = each_variant_pair(&mut a, &mut b, |v_a, v_b| {
let mut t = TokenStream::new();
for (b_a, b_b) in v_a.bindings().iter().zip(v_b.bindings().iter()) {
quote!(chalk_ir::zip::Zip::zip_with(zipper, #b_a, #b_b)?;).to_tokens(&mut t);
}
quote!(Ok(())).to_tokens(&mut t);
t
});

// when the two variants are different
quote!((_, _) => Err(::chalk_engine::fallible::NoSolution)).to_tokens(&mut body);

s.add_bounds(synstructure::AddBounds::None);
s.bound_impl(
quote!(::chalk_ir::zip::Zip<#interner>),
quote! {

fn zip_with<'i, Z: ::chalk_ir::zip::Zipper<'i, #interner>>(
zipper: &mut Z,
a: &Self,
b: &Self,
) -> ::chalk_engine::fallible::Fallible<()>
where
#interner: 'i,
{
match (a, b) { #body }
}
},
)
}

/// Derives Fold 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`)
Expand Down
34 changes: 17 additions & 17 deletions chalk-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::cast::{Cast, CastTo};
use crate::fold::shift::Shift;
use crate::fold::{Fold, Folder, Subst, SuperFold};
use crate::visit::{SuperVisit, Visit, VisitExt, VisitResult, Visitor};
use chalk_derive::{Fold, HasInterner, SuperVisit, Visit};
use chalk_derive::{Fold, HasInterner, SuperVisit, Visit, Zip};
use chalk_engine::fallible::*;
use std::iter;
use std::marker::PhantomData;
Expand Down Expand Up @@ -602,7 +602,7 @@ impl DebruijnIndex {
/// known. It is referenced within the type using `^1`, indicating
/// a bound type with debruijn index 1 (i.e., skipping through one
/// level of binder).
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct DynTy<I: Interner> {
pub bounds: Binders<QuantifiedWhereClauses<I>>,
}
Expand Down Expand Up @@ -719,7 +719,7 @@ impl PlaceholderIndex {
}

// Fold derive intentionally omitted, folded through Ty
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct ApplicationTy<I: Interner> {
pub name: TypeName<I>,
pub substitution: Substitution<I>,
Expand Down Expand Up @@ -884,7 +884,7 @@ impl<I: Interner> ParameterData<I> {
}
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub enum AliasTy<I: Interner> {
Projection(ProjectionTy<I>),
Opaque(OpaqueTy<I>),
Expand All @@ -908,19 +908,19 @@ impl<I: Interner> AliasTy<I> {
}
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct ProjectionTy<I: Interner> {
pub associated_ty_id: AssocTypeId<I>,
pub substitution: Substitution<I>,
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct OpaqueTy<I: Interner> {
pub opaque_ty_id: OpaqueTyId<I>,
pub substitution: Substitution<I>,
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct TraitRef<I: Interner> {
pub trait_id: TraitId<I>,
pub substitution: Substitution<I>,
Expand Down Expand Up @@ -948,13 +948,13 @@ impl<I: Interner> TraitRef<I> {
}

/// Where clauses that can be written by a Rust programmer.
#[derive(Clone, PartialEq, Eq, Hash, Fold, SuperVisit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, SuperVisit, HasInterner, Zip)]
pub enum WhereClause<I: Interner> {
Implemented(TraitRef<I>),
AliasEq(AliasEq<I>),
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub enum WellFormed<I: Interner> {
/// A predicate which is true is some trait ref is well-formed.
/// For example, given the following trait definitions:
Expand Down Expand Up @@ -984,7 +984,7 @@ pub enum WellFormed<I: Interner> {
Ty(Ty<I>),
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub enum FromEnv<I: Interner> {
/// A predicate which enables deriving everything which should be true if we *know* that
/// some trait ref is well-formed. For example given the above trait definitions, we can use
Expand Down Expand Up @@ -1016,7 +1016,7 @@ pub enum FromEnv<I: Interner> {
/// A "domain goal" is a goal that is directly about Rust, rather than a pure
/// logical statement. As much as possible, the Chalk solver should avoid
/// decomposing this enum, and instead treat its values opaquely.
#[derive(Clone, PartialEq, Eq, Hash, Fold, SuperVisit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, SuperVisit, HasInterner, Zip)]
pub enum DomainGoal<I: Interner> {
Holds(WhereClause<I>),

Expand Down Expand Up @@ -1201,7 +1201,7 @@ impl<I: Interner> DomainGoal<I> {
}
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, Zip)]
pub struct EqGoal<I: Interner> {
pub a: Parameter<I>,
pub b: Parameter<I>,
Expand All @@ -1211,14 +1211,14 @@ pub struct EqGoal<I: Interner> {
/// type. A projection `T::Foo` normalizes to the type `U` if we can
/// **match it to an impl** and that impl has a `type Foo = V` where
/// `U = V`.
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, Zip)]
pub struct Normalize<I: Interner> {
pub alias: AliasTy<I>,
pub ty: Ty<I>,
}

/// Proves **equality** between an alias and a type.
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, Zip)]
pub struct AliasEq<I: Interner> {
pub alias: AliasTy<I>,
pub ty: Ty<I>,
Expand Down Expand Up @@ -1398,7 +1398,7 @@ where
/// Represents one clause of the form `consequence :- conditions` where
/// `conditions = cond_1 && cond_2 && ...` is the conjunction of the individual
/// conditions.
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
pub struct ProgramClauseImplication<I: Interner> {
pub consequence: DomainGoal<I>,
pub conditions: Goals<I>,
Expand All @@ -1421,7 +1421,7 @@ impl std::ops::BitAnd for ClausePriority {
}
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner, Zip)]
pub enum ProgramClauseData<I: Interner> {
Implies(ProgramClauseImplication<I>),
ForAll(Binders<ProgramClauseImplication<I>>),
Expand Down Expand Up @@ -1860,7 +1860,7 @@ where
}
}

#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
/// A general goal; this is the full range of questions you can pose to Chalk.
pub enum GoalData<I: Interner> {
/// Introduces a binding at depth 0, shifting other bindings up
Expand Down
136 changes: 8 additions & 128 deletions chalk-ir/src/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,55 +205,17 @@ eq_zip!(I => PhantomData<I>);
eq_zip!(I => PlaceholderIndex);
eq_zip!(I => ClausePriority);

/// Generates a Zip impl that zips each field of the struct in turn.
macro_rules! struct_zip {
(impl[$($param:tt)*] Zip<$I:ty> for $self:ty { $($field:ident),* $(,)* } $($w:tt)*) => {
impl<$($param)*> Zip<$I> for $self $($w)* {
fn zip_with<'i, Z: Zipper<'i, $I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
I: 'i,
{
// Validate that we have indeed listed all fields
let Self { $($field: _),* } = *a;
$(
Zip::zip_with(zipper, &a.$field, &b.$field)?;
)*
Ok(())
}
}
impl<T: HasInterner<Interner = I> + Zip<I>, I: Interner> Zip<I> for InEnvironment<T> {
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
I: 'i,
{
Zip::zip_with(zipper, &a.environment, &b.environment)?;
Zip::zip_with(zipper, &a.goal, &b.goal)?;
Ok(())
}
}

struct_zip!(impl[I: Interner] Zip<I> for TraitRef<I> {
trait_id,
substitution,
});
struct_zip!(impl[
T: HasInterner<Interner = I> + Zip<I>,
I: Interner,
] Zip<I> for InEnvironment<T> {
environment,
goal,
});
struct_zip!(impl[I: Interner] Zip<I> for ApplicationTy<I> { name, substitution });
struct_zip!(impl[I: Interner] Zip<I> for DynTy<I> { bounds });
struct_zip!(impl[I: Interner] Zip<I> for Normalize<I> { alias, ty });
struct_zip!(impl[I: Interner] Zip<I> for AliasEq<I> { alias, ty });
struct_zip!(impl[I: Interner] Zip<I> for EqGoal<I> { a, b });
struct_zip!(impl[I: Interner] Zip<I> for ProgramClauseImplication<I> {
consequence,
conditions,
priority,
});
struct_zip!(impl[I: Interner] Zip<I> for ProjectionTy<I> {
associated_ty_id,
substitution
});
struct_zip!(impl[I: Interner] Zip<I> for OpaqueTy<I> {
opaque_ty_id,
substitution
});

impl<I: Interner> Zip<I> for Environment<I> {
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
Expand Down Expand Up @@ -303,52 +265,6 @@ impl<I: Interner> Zip<I> for QuantifiedWhereClauses<I> {
}
}

/// Generates a Zip impl that requires the two enums be the same
/// variant, then zips each field of the variant in turn. Only works
/// if all variants have a single parenthesized value right now.
macro_rules! enum_zip {
(impl<$I:ident $(, $param:ident)*> for $self:ty { $( $variant:ident ),* $(,)* } $($w:tt)*) => {
impl<$I: Interner, $(, $param)*> Zip<$I> for $self $($w)* {
fn zip_with<'i, Z: Zipper<'i, $I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
I: 'i,
{
match (a, b) {
$(
(Self :: $variant (f_a), Self :: $variant (f_b)) => {
Zip::zip_with(zipper, f_a, f_b)
}
)*

#[allow(unreachable_patterns)] // needed if there is exactly one variant
$((Self :: $variant ( .. ), _))|* => {
return Err(NoSolution);
}
}
}
}
}
}

enum_zip!(impl<I> for WellFormed<I> { Trait, Ty });
enum_zip!(impl<I> for FromEnv<I> { Trait, Ty });
enum_zip!(impl<I> for WhereClause<I> { Implemented, AliasEq });
enum_zip!(impl<I> for DomainGoal<I> {
Holds,
WellFormed,
FromEnv,
Normalize,
IsLocal,
IsUpstream,
IsFullyVisible,
LocalImplAllowed,
Compatible,
DownstreamType,
Reveal,
});
enum_zip!(impl<I> for ProgramClauseData<I> { Implies, ForAll });
enum_zip!(impl<I> for AliasTy<I> { Projection, Opaque });

impl<I: Interner> Zip<I> for Substitution<I> {
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
Expand All @@ -372,42 +288,6 @@ impl<I: Interner> Zip<I> for Goal<I> {
}
}

impl<I: Interner> Zip<I> for GoalData<I> {
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
where
I: 'i,
{
match (a, b) {
(&GoalData::Quantified(ref f_a, ref g_a), &GoalData::Quantified(ref f_b, ref g_b)) => {
Zip::zip_with(zipper, f_a, f_b)?;
Zip::zip_with(zipper, g_a, g_b)
}
(&GoalData::Implies(ref f_a, ref g_a), &GoalData::Implies(ref f_b, ref g_b)) => {
Zip::zip_with(zipper, f_a, f_b)?;
Zip::zip_with(zipper, g_a, g_b)
}
(&GoalData::All(ref g_a), &GoalData::All(ref g_b)) => Zip::zip_with(zipper, g_a, g_b),
(&GoalData::Not(ref f_a), &GoalData::Not(ref f_b)) => Zip::zip_with(zipper, f_a, f_b),
(&GoalData::EqGoal(ref f_a), &GoalData::EqGoal(ref f_b)) => {
Zip::zip_with(zipper, f_a, f_b)
}
(&GoalData::DomainGoal(ref f_a), &GoalData::DomainGoal(ref f_b)) => {
Zip::zip_with(zipper, f_a, f_b)
}
(&GoalData::CannotProve(()), &GoalData::CannotProve(())) => Ok(()),
(&GoalData::Quantified(..), _)
| (&GoalData::Implies(..), _)
| (&GoalData::All(..), _)
| (&GoalData::Not(..), _)
| (&GoalData::EqGoal(..), _)
| (&GoalData::DomainGoal(..), _)
| (&GoalData::CannotProve(..), _) => {
return Err(NoSolution);
}
}
}
}

// I'm too lazy to make `enum_zip` support type parameters.
impl<T: Zip<I>, L: Zip<I>, I: Interner> Zip<I> for ParameterKind<T, L> {
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
Expand Down

0 comments on commit bc9f4de

Please sign in to comment.