From 63fad69a9967a56e33927aa31c50768bc1498588 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 8 Sep 2019 21:22:51 +0100 Subject: [PATCH] lowering: extend temporary lifetimes around await This commit changes the HIR lowering around `await` so that temporary lifetimes are extended. Previously, await was lowered as: ```rust { let mut pinned = future; loop { match ::std::future::poll_with_tls_context(unsafe { <::std::pin::Pin>::new_unchecked(&mut pinned) }) { ::std::task::Poll::Ready(result) => break result, ::std::task::Poll::Pending => {} } yield (); } } ``` With this commit, await is lowered as: ```rust match future { mut pinned => loop { match ::std::future::poll_with_tls_context(unsafe { <::std::pin::Pin>::new_unchecked(&mut pinned) }) { ::std::task::Poll::Ready(result) => break result, ::std::task::Poll::Pending => {} } yield (); } } ``` However, this change has the following side-effects: - All temporaries in future will be considered to live across a yield for the purpose of auto-traits. - Borrowed temporaries in future are likely to be considered to be live across the yield for the purpose of the generator transform. Signed-off-by: David Wood --- src/librustc/hir/lowering/expr.rs | 35 ++++++++----------- .../ui/async-await/async-fn-nonsend.stderr | 24 ++++++------- ...-63832-await-short-temporary-lifetime-1.rs | 19 ++++++++++ ...ue-63832-await-short-temporary-lifetime.rs | 12 +++++++ 4 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 src/test/ui/async-await/issue-63832-await-short-temporary-lifetime-1.rs create mode 100644 src/test/ui/async-await/issue-63832-await-short-temporary-lifetime.rs diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs index 0d8986ddec3c7..a46cdabbb518f 100644 --- a/src/librustc/hir/lowering/expr.rs +++ b/src/librustc/hir/lowering/expr.rs @@ -507,14 +507,13 @@ impl LoweringContext<'_> { /// Desugar `.await` into: /// ```rust - /// { - /// let mut pinned = ; - /// loop { + /// match { + /// mut pinned => loop { /// match ::std::future::poll_with_tls_context(unsafe { - /// ::std::pin::Pin::new_unchecked(&mut pinned) + /// <::std::pin::Pin>::new_unchecked(&mut pinned) /// }) { /// ::std::task::Poll::Ready(result) => break result, - /// ::std::task::Poll::Pending => {}, + /// ::std::task::Poll::Pending => {} /// } /// yield (); /// } @@ -549,21 +548,12 @@ impl LoweringContext<'_> { self.allow_gen_future.clone(), ); - // let mut pinned = ; - let expr = P(self.lower_expr(expr)); let pinned_ident = Ident::with_dummy_span(sym::pinned); let (pinned_pat, pinned_pat_hid) = self.pat_ident_binding_mode( span, pinned_ident, hir::BindingAnnotation::Mutable, ); - let pinned_let = self.stmt_let_pat( - ThinVec::new(), - span, - Some(expr), - pinned_pat, - hir::LocalSource::AwaitDesugar, - ); // ::std::future::poll_with_tls_context(unsafe { // ::std::pin::Pin::new_unchecked(&mut pinned) @@ -621,7 +611,7 @@ impl LoweringContext<'_> { self.arm(hir_vec![pending_pat], empty_block) }; - let match_stmt = { + let inner_match_stmt = { let match_expr = self.expr_match( span, poll_expr, @@ -643,10 +633,11 @@ impl LoweringContext<'_> { let loop_block = P(self.block_all( span, - hir_vec![match_stmt, yield_stmt], + hir_vec![inner_match_stmt, yield_stmt], None, )); + // loop { .. } let loop_expr = P(hir::Expr { hir_id: loop_hir_id, node: hir::ExprKind::Loop( @@ -658,10 +649,14 @@ impl LoweringContext<'_> { attrs: ThinVec::new(), }); - hir::ExprKind::Block( - P(self.block_all(span, hir_vec![pinned_let], Some(loop_expr))), - None, - ) + // mut pinned => loop { ... } + let pinned_arm = self.arm(hir_vec![pinned_pat], loop_expr); + + // match { + // mut pinned => loop { .. } + // } + let expr = P(self.lower_expr(expr)); + hir::ExprKind::Match(expr, hir_vec![pinned_arm], hir::MatchSource::AwaitDesugar) } fn lower_expr_closure( diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr index fad90b29c0e6e..d2f92f04f40a7 100644 --- a/src/test/ui/async-await/async-fn-nonsend.stderr +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -9,9 +9,9 @@ LL | assert_send(local_dropped_before_await()); | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `impl std::fmt::Debug` - = note: required because it appears within the type `{impl std::fmt::Debug, impl std::future::Future, ()}` - = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, ()}]` - = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, ()}]>` + = note: required because it appears within the type `{impl std::fmt::Debug, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]>` = note: required because it appears within the type `impl std::future::Future` = note: required because it appears within the type `impl std::future::Future` @@ -26,9 +26,9 @@ LL | assert_send(non_send_temporary_in_match()); | = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>` = note: required because it appears within the type `impl std::fmt::Debug` - = note: required because it appears within the type `{fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}` - = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}]` - = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, impl std::future::Future, ()}]>` + = note: required because it appears within the type `{fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option {std::option::Option::::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]>` = note: required because it appears within the type `impl std::future::Future` = note: required because it appears within the type `impl std::future::Future` @@ -45,9 +45,9 @@ LL | assert_send(non_sync_with_method_call()); = note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write` = note: required because it appears within the type `std::fmt::Formatter<'_>` = note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>` - = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}` - = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]` - = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>` + = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]>` = note: required because it appears within the type `impl std::future::Future` = note: required because it appears within the type `impl std::future::Future` @@ -68,9 +68,9 @@ LL | assert_send(non_sync_with_method_call()); = note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>` = note: required because it appears within the type `std::fmt::Formatter<'_>` = note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>` - = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}` - = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]` - = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, impl std::future::Future, ()}]>` + = note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}` + = note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]` + = note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, fn() -> impl std::future::Future {fut}, impl std::future::Future, ()}]>` = note: required because it appears within the type `impl std::future::Future` = note: required because it appears within the type `impl std::future::Future` diff --git a/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime-1.rs b/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime-1.rs new file mode 100644 index 0000000000000..54059b29f72e2 --- /dev/null +++ b/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime-1.rs @@ -0,0 +1,19 @@ +// check-pass +// edition:2018 + +struct Test(String); + +impl Test { + async fn borrow_async(&self) {} + + fn with(&mut self, s: &str) -> &mut Self { + self.0 = s.into(); + self + } +} + +async fn test() { + Test("".to_string()).with("123").borrow_async().await; +} + +fn main() { } diff --git a/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime.rs b/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime.rs new file mode 100644 index 0000000000000..c5ea2b821ad78 --- /dev/null +++ b/src/test/ui/async-await/issue-63832-await-short-temporary-lifetime.rs @@ -0,0 +1,12 @@ +// check-pass +// edition:2018 + +async fn foo(x: &[Vec]) -> u32 { + 0 +} + +async fn bar() { + foo(&[vec![123]]).await; +} + +fn main() { }