Skip to content

Commit a2968de

Browse files
committed
Don't mark generators as !Send if a Copy + !Send value is dropped across a yield
Copy types can neither read nor write their values when dropped (the language guarantees Drop is a no-op). So it doesn't make sense for them to make the generator non-Send.
1 parent 17355a3 commit a2968de

File tree

5 files changed

+77
-0
lines changed

5 files changed

+77
-0
lines changed

compiler/rustc_typeck/src/check/generator_interior.rs

+14
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
1717
use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
1818
use rustc_span::symbol::sym;
1919
use rustc_span::Span;
20+
use rustc_trait_selection::infer::InferCtxtExt;
2021
use tracing::debug;
2122

2223
mod drop_ranges;
@@ -51,6 +52,19 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
5152
ty, hir_id, scope, expr, source_span, self.expr_count,
5253
);
5354

55+
if self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty, source_span) {
56+
// This is only used because it's dropped after the yield.
57+
// But it's a Copy type, so it's not possible for the drop to read or write the value.
58+
// Ignore it instead of giving an error.
59+
if matches!(
60+
scope,
61+
Some(region::Scope { data: region::ScopeData::Remainder { .. }, .. })
62+
) {
63+
debug!("ignoring Copy type only used in drop");
64+
return;
65+
}
66+
}
67+
5468
let live_across_yield = scope
5569
.map(|s| {
5670
self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// edition:2018
2+
// build-pass
3+
async fn trivial_drop() {
4+
let x: *const usize = &0;
5+
async {}.await;
6+
}
7+
8+
fn assert_send<T: Send>(_: T) {}
9+
10+
fn main() {
11+
assert_send(trivial_drop());
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(generators, negative_impls)]
2+
3+
fn assert_send<T: Send>(_: T) {}
4+
5+
struct S;
6+
impl !Send for S {}
7+
8+
fn main() {
9+
println!("{}", std::mem::needs_drop::<S>());
10+
let g = || {
11+
let x = S; //~ type `S`
12+
yield; //~ `x` maybe used later
13+
};
14+
assert_send(g); //~ ERROR generator cannot be sent between threads
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: generator cannot be sent between threads safely
2+
--> $DIR/trivial-drop-non-copy.rs:14:5
3+
|
4+
LL | assert_send(g);
5+
| ^^^^^^^^^^^ generator is not `Send`
6+
|
7+
= help: within `[generator@$DIR/trivial-drop-non-copy.rs:10:13: 10:15]`, the trait `Send` is not implemented for `S`
8+
note: generator is not `Send` as this value is used across a yield
9+
--> $DIR/trivial-drop-non-copy.rs:12:9
10+
|
11+
LL | let x = S;
12+
| - has type `S` which is not `Send`
13+
LL | yield;
14+
| ^^^^^ yield occurs here, with `x` maybe used later
15+
LL | };
16+
| - `x` is later dropped here
17+
note: required by a bound in `assert_send`
18+
--> $DIR/trivial-drop-non-copy.rs:3:19
19+
|
20+
LL | fn assert_send<T: Send>(_: T) {}
21+
| ^^^^ required by this bound in `assert_send`
22+
23+
error: aborting due to previous error
24+

src/test/ui/generator/trivial-drop.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// build-pass
2+
#![feature(generators)]
3+
4+
fn assert_send<T: Send>(_: T) {}
5+
6+
fn main() {
7+
let g = || {
8+
let x: *const usize = &0;
9+
yield;
10+
};
11+
assert_send(g);
12+
}

0 commit comments

Comments
 (0)