about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-04-23 21:50:52 +0200
committerGitHub <noreply@github.com>2019-04-23 21:50:52 +0200
commit62d1574876f5531bce1b267e62dff520d7adcbbb (patch)
treedf1cbb9f6f71a431a0d89164abcd9db6075a4955
parent4eff8526a789e0dfa8b97f7dec91b7b5c18e8544 (diff)
parent119e67ac6b7d72c2b314777ba36b2191cbfa7309 (diff)
downloadrust-62d1574876f5531bce1b267e62dff520d7adcbbb.tar.gz
rust-62d1574876f5531bce1b267e62dff520d7adcbbb.zip
Rollup merge of #59823 - davidtwco:issue-54716, r=cramertj
[wg-async-await] Drop `async fn` arguments in async block

Fixes #54716.

This PR modifies the HIR lowering (and some other places to make this work) so that unused arguments to a async function are always dropped inside the async move block and not at the end of the function body.

```
async fn foo(<pattern>: <type>) {
  async move {
  }
} // <-- dropped as you "exit" the fn

// ...becomes...
fn foo(__arg0: <ty>) {
  async move {
    let <pattern>: <ty> = __arg0;
  } // <-- dropped as you "exit" the async block
}
```

However, the exact ordering of drops is not the same as a regular function, [as visible in this playground example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=be39af1a58e5d430be1eb3c722cb1ec3) - I believe this to be an unrelated issue. There is a [Zulip topic](https://rust-lang.zulipchat.com/#narrow/stream/187312-t-compiler.2Fwg-async-await/topic/.2354716.20drop.20order) for this.

r? @cramertj
cc @nikomatsakis
-rw-r--r--src/librustc/hir/intravisit.rs16
-rw-r--r--src/librustc/hir/lowering.rs184
-rw-r--r--src/librustc/hir/map/def_collector.rs39
-rw-r--r--src/librustc/hir/mod.rs31
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs15
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs9
-rw-r--r--src/librustc/lint/context.rs19
-rw-r--r--src/librustc/middle/resolve_lifetime.rs2
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs1
-rw-r--r--src/librustc_passes/ast_validation.rs6
-rw-r--r--src/librustc_privacy/lib.rs20
-rw-r--r--src/librustc_resolve/lib.rs36
-rw-r--r--src/librustc_save_analysis/sig.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs10
-rw-r--r--src/librustc_typeck/check/writeback.rs10
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/libsyntax/ast.rs51
-rw-r--r--src/libsyntax/ext/build.rs6
-rw-r--r--src/libsyntax/ext/placeholders.rs20
-rw-r--r--src/libsyntax/mut_visit.rs34
-rw-r--r--src/libsyntax/parse/parser.rs87
-rw-r--r--src/libsyntax/print/pprust.rs23
-rw-r--r--src/libsyntax/visit.rs3
-rw-r--r--src/libsyntax_ext/deriving/debug.rs1
-rw-r--r--src/test/mir-opt/inline-closure-borrows-arg.rs2
-rw-r--r--src/test/mir-opt/inline-closure.rs2
-rw-r--r--src/test/run-pass/issue-54716.rs184
27 files changed, 685 insertions, 130 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 007eaef74a7..a0c9e5983a1 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -58,10 +58,10 @@ impl<'a> FnKind<'a> {
         }
     }
 
-    pub fn header(&self) -> Option<FnHeader> {
+    pub fn header(&self) -> Option<&FnHeader> {
         match *self {
-            FnKind::ItemFn(_, _, header, _, _) => Some(header),
-            FnKind::Method(_, sig, _, _) => Some(sig.header),
+            FnKind::ItemFn(_, _, ref header, _, _) => Some(header),
+            FnKind::Method(_, ref sig, _, _) => Some(&sig.header),
             FnKind::Closure(_) => None,
         }
     }
@@ -262,6 +262,9 @@ pub trait Visitor<'v> : Sized {
     fn visit_pat(&mut self, p: &'v Pat) {
         walk_pat(self, p)
     }
+    fn visit_argument_source(&mut self, s: &'v ArgSource) {
+        walk_argument_source(self, s)
+    }
     fn visit_anon_const(&mut self, c: &'v AnonConst) {
         walk_anon_const(self, c)
     }
@@ -399,10 +402,17 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body) {
     for argument in &body.arguments {
         visitor.visit_id(argument.hir_id);
         visitor.visit_pat(&argument.pat);
+        visitor.visit_argument_source(&argument.source);
     }
     visitor.visit_expr(&body.value);
 }
 
+pub fn walk_argument_source<'v, V: Visitor<'v>>(visitor: &mut V, source: &'v ArgSource) {
+    if let ArgSource::AsyncFn(pat) = source {
+        visitor.visit_pat(pat);
+    }
+}
+
 pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
     // Intentionally visiting the expr first - the initialization expr
     // dominates the local's definition.
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 99cae00fafc..380dee5fcdc 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -448,10 +448,9 @@ impl<'a> LoweringContext<'a> {
         impl<'lcx, 'interner> Visitor<'lcx> for MiscCollector<'lcx, 'interner> {
             fn visit_pat(&mut self, p: &'lcx Pat) {
                 match p.node {
-                    // Doesn't generate a Hir node
+                    // Doesn't generate a HIR node
                     PatKind::Paren(..) => {},
                     _ => {
-
                         if let Some(owner) = self.hir_id_owner {
                             self.lctx.lower_node_id_with_owner(p.id, owner);
                         }
@@ -461,6 +460,32 @@ impl<'a> LoweringContext<'a> {
                 visit::walk_pat(self, p)
             }
 
+            fn visit_fn(&mut self, fk: visit::FnKind<'lcx>, fd: &'lcx FnDecl, s: Span, _: NodeId) {
+                if fk.header().map(|h| h.asyncness.node.is_async()).unwrap_or(false) {
+                    // Don't visit the original pattern for async functions as it will be
+                    // replaced.
+                    for arg in &fd.inputs {
+                        if let ArgSource::AsyncFn(pat) = &arg.source { self.visit_pat(pat); }
+                        self.visit_ty(&arg.ty)
+                    }
+                    self.visit_fn_ret_ty(&fd.output);
+
+                    match fk {
+                        visit::FnKind::ItemFn(_, decl, _, body) => {
+                            self.visit_fn_header(decl);
+                            self.visit_block(body)
+                        },
+                        visit::FnKind::Method(_, sig, _, body) => {
+                            self.visit_fn_header(&sig.header);
+                            self.visit_block(body)
+                        },
+                        visit::FnKind::Closure(body) => self.visit_expr(body),
+                    }
+                } else {
+                    visit::walk_fn(self, fk, fd, s)
+                }
+            }
+
             fn visit_item(&mut self, item: &'lcx Item) {
                 let hir_id = self.lctx.allocate_hir_id_counter(item.id).hir_id;
 
@@ -784,12 +809,10 @@ impl<'a> LoweringContext<'a> {
         })
     }
 
-    fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>) -> hir::BodyId {
+    fn record_body(&mut self, value: hir::Expr, arguments: HirVec<hir::Arg>) -> hir::BodyId {
         let body = hir::Body {
-            arguments: decl.map_or(hir_vec![], |decl| {
-                decl.inputs.iter().map(|x| self.lower_arg(x)).collect()
-            }),
             is_generator: self.is_generator,
+            arguments,
             value,
         };
         let id = body.id();
@@ -1112,11 +1135,10 @@ impl<'a> LoweringContext<'a> {
         capture_clause: CaptureBy,
         closure_node_id: NodeId,
         ret_ty: Option<&Ty>,
+        span: Span,
         body: impl FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
     ) -> hir::ExprKind {
         let prev_is_generator = mem::replace(&mut self.is_generator, true);
-        let body_expr = body(self);
-        let span = body_expr.span;
         let output = match ret_ty {
             Some(ty) => FunctionRetTy::Ty(P(ty.clone())),
             None => FunctionRetTy::Default(span),
@@ -1126,7 +1148,11 @@ impl<'a> LoweringContext<'a> {
             output,
             c_variadic: false
         };
-        let body_id = self.record_body(body_expr, Some(&decl));
+        // Lower the arguments before the body otherwise the body will call `lower_def` expecting
+        // the argument to have been assigned an id already.
+        let arguments = self.lower_args(Some(&decl));
+        let body_expr = body(self);
+        let body_id = self.record_body(body_expr, arguments);
         self.is_generator = prev_is_generator;
 
         let capture_clause = self.lower_capture_clause(capture_clause);
@@ -1157,8 +1183,9 @@ impl<'a> LoweringContext<'a> {
         F: FnOnce(&mut LoweringContext<'_>) -> hir::Expr,
     {
         let prev = mem::replace(&mut self.is_generator, false);
+        let arguments = self.lower_args(decl);
         let result = f(self);
-        let r = self.record_body(result, decl);
+        let r = self.record_body(result, arguments);
         self.is_generator = prev;
         return r;
     }
@@ -2224,10 +2251,17 @@ impl<'a> LoweringContext<'a> {
             init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
             span: l.span,
             attrs: l.attrs.clone(),
-            source: hir::LocalSource::Normal,
+            source: self.lower_local_source(l.source),
         }, ids)
     }
 
+    fn lower_local_source(&mut self, ls: LocalSource) -> hir::LocalSource {
+        match ls {
+            LocalSource::Normal => hir::LocalSource::Normal,
+            LocalSource::AsyncFn => hir::LocalSource::AsyncFn,
+        }
+    }
+
     fn lower_mutability(&mut self, m: Mutability) -> hir::Mutability {
         match m {
             Mutability::Mutable => hir::MutMutable,
@@ -2235,11 +2269,23 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
+    fn lower_args(&mut self, decl: Option<&FnDecl>) -> HirVec<hir::Arg> {
+        decl.map_or(hir_vec![], |decl| decl.inputs.iter().map(|x| self.lower_arg(x)).collect())
+    }
+
     fn lower_arg(&mut self, arg: &Arg) -> hir::Arg {
         let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(arg.id);
         hir::Arg {
             hir_id,
             pat: self.lower_pat(&arg.pat),
+            source: self.lower_arg_source(&arg.source),
+        }
+    }
+
+    fn lower_arg_source(&mut self, source: &ArgSource) -> hir::ArgSource {
+        match source {
+            ArgSource::Normal => hir::ArgSource::Normal,
+            ArgSource::AsyncFn(pat) => hir::ArgSource::AsyncFn(self.lower_pat(pat)),
         }
     }
 
@@ -2993,15 +3039,21 @@ impl<'a> LoweringContext<'a> {
     fn lower_async_body(
         &mut self,
         decl: &FnDecl,
-        asyncness: IsAsync,
+        asyncness: &IsAsync,
         body: &Block,
     ) -> hir::BodyId {
-        self.lower_body(Some(decl), |this| {
-            if let IsAsync::Async { closure_id, .. } = asyncness {
+        self.lower_body(Some(&decl), |this| {
+            if let IsAsync::Async { closure_id, ref arguments, .. } = asyncness {
+                let mut body = body.clone();
+
+                for a in arguments.iter().rev() {
+                    body.stmts.insert(0, a.stmt.clone());
+                }
+
                 let async_expr = this.make_async_expr(
-                    CaptureBy::Value, closure_id, None,
+                    CaptureBy::Value, *closure_id, None, body.span,
                     |this| {
-                        let body = this.lower_block(body, false);
+                        let body = this.lower_block(&body, false);
                         this.expr_block(body, ThinVec::new())
                     });
                 this.expr(body.span, async_expr, ThinVec::new())
@@ -3060,26 +3112,42 @@ impl<'a> LoweringContext<'a> {
                     value
                 )
             }
-            ItemKind::Fn(ref decl, header, ref generics, ref body) => {
+            ItemKind::Fn(ref decl, ref header, ref generics, ref body) => {
                 let fn_def_id = self.resolver.definitions().local_def_id(id);
                 self.with_new_scopes(|this| {
-                    // Note: we don't need to change the return type from `T` to
-                    // `impl Future<Output = T>` here because lower_body
-                    // only cares about the input argument patterns in the function
-                    // declaration (decl), not the return types.
-                    let body_id = this.lower_async_body(decl, header.asyncness.node, body);
+                    let mut lower_fn = |decl: &FnDecl| {
+                        // Note: we don't need to change the return type from `T` to
+                        // `impl Future<Output = T>` here because lower_body
+                        // only cares about the input argument patterns in the function
+                        // declaration (decl), not the return types.
+                        let body_id = this.lower_async_body(&decl, &header.asyncness.node, body);
+
+                        let (generics, fn_decl) = this.add_in_band_defs(
+                            generics,
+                            fn_def_id,
+                            AnonymousLifetimeMode::PassThrough,
+                            |this, idty| this.lower_fn_decl(
+                                &decl,
+                                Some((fn_def_id, idty)),
+                                true,
+                                header.asyncness.node.opt_return_id()
+                            ),
+                        );
 
-                    let (generics, fn_decl) = this.add_in_band_defs(
-                        generics,
-                        fn_def_id,
-                        AnonymousLifetimeMode::PassThrough,
-                        |this, idty| this.lower_fn_decl(
-                            decl,
-                            Some((fn_def_id, idty)),
-                            true,
-                            header.asyncness.node.opt_return_id()
-                        ),
-                    );
+                        (body_id, generics, fn_decl)
+                    };
+
+                    let (body_id, generics, fn_decl) = if let IsAsync::Async {
+                        arguments, ..
+                    } = &header.asyncness.node {
+                        let mut decl = decl.clone();
+                        // Replace the arguments of this async function with the generated
+                        // arguments that will be moved into the closure.
+                        decl.inputs = arguments.clone().drain(..).map(|a| a.arg).collect();
+                        lower_fn(&decl)
+                    } else {
+                        lower_fn(decl)
+                    };
 
                     hir::ItemKind::Fn(
                         fn_decl,
@@ -3558,15 +3626,33 @@ impl<'a> LoweringContext<'a> {
                 )
             }
             ImplItemKind::Method(ref sig, ref body) => {
-                let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness.node, body);
-                let impl_trait_return_allow = !self.is_in_trait_impl;
-                let (generics, sig) = self.lower_method_sig(
-                    &i.generics,
-                    sig,
-                    impl_item_def_id,
-                    impl_trait_return_allow,
-                    sig.header.asyncness.node.opt_return_id(),
-                );
+                let mut lower_method = |sig: &MethodSig| {
+                    let body_id = self.lower_async_body(
+                        &sig.decl, &sig.header.asyncness.node, body
+                    );
+                    let impl_trait_return_allow = !self.is_in_trait_impl;
+                    let (generics, sig) = self.lower_method_sig(
+                        &i.generics,
+                        sig,
+                        impl_item_def_id,
+                        impl_trait_return_allow,
+                        sig.header.asyncness.node.opt_return_id(),
+                    );
+                    (body_id, generics, sig)
+                };
+
+                let (body_id, generics, sig) = if let IsAsync::Async {
+                    ref arguments, ..
+                } = sig.header.asyncness.node {
+                    let mut sig = sig.clone();
+                    // Replace the arguments of this async function with the generated
+                    // arguments that will be moved into the closure.
+                    sig.decl.inputs = arguments.clone().drain(..).map(|a| a.arg).collect();
+                    lower_method(&sig)
+                } else {
+                    lower_method(sig)
+                };
+
                 (generics, hir::ImplItemKind::Method(sig, body_id))
             }
             ImplItemKind::Type(ref ty) => (
@@ -3760,7 +3846,7 @@ impl<'a> LoweringContext<'a> {
         impl_trait_return_allow: bool,
         is_async: Option<NodeId>,
     ) -> (hir::Generics, hir::MethodSig) {
-        let header = self.lower_fn_header(sig.header);
+        let header = self.lower_fn_header(&sig.header);
         let (generics, decl) = self.add_in_band_defs(
             generics,
             fn_def_id,
@@ -3782,10 +3868,10 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
+    fn lower_fn_header(&mut self, h: &FnHeader) -> hir::FnHeader {
         hir::FnHeader {
             unsafety: self.lower_unsafety(h.unsafety),
-            asyncness: self.lower_asyncness(h.asyncness.node),
+            asyncness: self.lower_asyncness(&h.asyncness.node),
             constness: self.lower_constness(h.constness),
             abi: h.abi,
         }
@@ -3805,7 +3891,7 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_asyncness(&mut self, a: IsAsync) -> hir::IsAsync {
+    fn lower_asyncness(&mut self, a: &IsAsync) -> hir::IsAsync {
         match a {
             IsAsync::Async { .. } => hir::IsAsync::Async,
             IsAsync::NotAsync => hir::IsAsync::NotAsync,
@@ -4110,7 +4196,7 @@ impl<'a> LoweringContext<'a> {
                 hir::MatchSource::Normal,
             ),
             ExprKind::Async(capture_clause, closure_node_id, ref block) => {
-                self.make_async_expr(capture_clause, closure_node_id, None, |this| {
+                self.make_async_expr(capture_clause, closure_node_id, None, block.span, |this| {
                     this.with_new_scopes(|this| {
                         let block = this.lower_block(block, false);
                         this.expr_block(block, ThinVec::new())
@@ -4118,7 +4204,7 @@ impl<'a> LoweringContext<'a> {
                 })
             }
             ExprKind::Closure(
-                capture_clause, asyncness, movability, ref decl, ref body, fn_decl_span
+                capture_clause, ref asyncness, movability, ref decl, ref body, fn_decl_span
             ) => {
                 if let IsAsync::Async { closure_id, .. } = asyncness {
                     let outer_decl = FnDecl {
@@ -4156,7 +4242,7 @@ impl<'a> LoweringContext<'a> {
                                 Some(&**ty)
                             } else { None };
                             let async_body = this.make_async_expr(
-                                capture_clause, closure_id, async_ret_ty,
+                                capture_clause, *closure_id, async_ret_ty, body.span,
                                 |this| {
                                     this.with_new_scopes(|this| this.lower_expr(body))
                                 });
diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs
index 1a3bbc5ecc4..0fa97385322 100644
--- a/src/librustc/hir/map/def_collector.rs
+++ b/src/librustc/hir/map/def_collector.rs
@@ -68,16 +68,17 @@ impl<'a> DefCollector<'a> {
         id: NodeId,
         name: Name,
         span: Span,
-        header: &FnHeader,
+        header: &'a FnHeader,
         generics: &'a Generics,
         decl: &'a FnDecl,
         body: &'a Block,
     ) {
-        let (closure_id, return_impl_trait_id) = match header.asyncness.node {
+        let (closure_id, return_impl_trait_id, arguments) = match &header.asyncness.node {
             IsAsync::Async {
                 closure_id,
                 return_impl_trait_id,
-            } => (closure_id, return_impl_trait_id),
+                arguments,
+            } => (closure_id, return_impl_trait_id, arguments),
             _ => unreachable!(),
         };
 
@@ -86,17 +87,31 @@ impl<'a> DefCollector<'a> {
         let fn_def_data = DefPathData::ValueNs(name.as_interned_str());
         let fn_def = self.create_def(id, fn_def_data, ITEM_LIKE_SPACE, span);
         return self.with_parent(fn_def, |this| {
-            this.create_def(return_impl_trait_id, DefPathData::ImplTrait, REGULAR_SPACE, span);
+            this.create_def(*return_impl_trait_id, DefPathData::ImplTrait, REGULAR_SPACE, span);
 
             visit::walk_generics(this, generics);
-            visit::walk_fn_decl(this, decl);
 
-            let closure_def = this.create_def(closure_id,
-                                              DefPathData::ClosureExpr,
-                                              REGULAR_SPACE,
-                                              span);
+            // Walk the generated arguments for the `async fn`.
+            for a in arguments {
+                use visit::Visitor;
+                this.visit_ty(&a.arg.ty);
+            }
+
+            // We do not invoke `walk_fn_decl` as this will walk the arguments that are being
+            // replaced.
+            visit::walk_fn_ret_ty(this, &decl.output);
+
+            let closure_def = this.create_def(
+                *closure_id, DefPathData::ClosureExpr, REGULAR_SPACE, span,
+            );
             this.with_parent(closure_def, |this| {
-                visit::walk_block(this, body);
+                for a in arguments {
+                    use visit::Visitor;
+                    // Walk each of the generated statements before the regular block body.
+                    this.visit_stmt(&a.stmt);
+                }
+
+                visit::walk_block(this, &body);
             })
         })
     }
@@ -290,7 +305,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
 
         match expr.node {
             ExprKind::Mac(..) => return self.visit_macro_invoc(expr.id),
-            ExprKind::Closure(_, asyncness, ..) => {
+            ExprKind::Closure(_, ref asyncness, ..) => {
                 let closure_def = self.create_def(expr.id,
                                           DefPathData::ClosureExpr,
                                           REGULAR_SPACE,
@@ -300,7 +315,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
                 // Async closures desugar to closures inside of closures, so
                 // we must create two defs.
                 if let IsAsync::Async { closure_id, .. } = asyncness {
-                    let async_def = self.create_def(closure_id,
+                    let async_def = self.create_def(*closure_id,
                                                     DefPathData::ClosureExpr,
                                                     REGULAR_SPACE,
                                                     expr.span);
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 630c163bcaf..f114f0fc236 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1583,6 +1583,17 @@ pub enum LocalSource {
     Normal,
     /// A desugared `for _ in _ { .. }` loop.
     ForLoopDesugar,
+    /// When lowering async functions, we create locals within the `async move` so that
+    /// all arguments are dropped after the future is polled.
+    ///
+    /// ```ignore (pseudo-Rust)
+    /// async fn foo(<pattern> @ x: Type) {
+    ///     async move {
+    ///         let <pattern> = x;
+    ///     }
+    /// }
+    /// ```
+    AsyncFn,
 }
 
 /// Hints at the original code for a `match _ { .. }`.
@@ -1883,6 +1894,26 @@ pub struct InlineAsm {
 pub struct Arg {
     pub pat: P<Pat>,
     pub hir_id: HirId,
+    pub source: ArgSource,
+}
+
+impl Arg {
+    /// Returns the pattern representing the original binding for this argument.
+    pub fn original_pat(&self) -> &P<Pat> {
+        match &self.source {
+            ArgSource::Normal => &self.pat,
+            ArgSource::AsyncFn(pat) => &pat,
+        }
+    }
+}
+
+/// Represents the source of an argument in a function header.
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
+pub enum ArgSource {
+    /// Argument as specified by the user.
+    Normal,
+    /// Generated argument from `async fn` lowering, contains the original binding pattern.
+    AsyncFn(P<Pat>),
 }
 
 /// Represents the header (not the body) of a function declaration.
diff --git a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
index 86d7a19bc83..944cc8a8b19 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -86,19 +86,16 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
         let sub_is_ret_type =
             self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
 
-        let span_label_var1 = if let Some(simple_ident) = anon_arg_sup.pat.simple_ident() {
-            format!(" from `{}`", simple_ident)
-        } else {
-            String::new()
+        let span_label_var1 = match anon_arg_sup.original_pat().simple_ident() {
+            Some(simple_ident) => format!(" from `{}`", simple_ident),
+            None => String::new(),
         };
 
-        let span_label_var2 = if let Some(simple_ident) = anon_arg_sub.pat.simple_ident() {
-            format!(" into `{}`", simple_ident)
-        } else {
-            String::new()
+        let span_label_var2 = match anon_arg_sub.original_pat().simple_ident() {
+            Some(simple_ident) => format!(" into `{}`", simple_ident),
+            None => String::new(),
         };
 
-
         let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) {
             (None, None) => {
                 let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id {
diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 7403a5d7dbb..2d7587b11b6 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -95,13 +95,12 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
             }
         }
 
-        let (error_var, span_label_var) = if let Some(simple_ident) = arg.pat.simple_ident() {
-            (
+        let (error_var, span_label_var) = match arg.original_pat().simple_ident() {
+            Some(simple_ident) => (
                 format!("the type of `{}`", simple_ident),
                 format!("the type of `{}`", simple_ident),
-            )
-        } else {
-            ("parameter type".to_owned(), "type".to_owned())
+            ),
+            None => ("parameter type".to_owned(), "type".to_owned()),
         };
 
         let mut diag = struct_span_err!(
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 34bf4adffc6..f5cb4cfa29f 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -1326,6 +1326,25 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
 
         run_early_pass!(self, check_mac, mac);
     }
+
+    fn visit_fn_header(&mut self, header: &'a ast::FnHeader) {
+        // Unlike in HIR lowering and name resolution, the `AsyncArgument` statements are not added
+        // to the function body and the arguments do not replace those in the declaration. They are
+        // still visited manually here so that buffered lints can be emitted.
+        if let ast::IsAsync::Async { ref arguments, .. } = header.asyncness.node {
+            for a in arguments {
+                // Visit the argument..
+                self.visit_pat(&a.arg.pat);
+                if let ast::ArgSource::AsyncFn(pat) = &a.arg.source {
+                    self.visit_pat(pat);
+                }
+                self.visit_ty(&a.arg.ty);
+
+                // ..and the statement.
+                self.visit_stmt(&a.stmt);
+            }
+        }
+    }
 }
 
 struct LateLintPassObjects<'a> {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 3306bcae212..814776c21bd 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -2421,7 +2421,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
             let help_name = if let Some(body) = parent {
                 let arg = &self.tcx.hir().body(body).arguments[index];
-                format!("`{}`", self.tcx.hir().hir_to_pretty_string(arg.pat.hir_id))
+                format!("`{}`", self.tcx.hir().hir_to_pretty_string(arg.original_pat().hir_id))
             } else {
                 format!("argument {}", index + 1)
             };
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index e0b9921ca8d..2d7f8b1a1ae 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -76,6 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> {
         self.check_irrefutable(&loc.pat, match loc.source {
             hir::LocalSource::Normal => "local binding",
             hir::LocalSource::ForLoopDesugar => "`for` loop binding",
+            hir::LocalSource::AsyncFn => "async fn binding",
         });
 
         // Check legality of move bindings and `@` patterns.
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index a9a604cad8b..9dd8a7050fd 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -222,7 +222,7 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn check_trait_fn_not_async(&self, span: Span, asyncness: IsAsync) {
+    fn check_trait_fn_not_async(&self, span: Span, asyncness: &IsAsync) {
         if asyncness.is_async() {
             struct_span_err!(self.session, span, E0706,
                              "trait fns cannot be declared `async`").emit()
@@ -570,7 +570,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.invalid_visibility(&impl_item.vis, None);
                     if let ImplItemKind::Method(ref sig, _) = impl_item.node {
                         self.check_trait_fn_not_const(sig.header.constness);
-                        self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node);
+                        self.check_trait_fn_not_async(impl_item.span, &sig.header.asyncness.node);
                     }
                 }
             }
@@ -642,7 +642,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.no_questions_in_bounds(bounds, "supertraits", true);
                 for trait_item in trait_items {
                     if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
-                        self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness.node);
+                        self.check_trait_fn_not_async(trait_item.span, &sig.header.asyncness.node);
                         self.check_trait_fn_not_const(sig.header.constness);
                         if block.is_none() {
                             self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index edb3efb78a3..57e17eb6878 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -948,6 +948,16 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
 
         intravisit::walk_pat(self, pat);
     }
+
+    fn visit_argument_source(&mut self, s: &'tcx hir::ArgSource) {
+        match s {
+            // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has
+            // a `NodeId` w/out a type, as it is only used for getting the name of the original
+            // pattern for diagnostics where only an `hir::Arg` is present.
+            hir::ArgSource::AsyncFn(..) => {},
+            _ => intravisit::walk_argument_source(self, s),
+        }
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////
@@ -1133,6 +1143,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
         intravisit::walk_pat(self, pattern);
     }
 
+    fn visit_argument_source(&mut self, s: &'tcx hir::ArgSource) {
+        match s {
+            // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has
+            // a `NodeId` w/out a type, as it is only used for getting the name of the original
+            // pattern for diagnostics where only an `hir::Arg` is present.
+            hir::ArgSource::AsyncFn(..) => {},
+            _ => intravisit::walk_argument_source(self, s),
+        }
+    }
+
     fn visit_local(&mut self, local: &'tcx hir::Local) {
         if let Some(ref init) = local.init {
             if self.check_expr_pat_type(init.hir_id, init.span) {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 8df83120738..2ef05f7efeb 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -817,13 +817,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
         debug!("(resolving function) entering function");
         let (rib_kind, asyncness) = match function_kind {
             FnKind::ItemFn(_, ref header, ..) =>
-                (FnItemRibKind, header.asyncness.node),
+                (FnItemRibKind, &header.asyncness.node),
             FnKind::Method(_, ref sig, _, _) =>
-                (TraitOrImplItemRibKind, sig.header.asyncness.node),
+                (TraitOrImplItemRibKind, &sig.header.asyncness.node),
             FnKind::Closure(_) =>
                 // Async closures aren't resolved through `visit_fn`-- they're
                 // processed separately
-                (ClosureRibKind(node_id), IsAsync::NotAsync),
+                (ClosureRibKind(node_id), &IsAsync::NotAsync),
         };
 
         // Create a value rib for the function.
@@ -834,26 +834,42 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
 
         // Add each argument to the rib.
         let mut bindings_list = FxHashMap::default();
-        for argument in &declaration.inputs {
+        let mut add_argument = |argument: &ast::Arg| {
             self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
-
             self.visit_ty(&argument.ty);
-
             debug!("(resolving function) recorded argument");
+        };
+
+        // Walk the generated async arguments if this is an `async fn`, otherwise walk the
+        // normal arguments.
+        if let IsAsync::Async { ref arguments, .. } = asyncness {
+            for a in arguments { add_argument(&a.arg); }
+        } else {
+            for a in &declaration.inputs { add_argument(a); }
         }
+
         visit::walk_fn_ret_ty(self, &declaration.output);
 
         // Resolve the function body, potentially inside the body of an async closure
         if let IsAsync::Async { closure_id, .. } = asyncness {
-            let rib_kind = ClosureRibKind(closure_id);
+            let rib_kind = ClosureRibKind(*closure_id);
             self.ribs[ValueNS].push(Rib::new(rib_kind));
             self.label_ribs.push(Rib::new(rib_kind));
         }
 
         match function_kind {
-            FnKind::ItemFn(.., body) |
-            FnKind::Method(.., body) => {
-                self.visit_block(body);
+            FnKind::ItemFn(.., body) | FnKind::Method(.., body) => {
+                if let IsAsync::Async { ref arguments, .. } = asyncness {
+                    let mut body = body.clone();
+                    // Insert the generated statements into the body before attempting to
+                    // resolve names.
+                    for a in arguments {
+                        body.stmts.insert(0, a.stmt.clone());
+                    }
+                    self.visit_block(&body);
+                } else {
+                    self.visit_block(body);
+                }
             }
             FnKind::Closure(body) => {
                 self.visit_expr(body);
diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs
index 90fe6a60dd7..5ac75d01275 100644
--- a/src/librustc_save_analysis/sig.rs
+++ b/src/librustc_save_analysis/sig.rs
@@ -374,7 +374,7 @@ impl Sig for ast::Item {
 
                 Ok(extend_sig(ty, text, defs, vec![]))
             }
-            ast::ItemKind::Fn(ref decl, header, ref generics, _) => {
+            ast::ItemKind::Fn(ref decl, ref header, ref generics, _) => {
                 let mut text = String::new();
                 if header.constness.node == ast::Constness::Const {
                     text.push_str("const ");
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 15190f56965..79477b6fea8 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1005,6 +1005,16 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
     // Don't descend into the bodies of nested closures
     fn visit_fn(&mut self, _: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl,
                 _: hir::BodyId, _: Span, _: hir::HirId) { }
+
+    fn visit_argument_source(&mut self, s: &'gcx hir::ArgSource) {
+        match s {
+            // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has
+            // a `NodeId` w/out a type, as it is only used for getting the name of the original
+            // pattern for diagnostics where only an `hir::Arg` is present.
+            hir::ArgSource::AsyncFn(..) => {},
+            _ => intravisit::walk_argument_source(self, s),
+        }
+    }
 }
 
 /// When `check_fn` is invoked on a generator (i.e., a body that
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index d9df4672f14..efff08f6696 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -297,6 +297,16 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
         let ty = self.resolve(&ty, &hir_ty.span);
         self.write_ty_to_tables(hir_ty.hir_id, ty);
     }
+
+    fn visit_argument_source(&mut self, s: &'gcx hir::ArgSource) {
+        match s {
+            // Don't visit the pattern in `ArgSource::AsyncFn`, it contains a pattern which has
+            // a `NodeId` w/out a type, as it is only used for getting the name of the original
+            // pattern for diagnostics where only an `hir::Arg` is present.
+            hir::ArgSource::AsyncFn(..) => {},
+            _ => intravisit::walk_argument_source(self, s),
+        }
+    }
 }
 
 impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 586ae6659bb..4ff16e4a267 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2017,7 +2017,7 @@ impl<'a> Clean<Arguments> for (&'a [hir::Ty], hir::BodyId) {
         Arguments {
             values: self.0.iter().enumerate().map(|(i, ty)| {
                 Argument {
-                    name: name_from_pat(&body.arguments[i].pat),
+                    name: name_from_pat(&body.arguments[i].original_pat()),
                     type_: ty.clean(cx),
                 }
             }).collect()
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index a5472c622e6..334fcfd74f3 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -888,6 +888,17 @@ pub struct Local {
     pub id: NodeId,
     pub span: Span,
     pub attrs: ThinVec<Attribute>,
+    /// Origin of this local variable.
+    pub source: LocalSource,
+}
+
+#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
+pub enum LocalSource {
+    /// Local was parsed from source.
+    Normal,
+    /// Within `ast::IsAsync::Async`, a local is generated that will contain the moved arguments
+    /// of an `async fn`.
+    AsyncFn,
 }
 
 /// An arm of a 'match'.
@@ -1725,6 +1736,16 @@ pub struct Arg {
     pub ty: P<Ty>,
     pub pat: P<Pat>,
     pub id: NodeId,
+    pub source: ArgSource,
+}
+
+/// The source of an argument in a function header.
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub enum ArgSource {
+    /// Argument as written by the user.
+    Normal,
+    /// Argument from `async fn` lowering, contains the original binding pattern.
+    AsyncFn(P<Pat>),
 }
 
 /// Alternative representation for `Arg`s describing `self` parameter of methods.
@@ -1784,6 +1805,7 @@ impl Arg {
             }),
             ty,
             id: DUMMY_NODE_ID,
+            source: ArgSource::Normal,
         };
         match eself.node {
             SelfKind::Explicit(ty, mutbl) => arg(mutbl, ty),
@@ -1838,18 +1860,35 @@ pub enum Unsafety {
     Normal,
 }
 
-#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub struct AsyncArgument {
+    /// `__arg0`
+    pub ident: Ident,
+    /// `__arg0: <ty>` argument to replace existing function argument `<pat>: <ty>`.
+    pub arg: Arg,
+    /// `let <pat>: <ty> = __arg0;` statement to be inserted at the start of the block.
+    pub stmt: Stmt,
+}
+
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub enum IsAsync {
     Async {
         closure_id: NodeId,
         return_impl_trait_id: NodeId,
+        /// This field stores the arguments and statements that are used in HIR lowering to
+        /// ensure that `async fn` arguments are dropped at the correct time.
+        ///
+        /// The argument and statements here are generated at parse time as they are required in
+        /// both the hir lowering, def collection and name resolution and this stops them needing
+        /// to be created in each place.
+        arguments: Vec<AsyncArgument>,
     },
     NotAsync,
 }
 
 impl IsAsync {
-    pub fn is_async(self) -> bool {
-        if let IsAsync::Async { .. } = self {
+    pub fn is_async(&self) -> bool {
+        if let IsAsync::Async { .. } = *self {
             true
         } else {
             false
@@ -1857,12 +1896,12 @@ impl IsAsync {
     }
 
     /// In ths case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
-    pub fn opt_return_id(self) -> Option<NodeId> {
+    pub fn opt_return_id(&self) -> Option<NodeId> {
         match self {
             IsAsync::Async {
                 return_impl_trait_id,
                 ..
-            } => Some(return_impl_trait_id),
+            } => Some(*return_impl_trait_id),
             IsAsync::NotAsync => None,
         }
     }
@@ -2202,7 +2241,7 @@ impl Item {
 ///
 /// All the information between the visibility and the name of the function is
 /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
-#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct FnHeader {
     pub unsafety: Unsafety,
     pub asyncness: Spanned<IsAsync>,
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 614967bdeb4..40dd187ed28 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -526,6 +526,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             span: sp,
             attrs: ThinVec::new(),
+            source: ast::LocalSource::Normal,
         });
         ast::Stmt {
             id: ast::DUMMY_NODE_ID,
@@ -554,6 +555,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             span: sp,
             attrs: ThinVec::new(),
+            source: ast::LocalSource::Normal,
         });
         ast::Stmt {
             id: ast::DUMMY_NODE_ID,
@@ -571,6 +573,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             span,
             attrs: ThinVec::new(),
+            source: ast::LocalSource::Normal,
         });
         ast::Stmt {
             id: ast::DUMMY_NODE_ID,
@@ -976,7 +979,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         ast::Arg {
             ty,
             pat: arg_pat,
-            id: ast::DUMMY_NODE_ID
+            id: ast::DUMMY_NODE_ID,
+            source: ast::ArgSource::Normal,
         }
     }
 
diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs
index 3e60dd81a3b..68cd3c28676 100644
--- a/src/libsyntax/ext/placeholders.rs
+++ b/src/libsyntax/ext/placeholders.rs
@@ -102,6 +102,13 @@ impl<'a, 'b> PlaceholderExpander<'a, 'b> {
     fn remove(&mut self, id: ast::NodeId) -> AstFragment {
         self.expanded_fragments.remove(&id).unwrap()
     }
+
+    fn next_id(&mut self, id: &mut ast::NodeId) {
+        if self.monotonic {
+            assert_eq!(*id, ast::DUMMY_NODE_ID);
+            *id = self.cx.resolver.next_node_id()
+        }
+    }
 }
 
 impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
@@ -183,9 +190,16 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
         noop_visit_block(block, self);
 
         for stmt in block.stmts.iter_mut() {
-            if self.monotonic {
-                assert_eq!(stmt.id, ast::DUMMY_NODE_ID);
-                stmt.id = self.cx.resolver.next_node_id();
+            self.next_id(&mut stmt.id);
+        }
+    }
+
+    fn visit_asyncness(&mut self, a: &mut ast::IsAsync) {
+        noop_visit_asyncness(a, self);
+
+        if let ast::IsAsync::Async { ref mut arguments, .. } = a {
+            for argument in arguments.iter_mut() {
+                self.next_id(&mut argument.stmt.id);
             }
         }
     }
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 784d0049ac5..d3441a2039b 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -208,6 +208,10 @@ pub trait MutVisitor: Sized {
         noop_visit_local(l, self);
     }
 
+    fn visit_local_source(&mut self, l: &mut LocalSource) {
+        noop_visit_local_source(l, self);
+    }
+
     fn visit_mac(&mut self, _mac: &mut Mac) {
         panic!("visit_mac disabled by default");
         // N.B., see note about macros above. If you really want a visitor that
@@ -231,6 +235,10 @@ pub trait MutVisitor: Sized {
         noop_visit_arg(a, self);
     }
 
+    fn visit_arg_source(&mut self, a: &mut ArgSource) {
+        noop_visit_arg_source(a, self);
+    }
+
     fn visit_generics(&mut self, generics: &mut Generics) {
         noop_visit_generics(generics, self);
     }
@@ -511,13 +519,17 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(args: &mut Parenth
 }
 
 pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
-    let Local { id, pat, ty, init, span, attrs } = local.deref_mut();
+    let Local { id, pat, ty, init, span, attrs, source } = local.deref_mut();
     vis.visit_id(id);
     vis.visit_pat(pat);
     visit_opt(ty, |ty| vis.visit_ty(ty));
     visit_opt(init, |init| vis.visit_expr(init));
     vis.visit_span(span);
     visit_thin_attrs(attrs, vis);
+    vis.visit_local_source(source);
+}
+
+pub fn noop_visit_local_source<T: MutVisitor>(_local_source: &mut LocalSource, _vis: &mut T) {
 }
 
 pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
@@ -556,10 +568,18 @@ pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty }: &mut Arg, vis: &mut T) {
+pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty, source }: &mut Arg, vis: &mut T) {
     vis.visit_id(id);
     vis.visit_pat(pat);
     vis.visit_ty(ty);
+    vis.visit_arg_source(source);
+}
+
+pub fn noop_visit_arg_source<T: MutVisitor>(source: &mut ArgSource, vis: &mut T) {
+    match source {
+        ArgSource::Normal => {},
+        ArgSource::AsyncFn(pat) => vis.visit_pat(pat),
+    }
 }
 
 pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
@@ -671,9 +691,17 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
 
 pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T) {
     match asyncness {
-        IsAsync::Async { closure_id, return_impl_trait_id } => {
+        IsAsync::Async { closure_id, return_impl_trait_id, ref mut arguments } => {
             vis.visit_id(closure_id);
             vis.visit_id(return_impl_trait_id);
+            for AsyncArgument { ident, arg, stmt } in arguments.iter_mut() {
+                vis.visit_ident(ident);
+                vis.visit_arg(arg);
+                visit_clobber(stmt, |stmt| {
+                    vis.flat_map_stmt(stmt)
+                        .expect_one("expected visitor to produce exactly one item")
+                });
+            }
         }
         IsAsync::NotAsync => {}
     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 824192f0739..53dab510ac3 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1,7 +1,7 @@
-use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy};
+use crate::ast::{AngleBracketedArgs, AsyncArgument, ParenthesizedArgs, AttrStyle, BareFnTy};
 use crate::ast::{GenericBound, TraitBoundModifier};
 use crate::ast::Unsafety;
-use crate::ast::{Mod, AnonConst, Arg, Arm, Guard, Attribute, BindingMode, TraitItemKind};
+use crate::ast::{Mod, AnonConst, Arg, ArgSource, Arm, Guard, Attribute, BindingMode, TraitItemKind};
 use crate::ast::Block;
 use crate::ast::{BlockCheckMode, CaptureBy, Movability};
 use crate::ast::{Constness, Crate};
@@ -14,7 +14,7 @@ use crate::ast::{GenericParam, GenericParamKind};
 use crate::ast::GenericArg;
 use crate::ast::{Ident, ImplItem, IsAsync, IsAuto, Item, ItemKind};
 use crate::ast::{Label, Lifetime, Lit, LitKind};
-use crate::ast::Local;
+use crate::ast::{Local, LocalSource};
 use crate::ast::MacStmtStyle;
 use crate::ast::{Mac, Mac_, MacDelimiter};
 use crate::ast::{MutTy, Mutability};
@@ -550,7 +550,7 @@ fn dummy_arg(span: Span) -> Arg {
         span,
         id: ast::DUMMY_NODE_ID
     };
-    Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID }
+    Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal }
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -1517,6 +1517,7 @@ impl<'a> Parser<'a> {
             IsAsync::Async {
                 closure_id: ast::DUMMY_NODE_ID,
                 return_impl_trait_id: ast::DUMMY_NODE_ID,
+                arguments: Vec::new(),
             }
         } else {
             IsAsync::NotAsync
@@ -1575,7 +1576,7 @@ impl<'a> Parser<'a> {
             // trait item macro.
             (keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default())
         } else {
-            let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?;
+            let (constness, unsafety, mut asyncness, abi) = self.parse_fn_front_matter()?;
 
             let ident = self.parse_ident()?;
             let mut generics = self.parse_generics()?;
@@ -1589,6 +1590,7 @@ impl<'a> Parser<'a> {
                 p.parse_arg_general(p.span.rust_2018(), true, false)
             })?;
             generics.where_clause = self.parse_where_clause()?;
+            self.construct_async_arguments(&mut asyncness, &d);
 
             let sig = ast::MethodSig {
                 header: FnHeader {
@@ -2124,7 +2126,7 @@ impl<'a> Parser<'a> {
             }
         };
 
-        Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID })
+        Ok(Arg { ty, pat, id: ast::DUMMY_NODE_ID, source: ast::ArgSource::Normal })
     }
 
     /// Parses a single function argument.
@@ -2147,7 +2149,8 @@ impl<'a> Parser<'a> {
         Ok(Arg {
             ty: t,
             pat,
-            id: ast::DUMMY_NODE_ID
+            id: ast::DUMMY_NODE_ID,
+            source: ast::ArgSource::Normal,
         })
     }
 
@@ -5029,6 +5032,7 @@ impl<'a> Parser<'a> {
             id: ast::DUMMY_NODE_ID,
             span: lo.to(hi),
             attrs,
+            source: LocalSource::Normal,
         }))
     }
 
@@ -6566,7 +6570,7 @@ impl<'a> Parser<'a> {
     /// Parses an item-position function declaration.
     fn parse_item_fn(&mut self,
                      unsafety: Unsafety,
-                     asyncness: Spanned<IsAsync>,
+                     mut asyncness: Spanned<IsAsync>,
                      constness: Spanned<Constness>,
                      abi: Abi)
                      -> PResult<'a, ItemInfo> {
@@ -6575,6 +6579,7 @@ impl<'a> Parser<'a> {
         let decl = self.parse_fn_decl(allow_c_variadic)?;
         generics.where_clause = self.parse_where_clause()?;
         let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
+        self.construct_async_arguments(&mut asyncness, &decl);
         let header = FnHeader { unsafety, asyncness, constness, abi };
         Ok((ident, ItemKind::Fn(decl, header, generics, body), Some(inner_attrs)))
     }
@@ -6755,11 +6760,12 @@ impl<'a> Parser<'a> {
             Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(),
                 ast::ImplItemKind::Macro(mac)))
         } else {
-            let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?;
+            let (constness, unsafety, mut asyncness, abi) = self.parse_fn_front_matter()?;
             let ident = self.parse_ident()?;
             let mut generics = self.parse_generics()?;
             let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?;
             generics.where_clause = self.parse_where_clause()?;
+            self.construct_async_arguments(&mut asyncness, &decl);
             *at_end = true;
             let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
             let header = ast::FnHeader { abi, unsafety, constness, asyncness };
@@ -8181,6 +8187,7 @@ impl<'a> Parser<'a> {
                                    respan(async_span, IsAsync::Async {
                                        closure_id: ast::DUMMY_NODE_ID,
                                        return_impl_trait_id: ast::DUMMY_NODE_ID,
+                                       arguments: Vec::new(),
                                    }),
                                    respan(fn_span, Constness::NotConst),
                                    Abi::Rust)?;
@@ -8826,6 +8833,68 @@ impl<'a> Parser<'a> {
             }
         }
     }
+
+    /// When lowering a `async fn` to the HIR, we need to move all of the arguments of the function
+    /// into the generated closure so that they are dropped when the future is polled and not when
+    /// it is created.
+    ///
+    /// The arguments of the function are replaced in HIR lowering with the arguments created by
+    /// this function and the statements created here are inserted at the top of the closure body.
+    fn construct_async_arguments(&mut self, asyncness: &mut Spanned<IsAsync>, decl: &FnDecl) {
+        if let IsAsync::Async { ref mut arguments, .. } = asyncness.node {
+            for (index, input) in decl.inputs.iter().enumerate() {
+                let id = ast::DUMMY_NODE_ID;
+                let span = input.pat.span;
+
+                // Construct a name for our temporary argument.
+                let name = format!("__arg{}", index);
+                let ident = Ident::from_str(&name);
+
+                // Construct an argument representing `__argN: <ty>` to replace the argument of the
+                // async function.
+                let arg = Arg {
+                    ty: input.ty.clone(),
+                    id,
+                    pat: P(Pat {
+                        id,
+                        node: PatKind::Ident(
+                            BindingMode::ByValue(Mutability::Immutable), ident, None,
+                        ),
+                        span,
+                    }),
+                    source: ArgSource::AsyncFn(input.pat.clone()),
+                };
+
+                // Construct a `let <pat> = __argN;` statement to insert at the top of the
+                // async closure.
+                let local = P(Local {
+                    pat: input.pat.clone(),
+                    // We explicitly do not specify the type for this statement. When the user's
+                    // argument type is `impl Trait` then this would require the
+                    // `impl_trait_in_bindings` feature to also be present for that same type to
+                    // be valid in this binding. At the time of writing (13 Mar 19),
+                    // `impl_trait_in_bindings` is not stable.
+                    ty: None,
+                    init: Some(P(Expr {
+                        id,
+                        node: ExprKind::Path(None, ast::Path {
+                            span,
+                            segments: vec![PathSegment { ident, id, args: None }],
+                        }),
+                        span,
+                        attrs: ThinVec::new(),
+                    })),
+                    id,
+                    span,
+                    attrs: ThinVec::new(),
+                    source: LocalSource::AsyncFn,
+                });
+                let stmt = Stmt { id, node: StmtKind::Local(local), span, };
+
+                arguments.push(AsyncArgument { ident, arg, stmt });
+            }
+        }
+    }
 }
 
 pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, handler: &errors::Handler) {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index d94e4762e67..7ce3951f13e 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -372,7 +372,7 @@ pub fn vis_to_string(v: &ast::Visibility) -> String {
 }
 
 pub fn fun_to_string(decl: &ast::FnDecl,
-                     header: ast::FnHeader,
+                     header: &ast::FnHeader,
                      name: ast::Ident,
                      generics: &ast::Generics)
                      -> String {
@@ -1133,7 +1133,7 @@ impl<'a> State<'a> {
         match item.node {
             ast::ForeignItemKind::Fn(ref decl, ref generics) => {
                 self.head("")?;
-                self.print_fn(decl, ast::FnHeader::default(),
+                self.print_fn(decl, &ast::FnHeader::default(),
                               Some(item.ident),
                               generics, &item.vis)?;
                 self.end()?; // end head-ibox
@@ -1263,7 +1263,7 @@ impl<'a> State<'a> {
                 self.s.word(";")?;
                 self.end()?; // end the outer cbox
             }
-            ast::ItemKind::Fn(ref decl, header, ref param_names, ref body) => {
+            ast::ItemKind::Fn(ref decl, ref header, ref param_names, ref body) => {
                 self.head("")?;
                 self.print_fn(
                     decl,
@@ -1615,7 +1615,7 @@ impl<'a> State<'a> {
                             vis: &ast::Visibility)
                             -> io::Result<()> {
         self.print_fn(&m.decl,
-                      m.header,
+                      &m.header,
                       Some(ident),
                       &generics,
                       vis)
@@ -2213,7 +2213,7 @@ impl<'a> State<'a> {
                 self.bclose_(expr.span, INDENT_UNIT)?;
             }
             ast::ExprKind::Closure(
-                capture_clause, asyncness, movability, ref decl, ref body, _) => {
+                capture_clause, ref asyncness, movability, ref decl, ref body, _) => {
                 self.print_movability(movability)?;
                 self.print_asyncness(asyncness)?;
                 self.print_capture_clause(capture_clause)?;
@@ -2798,7 +2798,7 @@ impl<'a> State<'a> {
 
     pub fn print_fn(&mut self,
                     decl: &ast::FnDecl,
-                    header: ast::FnHeader,
+                    header: &ast::FnHeader,
                     name: Option<ast::Ident>,
                     generics: &ast::Generics,
                     vis: &ast::Visibility) -> io::Result<()> {
@@ -2853,8 +2853,7 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn print_asyncness(&mut self, asyncness: ast::IsAsync)
-                                -> io::Result<()> {
+    pub fn print_asyncness(&mut self, asyncness: &ast::IsAsync) -> io::Result<()> {
         if asyncness.is_async() {
             self.word_nbsp("async")?;
         }
@@ -3126,7 +3125,7 @@ impl<'a> State<'a> {
             span: syntax_pos::DUMMY_SP,
         };
         self.print_fn(decl,
-                      ast::FnHeader { unsafety, abi, ..ast::FnHeader::default() },
+                      &ast::FnHeader { unsafety, abi, ..ast::FnHeader::default() },
                       name,
                       &generics,
                       &source_map::dummy_spanned(ast::VisibilityKind::Inherited))?;
@@ -3189,7 +3188,7 @@ impl<'a> State<'a> {
     }
 
     pub fn print_fn_header_info(&mut self,
-                                header: ast::FnHeader,
+                                header: &ast::FnHeader,
                                 vis: &ast::Visibility) -> io::Result<()> {
         self.s.word(visibility_qualified(vis, ""))?;
 
@@ -3198,7 +3197,7 @@ impl<'a> State<'a> {
             ast::Constness::Const => self.word_nbsp("const")?
         }
 
-        self.print_asyncness(header.asyncness.node)?;
+        self.print_asyncness(&header.asyncness.node)?;
         self.print_unsafety(header.unsafety)?;
 
         if header.abi != Abi::Rust {
@@ -3247,7 +3246,7 @@ mod tests {
             assert_eq!(
                 fun_to_string(
                     &decl,
-                    ast::FnHeader {
+                    &ast::FnHeader {
                         unsafety: ast::Unsafety::Normal,
                         constness: source_map::dummy_spanned(ast::Constness::NotConst),
                         asyncness: source_map::dummy_spanned(ast::IsAsync::NotAsync),
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index fe74cbd6496..fc99d10b0b6 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -544,6 +544,9 @@ pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FunctionR
 pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) {
     for argument in &function_declaration.inputs {
         visitor.visit_pat(&argument.pat);
+        if let ArgSource::AsyncFn(pat) = &argument.source {
+            visitor.visit_pat(pat);
+        }
         visitor.visit_ty(&argument.ty)
     }
     visitor.visit_fn_ret_ty(&function_declaration.output)
diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs
index 7c47c6ff79a..2fc1fc9140d 100644
--- a/src/libsyntax_ext/deriving/debug.rs
+++ b/src/libsyntax_ext/deriving/debug.rs
@@ -128,6 +128,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast
         id: ast::DUMMY_NODE_ID,
         span: sp,
         attrs: ThinVec::new(),
+        source: ast::LocalSource::Normal,
     });
     ast::Stmt {
         id: ast::DUMMY_NODE_ID,
diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs
index 2c30c7f3651..84567e1b4b8 100644
--- a/src/test/mir-opt/inline-closure-borrows-arg.rs
+++ b/src/test/mir-opt/inline-closure-borrows-arg.rs
@@ -20,7 +20,7 @@ fn foo<T: Copy>(_t: T, q: &i32) -> i32 {
 // ...
 // bb0: {
 //     ...
-//     _3 = [closure@HirId { owner: DefIndex(0:4), local_id: 29 }];
+//     _3 = [closure@HirId { owner: DefIndex(0:4), local_id: 31 }];
 //     ...
 //     _4 = &_3;
 //     ...
diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs
index 8116a445467..2be48927fd3 100644
--- a/src/test/mir-opt/inline-closure.rs
+++ b/src/test/mir-opt/inline-closure.rs
@@ -16,7 +16,7 @@ fn foo<T: Copy>(_t: T, q: i32) -> i32 {
 // ...
 // bb0: {
 //     ...
-//     _3 = [closure@HirId { owner: DefIndex(0:4), local_id: 13 }];
+//     _3 = [closure@HirId { owner: DefIndex(0:4), local_id: 15 }];
 //     ...
 //     _4 = &_3;
 //     ...
diff --git a/src/test/run-pass/issue-54716.rs b/src/test/run-pass/issue-54716.rs
new file mode 100644
index 00000000000..ea4f5e076b0
--- /dev/null
+++ b/src/test/run-pass/issue-54716.rs
@@ -0,0 +1,184 @@
+// aux-build:arc_wake.rs
+// edition:2018
+// run-pass
+
+#![allow(unused_variables)]
+#![feature(async_await, await_macro, futures_api)]
+
+extern crate arc_wake;
+
+use arc_wake::ArcWake;
+use std::cell::RefCell;
+use std::future::Future;
+use std::marker::PhantomData;
+use std::sync::Arc;
+use std::rc::Rc;
+use std::task::Context;
+
+struct EmptyWaker;
+
+impl ArcWake for EmptyWaker {
+    fn wake(self: Arc<Self>) {}
+}
+
+#[derive(Debug, Eq, PartialEq)]
+enum DropOrder {
+    Function,
+    Val(&'static str),
+}
+
+type DropOrderListPtr = Rc<RefCell<Vec<DropOrder>>>;
+
+struct D(&'static str, DropOrderListPtr);
+
+impl Drop for D {
+    fn drop(&mut self) {
+        self.1.borrow_mut().push(DropOrder::Val(self.0));
+    }
+}
+
+/// Check that unused bindings are dropped after the function is polled.
+async fn foo(x: D, _y: D) {
+    x.1.borrow_mut().push(DropOrder::Function);
+}
+
+/// Check that underscore patterns are dropped after the function is polled.
+async fn bar(x: D, _: D) {
+    x.1.borrow_mut().push(DropOrder::Function);
+}
+
+/// Check that underscore patterns within more complex patterns are dropped after the function
+/// is polled.
+async fn baz((x, _): (D, D)) {
+    x.1.borrow_mut().push(DropOrder::Function);
+}
+
+/// Check that underscore and unused bindings within and outwith more complex patterns are dropped
+/// after the function is polled.
+async fn foobar(x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
+    x.1.borrow_mut().push(DropOrder::Function);
+}
+
+struct Foo;
+
+impl Foo {
+    /// Check that unused bindings are dropped after the method is polled.
+    async fn foo(x: D, _y: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore patterns are dropped after the method is polled.
+    async fn bar(x: D, _: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore patterns within more complex patterns are dropped after the method
+    /// is polled.
+    async fn baz((x, _): (D, D)) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore and unused bindings within and outwith more complex patterns are
+    /// dropped after the method is polled.
+    async fn foobar(x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+}
+
+struct Bar<'a>(PhantomData<&'a ()>);
+
+impl<'a> Bar<'a> {
+    /// Check that unused bindings are dropped after the method with self is polled.
+    async fn foo(&'a self, x: D, _y: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore patterns are dropped after the method with self is polled.
+    async fn bar(&'a self, x: D, _: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore patterns within more complex patterns are dropped after the method
+    /// with self is polled.
+    async fn baz(&'a self, (x, _): (D, D)) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+
+    /// Check that underscore and unused bindings within and outwith more complex patterns are
+    /// dropped after the method with self is polled.
+    async fn foobar(&'a self, x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
+        x.1.borrow_mut().push(DropOrder::Function);
+    }
+}
+
+fn assert_drop_order_after_poll<Fut: Future<Output = ()>>(
+    f: impl FnOnce(DropOrderListPtr) -> Fut,
+    expected_order: &[DropOrder],
+) {
+    let empty = Arc::new(EmptyWaker);
+    let waker = ArcWake::into_waker(empty);
+    let mut cx = Context::from_waker(&waker);
+
+    let actual_order = Rc::new(RefCell::new(Vec::new()));
+    let mut fut = Box::pin(f(actual_order.clone()));
+    let _ = fut.as_mut().poll(&mut cx);
+
+    assert_eq!(*actual_order.borrow(), expected_order);
+}
+
+fn main() {
+    use DropOrder::*;
+
+    // At time of writing (23/04/19), the `bar` and `foobar` tests do not output the same order as
+    // the equivalent non-async functions. This is because the drop order of captured variables
+    // doesn't match the drop order of arguments in a function.
+
+    // Free functions (see doc comment on function for what it tests).
+    assert_drop_order_after_poll(|l| foo(D("x", l.clone()), D("_y", l.clone())),
+                                 &[Function, Val("_y"), Val("x")]);
+    assert_drop_order_after_poll(|l| bar(D("x", l.clone()), D("_", l.clone())),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| baz((D("x", l.clone()), D("_", l.clone()))),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| {
+        foobar(
+            D("x", l.clone()),
+            (D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
+            D("_", l.clone()),
+            D("_y", l.clone()),
+        )
+    }, &[Function, Val("_y"), Val("_c"), Val("a"), Val("x"), Val("_"), Val("_")]);
+
+    // Methods w/out self (see doc comment on function for what it tests).
+    assert_drop_order_after_poll(|l| Foo::foo(D("x", l.clone()), D("_y", l.clone())),
+                                 &[Function, Val("_y"), Val("x")]);
+    assert_drop_order_after_poll(|l| Foo::bar(D("x", l.clone()), D("_", l.clone())),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| Foo::baz((D("x", l.clone()), D("_", l.clone()))),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| {
+        Foo::foobar(
+            D("x", l.clone()),
+            (D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
+            D("_", l.clone()),
+            D("_y", l.clone()),
+        )
+    }, &[Function, Val("_y"), Val("_c"), Val("a"), Val("x"), Val("_"), Val("_")]);
+
+    // Methods (see doc comment on function for what it tests).
+    let b = Bar(Default::default());
+    assert_drop_order_after_poll(|l| b.foo(D("x", l.clone()), D("_y", l.clone())),
+                                 &[Function, Val("_y"), Val("x")]);
+    assert_drop_order_after_poll(|l| b.bar(D("x", l.clone()), D("_", l.clone())),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| b.baz((D("x", l.clone()), D("_", l.clone()))),
+                                 &[Function, Val("x"), Val("_")]);
+    assert_drop_order_after_poll(|l| {
+        b.foobar(
+            D("x", l.clone()),
+            (D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
+            D("_", l.clone()),
+            D("_y", l.clone()),
+        )
+    }, &[Function, Val("_y"), Val("_c"), Val("a"), Val("x"), Val("_"), Val("_")]);
+}