Skip to content

Commit 1a9c3e5

Browse files
committed
Generify traversable derivation macros
1 parent 3c58c0e commit 1a9c3e5

File tree

4 files changed

+128
-111
lines changed

4 files changed

+128
-111
lines changed

compiler/rustc_macros/src/lib.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ mod newtype;
2020
mod query;
2121
mod serialize;
2222
mod symbols;
23-
mod type_foldable;
24-
mod type_visitable;
23+
mod traversable;
2524

2625
#[proc_macro]
2726
pub fn rustc_queries(input: TokenStream) -> TokenStream {
@@ -144,7 +143,7 @@ decl_derive!(
144143
/// and
145144
///
146145
/// `impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Bar<'tcx>`
147-
type_foldable::type_foldable_derive
146+
traversable::traversable_derive::<traversable::Foldable>
148147
);
149148
decl_derive!(
150149
[TypeVisitable, attributes(type_visitable)] =>
@@ -170,7 +169,7 @@ decl_derive!(
170169
/// and
171170
///
172171
/// `impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for Bar<'tcx>`
173-
type_visitable::type_visitable_derive
172+
traversable::traversable_derive::<traversable::Visitable>
174173
);
175174
decl_derive!([Lift, attributes(lift)] => lift::lift_derive);
176175
decl_derive!(
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{quote, ToTokens};
3+
use syn::{parse_quote, Attribute, Meta, NestedMeta};
4+
5+
pub struct Foldable;
6+
pub struct Visitable;
7+
8+
/// An abstraction over traversable traits.
9+
pub trait Traversable {
10+
/// The trait that this `Traversable` represents.
11+
fn traversable() -> TokenStream;
12+
13+
/// The `match` arms for a traversal of this type.
14+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream;
15+
16+
/// The body of an implementation given the match `arms`.
17+
fn impl_body(arms: impl ToTokens) -> TokenStream;
18+
}
19+
20+
impl Traversable for Foldable {
21+
fn traversable() -> TokenStream {
22+
quote! { ::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>> }
23+
}
24+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream {
25+
structure.each_variant(|vi| {
26+
let bindings = vi.bindings();
27+
vi.construct(|_, index| {
28+
let bind = &bindings[index];
29+
30+
// retain value of fields with #[type_foldable(identity)]
31+
let fixed = bind
32+
.ast()
33+
.attrs
34+
.iter()
35+
.map(Attribute::parse_meta)
36+
.filter_map(Result::ok)
37+
.flat_map(|attr| match attr {
38+
Meta::List(list) if list.path.is_ident("type_foldable") => list.nested,
39+
_ => Default::default(),
40+
})
41+
.any(|nested| match nested {
42+
NestedMeta::Meta(Meta::Path(path)) => path.is_ident("identity"),
43+
_ => false,
44+
});
45+
46+
if fixed {
47+
bind.to_token_stream()
48+
} else {
49+
quote! {
50+
::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)?
51+
}
52+
}
53+
})
54+
})
55+
}
56+
fn impl_body(arms: impl ToTokens) -> TokenStream {
57+
quote! {
58+
fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>(
59+
self,
60+
__folder: &mut __F
61+
) -> ::core::result::Result<Self, __F::Error> {
62+
::core::result::Result::Ok(match self { #arms })
63+
}
64+
}
65+
}
66+
}
67+
68+
impl Traversable for Visitable {
69+
fn traversable() -> TokenStream {
70+
quote! { ::rustc_middle::ty::visit::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>> }
71+
}
72+
fn arms(structure: &mut synstructure::Structure<'_>) -> TokenStream {
73+
// ignore fields with #[type_visitable(ignore)]
74+
structure.filter(|bi| {
75+
!bi.ast()
76+
.attrs
77+
.iter()
78+
.map(Attribute::parse_meta)
79+
.filter_map(Result::ok)
80+
.flat_map(|attr| match attr {
81+
Meta::List(list) if list.path.is_ident("type_visitable") => list.nested,
82+
_ => Default::default(),
83+
})
84+
.any(|nested| match nested {
85+
NestedMeta::Meta(Meta::Path(path)) => path.is_ident("ignore"),
86+
_ => false,
87+
})
88+
});
89+
90+
structure.each(|bind| {
91+
quote! {
92+
::rustc_middle::ty::visit::TypeVisitable::visit_with(#bind, __visitor)?;
93+
}
94+
})
95+
}
96+
fn impl_body(arms: impl ToTokens) -> TokenStream {
97+
quote! {
98+
fn visit_with<__V: ::rustc_middle::ty::visit::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(
99+
&self,
100+
__visitor: &mut __V
101+
) -> ::std::ops::ControlFlow<__V::BreakTy> {
102+
match self { #arms }
103+
::std::ops::ControlFlow::Continue(())
104+
}
105+
}
106+
}
107+
}
108+
109+
pub fn traversable_derive<T: Traversable>(
110+
mut structure: synstructure::Structure<'_>,
111+
) -> TokenStream {
112+
if let syn::Data::Union(_) = structure.ast().data {
113+
panic!("cannot derive on union")
114+
}
115+
116+
structure.add_bounds(synstructure::AddBounds::Generics);
117+
structure.bind_with(|_| synstructure::BindStyle::Move);
118+
119+
if !structure.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
120+
structure.add_impl_generic(parse_quote! { 'tcx });
121+
}
122+
123+
let arms = T::arms(&mut structure);
124+
structure.bound_impl(T::traversable(), T::impl_body(arms))
125+
}

compiler/rustc_macros/src/type_foldable.rs

Lines changed: 0 additions & 57 deletions
This file was deleted.

compiler/rustc_macros/src/type_visitable.rs

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)