Skip to content

Commit a3ef360

Browse files
committed
Auto merge of #70679 - tmandry:issue-68112, r=nikomatsakis
Improve async-await/generator obligation errors in some cases Fixes #68112. This change is best read one commit at a time (I add a test at the beginning and update it in each change after). The `test2` function is a case I found while writing the test that we don't handle with this code yet. I don't attempt to fix it in this PR, but it's a good candidate for future work. r? @davidtwco, @nikomatsakis
2 parents 513a647 + 4326d95 commit a3ef360

18 files changed

+411
-106
lines changed

src/librustc_ast_lowering/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
590590
await_span,
591591
self.allow_gen_future.clone(),
592592
);
593+
let expr = self.lower_expr(expr);
593594

594595
let pinned_ident = Ident::with_dummy_span(sym::pinned);
595596
let (pinned_pat, pinned_pat_hid) =
@@ -671,7 +672,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
671672
let unit = self.expr_unit(span);
672673
let yield_expr = self.expr(
673674
span,
674-
hir::ExprKind::Yield(unit, hir::YieldSource::Await),
675+
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr.hir_id) }),
675676
ThinVec::new(),
676677
);
677678
let yield_expr = self.arena.alloc(yield_expr);
@@ -704,7 +705,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
704705
// match <expr> {
705706
// mut pinned => loop { .. }
706707
// }
707-
let expr = self.lower_expr(expr);
708708
hir::ExprKind::Match(expr, arena_vec![self; pinned_arm], hir::MatchSource::AwaitDesugar)
709709
}
710710

src/librustc_hir/hir.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1736,15 +1736,24 @@ pub struct Destination {
17361736
#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)]
17371737
pub enum YieldSource {
17381738
/// An `<expr>.await`.
1739-
Await,
1739+
Await { expr: Option<HirId> },
17401740
/// A plain `yield`.
17411741
Yield,
17421742
}
17431743

1744+
impl YieldSource {
1745+
pub fn is_await(&self) -> bool {
1746+
match self {
1747+
YieldSource::Await { .. } => true,
1748+
YieldSource::Yield => false,
1749+
}
1750+
}
1751+
}
1752+
17441753
impl fmt::Display for YieldSource {
17451754
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17461755
f.write_str(match self {
1747-
YieldSource::Await => "`await`",
1756+
YieldSource::Await { .. } => "`await`",
17481757
YieldSource::Yield => "`yield`",
17491758
})
17501759
}
@@ -1755,7 +1764,7 @@ impl From<GeneratorKind> for YieldSource {
17551764
match kind {
17561765
// Guess based on the kind of the current generator.
17571766
GeneratorKind::Gen => Self::Yield,
1758-
GeneratorKind::Async(_) => Self::Await,
1767+
GeneratorKind::Async(_) => Self::Await { expr: None },
17591768
}
17601769
}
17611770
}

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+162-82
Large diffs are not rendered by default.

src/librustc_typeck/check/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17971797
// we know that the yield type must be `()`; however, the context won't contain this
17981798
// information. Hence, we check the source of the yield expression here and check its
17991799
// value's type against `()` (this check should always hold).
1800-
None if src == &hir::YieldSource::Await => {
1800+
None if src.is_await() => {
18011801
self.check_expr_coercable_to_type(&value, self.tcx.mk_unit());
18021802
self.tcx.mk_unit()
18031803
}

src/test/ui/async-await/async-fn-nonsend.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
1212
--> $DIR/async-fn-nonsend.rs:24:5
1313
|
1414
LL | let x = non_send();
15-
| - has type `impl std::fmt::Debug`
15+
| - has type `impl std::fmt::Debug` which is not `Send`
1616
LL | drop(x);
1717
LL | fut().await;
1818
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
@@ -33,7 +33,7 @@ note: future is not `Send` as this value is used across an await
3333
--> $DIR/async-fn-nonsend.rs:33:20
3434
|
3535
LL | match Some(non_send()) {
36-
| ---------- has type `impl std::fmt::Debug`
36+
| ---------- has type `impl std::fmt::Debug` which is not `Send`
3737
LL | Some(_) => fut().await,
3838
| ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later
3939
...
@@ -54,7 +54,7 @@ note: future is not `Send` as this value is used across an await
5454
--> $DIR/async-fn-nonsend.rs:42:9
5555
|
5656
LL | let f: &mut std::fmt::Formatter = panic!();
57-
| - has type `&mut std::fmt::Formatter<'_>`
57+
| - has type `&mut std::fmt::Formatter<'_>` which is not `Send`
5858
LL | if non_sync().fmt(f).unwrap() == () {
5959
LL | fut().await;
6060
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later

src/test/ui/async-await/issue-64130-1-sync.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ note: future is not `Sync` as this value is used across an await
1212
--> $DIR/issue-64130-1-sync.rs:15:5
1313
|
1414
LL | let x = Foo;
15-
| - has type `Foo`
15+
| - has type `Foo` which is not `Sync`
1616
LL | baz().await;
1717
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
1818
LL | }

src/test/ui/async-await/issue-64130-2-send.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
1212
--> $DIR/issue-64130-2-send.rs:15:5
1313
|
1414
LL | let x = Foo;
15-
| - has type `Foo`
15+
| - has type `Foo` which is not `Send`
1616
LL | baz().await;
1717
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
1818
LL | }

src/test/ui/async-await/issue-64130-3-other.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ note: future does not implement `Qux` as this value is used across an await
1616
--> $DIR/issue-64130-3-other.rs:18:5
1717
|
1818
LL | let x = Foo;
19-
| - has type `Foo`
19+
| - has type `Foo` which does not implement `Qux`
2020
LL | baz().await;
2121
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
2222
LL | }

src/test/ui/async-await/issue-64130-4-async-move.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: future cannot be sent between threads safely
22
--> $DIR/issue-64130-4-async-move.rs:15:17
33
|
44
LL | pub fn foo() -> impl Future + Send {
5-
| ^^^^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
5+
| ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
66
...
77
LL | / async move {
88
LL | | match client.status() {
@@ -18,7 +18,7 @@ note: future is not `Send` as this value is used across an await
1818
--> $DIR/issue-64130-4-async-move.rs:21:26
1919
|
2020
LL | match client.status() {
21-
| ------ has type `&Client`
21+
| ------ has type `&Client` which is not `Send`
2222
LL | 200 => {
2323
LL | let _x = get().await;
2424
| ^^^^^^^^^^^ await occurs here, with `client` maybe used later

src/test/ui/async-await/issue-64130-non-send-future-diags.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
1212
--> $DIR/issue-64130-non-send-future-diags.rs:15:5
1313
|
1414
LL | let g = x.lock().unwrap();
15-
| - has type `std::sync::MutexGuard<'_, u32>`
15+
| - has type `std::sync::MutexGuard<'_, u32>` which is not `Send`
1616
LL | baz().await;
1717
| ^^^^^^^^^^^ await occurs here, with `g` maybe used later
1818
LL | }

src/test/ui/async-await/issue-67252-unnamed-future.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ LL | fn spawn<T: Send>(_: T) {}
55
| ---- required by this bound in `spawn`
66
...
77
LL | spawn(async {
8-
| ^^^^^ future is not `Send`
8+
| ^^^^^ future created by async block is not `Send`
99
|
1010
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()`
1111
note: future is not `Send` as this value is used across an await
1212
--> $DIR/issue-67252-unnamed-future.rs:20:9
1313
|
1414
LL | let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
15-
| -- has type `*mut ()`
15+
| -- has type `*mut ()` which is not `Send`
1616
LL | AFuture.await;
1717
| ^^^^^^^^^^^^^ await occurs here, with `_a` maybe used later
1818
LL | });
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// edition:2018
2+
3+
use std::{
4+
future::Future,
5+
cell::RefCell,
6+
sync::Arc,
7+
pin::Pin,
8+
task::{Context, Poll},
9+
};
10+
11+
fn require_send(_: impl Send) {}
12+
13+
struct Ready<T>(Option<T>);
14+
impl<T> Future for Ready<T> {
15+
type Output = T;
16+
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
17+
Poll::Ready(self.0.take().unwrap())
18+
}
19+
}
20+
fn ready<T>(t: T) -> Ready<T> {
21+
Ready(Some(t))
22+
}
23+
24+
fn make_non_send_future1() -> impl Future<Output = Arc<RefCell<i32>>> {
25+
ready(Arc::new(RefCell::new(0)))
26+
}
27+
28+
fn test1() {
29+
let send_fut = async {
30+
let non_send_fut = make_non_send_future1();
31+
let _ = non_send_fut.await;
32+
ready(0).await;
33+
};
34+
require_send(send_fut);
35+
//~^ ERROR future cannot be sent between threads
36+
}
37+
38+
fn test1_no_let() {
39+
let send_fut = async {
40+
let _ = make_non_send_future1().await;
41+
ready(0).await;
42+
};
43+
require_send(send_fut);
44+
//~^ ERROR future cannot be sent between threads
45+
}
46+
47+
async fn ready2<T>(t: T) -> T { t }
48+
fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
49+
ready2(Arc::new(RefCell::new(0)))
50+
}
51+
52+
// Ideally this test would have diagnostics similar to the test above, but right
53+
// now it doesn't.
54+
fn test2() {
55+
let send_fut = async {
56+
let non_send_fut = make_non_send_future2();
57+
let _ = non_send_fut.await;
58+
ready(0).await;
59+
};
60+
require_send(send_fut);
61+
//~^ ERROR `std::cell::RefCell<i32>` cannot be shared between threads safely
62+
}
63+
64+
fn main() {}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error: future cannot be sent between threads safely
2+
--> $DIR/issue-68112.rs:34:5
3+
|
4+
LL | fn require_send(_: impl Send) {}
5+
| ---- required by this bound in `require_send`
6+
...
7+
LL | require_send(send_fut);
8+
| ^^^^^^^^^^^^ future created by async block is not `Send`
9+
|
10+
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
11+
note: future is not `Send` as it awaits another future which is not `Send`
12+
--> $DIR/issue-68112.rs:31:17
13+
|
14+
LL | let _ = non_send_fut.await;
15+
| ^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send`
16+
17+
error: future cannot be sent between threads safely
18+
--> $DIR/issue-68112.rs:43:5
19+
|
20+
LL | fn require_send(_: impl Send) {}
21+
| ---- required by this bound in `require_send`
22+
...
23+
LL | require_send(send_fut);
24+
| ^^^^^^^^^^^^ future created by async block is not `Send`
25+
|
26+
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
27+
note: future is not `Send` as it awaits another future which is not `Send`
28+
--> $DIR/issue-68112.rs:40:17
29+
|
30+
LL | let _ = make_non_send_future1().await;
31+
| ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send`
32+
33+
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
34+
--> $DIR/issue-68112.rs:60:5
35+
|
36+
LL | fn require_send(_: impl Send) {}
37+
| ---- required by this bound in `require_send`
38+
...
39+
LL | require_send(send_fut);
40+
| ^^^^^^^^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
41+
|
42+
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
43+
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::cell::RefCell<i32>>`
44+
= note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc<std::cell::RefCell<i32>> {}]`
45+
= note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc<std::cell::RefCell<i32>> {}]>`
46+
= note: required because it appears within the type `impl std::future::Future`
47+
= note: required because it appears within the type `impl std::future::Future`
48+
= note: required because it appears within the type `impl std::future::Future`
49+
= note: required because it appears within the type `{std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}`
50+
= note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}]`
51+
= note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}]>`
52+
= note: required because it appears within the type `impl std::future::Future`
53+
54+
error: aborting due to 3 previous errors
55+
56+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | fn assert_send<T: Send>(_: T) {}
55
| ---- required by this bound in `assert_send`
66
...
77
LL | assert_send(async {
8-
| ^^^^^^^^^^^ future returned by `main` is not `Send`
8+
| ^^^^^^^^^^^ future created by async block is not `Send`
99
|
1010
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*const u8`
1111
note: future is not `Send` as this value is used across an await
@@ -14,7 +14,7 @@ note: future is not `Send` as this value is used across an await
1414
LL | bar(Foo(std::ptr::null())).await;
1515
| ^^^^^^^^----------------^^^^^^^^- `std::ptr::null()` is later dropped here
1616
| | |
17-
| | has type `*const u8`
17+
| | has type `*const u8` which is not `Send`
1818
| await occurs here, with `std::ptr::null()` maybe used later
1919
help: consider moving this into a `let` binding to create a shorter lived borrow
2020
--> $DIR/issue-65436-raw-ptr-not-send.rs:14:13

src/test/ui/generator/issue-68112.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#![feature(generators, generator_trait)]
2+
3+
use std::{
4+
cell::RefCell,
5+
sync::Arc,
6+
pin::Pin,
7+
ops::{Generator, GeneratorState},
8+
};
9+
10+
pub struct Ready<T>(Option<T>);
11+
impl<T> Generator<()> for Ready<T> {
12+
type Return = T;
13+
type Yield = ();
14+
fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> {
15+
GeneratorState::Complete(self.0.take().unwrap())
16+
}
17+
}
18+
pub fn make_gen1<T>(t: T) -> Ready<T> {
19+
Ready(Some(t))
20+
}
21+
22+
fn require_send(_: impl Send) {}
23+
24+
fn make_non_send_generator() -> impl Generator<Return = Arc<RefCell<i32>>> {
25+
make_gen1(Arc::new(RefCell::new(0)))
26+
}
27+
28+
fn test1() {
29+
let send_gen = || {
30+
let _non_send_gen = make_non_send_generator();
31+
yield;
32+
};
33+
require_send(send_gen);
34+
//~^ ERROR generator cannot be sent between threads
35+
}
36+
37+
pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
38+
|| {
39+
yield;
40+
t
41+
}
42+
}
43+
fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
44+
make_gen2(Arc::new(RefCell::new(0)))
45+
}
46+
47+
fn test2() {
48+
let send_gen = || {
49+
let _non_send_gen = make_non_send_generator2();
50+
yield;
51+
};
52+
require_send(send_gen);
53+
//~^ ERROR `std::cell::RefCell<i32>` cannot be shared between threads safely
54+
}
55+
56+
fn main() {}

0 commit comments

Comments
 (0)