summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src/elaborate_box_derefs.rs')
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs184
1 files changed, 184 insertions, 0 deletions
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
new file mode 100644
index 000000000..44e3945d6
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -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 local_decls = &mut body.local_decls;
+
+ let mut visitor =
+ ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
+
+ for (block, BasicBlockData { statements, terminator, .. }) in
+ body.basic_blocks.as_mut().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
+ }
+ }
+}