Auto merge of #95576 - DrMeepster:box_erasure, r=oli-obk
Remove dereferencing of Box from codegen Through #94043, #94414, #94873, and #95328, I've been fixing issues caused by Box being treated like a pointer when it is not a pointer. However, these PRs just introduced special cases for Box. This PR removes those special cases and instead transforms a deref of Box into a deref of the pointer it contains. Hopefully, this is the end of the Box<T, A> ICEs.
This commit is contained in:
commit
a25b1315ee
18 changed files with 477 additions and 162 deletions
184
compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Normal file
184
compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
//! This pass transforms derefs of Box into a deref of the pointer inside Box.
|
||||
//!
|
||||
//! Box is not actually a pointer so it is incorrect to dereference it directly.
|
||||
|
||||
use crate::MirPass;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_middle::mir::visit::MutVisitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::subst::Subst;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
|
||||
/// Constructs the types used when accessing a Box's pointer
|
||||
pub fn build_ptr_tys<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pointee: Ty<'tcx>,
|
||||
unique_did: DefId,
|
||||
nonnull_did: DefId,
|
||||
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
|
||||
let substs = tcx.intern_substs(&[pointee.into()]);
|
||||
let unique_ty = tcx.bound_type_of(unique_did).subst(tcx, substs);
|
||||
let nonnull_ty = tcx.bound_type_of(nonnull_did).subst(tcx, substs);
|
||||
let ptr_ty = tcx.mk_imm_ptr(pointee);
|
||||
|
||||
(unique_ty, nonnull_ty, ptr_ty)
|
||||
}
|
||||
|
||||
// Constructs the projection needed to access a Box's pointer
|
||||
pub fn build_projection<'tcx>(
|
||||
unique_ty: Ty<'tcx>,
|
||||
nonnull_ty: Ty<'tcx>,
|
||||
ptr_ty: Ty<'tcx>,
|
||||
) -> [PlaceElem<'tcx>; 3] {
|
||||
[
|
||||
PlaceElem::Field(Field::new(0), unique_ty),
|
||||
PlaceElem::Field(Field::new(0), nonnull_ty),
|
||||
PlaceElem::Field(Field::new(0), ptr_ty),
|
||||
]
|
||||
}
|
||||
|
||||
struct ElaborateBoxDerefVisitor<'tcx, 'a> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
unique_did: DefId,
|
||||
nonnull_did: DefId,
|
||||
local_decls: &'a mut LocalDecls<'tcx>,
|
||||
patch: MirPatch<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn visit_place(
|
||||
&mut self,
|
||||
place: &mut Place<'tcx>,
|
||||
context: visit::PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let base_ty = self.local_decls[place.local].ty;
|
||||
|
||||
// Derefer ensures that derefs are always the first projection
|
||||
if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
|
||||
let source_info = self.local_decls[place.local].source_info;
|
||||
|
||||
let (unique_ty, nonnull_ty, ptr_ty) =
|
||||
build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);
|
||||
|
||||
let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
|
||||
self.local_decls.push(LocalDecl::new(ptr_ty, source_info.span));
|
||||
|
||||
self.patch.add_statement(location, StatementKind::StorageLive(ptr_local));
|
||||
|
||||
self.patch.add_assign(
|
||||
location,
|
||||
Place::from(ptr_local),
|
||||
Rvalue::Use(Operand::Copy(
|
||||
Place::from(place.local)
|
||||
.project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx),
|
||||
)),
|
||||
);
|
||||
|
||||
place.local = ptr_local;
|
||||
|
||||
self.patch.add_statement(
|
||||
Location { block: location.block, statement_index: location.statement_index + 1 },
|
||||
StatementKind::StorageDead(ptr_local),
|
||||
);
|
||||
}
|
||||
|
||||
self.super_place(place, context, location);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElaborateBoxDerefs;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
if let Some(def_id) = tcx.lang_items().owned_box() {
|
||||
let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
|
||||
|
||||
let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
|
||||
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
|
||||
};
|
||||
|
||||
let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
|
||||
|
||||
let patch = MirPatch::new(body);
|
||||
|
||||
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
|
||||
let mut visitor =
|
||||
ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
|
||||
|
||||
for (block, BasicBlockData { statements, terminator, .. }) in
|
||||
basic_blocks.iter_enumerated_mut()
|
||||
{
|
||||
let mut index = 0;
|
||||
for statement in statements {
|
||||
let location = Location { block, statement_index: index };
|
||||
visitor.visit_statement(statement, location);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if let Some(terminator) = terminator
|
||||
&& !matches!(terminator.kind, TerminatorKind::Yield{..})
|
||||
{
|
||||
let location = Location { block, statement_index: index };
|
||||
visitor.visit_terminator(terminator, location);
|
||||
}
|
||||
|
||||
let location = Location { block, statement_index: index };
|
||||
match terminator {
|
||||
// yielding into a box is handled when lowering generators
|
||||
Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
|
||||
visitor.visit_operand(value, location);
|
||||
}
|
||||
Some(terminator) => {
|
||||
visitor.visit_terminator(terminator, location);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
visitor.patch.apply(body);
|
||||
|
||||
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() {
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut new_projections) = new_projections {
|
||||
new_projections.extend_from_slice(&place.projection[last_deref..]);
|
||||
place.projection = tcx.intern_place_elems(&new_projections);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// box is not present, this pass doesn't need to do anything
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ use crate::MirPass;
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_index::bit_set::{BitMatrix, BitSet};
|
||||
use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet};
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc_middle::mir::dump_mir;
|
||||
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
||||
|
@ -211,7 +211,7 @@ struct SuspensionPoint<'tcx> {
|
|||
/// Which block to jump to if the generator is dropped in this state.
|
||||
drop: Option<BasicBlock>,
|
||||
/// Set of locals that have live storage while at this suspension point.
|
||||
storage_liveness: BitSet<Local>,
|
||||
storage_liveness: GrowableBitSet<Local>,
|
||||
}
|
||||
|
||||
struct TransformVisitor<'tcx> {
|
||||
|
@ -367,7 +367,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
|
|||
resume,
|
||||
resume_arg,
|
||||
drop,
|
||||
storage_liveness: self.storage_liveness[block].clone().unwrap(),
|
||||
storage_liveness: self.storage_liveness[block].clone().unwrap().into(),
|
||||
});
|
||||
|
||||
VariantIdx::new(state)
|
||||
|
@ -1182,6 +1182,8 @@ fn create_cases<'tcx>(
|
|||
transform: &TransformVisitor<'tcx>,
|
||||
operation: Operation,
|
||||
) -> Vec<(usize, BasicBlock)> {
|
||||
let tcx = transform.tcx;
|
||||
|
||||
let source_info = SourceInfo::outermost(body.span);
|
||||
|
||||
transform
|
||||
|
@ -1214,13 +1216,85 @@ fn create_cases<'tcx>(
|
|||
if operation == Operation::Resume {
|
||||
// Move the resume argument to the destination place of the `Yield` terminator
|
||||
let resume_arg = Local::new(2); // 0 = return, 1 = self
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
point.resume_arg,
|
||||
Rvalue::Use(Operand::Move(resume_arg.into())),
|
||||
))),
|
||||
});
|
||||
|
||||
// handle `box yield` properly
|
||||
let box_place = if let [projection @ .., ProjectionElem::Deref] =
|
||||
&**point.resume_arg.projection
|
||||
{
|
||||
let box_place =
|
||||
Place::from(point.resume_arg.local).project_deeper(projection, tcx);
|
||||
|
||||
let box_ty = box_place.ty(&body.local_decls, tcx).ty;
|
||||
|
||||
if box_ty.is_box() { Some((box_place, box_ty)) } else { None }
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some((box_place, box_ty)) = box_place {
|
||||
let unique_did = box_ty
|
||||
.ty_adt_def()
|
||||
.expect("expected Box to be an Adt")
|
||||
.non_enum_variant()
|
||||
.fields[0]
|
||||
.did;
|
||||
|
||||
let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
|
||||
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
|
||||
};
|
||||
|
||||
let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
|
||||
|
||||
let (unique_ty, nonnull_ty, ptr_ty) =
|
||||
crate::elaborate_box_derefs::build_ptr_tys(
|
||||
tcx,
|
||||
box_ty.boxed_ty(),
|
||||
unique_did,
|
||||
nonnull_did,
|
||||
);
|
||||
|
||||
let ptr_local = body.local_decls.push(LocalDecl::new(ptr_ty, body.span));
|
||||
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::StorageLive(ptr_local),
|
||||
});
|
||||
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::from(ptr_local),
|
||||
Rvalue::Use(Operand::Copy(box_place.project_deeper(
|
||||
&crate::elaborate_box_derefs::build_projection(
|
||||
unique_ty, nonnull_ty, ptr_ty,
|
||||
),
|
||||
tcx,
|
||||
))),
|
||||
))),
|
||||
});
|
||||
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::from(ptr_local)
|
||||
.project_deeper(&[ProjectionElem::Deref], tcx),
|
||||
Rvalue::Use(Operand::Move(resume_arg.into())),
|
||||
))),
|
||||
});
|
||||
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::StorageDead(ptr_local),
|
||||
});
|
||||
} else {
|
||||
statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
point.resume_arg,
|
||||
Rvalue::Use(Operand::Move(resume_arg.into())),
|
||||
))),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Then jump to the real target
|
||||
|
|
|
@ -57,6 +57,7 @@ mod deref_separator;
|
|||
mod dest_prop;
|
||||
pub mod dump_mir;
|
||||
mod early_otherwise_branch;
|
||||
mod elaborate_box_derefs;
|
||||
mod elaborate_drops;
|
||||
mod function_item_references;
|
||||
mod generator;
|
||||
|
@ -427,6 +428,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
|
|||
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
|
||||
// but before optimizations begin.
|
||||
&deref_separator::Derefer,
|
||||
&elaborate_box_derefs::ElaborateBoxDerefs,
|
||||
&add_retag::AddRetag,
|
||||
&lower_intrinsics::LowerIntrinsics,
|
||||
&simplify::SimplifyCfg::new("elaborate-drops"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue