rayon_attr/
lib.rs

1#![feature(proc_macro)]
2
3extern crate proc_macro;
4
5#[feature(full)]
6extern crate syn;
7
8#[macro_use]
9extern crate quote;
10
11use proc_macro::TokenStream;
12use quote::ToTokens;
13use syn::ExprForLoop;
14
15/// Converts a for-loop to rayon parallel for_each.
16///
17/// Only valid on when applied to a for statement, with #![feature(stmt_expr_attributes)] present.
18#[proc_macro_attribute]
19pub fn parallel(_args: TokenStream, input: TokenStream) -> TokenStream {
20    // Return the input unchanged if it failed to parse. The compiler will show
21    // the right diagnostics.
22    let input: ExprForLoop =
23        syn::parse(input.clone()).expect("parallel attribute may only be applied to for loops.");
24
25    // TODO support input.label
26    // TODO support input.attrs
27    let expr = input.expr.clone().into_tokens();
28    let body = input.body.clone().into_tokens();
29    let pat = input.pat.clone().into_tokens();
30
31    // TODO check for early returns in the for loop, they would be errors in the parallel version.
32    let parallelized = quote!((#expr).into_par_iter().for_each(|#pat| #body));
33
34    parallelized.to_string().parse().unwrap()
35}
36
37/*
38Unit test panics, possibly because we are a proc-macro crate:
39
40---- tests::for_loop stdout ----
41        thread 'tests::for_loop' panicked at 'proc_macro::__internal::with_sess() called before set_parse_sess()!', libproc_macro\lib.rs:898:9
42stack backtrace:
43   0: std::rt::lang_start_internal
44   1: std::sys::windows::c::TryAcquireSRWLockShared
45   2: std::panicking::take_hook
46   3: std::panicking::take_hook
47   4: std::panicking::rust_panic_with_hook
48   5: proc_macro::__internal::CURRENT_SESS::__getit
49   6: proc_macro::__internal::in_sess
50   7: <proc_macro::TokenStream as core::str::FromStr>::from_str
51   8: core::str::{{impl}}::parse<proc_macro::TokenStream>
52             at C:\projects\rust\src\libcore\str\mod.rs:2534
53   9: alloc::str::{{impl}}::parse<proc_macro::TokenStream>
54             at C:\projects\rust\src\liballoc\str.rs:1798
55  10: rayon_attr::tests::for_loop
56             at .\src\lib.rs:44
57  11: rayon_attr::__test::TESTS::{{closure}}
58             at .\src\lib.rs:43
59  12: core::ops::function::FnOnce::call_once<closure,()>
60             at C:\projects\rust\src\libcore\ops\function.rs:223
61  13: <unknown>
62  14: _rust_maybe_catch_panic
63  15: test::stats::winsorize
64  16: <test::TestOpts as core::fmt::Debug>::fmt
65  17: _rust_maybe_catch_panic
66  18: test::stats::winsorize
67  19: std::sync::mpsc::blocking::WaitToken::wait_max_until
68  20: std::sys::windows::thread::Thread::new
69  21: BaseThreadInitThunk
70  22: RtlUserThreadStart
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use proc_macro::TokenStream;
76
77    #[test]
78    fn for_loop() {
79        let input = quote!{
80            for x in 0..100 {
81                println!("{}", x);
82            }
83        }.to_string().parse().unwrap();
84
85        let transformed = parallel(TokenStream::empty(), input);
86        
87        let output = format!("{}", transformed);
88
89        assert_eq!(output, "");
90    }
91}
92*/