Skip to content

Commit

Permalink
Don't mark generators as !Send if a Copy + !Send value is dropped…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
jyn514 committed Jul 10, 2022
1 parent 17355a3 commit a2968de
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 0 deletions.
14 changes: 14 additions & 0 deletions compiler/rustc_typeck/src/check/generator_interior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;

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

if self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty, source_span) {
// This is only used because it's dropped after the yield.
// But it's a Copy type, so it's not possible for the drop to read or write the value.
// Ignore it instead of giving an error.
if matches!(
scope,
Some(region::Scope { data: region::ScopeData::Remainder { .. }, .. })
) {
debug!("ignoring Copy type only used in drop");
return;
}
}

let live_across_yield = scope
.map(|s| {
self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| {
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/async-await/trivial-drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// edition:2018
// build-pass
async fn trivial_drop() {
let x: *const usize = &0;
async {}.await;
}

fn assert_send<T: Send>(_: T) {}

fn main() {
assert_send(trivial_drop());
}
15 changes: 15 additions & 0 deletions src/test/ui/generator/trivial-drop-non-copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(generators, negative_impls)]

fn assert_send<T: Send>(_: T) {}

struct S;
impl !Send for S {}

fn main() {
println!("{}", std::mem::needs_drop::<S>());
let g = || {
let x = S; //~ type `S`
yield; //~ `x` maybe used later
};
assert_send(g); //~ ERROR generator cannot be sent between threads
}
24 changes: 24 additions & 0 deletions src/test/ui/generator/trivial-drop-non-copy.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error: generator cannot be sent between threads safely
--> $DIR/trivial-drop-non-copy.rs:14:5
|
LL | assert_send(g);
| ^^^^^^^^^^^ generator is not `Send`
|
= help: within `[generator@$DIR/trivial-drop-non-copy.rs:10:13: 10:15]`, the trait `Send` is not implemented for `S`
note: generator is not `Send` as this value is used across a yield
--> $DIR/trivial-drop-non-copy.rs:12:9
|
LL | let x = S;
| - has type `S` which is not `Send`
LL | yield;
| ^^^^^ yield occurs here, with `x` maybe used later
LL | };
| - `x` is later dropped here
note: required by a bound in `assert_send`
--> $DIR/trivial-drop-non-copy.rs:3:19
|
LL | fn assert_send<T: Send>(_: T) {}
| ^^^^ required by this bound in `assert_send`

error: aborting due to previous error

12 changes: 12 additions & 0 deletions src/test/ui/generator/trivial-drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// build-pass
#![feature(generators)]

fn assert_send<T: Send>(_: T) {}

fn main() {
let g = || {
let x: *const usize = &0;
yield;
};
assert_send(g);
}

0 comments on commit a2968de

Please sign in to comment.