Skip to content

Commit 22f04dd

Browse files
committed
first implementation
1 parent 73a74be commit 22f04dd

File tree

6 files changed

+896
-27
lines changed

6 files changed

+896
-27
lines changed

chalk-derive/src/lib.rs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,189 @@ fn bounded_by_trait<'p>(param: &'p GenericParam, name: &str) -> Option<&'p Ident
339339
_ => None,
340340
}
341341
}
342+
343+
/// Derives Visit for structs and enums for which one of the following is true:
344+
/// - It has a `#[has_interner(TheInterner)]` attribute
345+
/// - There is a single parameter `T: HasInterner` (does not have to be named `T`)
346+
/// - There is a single parameter `I: Interner` (does not have to be named `I`)
347+
#[proc_macro_derive(Visit, attributes(has_interner))]
348+
pub fn derive_visit(item: TokenStream) -> TokenStream {
349+
let input = parse_macro_input!(item as DeriveInput);
350+
let (impl_generics, ty_generics, where_clause_ref) = input.generics.split_for_impl();
351+
352+
let type_name = input.ident;
353+
let body = derive_visit_body(&type_name, input.data);
354+
355+
if let Some(attr) = input.attrs.iter().find(|a| a.path.is_ident("has_interner")) {
356+
// Hardcoded interner:
357+
//
358+
// impl Visit<ChalkIr> for Type {
359+
//
360+
// }
361+
let arg = attr
362+
.parse_args::<proc_macro2::TokenStream>()
363+
.expect("Expected has_interner argument");
364+
365+
return TokenStream::from(quote! {
366+
impl #impl_generics Visit < #arg > for #type_name #ty_generics #where_clause_ref {
367+
fn visit_with<'i, R: VisitResult>(
368+
&self,
369+
visitor: &mut dyn Visitor < 'i, #arg, Result = R >,
370+
outer_binder: DebruijnIndex,
371+
) -> R
372+
where
373+
I: 'i
374+
{
375+
#body
376+
}
377+
}
378+
});
379+
}
380+
381+
match input.generics.params.len() {
382+
1 => {}
383+
384+
0 => {
385+
panic!("Visit derive requires a single type parameter or a `#[has_interner]` attr");
386+
}
387+
388+
_ => {
389+
panic!("Visit derive only works with a single type parameter");
390+
}
391+
};
392+
393+
let generic_param0 = &input.generics.params[0];
394+
395+
if let Some(param) = has_interner(&generic_param0) {
396+
// HasInterner bound:
397+
//
398+
// Example:
399+
//
400+
// impl<T, _I> Visit<_I> for Binders<T>
401+
// where
402+
// T: HasInterner<Interner = _I>,
403+
// {
404+
// }
405+
406+
let mut impl_generics = input.generics.clone();
407+
impl_generics.params.extend(vec![
408+
GenericParam::Type(syn::parse(quote! { _I: Interner }.into()).unwrap()),
409+
]);
410+
411+
let mut where_clause = where_clause_ref
412+
.cloned()
413+
.unwrap_or_else(|| syn::parse2(quote![where]).unwrap());
414+
where_clause
415+
.predicates
416+
.push(syn::parse2(quote! { #param: HasInterner<Interner = _I> }).unwrap());
417+
where_clause
418+
.predicates
419+
.push(syn::parse2(quote! { #param: Visit<_I> }).unwrap());
420+
421+
return TokenStream::from(quote! {
422+
impl #impl_generics Visit < _I > for #type_name < #param >
423+
#where_clause
424+
{
425+
fn visit_with<'i, R: VisitResult>(
426+
&self,
427+
visitor: &mut dyn Visitor < 'i, _I, Result = R >,
428+
outer_binder: DebruijnIndex,
429+
) -> R
430+
where
431+
_I: 'i
432+
{
433+
#body
434+
}
435+
}
436+
});
437+
}
438+
439+
// Interner bound:
440+
//
441+
// Example:
442+
//
443+
// impl<I> Visit<I> for Foo<I>
444+
// where
445+
// I: Interner,
446+
// {
447+
// }
448+
449+
if let Some(i) = is_interner(&generic_param0) {
450+
let impl_generics = &input.generics;
451+
452+
return TokenStream::from(quote! {
453+
impl #impl_generics Visit < #i > for #type_name < #i >
454+
#where_clause_ref
455+
{
456+
fn visit_with<'i, R: VisitResult>(
457+
&self,
458+
visitor: &mut dyn Visitor < 'i, #i, Result = R >,
459+
outer_binder: DebruijnIndex,
460+
) -> R
461+
where
462+
I: 'i
463+
{
464+
#body
465+
}
466+
}
467+
});
468+
}
469+
470+
panic!("derive(Visit) requires a parameter that implements HasInterner or Interner");
471+
}
472+
473+
/// Generates the body of the Visit impl
474+
fn derive_visit_body(type_name: &Ident, data: Data) -> proc_macro2::TokenStream {
475+
match data {
476+
Data::Struct(s) => {
477+
let fields = s.fields.into_iter().map(|f| {
478+
let name = f.ident.as_ref().expect("Unnamed field in a struct");
479+
quote! { .and_then(|| self.#name.visit_with(visitor, outer_binder)) }
480+
});
481+
quote! {
482+
R::new()
483+
#(#fields)*
484+
}
485+
}
486+
Data::Enum(e) => {
487+
let matches = e.variants.into_iter().map(|v| {
488+
let variant = v.ident;
489+
match &v.fields {
490+
syn::Fields::Named(fields) => {
491+
let fnames: &Vec<_> = &fields.named.iter().map(|f| &f.ident).collect();
492+
quote! {
493+
#type_name :: #variant { #(#fnames),* } => {
494+
R::new()
495+
#(.and_then(|| #fnames.visit_with(visitor, outer_binder)))*
496+
}
497+
}
498+
}
499+
500+
syn::Fields::Unnamed(_fields) => {
501+
let names: Vec<_> = (0..v.fields.iter().count())
502+
.map(|index| format_ident!("a{}", index))
503+
.collect();
504+
quote! {
505+
#type_name::#variant( #(ref #names),* ) => {
506+
R::new()
507+
#(.and_then(|| #names.visit_with(visitor, outer_binder)))*
508+
}
509+
}
510+
}
511+
512+
syn::Fields::Unit => {
513+
quote! {
514+
#type_name::#variant => R::new(),
515+
}
516+
}
517+
}
518+
});
519+
quote! {
520+
match *self {
521+
#(#matches)*
522+
}
523+
}
524+
}
525+
Data::Union(..) => panic!("Visit can not be derived for unions"),
526+
}
527+
}

chalk-ir/src/lib.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
use crate::cast::{Cast, CastTo};
44
use crate::fold::shift::Shift;
55
use crate::fold::{Fold, Folder, Subst, SuperFold};
6-
use chalk_derive::{Fold, HasInterner};
6+
use crate::visit::{VisitResult, Visit, SuperVisit, Visitor};
7+
use chalk_derive::{Fold, HasInterner, Visit};
78
use chalk_engine::fallible::*;
89
use std::iter;
910
use std::marker::PhantomData;
@@ -34,6 +35,9 @@ pub mod zip;
3435
#[macro_use]
3536
pub mod fold;
3637

38+
#[macro_use]
39+
pub mod visit;
40+
3741
pub mod cast;
3842

3943
pub mod interner;
@@ -44,7 +48,7 @@ pub mod debug;
4448
#[cfg(any(test, feature = "default-interner"))]
4549
pub mod tls;
4650

47-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
51+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
4852
/// The set of assumptions we've made so far, and the current number of
4953
/// universal (forall) quantifiers we're within.
5054
pub struct Environment<I: Interner> {
@@ -69,7 +73,7 @@ impl<I: Interner> Environment<I> {
6973
}
7074
}
7175

72-
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold)]
76+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit)]
7377
pub struct InEnvironment<G: HasInterner> {
7478
pub environment: Environment<G::Interner>,
7579
pub goal: G,
@@ -99,7 +103,7 @@ impl<G: HasInterner> HasInterner for InEnvironment<G> {
99103
type Interner = G::Interner;
100104
}
101105

102-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold)]
106+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Fold, Visit)]
103107
pub enum TypeName<I: Interner> {
104108
/// a type like `Vec<T>`
105109
Struct(StructId<I>),
@@ -541,7 +545,7 @@ impl DebruijnIndex {
541545
/// known. It is referenced within the type using `^1`, indicating
542546
/// a bound type with debruijn index 1 (i.e., skipping through one
543547
/// level of binder).
544-
#[derive(Clone, PartialEq, Eq, Hash, Fold)]
548+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
545549
pub struct DynTy<I: Interner> {
546550
pub bounds: Binders<QuantifiedWhereClauses<I>>,
547551
}
@@ -658,7 +662,7 @@ impl PlaceholderIndex {
658662
}
659663

660664
// Fold derive intentionally omitted, folded through Ty
661-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
665+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
662666
pub struct ApplicationTy<I: Interner> {
663667
pub name: TypeName<I>,
664668
pub substitution: Substitution<I>,
@@ -821,7 +825,7 @@ impl<I: Interner> ParameterData<I> {
821825
}
822826
}
823827

824-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
828+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
825829
pub struct AliasTy<I: Interner> {
826830
pub associated_ty_id: AssocTypeId<I>,
827831
pub substitution: Substitution<I>,
@@ -833,7 +837,7 @@ impl<I: Interner> AliasTy<I> {
833837
}
834838
}
835839

836-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
840+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
837841
pub struct TraitRef<I: Interner> {
838842
pub trait_id: TraitId<I>,
839843
pub substitution: Substitution<I>,
@@ -861,13 +865,13 @@ impl<I: Interner> TraitRef<I> {
861865
}
862866

863867
/// Where clauses that can be written by a Rust programmer.
864-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
868+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
865869
pub enum WhereClause<I: Interner> {
866870
Implemented(TraitRef<I>),
867871
AliasEq(AliasEq<I>),
868872
}
869873

870-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
874+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
871875
pub enum WellFormed<I: Interner> {
872876
/// A predicate which is true is some trait ref is well-formed.
873877
/// For example, given the following trait definitions:
@@ -897,7 +901,7 @@ pub enum WellFormed<I: Interner> {
897901
Ty(Ty<I>),
898902
}
899903

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

@@ -1100,7 +1104,7 @@ impl<I: Interner> DomainGoal<I> {
11001104
}
11011105
}
11021106

1103-
#[derive(Clone, PartialEq, Eq, Hash, Fold)]
1107+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
11041108
pub struct EqGoal<I: Interner> {
11051109
pub a: Parameter<I>,
11061110
pub b: Parameter<I>,
@@ -1110,7 +1114,7 @@ pub struct EqGoal<I: Interner> {
11101114
/// type. A projection `T::Foo` normalizes to the type `U` if we can
11111115
/// **match it to an impl** and that impl has a `type Foo = V` where
11121116
/// `U = V`.
1113-
#[derive(Clone, PartialEq, Eq, Hash, Fold)]
1117+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
11141118
pub struct Normalize<I: Interner> {
11151119
pub alias: AliasTy<I>,
11161120
pub ty: Ty<I>,
@@ -1119,7 +1123,7 @@ pub struct Normalize<I: Interner> {
11191123
/// Proves **equality** between a projection `T::Foo` and a type
11201124
/// `U`. Equality can be proven via normalization, but we can also
11211125
/// prove that `T::Foo = V::Foo` if `T = V` without normalizing.
1122-
#[derive(Clone, PartialEq, Eq, Hash, Fold)]
1126+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit)]
11231127
pub struct AliasEq<I: Interner> {
11241128
pub alias: AliasTy<I>,
11251129
pub ty: Ty<I>,
@@ -1261,7 +1265,7 @@ impl<V: IntoIterator> Iterator for BindersIntoIterator<V> {
12611265
/// Represents one clause of the form `consequence :- conditions` where
12621266
/// `conditions = cond_1 && cond_2 && ...` is the conjunction of the individual
12631267
/// conditions.
1264-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
1268+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
12651269
pub struct ProgramClauseImplication<I: Interner> {
12661270
pub consequence: DomainGoal<I>,
12671271
pub conditions: Goals<I>,
@@ -1583,7 +1587,7 @@ where
15831587
}
15841588
}
15851589

1586-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
1590+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
15871591
/// A general goal; this is the full range of questions you can pose to Chalk.
15881592
pub enum GoalData<I: Interner> {
15891593
/// Introduces a binding at depth 0, shifting other bindings up
@@ -1630,7 +1634,7 @@ pub enum QuantifierKind {
16301634
/// lifetime constraints, instead gathering them up to return with our solution
16311635
/// for later checking. This allows for decoupling between type and region
16321636
/// checking in the compiler.
1633-
#[derive(Clone, PartialEq, Eq, Hash, Fold, HasInterner)]
1637+
#[derive(Clone, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
16341638
pub enum Constraint<I: Interner> {
16351639
LifetimeEq(Lifetime<I>, Lifetime<I>),
16361640
}
@@ -1837,13 +1841,13 @@ impl<'i, I: Interner> Folder<'i, I> for &SubstFolder<'i, I> {
18371841
/// substitution stores the values for the query's unknown variables,
18381842
/// and the constraints represents any region constraints that must
18391843
/// additionally be solved.
1840-
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)]
1844+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
18411845
pub struct ConstrainedSubst<I: Interner> {
18421846
pub subst: Substitution<I>, /* NB: The `is_trivial` routine relies on the fact that `subst` is folded first. */
18431847
pub constraints: Vec<InEnvironment<Constraint<I>>>,
18441848
}
18451849

1846-
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)]
1850+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, Visit, HasInterner)]
18471851
pub struct AnswerSubst<I: Interner> {
18481852
pub subst: Substitution<I>, /* NB: The `is_trivial` routine relies on the fact that `subst` is folded first. */
18491853
pub constraints: Vec<InEnvironment<Constraint<I>>>,

0 commit comments

Comments
 (0)