Skip to content

Commit 7093781

Browse files
committed
Auto merge of rust-lang#128846 - compiler-errors:drop-liveness, r=<try>
Stop considering moved-out locals when computing auto traits for generators (rebased) This PR revives rust-lang#112279. I wanted to reopen this to gauge `@cjgillot's` thoughts about this, since it's been a while since `-Zdrop-tracking-mir` has landed. If this PR looks OK, I can flesh it out and write up an FCP report -- I think this is a T-types FCP, since this has to do with the types that are considered live for auto traits, and doesn't otherwise affect the layout of coroutines. Open questions: * **"Why do we require storage to be live if the locals is not initialized?"** (rust-lang#112279 (comment)) * I agree that we should eventually fix the storage analysis for coroutines, but this seems like a problem that can be fixed after we fix witnesses for *the purposes of traits* here. * As far as I could tell, fixing the problem of moved locals for *storage* would require insertion of additional `StorageDead` statements, or would require further entangling the type system with the operational semantics in ways that T-opsem seemed uncomfortable with [when I asked](https://rust-lang.zulipchat.com/#narrow/stream/189540-t-compiler.2Fwg-mir-opt/topic/Inserting.20.60StorageDrop.60.20after.20unconditional.20moves). cc `@RalfJung` * Relevant: rust-lang/unsafe-code-guidelines#188 Fixes rust-lang#128095 Fixes rust-lang#94067 r? `@cjgillot`
2 parents 2048386 + 9c519d5 commit 7093781

6 files changed

+82
-134
lines changed

compiler/rustc_mir_transform/src/coroutine.rs

+55-17
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ use rustc_middle::mir::*;
6666
use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, InstanceKind, Ty, TyCtxt};
6767
use rustc_middle::{bug, span_bug};
6868
use rustc_mir_dataflow::impls::{
69-
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
69+
MaybeBorrowedLocals, MaybeInitializedPlaces, MaybeLiveLocals, MaybeRequiresStorage,
70+
MaybeStorageLive,
7071
};
72+
use rustc_mir_dataflow::move_paths::MoveData;
7173
use rustc_mir_dataflow::storage::always_storage_live_locals;
72-
use rustc_mir_dataflow::Analysis;
74+
use rustc_mir_dataflow::{self, Analysis, MaybeReachable};
7375
use rustc_span::def_id::{DefId, LocalDefId};
7476
use rustc_span::symbol::sym;
7577
use rustc_span::Span;
@@ -725,6 +727,10 @@ struct LivenessInfo {
725727
/// Which locals are live across any suspension point.
726728
saved_locals: CoroutineSavedLocals,
727729

730+
/// Which locals are live *and* initialized across any suspension point.
731+
/// A local that is live but is not initialized does not need to accounted in auto trait checking.
732+
init_locals: BitSet<Local>,
733+
728734
/// The set of saved locals live at each suspension point.
729735
live_locals_at_suspension_points: Vec<BitSet<CoroutineSavedLocal>>,
730736

@@ -784,10 +790,20 @@ fn locals_live_across_suspend_points<'tcx>(
784790
.iterate_to_fixpoint()
785791
.into_results_cursor(body);
786792

793+
let param_env = tcx.param_env(body.source.def_id());
794+
let move_data = MoveData::gather_moves(body, tcx, param_env, |_| true);
795+
796+
// Calculate the set of locals which are initialized
797+
let mut inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
798+
.into_engine(tcx, body)
799+
.iterate_to_fixpoint()
800+
.into_results_cursor(body);
801+
787802
let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
788803
let mut live_locals_at_suspension_points = Vec::new();
789804
let mut source_info_at_suspension_points = Vec::new();
790805
let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
806+
let mut init_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
791807

792808
for (block, data) in body.basic_blocks.iter_enumerated() {
793809
if let TerminatorKind::Yield { .. } = data.terminator().kind {
@@ -826,12 +842,26 @@ fn locals_live_across_suspend_points<'tcx>(
826842
// The coroutine argument is ignored.
827843
live_locals.remove(SELF_ARG);
828844

829-
debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
845+
inits.seek_to_block_end(block);
846+
let mut init_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len());
847+
if let MaybeReachable::Reachable(bitset) = inits.get() {
848+
for move_path_index in bitset.iter() {
849+
if let Some(local) = move_data.move_paths[move_path_index].place.as_local() {
850+
init_locals.insert(local);
851+
}
852+
}
853+
}
854+
init_locals.intersect(&live_locals);
855+
856+
debug!(
857+
"loc = {:?}, live_locals = {:?}, init_locals = {:?}",
858+
loc, live_locals, init_locals
859+
);
830860

831861
// Add the locals live at this suspension point to the set of locals which live across
832862
// any suspension points
833863
live_locals_at_any_suspension_point.union(&live_locals);
834-
864+
init_locals_at_any_suspension_point.union(&init_locals);
835865
live_locals_at_suspension_points.push(live_locals);
836866
source_info_at_suspension_points.push(data.terminator().source_info);
837867
}
@@ -856,6 +886,7 @@ fn locals_live_across_suspend_points<'tcx>(
856886

857887
LivenessInfo {
858888
saved_locals,
889+
init_locals: init_locals_at_any_suspension_point,
859890
live_locals_at_suspension_points,
860891
source_info_at_suspension_points,
861892
storage_conflicts,
@@ -1030,6 +1061,7 @@ fn compute_layout<'tcx>(
10301061
) {
10311062
let LivenessInfo {
10321063
saved_locals,
1064+
init_locals,
10331065
live_locals_at_suspension_points,
10341066
source_info_at_suspension_points,
10351067
storage_conflicts,
@@ -1046,20 +1078,26 @@ fn compute_layout<'tcx>(
10461078
let decl = &body.local_decls[local];
10471079
debug!(?decl);
10481080

1049-
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
1050-
// the information. This is alright, since `ignore_for_traits` is only relevant when
1051-
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
1052-
// default.
1053-
let ignore_for_traits = match decl.local_info {
1054-
// Do not include raw pointers created from accessing `static` items, as those could
1055-
// well be re-created by another access to the same static.
1056-
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
1057-
!is_thread_local
1081+
let ignore_for_traits = if !init_locals.contains(local) {
1082+
// If only the storage is required to be live, but local is not initialized, then we can
1083+
// ignore such type for auto trait purposes.
1084+
true
1085+
} else {
1086+
// Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared
1087+
// the information. This is alright, since `ignore_for_traits` is only relevant when
1088+
// this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
1089+
// default.
1090+
match decl.local_info {
1091+
// Do not include raw pointers created from accessing `static` items, as those could
1092+
// well be re-created by another access to the same static.
1093+
ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
1094+
!is_thread_local
1095+
}
1096+
// Fake borrows are only read by fake reads, so do not have any reality in
1097+
// post-analysis MIR.
1098+
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
1099+
_ => false,
10581100
}
1059-
// Fake borrows are only read by fake reads, so do not have any reality in
1060-
// post-analysis MIR.
1061-
ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true,
1062-
_ => false,
10631101
};
10641102
let decl =
10651103
CoroutineSavedTy { 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>`, which is required by `impl Future<Output = ()>: Send`
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)