Skip to content

Commit

Permalink
Auto merge of #128572 - compiler-errors:fix-elaborate-box-derefs-on-d…
Browse files Browse the repository at this point in the history
…ebug, 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.
  • Loading branch information
bors committed Aug 10, 2024
2 parents 04dff01 + 2e52d61 commit 730d5d4
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 8 deletions.
17 changes: 9 additions & 8 deletions compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}

20 changes: 20 additions & 0 deletions tests/mir-opt/elaborate_box_deref_in_debuginfo.rs
Original file line number Diff line number Diff line change
@@ -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() {}
35 changes: 35 additions & 0 deletions tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs
Original file line number Diff line number Diff line change
@@ -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;
});
}

0 comments on commit 730d5d4

Please sign in to comment.