Skip to content

Commit 9652ea9

Browse files
committed
Stop considering moved-out locals when computing auto traits for generators
1 parent c2583e2 commit 9652ea9

File tree

5 files changed

+136
-24
lines changed

5 files changed

+136
-24
lines changed

compiler/rustc_mir_transform/src/generator.rs

+54-17
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ use rustc_middle::mir::*;
6767
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
6868
use rustc_middle::ty::{GeneratorSubsts, SubstsRef};
6969
use rustc_mir_dataflow::impls::{
70-
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
70+
MaybeBorrowedLocals, MaybeInitializedPlaces, MaybeLiveLocals, MaybeRequiresStorage,
71+
MaybeStorageLive,
7172
};
73+
use rustc_mir_dataflow::move_paths::MoveData;
7274
use rustc_mir_dataflow::storage::always_storage_live_locals;
73-
use rustc_mir_dataflow::{self, Analysis};
75+
use rustc_mir_dataflow::{self, Analysis, MoveDataParamEnv};
7476
use rustc_span::def_id::{DefId, LocalDefId};
7577
use rustc_span::symbol::sym;
7678
use rustc_span::Span;
@@ -561,6 +563,10 @@ struct LivenessInfo {
561563
/// Which locals are live across any suspension point.
562564
saved_locals: GeneratorSavedLocals,
563565

566+
/// Which locals are live *and* initialized across any suspension point.
567+
/// A local that is live but is not initialized does not need to accounted in auto trait checking.
568+
init_locals: BitSet<Local>,
569+
564570
/// The set of saved locals live at each suspension point.
565571
live_locals_at_suspension_points: Vec<BitSet<GeneratorSavedLocal>>,
566572

@@ -615,10 +621,21 @@ fn locals_live_across_suspend_points<'tcx>(
615621
.iterate_to_fixpoint()
616622
.into_results_cursor(body_ref);
617623

624+
let param_env = tcx.param_env(body.source.def_id());
625+
let (_, move_data) = MoveData::gather_moves(body, tcx, param_env).unwrap();
626+
let mdpe = MoveDataParamEnv { move_data, param_env };
627+
628+
// Calculate the set of locals which are initialized
629+
let mut inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
630+
.into_engine(tcx, body)
631+
.iterate_to_fixpoint()
632+
.into_results_cursor(body_ref);
633+
618634
let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
619635
let mut live_locals_at_suspension_points = Vec::new();
620636
let mut source_info_at_suspension_points = Vec::new();
621637
let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
638+
let mut init_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
622639

623640
for (block, data) in body.basic_blocks.iter_enumerated() {
624641
if let TerminatorKind::Yield { .. } = data.terminator().kind {
@@ -657,12 +674,24 @@ fn locals_live_across_suspend_points<'tcx>(
657674
// The generator argument is ignored.
658675
live_locals.remove(SELF_ARG);
659676

660-
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
677+
inits.seek_to_block_end(block);
678+
let mut init_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len());
679+
for move_path_index in inits.get().iter() {
680+
if let Some(local) = mdpe.move_data.move_paths[move_path_index].place.as_local() {
681+
init_locals.insert(local);
682+
}
683+
}
684+
init_locals.intersect(&live_locals);
685+
686+
debug!(
687+
"loc = {:?}, live_locals = {:?}, init_locals = {:?}",
688+
loc, live_locals, init_locals
689+
);
661690

662691
// Add the locals live at this suspension point to the set of locals which live across
663692
// any suspension points
664693
live_locals_at_any_suspension_point.union(&live_locals);
665-
694+
init_locals_at_any_suspension_point.union(&init_locals);
666695
live_locals_at_suspension_points.push(live_locals);
667696
source_info_at_suspension_points.push(data.terminator().source_info);
668697
}
@@ -687,6 +716,7 @@ fn locals_live_across_suspend_points<'tcx>(
687716

688717
LivenessInfo {
689718
saved_locals,
719+
init_locals: init_locals_at_any_suspension_point,
690720
live_locals_at_suspension_points,
691721
source_info_at_suspension_points,
692722
storage_conflicts,
@@ -909,6 +939,7 @@ fn compute_layout<'tcx>(
909939
) {
910940
let LivenessInfo {
911941
saved_locals,
942+
init_locals,
912943
live_locals_at_suspension_points,
913944
source_info_at_suspension_points,
914945
storage_conflicts,
@@ -926,20 +957,26 @@ fn compute_layout<'tcx>(
926957
debug!(?decl);
927958

928959
let ignore_for_traits = if tcx.sess.opts.unstable_opts.drop_tracking_mir {
929-
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
930-
// the information. This is alright, since `ignore_for_traits` is only relevant when
931-
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
932-
// default.
933-
match decl.local_info {
934-
// Do not include raw pointers created from accessing `static` items, as those could
935-
// well be re-created by another access to the same static.
936-
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
937-
!is_thread_local
960+
if !init_locals.contains(local) {
961+
// If only the storage is required to be live, but local is not initialized, then we can
962+
// ignore such type for auto trait purposes.
963+
true
964+
} else {
965+
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
966+
// the information. This is alright, since `ignore_for_traits` is only relevant when
967+
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
968+
// default.
969+
match decl.local_info {
970+
// Do not include raw pointers created from accessing `static` items, as those could
971+
// well be re-created by another access to the same static.
972+
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
973+
!is_thread_local
974+
}
975+
// Fake borrows are only read by fake reads, so do not have any reality in
976+
// post-analysis MIR.
977+
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
978+
_ => false,
938979
}
939-
// Fake borrows are only read by fake reads, so do not have any reality in
940-
// post-analysis MIR.
941-
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
942-
_ => false,
943980
}
944981
} else {
945982
// FIXME(#105084) HIR-based drop tracking does not account for all the temporaries that

tests/ui/async-await/field-assign-nonsend.drop_tracking_mir.stderr

+4-7
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ LL | assert_send(agent.handle());
55
| ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
8-
note: future is not `Send` as this value is used across an await
9-
--> $DIR/field-assign-nonsend.rs:23:39
8+
note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
9+
--> $DIR/field-assign-nonsend.rs:19:21
1010
|
11-
LL | let mut info = self.info_result.clone();
12-
| -------- has type `InfoResult` which is not `Send`
13-
...
14-
LL | let _ = send_element(element).await;
15-
| ^^^^^ await occurs here, with `mut info` maybe used later
11+
LL | async fn handle(&mut self) {
12+
| ^^^^^^^^^ has type `&mut Agent` which is not `Send`, because `Agent` is not `Send`
1613
note: required by a bound in `assert_send`
1714
--> $DIR/field-assign-nonsend.rs:40:19
1815
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: future cannot be sent between threads safely
2+
--> $DIR/temp-borrow-nonsend.rs:25:17
3+
|
4+
LL | assert_send(test());
5+
| ^^^^^^ future returned by `test` is not `Send`
6+
|
7+
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `*const ()`
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/temp-borrow-nonsend.rs:19:11
10+
|
11+
LL | let b = B(PhantomData);
12+
| - has type `B` which is not `Send`
13+
...
14+
LL | foo().await;
15+
| ^^^^^ await occurs here, with `b` maybe used later
16+
LL | }
17+
| - `b` is later dropped here
18+
note: required by a bound in `assert_send`
19+
--> $DIR/temp-borrow-nonsend.rs:22:19
20+
|
21+
LL | fn assert_send<T: Send>(_: T) {}
22+
| ^^^^ required by this bound in `assert_send`
23+
24+
error: aborting due to previous error
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: future cannot be sent between threads safely
2+
--> $DIR/temp-borrow-nonsend.rs:25:17
3+
|
4+
LL | assert_send(test());
5+
| ^^^^^^ future returned by `test` is not `Send`
6+
|
7+
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `*const ()`
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/temp-borrow-nonsend.rs:19:11
10+
|
11+
LL | let b = B(PhantomData);
12+
| - has type `B` which is not `Send`
13+
...
14+
LL | foo().await;
15+
| ^^^^^ await occurs here, with `b` maybe used later
16+
LL | }
17+
| - `b` is later dropped here
18+
note: required by a bound in `assert_send`
19+
--> $DIR/temp-borrow-nonsend.rs:22:19
20+
|
21+
LL | fn assert_send<T: Send>(_: T) {}
22+
| ^^^^ required by this bound in `assert_send`
23+
24+
error: aborting due to previous error
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// revisions: no_drop_tracking drop_tracking drop_tracking_mir
2+
// [drop_tracking_mir] check-pass
3+
// [drop_tracking] compile-flags: -Zdrop-tracking
4+
// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
5+
// edition:2021
6+
7+
use core::marker::PhantomData;
8+
9+
struct B(PhantomData<*const ()>);
10+
11+
fn do_sth(_: &B) {}
12+
13+
async fn foo() {}
14+
15+
async fn test() {
16+
let b = B(PhantomData);
17+
do_sth(&b);
18+
drop(b);
19+
foo().await;
20+
}
21+
22+
fn assert_send<T: Send>(_: T) {}
23+
24+
fn main() {
25+
assert_send(test());
26+
//[no_drop_tracking]~^ cannot be sent between threads safely
27+
//[drop_tracking]~^^ cannot be sent between threads safely
28+
}

0 commit comments

Comments
 (0)