futures_macro_async/
lib.rs

1//! Procedural macro for the `#[async]` attribute.
2//!
3//! This crate is an implementation of the `#[async]` attribute as a procedural
4//! macro. This is nightly-only for now as it's using the unstable features of
5//! procedural macros. Furthermore it's generating code that's using a new
6//! keyword, `yield`, and a new construct, generators, both of which are also
7//! unstable.
8//!
9//! Currently this crate depends on `syn` and `quote` to do all the heavy
10//! lifting, this is just a very small shim around creating a closure/future out
11//! of a generator.
12#![cfg_attr(feature = "nightly", feature(proc_macro))]
13#![recursion_limit = "128"]
14
15
16macro_rules! if_nightly {
17    ($($i:item)*) => ($(
18        #[cfg(feature = "nightly")]
19        $i
20    )*)
21}
22
23if_nightly! {
24    extern crate proc_macro2;
25    extern crate proc_macro;
26    #[macro_use]
27    extern crate quote;
28    #[macro_use]
29    extern crate syn;
30
31    use proc_macro2::Span;
32    use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
33    use quote::{Tokens, ToTokens};
34    use syn::*;
35    use syn::punctuated::Punctuated;
36    use syn::fold::Fold;
37
38    mod attribute;
39    mod elision;
40
41    use attribute::Attribute;
42
43    macro_rules! quote_cs {
44        ($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*))
45    }
46
47    fn async_inner<F>(
48        attribute: Attribute,
49        function: TokenStream,
50        gen_function: Tokens,
51        return_ty: F,
52    ) -> TokenStream
53    where F: FnOnce(&Type, &[&Lifetime]) -> proc_macro2::TokenStream
54    {
55        // Parse our item, expecting a function. This function may be an actual
56        // top-level function or it could be a method (typically dictated by the
57        // arguments). We then extract everything we'd like to use.
58        let ItemFn {
59            ident,
60            vis,
61            unsafety,
62            constness,
63            abi,
64            block,
65            decl,
66            attrs,
67            ..
68        } = match syn::parse(function).expect("failed to parse tokens as a function") {
69            Item::Fn(item) => item,
70            _ => panic!("#[async] can only be applied to functions"),
71        };
72        let FnDecl {
73            inputs,
74            output,
75            variadic,
76            mut generics,
77            fn_token,
78            ..
79        } = { *decl };
80        let where_clause = &generics.where_clause;
81        assert!(variadic.is_none(), "variadic functions cannot be async");
82        let (output, rarrow_token) = match output {
83            ReturnType::Type(rarrow_token, t) => (*t, rarrow_token),
84            ReturnType::Default => {
85                (TypeTuple {
86                    elems: Default::default(),
87                    paren_token: Default::default(),
88                }.into(), Default::default())
89            }
90        };
91
92        // We've got to get a bit creative with our handling of arguments. For a
93        // number of reasons we translate this:
94        //
95        //      fn foo(ref a: u32) -> Result<u32, u32> {
96        //          // ...
97        //      }
98        //
99        // into roughly:
100        //
101        //      fn foo(__arg_0: u32) -> impl Future<...> {
102        //          gen_move(move || {
103        //              let ref a = __arg0;
104        //
105        //              // ...
106        //          })
107        //      }
108        //
109        // The intention here is to ensure that all local function variables get
110        // moved into the generator we're creating, and they're also all then bound
111        // appropriately according to their patterns and whatnot.
112        //
113        // We notably skip everything related to `self` which typically doesn't have
114        // many patterns with it and just gets captured naturally.
115        let mut inputs_no_patterns = Vec::new();
116        let mut patterns = Vec::new();
117        let mut temp_bindings = Vec::new();
118        for (i, input) in inputs.into_iter().enumerate() {
119            // `self: Box<Self>` will get captured naturally
120            let mut is_input_no_pattern = false;
121            if let FnArg::Captured(ref arg) = input {
122                if let Pat::Ident(PatIdent { ref ident, ..}) = arg.pat {
123                    if ident == "self" {
124                        is_input_no_pattern = true;
125                    }
126                }
127            }
128            if is_input_no_pattern {
129                inputs_no_patterns.push(input);
130                continue
131            }
132
133            match input {
134                FnArg::Captured(ArgCaptured {
135                    pat: syn::Pat::Ident(syn::PatIdent {
136                        by_ref: None,
137                        ..
138                    }),
139                    ..
140                }) => {
141                    inputs_no_patterns.push(input);
142                }
143
144                // `ref a: B` (or some similar pattern)
145                FnArg::Captured(ArgCaptured { pat, ty, colon_token }) => {
146                    patterns.push(pat);
147                    let ident = Ident::from(format!("__arg_{}", i));
148                    temp_bindings.push(ident.clone());
149                    let pat = PatIdent {
150                        by_ref: None,
151                        mutability: None,
152                        ident: ident,
153                        subpat: None,
154                    };
155                    inputs_no_patterns.push(ArgCaptured {
156                        pat: pat.into(),
157                        ty,
158                        colon_token,
159                    }.into());
160                }
161
162                // Other `self`-related arguments get captured naturally
163                _ => {
164                    inputs_no_patterns.push(input);
165                }
166            }
167        }
168
169
170        // This is the point where we handle
171        //
172        //      #[async]
173        //      for x in y {
174        //      }
175        //
176        // Basically just take all those expression and expand them.
177        let block = ExpandAsyncFor.fold_block(*block);
178
179
180        let inputs_no_patterns = elision::unelide_lifetimes(&mut generics.params, inputs_no_patterns);
181        let lifetimes: Vec<_> = generics.lifetimes().map(|l| &l.lifetime).collect();
182
183        let return_ty = return_ty(&output, &lifetimes);
184
185
186        let block_inner = quote_cs! {
187            #( let #patterns = #temp_bindings; )*
188            #block
189        };
190        let mut result = Tokens::new();
191        block.brace_token.surround(&mut result, |tokens| {
192            block_inner.to_tokens(tokens);
193        });
194        syn::token::Semi([block.brace_token.0]).to_tokens(&mut result);
195
196        let gen_body_inner = quote_cs! {
197            let __e: #output = #result
198
199            // Ensure that this closure is a generator, even if it doesn't
200            // have any `yield` statements.
201            #[allow(unreachable_code)]
202            {
203                return __e;
204                loop { yield ::futures::__rt::Async::Pending }
205            }
206        };
207        let mut gen_body = Tokens::new();
208        block.brace_token.surround(&mut gen_body, |tokens| {
209            gen_body_inner.to_tokens(tokens);
210        });
211
212        // Give the invocation of the `gen` function the same span as the output
213        // as currently errors related to it being a result are targeted here. Not
214        // sure if more errors will highlight this function call...
215        let output_span = first_last(&output);
216        let gen_function = respan(gen_function.into(), &output_span);
217        let body_inner = quote_cs! {
218            #gen_function (static move || -> #output #gen_body)
219        };
220
221        let body_inner = if attribute.boxed {
222            respan(if attribute.send {
223                quote_cs! { (#body_inner).pin() }
224            } else {
225                quote_cs! { (#body_inner).pin_local() }
226            }.into(), &output_span)
227        } else {
228            body_inner.into()
229        };
230
231        let mut body = Tokens::new();
232        block.brace_token.surround(&mut body, |tokens| {
233            body_inner.to_tokens(tokens);
234        });
235
236        let output = quote_cs! {
237            #(#attrs)*
238            #vis #unsafety #abi #constness
239            #fn_token #ident #generics(#(#inputs_no_patterns),*)
240                #rarrow_token #return_ty
241                #where_clause
242            #body
243        };
244
245        // println!("{}", output);
246        output.into()
247    }
248
249    #[proc_macro_attribute]
250    pub fn async(attribute: TokenStream, function: TokenStream) -> TokenStream {
251        let attr = attribute.to_string();
252        // Handle arguments to the #[async] attribute, if any
253        let args = syn::parse::<AsyncArgs>(attribute)
254            .expect(&format!("failed to parse attribute arguments: {}", attr));
255
256        let attribute = Attribute::from(args.0.into_iter().map(|arg| arg.0));
257
258        async_inner(attribute, function, quote_cs! { ::futures::__rt::gen_future }, |output, lifetimes| {
259            // TODO: can we lift the restriction that `futures` must be at the root of
260            //       the crate?
261            let output_span = first_last(&output);
262            let return_ty = match attribute {
263                Attribute::NONE => quote_cs! {
264                    impl ::futures::__rt::MyStableFuture<!> + #(#lifetimes +)*
265                },
266                Attribute::SEND => quote_cs! {
267                    impl ::futures::__rt::MyStableFuture<!> + Send + #(#lifetimes +)*
268                },
269                Attribute::BOXED => quote_cs! {
270                    ::futures::__rt::std::boxed::PinBox<::futures::__rt::Future<
271                        Item = <! as ::futures::__rt::IsResult>::Ok,
272                        Error = <! as ::futures::__rt::IsResult>::Err,
273                    > + #(#lifetimes +)*>
274                },
275                Attribute::BOXED_SEND => quote_cs! {
276                    ::futures::__rt::std::boxed::PinBox<::futures::__rt::Future<
277                        Item = <! as ::futures::__rt::IsResult>::Ok,
278                        Error = <! as ::futures::__rt::IsResult>::Err,
279                    > + Send + #(#lifetimes +)*>
280                },
281            };
282            let return_ty = respan(return_ty.into(), &output_span);
283            replace_bang(return_ty, &output)
284        })
285    }
286
287    #[proc_macro_attribute]
288    pub fn async_stream(attribute: TokenStream, function: TokenStream) -> TokenStream {
289        // Handle arguments to the #[async_stream] attribute, if any
290        let args = syn::parse::<AsyncStreamArgs>(attribute)
291            .expect("failed to parse attribute arguments");
292
293        let (args, valued_args): (Vec<_>, Vec<_>) = args.0.into_iter().partition(|arg| arg.1.is_none());
294        let args = args.into_iter().map(|arg| arg.0);
295        let valued_args = valued_args.into_iter().map(|arg| (arg.0, arg.1.unwrap()));
296
297        let mut item_ty = None;
298
299        for (term, ty) in valued_args {
300            match term.as_ref() {
301                "item" => {
302                    if item_ty.is_some() {
303                        panic!("duplicate 'item' argument");
304                    }
305                    item_ty = Some(ty);
306                }
307                _ => {
308                    panic!("unexpected macro argument '{}'", quote_cs!(#term = #ty));
309                }
310            }
311        }
312
313        let item_ty = item_ty.expect("#[async_stream] requires item type to be specified");
314        let attribute = Attribute::from(args);
315
316        async_inner(attribute, function, quote_cs! { ::futures::__rt::gen_stream }, |output, lifetimes| {
317            let return_ty = match attribute {
318                Attribute::NONE => quote_cs! {
319                    impl ::futures::__rt::MyStableStream<!, !> + #(#lifetimes +)*
320                },
321                Attribute::SEND => quote_cs! {
322                    impl ::futures::__rt::MyStableStream<!, !> + Send + #(#lifetimes +)*
323                },
324                Attribute::BOXED => quote_cs! {
325                    ::futures::__rt::std::boxed::PinBox<::futures::__rt::Stream<
326                        Item = !,
327                        Error = <! as ::futures::__rt::IsResult>::Err,
328                    > + #(#lifetimes +)*>
329                },
330                Attribute::BOXED_SEND => quote_cs! {
331                    ::futures::__rt::std::boxed::PinBox<::futures::__rt::Stream<
332                        Item = !,
333                        Error = <! as ::futures::__rt::IsResult>::Err,
334                    > + Send + #(#lifetimes +)*>
335                },
336            };
337            let output_span = first_last(&output);
338            let return_ty = respan(return_ty.into(), &output_span);
339            replace_bangs(return_ty, &[&item_ty, &output])
340        })
341    }
342
343    #[proc_macro]
344    pub fn async_block(input: TokenStream) -> TokenStream {
345        let input = TokenStream::from(TokenTree::Group(Group::new(Delimiter::Brace, input)));
346        let expr = syn::parse(input)
347            .expect("failed to parse tokens as an expression");
348        let expr = ExpandAsyncFor.fold_expr(expr);
349
350        let mut tokens = quote_cs! {
351            ::futures::__rt::gen_future
352        };
353
354        // Use some manual token construction here instead of `quote_cs!` to ensure
355        // that we get the `call_site` span instead of the default span.
356        let span = Span::call_site();
357        syn::token::Paren(span).surround(&mut tokens, |tokens| {
358            syn::token::Static(span).to_tokens(tokens);
359            syn::token::Move(span).to_tokens(tokens);
360            syn::token::OrOr([span, span]).to_tokens(tokens);
361            syn::token::Brace(span).surround(tokens, |tokens| {
362                (quote_cs! {
363                    if false { yield ::futures::__rt::Async::Pending }
364                }).to_tokens(tokens);
365                expr.to_tokens(tokens);
366            });
367        });
368
369        tokens.into()
370    }
371
372    #[proc_macro]
373    pub fn async_stream_block(input: TokenStream) -> TokenStream {
374        let input = TokenStream::from(TokenTree::Group(Group::new(Delimiter::Brace, input)));
375        let expr = syn::parse(input)
376            .expect("failed to parse tokens as an expression");
377        let expr = ExpandAsyncFor.fold_expr(expr);
378
379        let mut tokens = quote_cs! {
380            ::futures::__rt::gen_stream
381        };
382
383        // Use some manual token construction here instead of `quote_cs!` to ensure
384        // that we get the `call_site` span instead of the default span.
385        let span = Span::call_site();
386        syn::token::Paren(span).surround(&mut tokens, |tokens| {
387            syn::token::Static(span).to_tokens(tokens);
388            syn::token::Move(span).to_tokens(tokens);
389            syn::token::OrOr([span, span]).to_tokens(tokens);
390            syn::token::Brace(span).surround(tokens, |tokens| {
391                (quote_cs! {
392                    if false { yield ::futures::__rt::Async::Pending }
393                }).to_tokens(tokens);
394                expr.to_tokens(tokens);
395            });
396        });
397
398        tokens.into()
399    }
400
401    struct ExpandAsyncFor;
402
403    impl Fold for ExpandAsyncFor {
404        fn fold_expr(&mut self, expr: Expr) -> Expr {
405            let expr = fold::fold_expr(self, expr);
406            let mut async = false;
407            {
408                let attrs = match expr {
409                    Expr::ForLoop(syn::ExprForLoop { ref attrs, .. }) => attrs,
410                    _ => return expr,
411                };
412                if attrs.len() == 1 {
413                    // TODO: more validation here
414                    if attrs[0].path.segments.first().unwrap().value().ident == "async" {
415                        async = true;
416                    }
417                }
418            }
419            if !async {
420                return expr
421            }
422            let all = match expr {
423                Expr::ForLoop(item) => item,
424                _ => panic!("only for expressions can have #[async]"),
425            };
426            let ExprForLoop { pat, expr, body, label, .. } = all;
427
428            // Basically just expand to a `poll` loop
429            let tokens = quote_cs! {{
430                let mut __stream = #expr;
431                #label
432                loop {
433                    let #pat = {
434                        let r = {
435                            let pin = unsafe {
436                                ::futures::__rt::std::mem::Pin::new_unchecked(&mut __stream)
437                            };
438                            ::futures::__rt::in_ctx(|ctx| ::futures::__rt::StableStream::poll_next(pin, ctx))
439                        };
440                        match r? {
441                            ::futures::__rt::Async::Ready(e) => {
442                                match e {
443                                    ::futures::__rt::std::option::Option::Some(e) => e,
444                                    ::futures::__rt::std::option::Option::None => break,
445                                }
446                            }
447                            ::futures::__rt::Async::Pending => {
448                                yield ::futures::__rt::Async::Pending;
449                                continue
450                            }
451                        }
452                    };
453
454                    #body
455                }
456            }};
457            syn::parse(tokens.into()).unwrap()
458        }
459
460        // Don't recurse into items
461        fn fold_item(&mut self, item: Item) -> Item {
462            item
463        }
464    }
465
466    fn first_last(tokens: &ToTokens) -> (Span, Span) {
467        let mut spans = Tokens::new();
468        tokens.to_tokens(&mut spans);
469        let good_tokens = proc_macro2::TokenStream::from(spans).into_iter().collect::<Vec<_>>();
470        let first_span = good_tokens.first().map(|t| t.span()).unwrap_or(Span::call_site());
471        let last_span = good_tokens.last().map(|t| t.span()).unwrap_or(first_span);
472        (first_span, last_span)
473    }
474
475    fn respan(input: proc_macro2::TokenStream,
476              &(first_span, last_span): &(Span, Span)) -> proc_macro2::TokenStream {
477        let mut new_tokens = input.into_iter().collect::<Vec<_>>();
478        if let Some(token) = new_tokens.first_mut() {
479            token.set_span(first_span);
480        }
481        for token in new_tokens.iter_mut().skip(1) {
482            token.set_span(last_span);
483        }
484        new_tokens.into_iter().collect()
485    }
486
487    fn replace_bang(input: proc_macro2::TokenStream, tokens: &ToTokens)
488        -> proc_macro2::TokenStream
489    {
490        let mut new_tokens = Tokens::new();
491        for token in input.into_iter() {
492            match token {
493                proc_macro2::TokenTree::Op(op) if op.op() == '!' => tokens.to_tokens(&mut new_tokens),
494                _ => token.to_tokens(&mut new_tokens),
495            }
496        }
497        new_tokens.into()
498    }
499
500    fn replace_bangs(input: proc_macro2::TokenStream, replacements: &[&ToTokens])
501        -> proc_macro2::TokenStream
502    {
503        let mut replacements = replacements.iter().cycle();
504        let mut new_tokens = Tokens::new();
505        for token in input.into_iter() {
506            match token {
507                proc_macro2::TokenTree::Op(op) if op.op() == '!' => {
508                    replacements.next().unwrap().to_tokens(&mut new_tokens);
509                }
510                _ => token.to_tokens(&mut new_tokens),
511            }
512        }
513        new_tokens.into()
514    }
515
516    struct AsyncArg(syn::Ident);
517
518    impl synom::Synom for AsyncArg {
519        named!(parse -> Self, do_parse!(
520            i: syn!(syn::Ident) >>
521            (AsyncArg(i))));
522    }
523
524    struct AsyncArgs(Vec<AsyncArg>);
525
526    impl synom::Synom for AsyncArgs {
527        named!(parse -> Self, map!(
528            option!(parens!(call!(Punctuated::<AsyncArg, syn::token::Comma>::parse_separated_nonempty))),
529            |p| AsyncArgs(p.map(|d| d.1.into_iter().collect()).unwrap_or_default())
530        ));
531    }
532
533    struct AsyncStreamArg(syn::Ident, Option<syn::Type>);
534
535    impl synom::Synom for AsyncStreamArg {
536        named!(parse -> Self, do_parse!(
537            i: syn!(syn::Ident) >>
538            p: option!(do_parse!(
539                syn!(syn::token::Eq) >>
540                p: syn!(syn::Type) >>
541                (p))) >>
542            (AsyncStreamArg(i, p))));
543    }
544
545    struct AsyncStreamArgs(Vec<AsyncStreamArg>);
546
547    impl synom::Synom for AsyncStreamArgs {
548        named!(parse -> Self, map!(
549            option!(parens!(call!(Punctuated::<AsyncStreamArg, syn::token::Comma>::parse_separated_nonempty))),
550            |p| AsyncStreamArgs(p.map(|d| d.1.into_iter().collect()).unwrap_or_default())
551        ));
552    }
553}