-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Correct handling of arguments in async fn #60535
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// edition:2018 | ||
// run-pass | ||
|
||
#![allow(unused_variables)] | ||
#![deny(unused_mut)] | ||
#![feature(async_await)] | ||
|
||
type A = Vec<u32>; | ||
|
||
async fn a(n: u32, mut vec: A) { | ||
vec.push(n); | ||
} | ||
|
||
async fn b(n: u32, ref mut vec: A) { | ||
vec.push(n); | ||
} | ||
|
||
async fn c(ref vec: A) { | ||
vec.contains(&0); | ||
} | ||
|
||
async fn d((a, mut b): (A, A)) { | ||
b.push(1); | ||
} | ||
|
||
async fn f((ref mut a, ref b): (A, A)) {} | ||
|
||
async fn g(((ref a, ref mut b), (ref mut c, ref d)): ((A, A), (A, A))) {} | ||
|
||
fn main() {} |
271 changes: 271 additions & 0 deletions
271
src/test/ui/async-await/drop-order-for-async-fn-parameters-by-ref-binding.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
// 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(ref mut x: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foo_sync(ref mut x: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
/// Check that underscore patterns are dropped after the function is polled. | ||
async fn bar_async(ref mut x: D, _: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn bar_sync(ref mut 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((ref mut x, _): (D, D)) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn baz_sync((ref mut 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(ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foobar_sync(ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _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(ref mut x: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foo_sync(ref mut x: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
/// Check that underscore patterns are dropped after the method is polled. | ||
async fn bar_async(ref mut x: D, _: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn bar_sync(ref mut 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((ref mut x, _): (D, D)) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn baz_sync((ref mut 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( | ||
ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _y: D, | ||
) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foobar_sync( | ||
ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _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, ref mut x: D, ref mut _y: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foo_sync(&'a self, ref mut x: D, ref mut _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, ref mut x: D, _: D) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn bar_sync(&'a self, ref mut 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, (ref mut x, _): (D, D)) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn baz_sync(&'a self, (ref mut 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, ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _y: D, | ||
) { | ||
x.1.borrow_mut().push(DropOrder::Function); | ||
} | ||
|
||
fn foobar_sync( | ||
&'a self, ref mut x: D, (ref mut a, _, ref mut _c): (D, D, D), _: D, ref mut _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()), | ||
) | ||
}, | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davidtwco This is the hack that we'd want to move to HIR lowering but we can't because of
rustc_resolve
interacting with upvars, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eddyb Yeah, we'll presumably want to lower to the same HIR in the end. Because of the interaction with
rustc_resolve
, it's easier to insert the statements/replace the arguments in the AST during name resolution so it can make the correct locals resolve toDef::Upvar
. If we didn't construct them here, then these same types would need to be constructed inrustc_resolve
and in HIR lowering.Other places, like the def collector, that use these constructed types would be able to get away with just using
NodeId
s that we'd create here (much like theIsAsync
type worked previously), and we'd only construct the actual statements and arguments during HIR lowering.