Skip to content

Commit bc9f4de

Browse files
authored
Merge pull request #420 from zaharidichev/zd/derive-zip
Derive Zip
2 parents 7e7b624 + 104c1ff commit bc9f4de

File tree

3 files changed

+88
-146
lines changed

3 files changed

+88
-146
lines changed

chalk-derive/src/lib.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
extern crate proc_macro;
22

3-
use proc_macro2::TokenStream;
3+
use proc_macro2::{Span, TokenStream};
44
use quote::quote;
5+
use quote::ToTokens;
56
use syn::{parse_quote, DeriveInput, GenericParam, Ident, TypeParamBound};
67

78
use synstructure::decl_derive;
@@ -120,6 +121,7 @@ decl_derive!([HasInterner, attributes(has_interner)] => derive_has_interner);
120121
decl_derive!([Visit, attributes(has_interner)] => derive_visit);
121122
decl_derive!([SuperVisit, attributes(has_interner)] => derive_super_visit);
122123
decl_derive!([Fold, attributes(has_interner)] => derive_fold);
124+
decl_derive!([Zip, attributes(has_interner)] => derive_zip);
123125

124126
fn derive_has_interner(mut s: synstructure::Structure) -> TokenStream {
125127
let (interner, _) = find_interner(&mut s);
@@ -194,6 +196,66 @@ fn derive_any_visit(
194196
)
195197
}
196198

199+
fn each_variant_pair<F, R>(
200+
a: &mut synstructure::Structure,
201+
b: &mut synstructure::Structure,
202+
mut f: F,
203+
) -> TokenStream
204+
where
205+
F: FnMut(&synstructure::VariantInfo<'_>, &synstructure::VariantInfo<'_>) -> R,
206+
R: ToTokens,
207+
{
208+
let mut t = TokenStream::new();
209+
for (v_a, v_b) in a.variants_mut().iter_mut().zip(b.variants_mut().iter_mut()) {
210+
v_a.binding_name(|_, i| Ident::new(&format!("a_{}", i), Span::call_site()));
211+
v_b.binding_name(|_, i| Ident::new(&format!("b_{}", i), Span::call_site()));
212+
213+
let pat_a = v_a.pat();
214+
let pat_b = v_b.pat();
215+
let body = f(v_a, v_b);
216+
217+
quote!((#pat_a, #pat_b) => {#body}).to_tokens(&mut t);
218+
}
219+
t
220+
}
221+
222+
fn derive_zip(mut s: synstructure::Structure) -> TokenStream {
223+
let (interner, _) = find_interner(&mut s);
224+
225+
let mut a = s.clone();
226+
let mut b = s.clone();
227+
228+
let mut body = each_variant_pair(&mut a, &mut b, |v_a, v_b| {
229+
let mut t = TokenStream::new();
230+
for (b_a, b_b) in v_a.bindings().iter().zip(v_b.bindings().iter()) {
231+
quote!(chalk_ir::zip::Zip::zip_with(zipper, #b_a, #b_b)?;).to_tokens(&mut t);
232+
}
233+
quote!(Ok(())).to_tokens(&mut t);
234+
t
235+
});
236+
237+
// when the two variants are different
238+
quote!((_, _) => Err(::chalk_engine::fallible::NoSolution)).to_tokens(&mut body);
239+
240+
s.add_bounds(synstructure::AddBounds::None);
241+
s.bound_impl(
242+
quote!(::chalk_ir::zip::Zip<#interner>),
243+
quote! {
244+
245+
fn zip_with<'i, Z: ::chalk_ir::zip::Zipper<'i, #interner>>(
246+
zipper: &mut Z,
247+
a: &Self,
248+
b: &Self,
249+
) -> ::chalk_engine::fallible::Fallible<()>
250+
where
251+
#interner: 'i,
252+
{
253+
match (a, b) { #body }
254+
}
255+
},
256+
)
257+
}
258+
197259
/// Derives Fold for structs and enums for which one of the following is true:
198260
/// - It has a `#[has_interner(TheInterner)]` attribute
199261
/// - There is a single parameter `T: HasInterner` (does not have to be named `T`)

chalk-ir/src/lib.rs

+17-17
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::cast::{Cast, CastTo};
77
use crate::fold::shift::Shift;
88
use crate::fold::{Fold, Folder, Subst, SuperFold};
99
use crate::visit::{SuperVisit, Visit, VisitExt, VisitResult, Visitor};
10-
use chalk_derive::{Fold, HasInterner, SuperVisit, Visit};
10+
use chalk_derive::{Fold, HasInterner, SuperVisit, Visit, Zip};
1111
use chalk_engine::fallible::*;
1212
use std::iter;
1313
use std::marker::PhantomData;
@@ -602,7 +602,7 @@ impl DebruijnIndex {
602602
/// known. It is referenced within the type using `^1`, indicating
603603
/// a bound type with debruijn index 1 (i.e., skipping through one
604604
/// level of binder).
605-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
605+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
606606
pub struct DynTy<I: Interner> {
607607
pub bounds: Binders<QuantifiedWhereClauses<I>>,
608608
}
@@ -719,7 +719,7 @@ impl PlaceholderIndex {
719719
}
720720

721721
// Fold derive intentionally omitted, folded through Ty
722-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
722+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
723723
pub struct ApplicationTy<I: Interner> {
724724
pub name: TypeName<I>,
725725
pub substitution: Substitution<I>,
@@ -884,7 +884,7 @@ impl<I: Interner> ParameterData<I> {
884884
}
885885
}
886886

887-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
887+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
888888
pub enum AliasTy<I: Interner> {
889889
Projection(ProjectionTy<I>),
890890
Opaque(OpaqueTy<I>),
@@ -908,19 +908,19 @@ impl<I: Interner> AliasTy<I> {
908908
}
909909
}
910910

911-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
911+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
912912
pub struct ProjectionTy<I: Interner> {
913913
pub associated_ty_id: AssocTypeId<I>,
914914
pub substitution: Substitution<I>,
915915
}
916916

917-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
917+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
918918
pub struct OpaqueTy<I: Interner> {
919919
pub opaque_ty_id: OpaqueTyId<I>,
920920
pub substitution: Substitution<I>,
921921
}
922922

923-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
923+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
924924
pub struct TraitRef<I: Interner> {
925925
pub trait_id: TraitId<I>,
926926
pub substitution: Substitution<I>,
@@ -948,13 +948,13 @@ impl<I: Interner> TraitRef<I> {
948948
}
949949

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

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

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

@@ -1201,7 +1201,7 @@ impl<I: Interner> DomainGoal<I> {
12011201
}
12021202
}
12031203

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

12201220
/// Proves **equality** between an alias and a type.
1221-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
1221+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, Zip)]
12221222
pub struct AliasEq<I: Interner> {
12231223
pub alias: AliasTy<I>,
12241224
pub ty: Ty<I>,
@@ -1398,7 +1398,7 @@ where
13981398
/// Represents one clause of the form `consequence :- conditions` where
13991399
/// `conditions = cond_1 && cond_2 && ...` is the conjunction of the individual
14001400
/// conditions.
1401-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
1401+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
14021402
pub struct ProgramClauseImplication<I: Interner> {
14031403
pub consequence: DomainGoal<I>,
14041404
pub conditions: Goals<I>,
@@ -1421,7 +1421,7 @@ impl std::ops::BitAnd for ClausePriority {
14211421
}
14221422
}
14231423

1424-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
1424+
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner, Zip)]
14251425
pub enum ProgramClauseData<I: Interner> {
14261426
Implies(ProgramClauseImplication<I>),
14271427
ForAll(Binders<ProgramClauseImplication<I>>),
@@ -1860,7 +1860,7 @@ where
18601860
}
18611861
}
18621862

1863-
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
1863+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner, Zip)]
18641864
/// A general goal; this is the full range of questions you can pose to Chalk.
18651865
pub enum GoalData<I: Interner> {
18661866
/// Introduces a binding at depth 0, shifting other bindings up

chalk-ir/src/zip.rs

+8-128
Original file line numberDiff line numberDiff line change
@@ -205,55 +205,17 @@ eq_zip!(I => PhantomData<I>);
205205
eq_zip!(I => PlaceholderIndex);
206206
eq_zip!(I => ClausePriority);
207207

208-
/// Generates a Zip impl that zips each field of the struct in turn.
209-
macro_rules! struct_zip {
210-
(impl[$($param:tt)*] Zip<$I:ty> for $self:ty { $($field:ident),* $(,)* } $($w:tt)*) => {
211-
impl<$($param)*> Zip<$I> for $self $($w)* {
212-
fn zip_with<'i, Z: Zipper<'i, $I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
213-
where
214-
I: 'i,
215-
{
216-
// Validate that we have indeed listed all fields
217-
let Self { $($field: _),* } = *a;
218-
$(
219-
Zip::zip_with(zipper, &a.$field, &b.$field)?;
220-
)*
221-
Ok(())
222-
}
223-
}
208+
impl<T: HasInterner<Interner = I> + Zip<I>, I: Interner> Zip<I> for InEnvironment<T> {
209+
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
210+
where
211+
I: 'i,
212+
{
213+
Zip::zip_with(zipper, &a.environment, &b.environment)?;
214+
Zip::zip_with(zipper, &a.goal, &b.goal)?;
215+
Ok(())
224216
}
225217
}
226218

227-
struct_zip!(impl[I: Interner] Zip<I> for TraitRef<I> {
228-
trait_id,
229-
substitution,
230-
});
231-
struct_zip!(impl[
232-
T: HasInterner<Interner = I> + Zip<I>,
233-
I: Interner,
234-
] Zip<I> for InEnvironment<T> {
235-
environment,
236-
goal,
237-
});
238-
struct_zip!(impl[I: Interner] Zip<I> for ApplicationTy<I> { name, substitution });
239-
struct_zip!(impl[I: Interner] Zip<I> for DynTy<I> { bounds });
240-
struct_zip!(impl[I: Interner] Zip<I> for Normalize<I> { alias, ty });
241-
struct_zip!(impl[I: Interner] Zip<I> for AliasEq<I> { alias, ty });
242-
struct_zip!(impl[I: Interner] Zip<I> for EqGoal<I> { a, b });
243-
struct_zip!(impl[I: Interner] Zip<I> for ProgramClauseImplication<I> {
244-
consequence,
245-
conditions,
246-
priority,
247-
});
248-
struct_zip!(impl[I: Interner] Zip<I> for ProjectionTy<I> {
249-
associated_ty_id,
250-
substitution
251-
});
252-
struct_zip!(impl[I: Interner] Zip<I> for OpaqueTy<I> {
253-
opaque_ty_id,
254-
substitution
255-
});
256-
257219
impl<I: Interner> Zip<I> for Environment<I> {
258220
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
259221
where
@@ -303,52 +265,6 @@ impl<I: Interner> Zip<I> for QuantifiedWhereClauses<I> {
303265
}
304266
}
305267

306-
/// Generates a Zip impl that requires the two enums be the same
307-
/// variant, then zips each field of the variant in turn. Only works
308-
/// if all variants have a single parenthesized value right now.
309-
macro_rules! enum_zip {
310-
(impl<$I:ident $(, $param:ident)*> for $self:ty { $( $variant:ident ),* $(,)* } $($w:tt)*) => {
311-
impl<$I: Interner, $(, $param)*> Zip<$I> for $self $($w)* {
312-
fn zip_with<'i, Z: Zipper<'i, $I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
313-
where
314-
I: 'i,
315-
{
316-
match (a, b) {
317-
$(
318-
(Self :: $variant (f_a), Self :: $variant (f_b)) => {
319-
Zip::zip_with(zipper, f_a, f_b)
320-
}
321-
)*
322-
323-
#[allow(unreachable_patterns)] // needed if there is exactly one variant
324-
$((Self :: $variant ( .. ), _))|* => {
325-
return Err(NoSolution);
326-
}
327-
}
328-
}
329-
}
330-
}
331-
}
332-
333-
enum_zip!(impl<I> for WellFormed<I> { Trait, Ty });
334-
enum_zip!(impl<I> for FromEnv<I> { Trait, Ty });
335-
enum_zip!(impl<I> for WhereClause<I> { Implemented, AliasEq });
336-
enum_zip!(impl<I> for DomainGoal<I> {
337-
Holds,
338-
WellFormed,
339-
FromEnv,
340-
Normalize,
341-
IsLocal,
342-
IsUpstream,
343-
IsFullyVisible,
344-
LocalImplAllowed,
345-
Compatible,
346-
DownstreamType,
347-
Reveal,
348-
});
349-
enum_zip!(impl<I> for ProgramClauseData<I> { Implies, ForAll });
350-
enum_zip!(impl<I> for AliasTy<I> { Projection, Opaque });
351-
352268
impl<I: Interner> Zip<I> for Substitution<I> {
353269
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
354270
where
@@ -372,42 +288,6 @@ impl<I: Interner> Zip<I> for Goal<I> {
372288
}
373289
}
374290

375-
impl<I: Interner> Zip<I> for GoalData<I> {
376-
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>
377-
where
378-
I: 'i,
379-
{
380-
match (a, b) {
381-
(&GoalData::Quantified(ref f_a, ref g_a), &GoalData::Quantified(ref f_b, ref g_b)) => {
382-
Zip::zip_with(zipper, f_a, f_b)?;
383-
Zip::zip_with(zipper, g_a, g_b)
384-
}
385-
(&GoalData::Implies(ref f_a, ref g_a), &GoalData::Implies(ref f_b, ref g_b)) => {
386-
Zip::zip_with(zipper, f_a, f_b)?;
387-
Zip::zip_with(zipper, g_a, g_b)
388-
}
389-
(&GoalData::All(ref g_a), &GoalData::All(ref g_b)) => Zip::zip_with(zipper, g_a, g_b),
390-
(&GoalData::Not(ref f_a), &GoalData::Not(ref f_b)) => Zip::zip_with(zipper, f_a, f_b),
391-
(&GoalData::EqGoal(ref f_a), &GoalData::EqGoal(ref f_b)) => {
392-
Zip::zip_with(zipper, f_a, f_b)
393-
}
394-
(&GoalData::DomainGoal(ref f_a), &GoalData::DomainGoal(ref f_b)) => {
395-
Zip::zip_with(zipper, f_a, f_b)
396-
}
397-
(&GoalData::CannotProve(()), &GoalData::CannotProve(())) => Ok(()),
398-
(&GoalData::Quantified(..), _)
399-
| (&GoalData::Implies(..), _)
400-
| (&GoalData::All(..), _)
401-
| (&GoalData::Not(..), _)
402-
| (&GoalData::EqGoal(..), _)
403-
| (&GoalData::DomainGoal(..), _)
404-
| (&GoalData::CannotProve(..), _) => {
405-
return Err(NoSolution);
406-
}
407-
}
408-
}
409-
}
410-
411291
// I'm too lazy to make `enum_zip` support type parameters.
412292
impl<T: Zip<I>, L: Zip<I>, I: Interner> Zip<I> for ParameterKind<T, L> {
413293
fn zip_with<'i, Z: Zipper<'i, I>>(zipper: &mut Z, a: &Self, b: &Self) -> Fallible<()>

0 commit comments

Comments
 (0)