Skip to content

Commit 258decf

Browse files
committed
Auto merge of rust-lang#112279 - nbdd0121:generator, r=<try>
Stop considering moved-out locals when computing auto traits for generators Addresses rust-lang#94067 (but does not fix it since drop-tracking-mir is unstable) This PR, unlike rust-lang#110420 or rust-lang#112050, does not attempt to reduce the number of live locals across suspension points. It however ignores moved-out locals for trait purposes. So this PR solves the non-send issue, but not the size issue. Suggested by `@RalfJung` in [rust-lang/unsafe-code-guidelines#188](rust-lang/unsafe-code-guidelines#188 (comment)) cc `@b-naber` who's working on this from a different perspective. r? `@cjgillot`
2 parents eea2614 + 0259c6e commit 258decf

6 files changed

+88
-134
lines changed

compiler/rustc_mir_transform/src/generator.rs

+61-17
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ use rustc_middle::ty::InstanceDef;
7070
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
7171
use rustc_middle::ty::{GeneratorArgs, GenericArgsRef};
7272
use rustc_mir_dataflow::impls::{
73-
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
73+
MaybeBorrowedLocals, MaybeInitializedPlaces, MaybeLiveLocals, MaybeRequiresStorage,
74+
MaybeStorageLive,
7475
};
76+
use rustc_mir_dataflow::move_paths::MoveData;
7577
use rustc_mir_dataflow::storage::always_storage_live_locals;
76-
use rustc_mir_dataflow::{self, Analysis};
78+
use rustc_mir_dataflow::{self, Analysis, MaybeReachable, MoveDataParamEnv};
7779
use rustc_span::def_id::{DefId, LocalDefId};
7880
use rustc_span::symbol::sym;
7981
use rustc_span::Span;
@@ -567,6 +569,10 @@ struct LivenessInfo {
567569
/// Which locals are live across any suspension point.
568570
saved_locals: GeneratorSavedLocals,
569571

572+
/// Which locals are live *and* initialized across any suspension point.
573+
/// A local that is live but is not initialized does not need to accounted in auto trait checking.
574+
init_locals: BitSet<Local>,
575+
570576
/// The set of saved locals live at each suspension point.
571577
live_locals_at_suspension_points: Vec<BitSet<GeneratorSavedLocal>>,
572578

@@ -620,10 +626,25 @@ fn locals_live_across_suspend_points<'tcx>(
620626
.iterate_to_fixpoint()
621627
.into_results_cursor(body_ref);
622628

629+
let param_env = tcx.param_env(body.source.def_id());
630+
let move_data =
631+
MoveData::gather_moves(body, tcx, param_env).unwrap_or_else(|(move_data, _)| {
632+
tcx.sess.delay_span_bug(body.span, "gather_moves failed");
633+
move_data
634+
});
635+
let mdpe = MoveDataParamEnv { move_data, param_env };
636+
637+
// Calculate the set of locals which are initialized
638+
let mut inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
639+
.into_engine(tcx, body)
640+
.iterate_to_fixpoint()
641+
.into_results_cursor(body_ref);
642+
623643
let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
624644
let mut live_locals_at_suspension_points = Vec::new();
625645
let mut source_info_at_suspension_points = Vec::new();
626646
let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
647+
let mut init_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
627648

628649
for (block, data) in body.basic_blocks.iter_enumerated() {
629650
if let TerminatorKind::Yield { .. } = data.terminator().kind {
@@ -662,12 +683,27 @@ fn locals_live_across_suspend_points<'tcx>(
662683
// The generator argument is ignored.
663684
live_locals.remove(SELF_ARG);
664685

665-
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
686+
inits.seek_to_block_end(block);
687+
let mut init_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len());
688+
if let MaybeReachable::Reachable(bitset) = inits.get() {
689+
for move_path_index in bitset.iter() {
690+
if let Some(local) = mdpe.move_data.move_paths[move_path_index].place.as_local()
691+
{
692+
init_locals.insert(local);
693+
}
694+
}
695+
}
696+
init_locals.intersect(&live_locals);
697+
698+
debug!(
699+
"loc = {:?}, live_locals = {:?}, init_locals = {:?}",
700+
loc, live_locals, init_locals
701+
);
666702

667703
// Add the locals live at this suspension point to the set of locals which live across
668704
// any suspension points
669705
live_locals_at_any_suspension_point.union(&live_locals);
670-
706+
init_locals_at_any_suspension_point.union(&init_locals);
671707
live_locals_at_suspension_points.push(live_locals);
672708
source_info_at_suspension_points.push(data.terminator().source_info);
673709
}
@@ -692,6 +728,7 @@ fn locals_live_across_suspend_points<'tcx>(
692728

693729
LivenessInfo {
694730
saved_locals,
731+
init_locals: init_locals_at_any_suspension_point,
695732
live_locals_at_suspension_points,
696733
source_info_at_suspension_points,
697734
storage_conflicts,
@@ -863,6 +900,7 @@ fn compute_layout<'tcx>(
863900
) {
864901
let LivenessInfo {
865902
saved_locals,
903+
init_locals,
866904
live_locals_at_suspension_points,
867905
source_info_at_suspension_points,
868906
storage_conflicts,
@@ -879,20 +917,26 @@ fn compute_layout<'tcx>(
879917
let decl = &body.local_decls[local];
880918
debug!(?decl);
881919

882-
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
883-
// the information. This is alright, since `ignore_for_traits` is only relevant when
884-
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
885-
// default.
886-
let ignore_for_traits = match decl.local_info {
887-
// Do not include raw pointers created from accessing `static` items, as those could
888-
// well be re-created by another access to the same static.
889-
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
890-
!is_thread_local
920+
let ignore_for_traits = if !init_locals.contains(local) {
921+
// If only the storage is required to be live, but local is not initialized, then we can
922+
// ignore such type for auto trait purposes.
923+
true
924+
} else {
925+
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
926+
// the information. This is alright, since `ignore_for_traits` is only relevant when
927+
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
928+
// default.
929+
match decl.local_info {
930+
// Do not include raw pointers created from accessing `static` items, as those could
931+
// well be re-created by another access to the same static.
932+
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
933+
!is_thread_local
934+
}
935+
// Fake borrows are only read by fake reads, so do not have any reality in
936+
// post-analysis MIR.
937+
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
938+
_ => false,
891939
}
892-
// Fake borrows are only read by fake reads, so do not have any reality in
893-
// post-analysis MIR.
894-
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
895-
_ => false,
896940
};
897941
let decl =
898942
GeneratorSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };

tests/ui/async-await/drop-track-field-assign-nonsend.rs

-44
This file was deleted.

tests/ui/async-await/drop-track-field-assign-nonsend.stderr

-23
This file was deleted.

tests/ui/async-await/drop-track-field-assign.rs

-43
This file was deleted.

tests/ui/async-await/field-assign-nonsend.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:20: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:16: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:37:19
1815
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// check-pass
2+
// edition:2021
3+
4+
use core::marker::PhantomData;
5+
6+
struct B(PhantomData<*const ()>);
7+
8+
fn do_sth(_: &B) {}
9+
10+
async fn foo() {}
11+
12+
async fn test() {
13+
let b = B(PhantomData);
14+
do_sth(&b);
15+
drop(b);
16+
foo().await;
17+
}
18+
19+
fn assert_send<T: Send>(_: T) {}
20+
21+
fn main() {
22+
assert_send(test());
23+
}

0 commit comments

Comments
 (0)