From a1f5ac6997269576f93a043fefdabf57d83e84ae Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
Date: Sun, 24 Sep 2023 04:58:50 +0100
Subject: [PATCH 1/2] Remove drop-track-field-assign[-nonsend] test

These tests are identical to the ones without drop-track prefix, so
remove.
---
 .../drop-track-field-assign-nonsend.rs        | 44 -------------------
 .../drop-track-field-assign-nonsend.stderr    | 23 ----------
 .../ui/async-await/drop-track-field-assign.rs | 43 ------------------
 3 files changed, 110 deletions(-)
 delete mode 100644 tests/ui/async-await/drop-track-field-assign-nonsend.rs
 delete mode 100644 tests/ui/async-await/drop-track-field-assign-nonsend.stderr
 delete mode 100644 tests/ui/async-await/drop-track-field-assign.rs

diff --git a/tests/ui/async-await/drop-track-field-assign-nonsend.rs b/tests/ui/async-await/drop-track-field-assign-nonsend.rs
deleted file mode 100644
index 7002836ee47ff..0000000000000
--- a/tests/ui/async-await/drop-track-field-assign-nonsend.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Derived from an ICE found in tokio-xmpp during a crater run.
-//@ edition:2021
-
-#![allow(dead_code)]
-
-#[derive(Clone)]
-struct InfoResult {
-    node: Option<std::rc::Rc<String>>
-}
-
-struct Agent {
-    info_result: InfoResult
-}
-
-impl Agent {
-    async fn handle(&mut self) {
-        let mut info = self.info_result.clone();
-        info.node = None;
-        let element = parse_info(info);
-        let _ = send_element(element).await;
-    }
-}
-
-struct Element {
-}
-
-async fn send_element(_: Element) {}
-
-fn parse(_: &[u8]) -> Result<(), ()> {
-    Ok(())
-}
-
-fn parse_info(_: InfoResult) -> Element {
-    Element { }
-}
-
-fn assert_send<T: Send>(_: T) {}
-
-fn main() {
-    let agent = Agent { info_result: InfoResult { node: None } };
-    // FIXME: It would be nice for this to work. See #94067.
-    assert_send(agent.handle());
-    //~^ cannot be sent between threads safely
-}
diff --git a/tests/ui/async-await/drop-track-field-assign-nonsend.stderr b/tests/ui/async-await/drop-track-field-assign-nonsend.stderr
deleted file mode 100644
index 9fce4d61b3b6f..0000000000000
--- a/tests/ui/async-await/drop-track-field-assign-nonsend.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error: future cannot be sent between threads safely
-  --> $DIR/drop-track-field-assign-nonsend.rs:42:17
-   |
-LL |     assert_send(agent.handle());
-   |                 ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
-   |
-   = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
-note: future is not `Send` as this value is used across an await
-  --> $DIR/drop-track-field-assign-nonsend.rs:20:39
-   |
-LL |         let mut info = self.info_result.clone();
-   |             -------- has type `InfoResult` which is not `Send`
-...
-LL |         let _ = send_element(element).await;
-   |                                       ^^^^^ await occurs here, with `mut info` maybe used later
-note: required by a bound in `assert_send`
-  --> $DIR/drop-track-field-assign-nonsend.rs:37:19
-   |
-LL | fn assert_send<T: Send>(_: T) {}
-   |                   ^^^^ required by this bound in `assert_send`
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/async-await/drop-track-field-assign.rs b/tests/ui/async-await/drop-track-field-assign.rs
deleted file mode 100644
index 491f80d062bbb..0000000000000
--- a/tests/ui/async-await/drop-track-field-assign.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Derived from an ICE found in tokio-xmpp during a crater run.
-//@ edition:2021
-//@ build-pass
-
-#![allow(dead_code)]
-
-#[derive(Clone)]
-struct InfoResult {
-    node: Option<String>
-}
-
-struct Agent {
-    info_result: InfoResult
-}
-
-impl Agent {
-    async fn handle(&mut self) {
-        let mut info = self.info_result.clone();
-        info.node = Some("bar".into());
-        let element = parse_info(info);
-        send_element(element).await;
-    }
-}
-
-struct Element {
-}
-
-async fn send_element(_: Element) {}
-
-fn parse(_: &[u8]) -> Result<(), ()> {
-    Ok(())
-}
-
-fn parse_info(_: InfoResult) -> Element {
-    Element { }
-}
-
-fn main() {
-    let mut agent = Agent {
-        info_result: InfoResult { node: None }
-    };
-    let _ = agent.handle();
-}

From 197ad7537f24ff3441d6866d4f25b837e4708572 Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
Date: Sun, 4 Jun 2023 16:50:59 +0100
Subject: [PATCH 2/2] Stop considering moved-out locals when computing auto
 traits for generators

---
 compiler/rustc_mir_transform/src/coroutine.rs | 44 ++++++++++++++++---
 .../async-await/field-assign-nonsend.stderr   | 11 ++---
 tests/ui/async-await/temp-borrow-nonsend.rs   | 23 ++++++++++
 3 files changed, 66 insertions(+), 12 deletions(-)
 create mode 100644 tests/ui/async-await/temp-borrow-nonsend.rs

diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 04d96f117072f..3ebc90632ee1d 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -69,10 +69,11 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::impls::{
-    MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
-    always_storage_live_locals,
+    MaybeBorrowedLocals, MaybeInitializedPlaces, MaybeLiveLocals, MaybeRequiresStorage,
+    MaybeStorageLive, always_storage_live_locals,
 };
-use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
+use rustc_mir_dataflow::move_paths::MoveData;
+use rustc_mir_dataflow::{self, Analysis, MaybeReachable, Results, ResultsVisitor};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{Span, sym};
 use rustc_target::spec::PanicStrategy;
@@ -633,6 +634,12 @@ struct LivenessInfo {
     /// Which locals are live across any suspension point.
     saved_locals: CoroutineSavedLocals,
 
+    /// Which locals are live *and* initialized across any suspension point.
+    ///
+    /// A local that is live but is not initialized (i.e. it has been moved out of) does
+    /// not need to be accounted for in auto trait checking.
+    init_locals: DenseBitSet<Local>,
+
     /// The set of saved locals live at each suspension point.
     live_locals_at_suspension_points: Vec<DenseBitSet<CoroutineSavedLocal>>,
 
@@ -686,10 +693,18 @@ fn locals_live_across_suspend_points<'tcx>(
     let mut liveness =
         MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")).into_results_cursor(body);
 
+    let move_data = MoveData::gather_moves(body, tcx, |_| true);
+
+    // Calculate the set of locals which are initialized
+    let mut inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
+        .iterate_to_fixpoint(tcx, body, Some("coroutine"))
+        .into_results_cursor(body);
+
     let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
     let mut live_locals_at_suspension_points = Vec::new();
     let mut source_info_at_suspension_points = Vec::new();
     let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
+    let mut init_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len());
 
     for (block, data) in body.basic_blocks.iter_enumerated() {
         if let TerminatorKind::Yield { .. } = data.terminator().kind {
@@ -727,12 +742,26 @@ fn locals_live_across_suspend_points<'tcx>(
             // The coroutine argument is ignored.
             live_locals.remove(SELF_ARG);
 
-            debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
+            inits.seek_to_block_end(block);
+            let mut init_locals: DenseBitSet<_> = DenseBitSet::new_empty(body.local_decls.len());
+            if let MaybeReachable::Reachable(bitset) = inits.get() {
+                for move_path_index in bitset.iter() {
+                    if let Some(local) = move_data.move_paths[move_path_index].place.as_local() {
+                        init_locals.insert(local);
+                    }
+                }
+            }
+            init_locals.intersect(&live_locals);
+
+            debug!(
+                "loc = {:?}, live_locals = {:?}, init_locals = {:?}",
+                loc, live_locals, init_locals
+            );
 
             // Add the locals live at this suspension point to the set of locals which live across
             // any suspension points
             live_locals_at_any_suspension_point.union(&live_locals);
-
+            init_locals_at_any_suspension_point.union(&init_locals);
             live_locals_at_suspension_points.push(live_locals);
             source_info_at_suspension_points.push(data.terminator().source_info);
         }
@@ -757,6 +786,7 @@ fn locals_live_across_suspend_points<'tcx>(
 
     LivenessInfo {
         saved_locals,
+        init_locals: init_locals_at_any_suspension_point,
         live_locals_at_suspension_points,
         source_info_at_suspension_points,
         storage_conflicts,
@@ -929,6 +959,7 @@ fn compute_layout<'tcx>(
 ) {
     let LivenessInfo {
         saved_locals,
+        init_locals,
         live_locals_at_suspension_points,
         source_info_at_suspension_points,
         storage_conflicts,
@@ -950,6 +981,9 @@ fn compute_layout<'tcx>(
         // this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer
         // default.
         let ignore_for_traits = match decl.local_info {
+            // If only the storage is required to be live, but local is not initialized,
+            // then we can ignore such type for auto trait purposes.
+            _ if !init_locals.contains(local) => true,
             // Do not include raw pointers created from accessing `static` items, as those could
             // well be re-created by another access to the same static.
             ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => {
diff --git a/tests/ui/async-await/field-assign-nonsend.stderr b/tests/ui/async-await/field-assign-nonsend.stderr
index 418a0829c657e..9fec60618b8e2 100644
--- a/tests/ui/async-await/field-assign-nonsend.stderr
+++ b/tests/ui/async-await/field-assign-nonsend.stderr
@@ -5,14 +5,11 @@ LL |     assert_send(agent.handle());
    |                 ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
    |
    = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
-note: future is not `Send` as this value is used across an await
-  --> $DIR/field-assign-nonsend.rs:20:39
+note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
+  --> $DIR/field-assign-nonsend.rs:16:21
    |
-LL |         let mut info = self.info_result.clone();
-   |             -------- has type `InfoResult` which is not `Send`
-...
-LL |         let _ = send_element(element).await;
-   |                                       ^^^^^ await occurs here, with `mut info` maybe used later
+LL |     async fn handle(&mut self) {
+   |                     ^^^^^^^^^ has type `&mut Agent` which is not `Send`, because `Agent` is not `Send`
 note: required by a bound in `assert_send`
   --> $DIR/field-assign-nonsend.rs:37:19
    |
diff --git a/tests/ui/async-await/temp-borrow-nonsend.rs b/tests/ui/async-await/temp-borrow-nonsend.rs
new file mode 100644
index 0000000000000..d5f573347fcec
--- /dev/null
+++ b/tests/ui/async-await/temp-borrow-nonsend.rs
@@ -0,0 +1,23 @@
+//@ check-pass
+//@ edition:2021
+
+use core::marker::PhantomData;
+
+struct B(PhantomData<*const ()>);
+
+fn do_sth(_: &B) {}
+
+async fn foo() {}
+
+async fn test() {
+    let b = B(PhantomData);
+    do_sth(&b);
+    drop(b);
+    foo().await;
+}
+
+fn assert_send<T: Send>(_: T) {}
+
+fn main() {
+    assert_send(test());
+}