about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-10 21:24:25 +0000
committerbors <bors@rust-lang.org>2024-08-10 21:24:25 +0000
commit730d5d4095a264ef5f7c0a0781eea68c15431d45 (patch)
tree92411275dbc66551b4e6f92854b901c6f2acdc3b
parent04dff01740de7477013ef810e2639ab70042412a (diff)
parent2e52d6180724ee0cbd8b47bc7806a53bf13ab240 (diff)
downloadrust-730d5d4095a264ef5f7c0a0781eea68c15431d45.tar.gz
rust-730d5d4095a264ef5f7c0a0781eea68c15431d45.zip
Auto merge of #128572 - compiler-errors:fix-elaborate-box-derefs-on-debug, r=saethlin
Fix `ElaborateBoxDerefs` on debug varinfo

Slightly simplifies the `ElaborateBoxDerefs` pass to fix cases where it was applying the wrong projections to debug var infos containing places that deref boxes.

From what I can tell[^1], we don't actually have any tests (or code anywhere, really) that exercise `debug x => *(...: Box<T>)`, and it's very difficult to trigger this in surface Rust, so I wrote a custom MIR test.

What happens is that the pass was turning `*(SOME_PLACE: Box<T>)` into `*(*((((SOME_PLACE).0: Unique<T>).0: NonNull<T>).0: *const T))` in debug var infos. In particular, notice the *double deref*, which was wrong.

This is the root cause of #128554, so this PR fixes #128554 as well. The reason that async closures was affected is because of the way that we compute the [`ByMove` body](https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs), which resulted in `*(...: Box<T>)` in debug var info. But this really has nothing to do with async closures.

[^1]: Validated by literally replacing the `if elem == PlaceElem::Deref && base_ty.is_box() { ... }` innards with a `panic!()`, which compiled all of stage2 without panicking.
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs17
-rw-r--r--tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff13
-rw-r--r--tests/mir-opt/elaborate_box_deref_in_debuginfo.rs20
-rw-r--r--tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs35
4 files changed, 77 insertions, 8 deletions
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index d955b96d06a..e5778f8a05d 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -116,29 +116,30 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
             for debug_info in body.var_debug_info.iter_mut() {
                 if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
                     let mut new_projections: Option<Vec<_>> = None;
-                    let mut last_deref = 0;
 
-                    for (i, (base, elem)) in place.iter_projections().enumerate() {
+                    for (base, elem) in place.iter_projections() {
                         let base_ty = base.ty(&body.local_decls, tcx).ty;
 
                         if elem == PlaceElem::Deref && base_ty.is_box() {
-                            let new_projections = new_projections.get_or_insert_default();
+                            // Clone the projections before us, since now we need to mutate them.
+                            let new_projections =
+                                new_projections.get_or_insert_with(|| base.projection.to_vec());
 
                             let (unique_ty, nonnull_ty, ptr_ty) =
                                 build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
 
-                            new_projections.extend_from_slice(&base.projection[last_deref..]);
                             new_projections.extend_from_slice(&build_projection(
                                 unique_ty, nonnull_ty, ptr_ty,
                             ));
                             new_projections.push(PlaceElem::Deref);
-
-                            last_deref = i;
+                        } else if let Some(new_projections) = new_projections.as_mut() {
+                            // Keep building up our projections list once we've started it.
+                            new_projections.push(elem);
                         }
                     }
 
-                    if let Some(mut new_projections) = new_projections {
-                        new_projections.extend_from_slice(&place.projection[last_deref..]);
+                    // Store the mutated projections if we actually changed something.
+                    if let Some(new_projections) = new_projections {
                         place.projection = tcx.mk_place_elems(&new_projections);
                     }
                 }
diff --git a/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
new file mode 100644
index 00000000000..279c1a1990d
--- /dev/null
+++ b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
@@ -0,0 +1,13 @@
+- // MIR for `pointee` before ElaborateBoxDerefs
++ // MIR for `pointee` after ElaborateBoxDerefs
+  
+  fn pointee(_1: Box<i32>) -> () {
+-     debug foo => (*_1);
++     debug foo => (*(((_1.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32));
+      let mut _0: ();
+  
+      bb0: {
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs b/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs
new file mode 100644
index 00000000000..0046e7104f1
--- /dev/null
+++ b/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs
@@ -0,0 +1,20 @@
+// skip-filecheck
+//@ test-mir-pass: ElaborateBoxDerefs
+
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+// EMIT_MIR elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff
+#[custom_mir(dialect = "built")]
+fn pointee(opt: Box<i32>) {
+    mir!(
+        debug foo => *opt;
+        {
+            Return()
+        }
+    )
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs b/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs
new file mode 100644
index 00000000000..8b2de578b24
--- /dev/null
+++ b/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs
@@ -0,0 +1,35 @@
+//@ aux-build:block-on.rs
+//@ edition:2021
+//@ run-pass
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+pub trait Trait {
+    fn callback(&mut self);
+}
+impl Trait for (i32,) {
+    fn callback(&mut self) {
+        println!("hi {}", self.0);
+        self.0 += 1;
+    }
+}
+
+async fn call_once(f: impl async FnOnce()) {
+    f().await;
+}
+
+async fn run(mut loader: Box<dyn Trait>) {
+    let f = async move || {
+        loader.callback();
+        loader.callback();
+    };
+    call_once(f).await;
+}
+
+fn main() {
+    block_on::block_on(async {
+        run(Box::new((42,))).await;
+    });
+}