]> git.proxmox.com Git - rustc.git/blob - vendor/chalk-derive-0.25.0/src/lib.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / vendor / chalk-derive-0.25.0 / src / lib.rs
1 extern crate proc_macro;
2
3 use proc_macro2::{Span, TokenStream};
4 use quote::quote;
5 use quote::ToTokens;
6 use syn::{parse_quote, DeriveInput, GenericParam, Ident, TypeParamBound};
7
8 use synstructure::decl_derive;
9
10 /// Checks whether a generic parameter has a `: HasInterner` bound
11 fn has_interner(param: &GenericParam) -> Option<&Ident> {
12 bounded_by_trait(param, "HasInterner")
13 }
14
15 /// Checks whether a generic parameter has a `: Interner` bound
16 fn is_interner(param: &GenericParam) -> Option<&Ident> {
17 bounded_by_trait(param, "Interner")
18 }
19
20 fn has_interner_attr(input: &DeriveInput) -> Option<TokenStream> {
21 Some(
22 input
23 .attrs
24 .iter()
25 .find(|a| a.path.is_ident("has_interner"))?
26 .parse_args::<TokenStream>()
27 .expect("Expected has_interner argument"),
28 )
29 }
30
31 fn bounded_by_trait<'p>(param: &'p GenericParam, name: &str) -> Option<&'p Ident> {
32 let name = Some(String::from(name));
33 match param {
34 GenericParam::Type(ref t) => t.bounds.iter().find_map(|b| {
35 if let TypeParamBound::Trait(trait_bound) = b {
36 if trait_bound
37 .path
38 .segments
39 .last()
40 .map(|s| s.ident.to_string())
41 == name
42 {
43 return Some(&t.ident);
44 }
45 }
46 None
47 }),
48 _ => None,
49 }
50 }
51
52 fn get_generic_param(input: &DeriveInput) -> &GenericParam {
53 match input.generics.params.len() {
54 1 => {}
55
56 0 => panic!(
57 "deriving this trait requires a single type parameter or a `#[has_interner]` attr"
58 ),
59
60 _ => panic!("deriving this trait only works with a single type parameter"),
61 };
62 &input.generics.params[0]
63 }
64
65 fn get_generic_param_name(input: &DeriveInput) -> Option<&Ident> {
66 match get_generic_param(input) {
67 GenericParam::Type(t) => Some(&t.ident),
68 _ => None,
69 }
70 }
71
72 fn find_interner(s: &mut synstructure::Structure) -> (TokenStream, DeriveKind) {
73 let input = s.ast();
74
75 if let Some(arg) = has_interner_attr(input) {
76 // Hardcoded interner:
77 //
78 // #[has_interner(ChalkIr)]
79 // struct S {
80 //
81 // }
82 return (arg, DeriveKind::FromHasInternerAttr);
83 }
84
85 let generic_param0 = get_generic_param(input);
86
87 if let Some(param) = has_interner(&generic_param0) {
88 // HasInterner bound:
89 //
90 // Example:
91 //
92 // struct Binders<T: HasInterner> { }
93 s.add_impl_generic(parse_quote! { _I });
94
95 s.add_where_predicate(parse_quote! { _I: ::chalk_ir::interner::Interner });
96 s.add_where_predicate(
97 parse_quote! { #param: ::chalk_ir::interner::HasInterner<Interner = _I> },
98 );
99
100 (quote! { _I }, DeriveKind::FromHasInterner)
101 } else if let Some(i) = is_interner(&generic_param0) {
102 // Interner bound:
103 //
104 // Example:
105 //
106 // struct Foo<I: Interner> { }
107 (quote! { #i }, DeriveKind::FromInterner)
108 } else {
109 panic!("deriving this trait requires a parameter that implements HasInterner or Interner",);
110 }
111 }
112
113 #[derive(Copy, Clone, PartialEq)]
114 enum DeriveKind {
115 FromHasInternerAttr,
116 FromHasInterner,
117 FromInterner,
118 }
119
120 decl_derive!([HasInterner, attributes(has_interner)] => derive_has_interner);
121 decl_derive!([Visit, attributes(has_interner)] => derive_visit);
122 decl_derive!([SuperVisit, attributes(has_interner)] => derive_super_visit);
123 decl_derive!([Fold, attributes(has_interner)] => derive_fold);
124 decl_derive!([Zip, attributes(has_interner)] => derive_zip);
125
126 fn derive_has_interner(mut s: synstructure::Structure) -> TokenStream {
127 let (interner, _) = find_interner(&mut s);
128
129 s.add_bounds(synstructure::AddBounds::None);
130 s.bound_impl(
131 quote!(::chalk_ir::interner::HasInterner),
132 quote! {
133 type Interner = #interner;
134 },
135 )
136 }
137
138 /// Derives Visit for structs and enums for which one of the following is true:
139 /// - It has a `#[has_interner(TheInterner)]` attribute
140 /// - There is a single parameter `T: HasInterner` (does not have to be named `T`)
141 /// - There is a single parameter `I: Interner` (does not have to be named `I`)
142 fn derive_visit(s: synstructure::Structure) -> TokenStream {
143 derive_any_visit(s, parse_quote! { Visit }, parse_quote! { visit_with })
144 }
145
146 /// Same as Visit, but derives SuperVisit instead
147 fn derive_super_visit(s: synstructure::Structure) -> TokenStream {
148 derive_any_visit(
149 s,
150 parse_quote! { SuperVisit },
151 parse_quote! { super_visit_with },
152 )
153 }
154
155 fn derive_any_visit(
156 mut s: synstructure::Structure,
157 trait_name: Ident,
158 method_name: Ident,
159 ) -> TokenStream {
160 let input = s.ast();
161 let (interner, kind) = find_interner(&mut s);
162
163 let body = s.each(|bi| {
164 quote! {
165 result = result.combine(::chalk_ir::visit::Visit::visit_with(#bi, visitor, outer_binder));
166 if result.return_early() {
167 return result;
168 }
169 }
170 });
171
172 if kind == DeriveKind::FromHasInterner {
173 let param = get_generic_param_name(input).unwrap();
174 s.add_where_predicate(parse_quote! { #param: ::chalk_ir::visit::Visit<#interner> });
175 }
176
177 s.add_bounds(synstructure::AddBounds::None);
178 s.bound_impl(
179 quote!(::chalk_ir::visit:: #trait_name <#interner>),
180 quote! {
181 fn #method_name <'i, R: ::chalk_ir::visit::VisitResult>(
182 &self,
183 visitor: &mut dyn ::chalk_ir::visit::Visitor < 'i, #interner, Result = R >,
184 outer_binder: ::chalk_ir::DebruijnIndex,
185 ) -> R
186 where
187 #interner: 'i
188 {
189 let mut result = R::new();
190 match *self {
191 #body
192 }
193 return result;
194 }
195 },
196 )
197 }
198
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_ir::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_ir::Fallible<()>
250 where
251 #interner: 'i,
252 {
253 match (a, b) { #body }
254 }
255 },
256 )
257 }
258
259 /// Derives Fold for structs and enums for which one of the following is true:
260 /// - It has a `#[has_interner(TheInterner)]` attribute
261 /// - There is a single parameter `T: HasInterner` (does not have to be named `T`)
262 /// - There is a single parameter `I: Interner` (does not have to be named `I`)
263 fn derive_fold(mut s: synstructure::Structure) -> TokenStream {
264 let input = s.ast();
265
266 let (interner, kind) = find_interner(&mut s);
267
268 let body = s.each_variant(|vi| {
269 let bindings = vi.bindings();
270 vi.construct(|_, index| {
271 let bind = &bindings[index];
272 quote! {
273 ::chalk_ir::fold::Fold::fold_with(#bind, folder, outer_binder)?
274 }
275 })
276 });
277
278 let type_name = &input.ident;
279
280 let (target_interner, result) = match kind {
281 DeriveKind::FromHasInternerAttr => (interner.clone(), quote! { #type_name }),
282 DeriveKind::FromHasInterner => {
283 let param = get_generic_param_name(input).unwrap();
284
285 s.add_impl_generic(parse_quote! { _U })
286 .add_impl_generic(parse_quote! { _TI })
287 .add_where_predicate(
288 parse_quote! { #param: ::chalk_ir::fold::Fold<#interner, _TI, Result = _U> },
289 )
290 .add_where_predicate(
291 parse_quote! { _U: ::chalk_ir::interner::HasInterner<Interner = _TI> },
292 )
293 .add_where_predicate(
294 parse_quote! { _TI: ::chalk_ir::interner::TargetInterner<#interner> },
295 );
296
297 (quote! { _TI }, quote! { #type_name<_U> })
298 }
299 DeriveKind::FromInterner => {
300 s.add_impl_generic(parse_quote! { _TI })
301 .add_where_predicate(
302 parse_quote! { _TI: ::chalk_ir::interner::TargetInterner<#interner> },
303 );
304
305 (quote! { _TI }, quote! { #type_name<_TI> })
306 }
307 };
308
309 s.add_bounds(synstructure::AddBounds::None);
310 s.bound_impl(
311 quote!(::chalk_ir::fold::Fold<#interner, #target_interner>),
312 quote! {
313 type Result = #result;
314
315 fn fold_with<'i>(
316 &self,
317 folder: &mut dyn ::chalk_ir::fold::Folder < 'i, #interner, #target_interner >,
318 outer_binder: ::chalk_ir::DebruijnIndex,
319 ) -> ::chalk_ir::Fallible<Self::Result>
320 where
321 #interner: 'i,
322 #target_interner: 'i,
323 {
324 Ok(match *self { #body })
325 }
326 },
327 )
328 }