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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
use super::*;
/// Transform an `async fn` into an equivalent `fn … -> impl Future` signature.
pub(in crate)
fn manually_unsugar_async (
mut fun: ImplItemMethod,
) -> ImplItemMethod
{
// 1) Remove the `async` keyword to get a normal `fn`.
fun.sig.asyncness = None;
// 2) Introduce a new "free" lifetime parameter, which will be the `'lt`
// in `-> impl 'lt + Future…`
// For such a lifetime to work (for the body of the function to compile)
// we need the types of each parameter (captured by the future) to
// be "usable within that `'lt`", _.i.e_, we need `ArgTy : 'lt` to hold
// for each `ArgTy` (including `Self`).
let ref Self_: Option<Type> = fun.sig.receiver().and_then(|it| {
// If there is a receiver *and* if it is in the shorthand form,
// we need to forge a `Self` type ourselves.
if let FnArg::Receiver(rc) = it {
Some({
let Self_ = format_ident!("Self", span = rc.self_token.span);
// If `Some`, it bundles a named lifetime, by construction.
if let Some((ref amp, ref lt)) = rc.reference {
parse_quote!(
#amp #lt #Self_
)
} else {
parse_quote!(
#Self_
)
}
})
} else {
None
}
});
let generics = &mut fun.sig.generics;
let minimum_lifetime = Lifetime::new(
"'__async_fut",
Span::call_site(),
);
let idx = generics.lifetimes().count();
generics.params.insert(idx, parse_quote!( #minimum_lifetime ));
let input_tys = fun.sig.inputs.iter().map(|fn_arg| match *fn_arg {
| FnArg::Receiver(_) => Self_.as_ref().unwrap(),
| FnArg::Typed(PatType { ref ty, .. }) => ty,
});
generics
.make_where_clause()
.predicates
.extend(input_tys.map(|ty| -> WherePredicate {
parse_quote!(
// ≥
#ty : #minimum_lifetime
)
}))
;
// 3) Replace `-> RetTy` with `-> impl 'lt + Future<Output = RetTy>`
// (Note: no need to worry about auto-traits here, since they leak anyways)
let Ret = match mem::replace(&mut fun.sig.output, ReturnType::Default) {
| ReturnType::Type(_arrow, ty) => ty,
| ReturnType::Default => parse_quote!( () ),
};
fun.sig.output = parse_quote!(
->
impl
#minimum_lifetime +
::fix_hidden_lifetime_bug::core::future::Future<
Output = #Ret,
>
);
// 4) In the to-be-generated `async move { … }` function body, we need to
// make sure all the function parameters are captured by it.
// In order to do that, we emit a dummy `let _ = (arg1, arg2, …);` statement
// at the beginning of that `async move` block.
//
// That being said, if the user fed to the macro some more exotic patterns,
// such as `async fn f(_: Arg) {}`: we cannot emit `let _ = _;`!
// (and even if we could, that would not capture that variable).
// So this gets a bit more annoying: we roll our own set of fallback var
// names, `__arg_0`, `__arg_1`, …, and replace such patterns with it.
// Then, to go back to the arg semantics that the user wrote, we change the
// LHS of the `let` to be the replaced pattern.
let capture_args_stmt = {
let self_comma = fun.sig.receiver().map(|_| {
quote!( self, )
});
let self_pat_comma = self_comma.as_ref().map(|_| {
quote!( _, )
});
let mut each_simple_arg_name =
(0 .. fun.sig.inputs.len())
.map(|i| format_ident!("__arg_{}", i))
.collect::<Vec<_>>()
;
let mut each_simple_arg_name = &mut each_simple_arg_name[..];
let mut inputs =
fun .sig
.inputs
.iter_mut()
;
if self_pat_comma.is_some() {
each_simple_arg_name = &mut each_simple_arg_name[1 ..];
inputs.by_ref().take(1).for_each(drop);
}
let each_pat =
inputs
.zip(&mut each_simple_arg_name[..])
.map(|(arg_pat, simple_name)| match *arg_pat {
| FnArg::Typed(PatType {
ref mut pat,
..
}) => {
simple_name.set_span(pat.span());
match **pat {
| Pat::Ident(PatIdent { ref ident, .. })
if ident.to_string().starts_with("__arg_").not()
=> {
simple_name.clone_from(ident);
(**pat).clone()
},
| ref mut it => mem::replace(
it,
parse_quote!(#simple_name),
),
}
},
| _ => unreachable!(),
})
;
// A comment for people looking at the output of `showme` to understand
// what is going on:
let comment = if cfg!(feature = "showme") {
::quote::quote!(
"Mention the input vars so that they get captured by the Future";
)
} else {
TokenStream2::new()
};
quote!(
#comment
let (
#self_pat_comma #(#each_pat ,)*
) = (
#self_comma #(#each_simple_arg_name ,)*
);
)
};
// 5) And voilà! 🙂
let stmts = &fun.block.stmts;
fun.block = parse_quote!({
async move {
#capture_args_stmt
#(#stmts)*
}
});
fun
}