1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;
use quote::quote;
use syn::fold::{self, Fold};
use syn::parse_macro_input;

#[proc_macro_hack]
pub fn parallel(input: TokenStream) -> TokenStream {
    // for #pat in #expr { #body }
    let syn::ExprForLoop {
        attrs,
        label,
        pat,
        expr,
        body,
        ..
    } = parse_macro_input!(input);

    assert!(attrs.is_empty(), "loop attributes are not supported");

    let mut transform = TransformBody::new(label.as_ref());
    let body = transform.fold_block(body);

    let imp = quote! { $crate::rayon_macro_impl };
    TokenStream::from(quote! {
        #imp::ParallelIterator::for_each(
            #imp::IntoParallelIterator::into_par_iter(#expr),
            |#pat| #label while { #body; false } {},
        )
    })
}

struct TransformBody {
    for_ident: Option<syn::Ident>,
    loop_level: usize,
    try_level: usize,
}

impl TransformBody {
    fn new(for_label: Option<&syn::Label>) -> Self {
        let for_ident = for_label.map(|l| l.name.ident.clone());
        TransformBody {
            for_ident,
            loop_level: 0,
            try_level: 0,
        }
    }

    fn is_controlled(&self, label: &Option<syn::Lifetime>) -> bool {
        if let Some(label) = label {
            self.for_ident.as_ref() == Some(&label.ident)
        } else {
            self.loop_level == 0
        }
    }

    fn take_shadowed(&mut self, label: &Option<syn::Label>) -> Option<syn::Ident> {
        if let Some(label) = label {
            if self.for_ident.as_ref() == Some(&label.name.ident) {
                return self.for_ident.take();
            }
        }
        None
    }
}

impl Fold for TransformBody {
    // We don't care about other items defined in our block
    fn fold_item(&mut self, i: syn::Item) -> syn::Item {
        i
    }

    fn fold_expr(&mut self, i: syn::Expr) -> syn::Expr {
        use syn::Expr::*;
        match i {
            // Control flow doesn't escape closures, so skip them.
            Async(_) | Closure(_) => return i,

            // Inner loops might intercept `break` and `continue`
            ForLoop(syn::ExprForLoop { ref label, .. })
            | Loop(syn::ExprLoop { ref label, .. })
            | While(syn::ExprWhile { ref label, .. }) => {
                let shadowed = self.take_shadowed(label);
                self.loop_level += 1;
                let ret = fold::fold_expr(self, i);
                self.loop_level -= 1;
                if shadowed.is_some() {
                    self.for_ident = shadowed;
                }
                ret
            }

            Continue(c) => {
                if self.is_controlled(&c.label) {
                    Return(syn::ExprReturn {
                        attrs: c.attrs,
                        return_token: syn::token::Return {
                            span: c.continue_token.span,
                        },
                        expr: None,
                    })
                } else {
                    Continue(fold::fold_expr_continue(self, c))
                }
            }

            Break(b) => {
                if self.is_controlled(&b.label) {
                    panic!("`break` is not supported yet")
                } else {
                    Break(fold::fold_expr_break(self, b))
                }
            }

            Return(_) => panic!("`return` is not supported yet"),

            Try(_) if self.try_level == 0 => panic!("`?` is not supported yet"),
            TryBlock(_) => {
                self.try_level += 1;
                let ret = fold::fold_expr(self, i);
                self.try_level -= 1;
                ret
            }

            Await(_) => panic!("`.await` is not supported"),
            Yield(_) => panic!("`yield` is not supported"),

            _ => fold::fold_expr(self, i),
        }
    }
}