Ensure that drop order of async fn matches fn.

This commit modifies the lowering of `async fn` arguments so that the
drop order matches the equivalent `fn`.

Previously, async function arguments were lowered as shown below:

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

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

After this PR, async function arguments will be lowered as:

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

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

If `<pattern>` is a simple ident, then it is lowered to a single
`let <pattern> = <pattern>;` statement as an optimization.
This commit is contained in:
David Wood 2019-05-01 13:35:34 +01:00
parent 47e0803d5c
commit b05d5db87b
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
10 changed files with 401 additions and 221 deletions

View File

@ -2996,8 +2996,33 @@ impl<'a> LoweringContext<'a> {
if let IsAsync::Async { closure_id, ref arguments, .. } = asyncness {
let mut body = body.clone();
// Async function arguments are lowered into the closure body so that they are
// captured and so that the drop order matches the equivalent non-async functions.
//
// async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
// async move {
// }
// }
//
// // ...becomes...
// fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
// async move {
// let __arg2 = __arg2;
// let <pattern> = __arg2;
// let __arg1 = __arg1;
// let <pattern> = __arg1;
// let __arg0 = __arg0;
// let <pattern> = __arg0;
// }
// }
//
// If `<pattern>` is a simple ident, then it is lowered to a single
// `let <pattern> = <pattern>;` statement as an optimization.
for a in arguments.iter().rev() {
body.stmts.insert(0, a.stmt.clone());
if let Some(pat_stmt) = a.pat_stmt.clone() {
body.stmts.insert(0, pat_stmt);
}
body.stmts.insert(0, a.move_stmt.clone());
}
let async_expr = this.make_async_expr(
@ -3093,7 +3118,11 @@ impl<'a> LoweringContext<'a> {
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();
for (i, a) in arguments.clone().drain(..).enumerate() {
if let Some(arg) = a.arg {
decl.inputs[i] = arg;
}
}
lower_fn(&decl)
} else {
lower_fn(decl)
@ -3590,7 +3619,11 @@ impl<'a> LoweringContext<'a> {
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();
for (i, a) in arguments.clone().drain(..).enumerate() {
if let Some(arg) = a.arg {
sig.decl.inputs[i] = arg;
}
}
lower_method(&sig)
} else {
lower_method(sig)

View File

@ -94,7 +94,9 @@ impl<'a> DefCollector<'a> {
// Walk the generated arguments for the `async fn`.
for a in arguments {
use visit::Visitor;
this.visit_ty(&a.arg.ty);
if let Some(arg) = &a.arg {
this.visit_ty(&arg.ty);
}
}
// We do not invoke `walk_fn_decl` as this will walk the arguments that are being
@ -105,10 +107,13 @@ impl<'a> DefCollector<'a> {
*closure_id, DefPathData::ClosureExpr, REGULAR_SPACE, span,
);
this.with_parent(closure_def, |this| {
use visit::Visitor;
// Walk each of the generated statements before the regular block body.
for a in arguments {
use visit::Visitor;
// Walk each of the generated statements before the regular block body.
this.visit_stmt(&a.stmt);
this.visit_stmt(&a.move_stmt);
if let Some(pat_stmt) = &a.pat_stmt {
this.visit_stmt(&pat_stmt);
}
}
visit::walk_block(this, &body);

View File

@ -1334,14 +1334,19 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
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);
if let Some(arg) = &a.arg {
self.visit_pat(&arg.pat);
if let ast::ArgSource::AsyncFn(pat) = &arg.source {
self.visit_pat(pat);
}
self.visit_ty(&arg.ty);
}
self.visit_ty(&a.arg.ty);
// ..and the statement.
self.visit_stmt(&a.stmt);
self.visit_stmt(&a.move_stmt);
if let Some(pat_stmt) = &a.pat_stmt {
self.visit_stmt(&pat_stmt);
}
}
}
}

View File

@ -862,7 +862,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
// 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); }
for (i, a) in arguments.iter().enumerate() {
if let Some(arg) = &a.arg {
add_argument(&arg);
} else {
add_argument(&declaration.inputs[i]);
}
}
} else {
for a in &declaration.inputs { add_argument(a); }
}
@ -882,8 +888,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
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());
for a in arguments.iter().rev() {
if let Some(pat_stmt) = a.pat_stmt.clone() {
body.stmts.insert(0, pat_stmt);
}
body.stmts.insert(0, a.move_stmt.clone());
}
self.visit_block(&body);
} else {

View File

@ -1865,10 +1865,14 @@ pub enum Unsafety {
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,
/// `__arg0: <ty>` argument to replace existing function argument `<pat>: <ty>`. Only if
/// argument is not a simple binding.
pub arg: Option<Arg>,
/// `let __arg0 = __arg0;` statement to be inserted at the start of the block.
pub move_stmt: Stmt,
/// `let <pat> = __arg0;` statement to be inserted at the start of the block, after matching
/// move statement. Only if argument is not a simple binding.
pub pat_stmt: Option<Stmt>,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]

View File

@ -199,7 +199,10 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
if let ast::IsAsync::Async { ref mut arguments, .. } = a {
for argument in arguments.iter_mut() {
self.next_id(&mut argument.stmt.id);
self.next_id(&mut argument.move_stmt.id);
if let Some(ref mut pat_stmt) = &mut argument.pat_stmt {
self.next_id(&mut pat_stmt.id);
}
}
}
}

View File

@ -694,13 +694,21 @@ pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T)
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() {
for AsyncArgument { ident, arg, pat_stmt, move_stmt } in arguments.iter_mut() {
vis.visit_ident(ident);
vis.visit_arg(arg);
visit_clobber(stmt, |stmt| {
if let Some(arg) = arg {
vis.visit_arg(arg);
}
visit_clobber(move_stmt, |stmt| {
vis.flat_map_stmt(stmt)
.expect_one("expected visitor to produce exactly one item")
});
visit_opt(pat_stmt, |stmt| {
visit_clobber(stmt, |stmt| {
vis.flat_map_stmt(stmt)
.expect_one("expected visitor to produce exactly one item")
})
});
}
}
IsAsync::NotAsync => {}

View File

@ -8880,11 +8880,37 @@ impl<'a> Parser<'a> {
let name = format!("__arg{}", index);
let ident = Ident::from_str(&name);
// Check if this is a ident pattern, if so, we can optimize and avoid adding a
// `let <pat> = __argN;` statement, instead just adding a `let <pat> = <pat>;`
// statement.
let (ident, is_simple_pattern) = match input.pat.node {
PatKind::Ident(_, ident, _) => (ident, true),
_ => (ident, false),
};
// Construct an argument representing `__argN: <ty>` to replace the argument of the
// async function.
let arg = Arg {
ty: input.ty.clone(),
id,
// async function if it isn't a simple pattern.
let arg = if is_simple_pattern {
None
} else {
Some(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 __argN = __argN;` statement to insert at the top of the
// async closure. This makes sure that the argument is captured by the closure and
// that the drop order is correct.
let move_local = Local {
pat: P(Pat {
id,
node: PatKind::Ident(
@ -8892,13 +8918,6 @@ impl<'a> Parser<'a> {
),
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
@ -8918,10 +8937,25 @@ impl<'a> Parser<'a> {
span,
attrs: ThinVec::new(),
source: LocalSource::AsyncFn,
});
let stmt = Stmt { id, node: StmtKind::Local(local), span, };
};
arguments.push(AsyncArgument { ident, arg, stmt });
// Construct a `let <pat> = __argN;` statement to insert at the top of the
// async closure if this isn't a simple pattern.
let pat_stmt = if is_simple_pattern {
None
} else {
Some(Stmt {
id,
node: StmtKind::Local(P(Local {
pat: input.pat.clone(),
..move_local.clone()
})),
span,
})
};
let move_stmt = Stmt { id, node: StmtKind::Local(P(move_local)), span };
arguments.push(AsyncArgument { ident, arg, pat_stmt, move_stmt });
}
}
}

View File

@ -0,0 +1,263 @@
// aux-build:arc_wake.rs
// edition:2018
// run-pass
#![allow(unused_variables)]
#![feature(async_await, await_macro)]
// Test that the drop order for parameters in a fn and async fn matches up. Also test that
// parameters (used or unused) are not dropped until the async fn completes execution.
// See also #54716.
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_async(x: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foo_sync(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_async(x: D, _: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn bar_sync(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_async((x, _): (D, D)) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn baz_sync((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_async(x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foobar_sync(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_async(x: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foo_sync(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_async(x: D, _: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn bar_sync(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_async((x, _): (D, D)) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn baz_sync((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_async(x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foobar_sync(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_async(&'a self, x: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foo_sync(&'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_async(&'a self, x: D, _: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn bar_sync(&'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_async(&'a self, (x, _): (D, D)) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn baz_sync(&'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_async(&'a self, x: D, (a, _, _c): (D, D, D), _: D, _y: D) {
x.1.borrow_mut().push(DropOrder::Function);
}
fn foobar_sync(&'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,
g: impl FnOnce(DropOrderListPtr),
) {
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);
let expected_order = Rc::new(RefCell::new(Vec::new()));
g(expected_order.clone());
assert_eq!(*actual_order.borrow(), *expected_order.borrow());
}
fn main() {
// Free functions (see doc comment on function for what it tests).
assert_drop_order_after_poll(|l| foo_async(D("x", l.clone()), D("_y", l.clone())),
|l| foo_sync(D("x", l.clone()), D("_y", l.clone())));
assert_drop_order_after_poll(|l| bar_async(D("x", l.clone()), D("_", l.clone())),
|l| bar_sync(D("x", l.clone()), D("_", l.clone())));
assert_drop_order_after_poll(|l| baz_async((D("x", l.clone()), D("_", l.clone()))),
|l| baz_sync((D("x", l.clone()), D("_", l.clone()))));
assert_drop_order_after_poll(
|l| {
foobar_async(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
|l| {
foobar_sync(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
);
// Methods w/out self (see doc comment on function for what it tests).
assert_drop_order_after_poll(|l| Foo::foo_async(D("x", l.clone()), D("_y", l.clone())),
|l| Foo::foo_sync(D("x", l.clone()), D("_y", l.clone())));
assert_drop_order_after_poll(|l| Foo::bar_async(D("x", l.clone()), D("_", l.clone())),
|l| Foo::bar_sync(D("x", l.clone()), D("_", l.clone())));
assert_drop_order_after_poll(|l| Foo::baz_async((D("x", l.clone()), D("_", l.clone()))),
|l| Foo::baz_sync((D("x", l.clone()), D("_", l.clone()))));
assert_drop_order_after_poll(
|l| {
Foo::foobar_async(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
|l| {
Foo::foobar_sync(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
);
// Methods (see doc comment on function for what it tests).
let b = Bar(Default::default());
assert_drop_order_after_poll(|l| b.foo_async(D("x", l.clone()), D("_y", l.clone())),
|l| b.foo_sync(D("x", l.clone()), D("_y", l.clone())));
assert_drop_order_after_poll(|l| b.bar_async(D("x", l.clone()), D("_", l.clone())),
|l| b.bar_sync(D("x", l.clone()), D("_", l.clone())));
assert_drop_order_after_poll(|l| b.baz_async((D("x", l.clone()), D("_", l.clone()))),
|l| b.baz_sync((D("x", l.clone()), D("_", l.clone()))));
assert_drop_order_after_poll(
|l| {
b.foobar_async(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
|l| {
b.foobar_sync(
D("x", l.clone()),
(D("a", l.clone()), D("_", l.clone()), D("_c", l.clone())),
D("_", l.clone()),
D("_y", l.clone()),
)
},
);
}

View File

@ -1,184 +0,0 @@
// aux-build:arc_wake.rs
// edition:2018
// run-pass
#![allow(unused_variables)]
#![feature(async_await, await_macro)]
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("_")]);
}