Skip to content

Commit

Permalink
lowering: extend temporary lifetimes around await
Browse files Browse the repository at this point in the history
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 <david@davidtw.co>
  • Loading branch information
davidtwco committed Sep 10, 2019
1 parent 43a5ff4 commit 63fad69
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 32 deletions.
35 changes: 15 additions & 20 deletions src/librustc/hir/lowering/expr.rs
Expand Up @@ -507,14 +507,13 @@ impl LoweringContext<'_> {

/// Desugar `<expr>.await` into:
/// ```rust
/// {
/// let mut pinned = <expr>;
/// loop {
/// match <expr> {
/// 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 ();
/// }
Expand Down Expand Up @@ -549,21 +548,12 @@ impl LoweringContext<'_> {
self.allow_gen_future.clone(),
);

// let mut pinned = <expr>;
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)
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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 <expr> {
// 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(
Expand Down
24 changes: 12 additions & 12 deletions src/test/ui/async-await/async-fn-nonsend.stderr
Expand Up @@ -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`

Expand All @@ -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<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, 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<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<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:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, ()}]>`
= note: required because it appears within the type `{fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<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:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<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:28:40: 37:2 {fn(impl std::fmt::Debug) -> std::option::Option<impl std::fmt::Debug> {std::option::Option::<impl std::fmt::Debug>::Some}, fn() -> impl std::fmt::Debug {non_send}, impl std::fmt::Debug, std::option::Option<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`

Expand All @@ -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`

Expand All @@ -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`

Expand Down
@@ -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() { }
@@ -0,0 +1,12 @@
// check-pass
// edition:2018

async fn foo(x: &[Vec<u32>]) -> u32 {
0
}

async fn bar() {
foo(&[vec![123]]).await;
}

fn main() { }

0 comments on commit 63fad69

Please sign in to comment.