mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
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:
parent
47e0803d5c
commit
b05d5db87b
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)]
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 => {}
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
@ -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("_")]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user