Skip to content

Commit f5e2df7

Browse files
committed
Auto merge of #147687 - cjgillot:noshallow-init-box, r=nnethercote
Forbid ShallowInitBox after box deref elaboration. MIR currently contains a `ShallowInitBox` rvalue. Its principal usage is to allow for in-place initialization of boxes. Having it is necessary for drop elaboration to be correct with that in-place initialization. As part of analysis->runtime MIR lowering, we canonicalize deref of boxes to use the stored raw pointer. But we did not perform the same change to the construction of the box. This PR replaces `ShallowInitBox` by the pointer manipulation it represents. Alternatives: - fully remove `ShallowInitBox` and implement `Box` in-place initialization differently; - remove the `ElaborateBoxDeref` pass and keep dereferencing `Box` in runtime MIR.
2 parents b2ee1b3 + 0156eaf commit f5e2df7

File tree

13 files changed

+271
-154
lines changed

13 files changed

+271
-154
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -829,13 +829,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
829829
fx.bcx.ins().nop();
830830
}
831831
}
832-
Rvalue::ShallowInitBox(ref operand, content_ty) => {
833-
let content_ty = fx.monomorphize(content_ty);
834-
let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty));
835-
let operand = codegen_operand(fx, operand);
836-
let operand = operand.load_scalar(fx);
837-
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
838-
}
839832
Rvalue::NullaryOp(ref null_op, ty) => {
840833
assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env()));
841834
let layout = fx.layout_of(fx.monomorphize(ty));
@@ -924,6 +917,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
924917
lval.write_cvalue_transmute(fx, operand);
925918
}
926919
Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"),
920+
Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"),
927921
}
928922
}
929923
StatementKind::StorageLive(_)

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -724,22 +724,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
724724
}
725725
}
726726
}
727-
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
728-
let operand = self.codegen_operand(bx, operand);
729-
let val = operand.immediate();
730-
731-
let content_ty = self.monomorphize(content_ty);
732-
let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty));
733-
734-
OperandRef { val: OperandValue::Immediate(val), layout: box_layout }
735-
}
736727
mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => {
737728
let operand = self.codegen_operand(bx, operand);
738729
let binder_ty = self.monomorphize(binder_ty);
739730
let layout = bx.cx().layout_of(binder_ty);
740731
OperandRef { val: operand.val, layout }
741732
}
742733
mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"),
734+
mir::Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in codegen"),
743735
}
744736
}
745737

compiler/rustc_index/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,13 @@ macro_rules! static_assert_size {
5050
const _: (usize, usize) = ($size, ::std::mem::size_of::<$ty>());
5151
};
5252
}
53+
54+
#[macro_export]
55+
macro_rules! indexvec {
56+
($expr:expr; $n:expr) => {
57+
IndexVec::from_raw(vec![$expr; $n])
58+
};
59+
($($expr:expr),* $(,)?) => {
60+
IndexVec::from_raw(vec![$($expr),*])
61+
};
62+
}

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use rustc_hir as hir;
6767
use rustc_hir::lang_items::LangItem;
6868
use rustc_hir::{CoroutineDesugaring, CoroutineKind};
6969
use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet};
70-
use rustc_index::{Idx, IndexVec};
70+
use rustc_index::{Idx, IndexVec, indexvec};
7171
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
7272
use rustc_middle::mir::*;
7373
use rustc_middle::ty::util::Discr;
@@ -289,7 +289,7 @@ impl<'tcx> TransformVisitor<'tcx> {
289289
let poll_def_id = self.tcx.require_lang_item(LangItem::Poll, source_info.span);
290290
let args = self.tcx.mk_args(&[self.old_ret_ty.into()]);
291291
let (variant_idx, operands) = if is_return {
292-
(ZERO, IndexVec::from_raw(vec![val])) // Poll::Ready(val)
292+
(ZERO, indexvec![val]) // Poll::Ready(val)
293293
} else {
294294
(ONE, IndexVec::new()) // Poll::Pending
295295
};
@@ -301,7 +301,7 @@ impl<'tcx> TransformVisitor<'tcx> {
301301
let (variant_idx, operands) = if is_return {
302302
(ZERO, IndexVec::new()) // None
303303
} else {
304-
(ONE, IndexVec::from_raw(vec![val])) // Some(val)
304+
(ONE, indexvec![val]) // Some(val)
305305
};
306306
make_aggregate_adt(option_def_id, variant_idx, args, operands)
307307
}
@@ -337,12 +337,7 @@ impl<'tcx> TransformVisitor<'tcx> {
337337
} else {
338338
ZERO // CoroutineState::Yielded(val)
339339
};
340-
make_aggregate_adt(
341-
coroutine_state_def_id,
342-
variant_idx,
343-
args,
344-
IndexVec::from_raw(vec![val]),
345-
)
340+
make_aggregate_adt(coroutine_state_def_id, variant_idx, args, indexvec![val])
346341
}
347342
};
348343

@@ -1122,7 +1117,7 @@ fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) ->
11221117
}));
11231118
let ready_val = Rvalue::Aggregate(
11241119
Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)),
1125-
IndexVec::from_raw(vec![val]),
1120+
indexvec![val],
11261121
);
11271122
Statement::new(source_info, StatementKind::Assign(Box::new((Place::return_place(), ready_val))))
11281123
}

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
480480
Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map),
481481
Rvalue::Use(operand) => return self.handle_operand(operand, state),
482482
Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"),
483+
Rvalue::ShallowInitBox(..) => bug!("`ShallowInitBox` in runtime MIR"),
483484
Rvalue::Ref(..) | Rvalue::RawPtr(..) => {
484485
// We don't track such places.
485486
return ValueOrPlace::TOP;
@@ -489,7 +490,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
489490
| Rvalue::Cast(..)
490491
| Rvalue::BinaryOp(..)
491492
| Rvalue::Aggregate(..)
492-
| Rvalue::ShallowInitBox(..)
493493
| Rvalue::WrapUnsafeBinder(..) => {
494494
// No modification is possible through these r-values.
495495
return ValueOrPlace::TOP;

compiler/rustc_mir_transform/src/elaborate_box_derefs.rs

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
//! This pass transforms derefs of Box into a deref of the pointer inside Box.
22
//!
33
//! Box is not actually a pointer so it is incorrect to dereference it directly.
4+
//!
5+
//! `ShallowInitBox` being a device for drop elaboration to understand deferred assignment to box
6+
//! contents, we do not need this any more on runtime MIR.
47
5-
use rustc_abi::FieldIdx;
6-
use rustc_hir::def_id::DefId;
8+
use rustc_abi::{FieldIdx, VariantIdx};
9+
use rustc_index::{IndexVec, indexvec};
710
use rustc_middle::mir::visit::MutVisitor;
811
use rustc_middle::mir::*;
912
use rustc_middle::span_bug;
10-
use rustc_middle::ty::{Ty, TyCtxt};
13+
use rustc_middle::ty::{self, Ty, TyCtxt};
1114

1215
use crate::patch::MirPatch;
1316

1417
/// Constructs the types used when accessing a Box's pointer
1518
fn build_ptr_tys<'tcx>(
1619
tcx: TyCtxt<'tcx>,
1720
pointee: Ty<'tcx>,
18-
unique_did: DefId,
19-
nonnull_did: DefId,
21+
unique_def: ty::AdtDef<'tcx>,
22+
nonnull_def: ty::AdtDef<'tcx>,
2023
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
2124
let args = tcx.mk_args(&[pointee.into()]);
22-
let unique_ty = tcx.type_of(unique_did).instantiate(tcx, args);
23-
let nonnull_ty = tcx.type_of(nonnull_did).instantiate(tcx, args);
25+
let unique_ty = Ty::new_adt(tcx, unique_def, args);
26+
let nonnull_ty = Ty::new_adt(tcx, nonnull_def, args);
2427
let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
2528

2629
(unique_ty, nonnull_ty, ptr_ty)
@@ -36,8 +39,8 @@ pub(super) fn build_projection<'tcx>(
3639

3740
struct ElaborateBoxDerefVisitor<'a, 'tcx> {
3841
tcx: TyCtxt<'tcx>,
39-
unique_did: DefId,
40-
nonnull_did: DefId,
42+
unique_def: ty::AdtDef<'tcx>,
43+
nonnull_def: ty::AdtDef<'tcx>,
4144
local_decls: &'a mut LocalDecls<'tcx>,
4245
patch: MirPatch<'tcx>,
4346
}
@@ -64,7 +67,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
6467
let source_info = self.local_decls[place.local].source_info;
6568

6669
let (unique_ty, nonnull_ty, ptr_ty) =
67-
build_ptr_tys(tcx, boxed_ty, self.unique_did, self.nonnull_did);
70+
build_ptr_tys(tcx, boxed_ty, self.unique_def, self.nonnull_def);
6871

6972
let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
7073

@@ -86,6 +89,68 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
8689

8790
self.super_place(place, context, location);
8891
}
92+
93+
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
94+
self.super_statement(stmt, location);
95+
96+
let tcx = self.tcx;
97+
let source_info = stmt.source_info;
98+
99+
if let StatementKind::Assign(box (_, ref mut rvalue)) = stmt.kind
100+
&& let Rvalue::ShallowInitBox(ref mut mutptr_to_u8, pointee) = *rvalue
101+
&& let ty::Adt(box_adt, box_args) = Ty::new_box(tcx, pointee).kind()
102+
{
103+
let args = tcx.mk_args(&[pointee.into()]);
104+
let (unique_ty, nonnull_ty, ptr_ty) =
105+
build_ptr_tys(tcx, pointee, self.unique_def, self.nonnull_def);
106+
let adt_kind = |def: ty::AdtDef<'tcx>, args| {
107+
Box::new(AggregateKind::Adt(def.did(), VariantIdx::ZERO, args, None, None))
108+
};
109+
let zst = |ty| {
110+
Operand::Constant(Box::new(ConstOperand {
111+
span: source_info.span,
112+
user_ty: None,
113+
const_: Const::zero_sized(ty),
114+
}))
115+
};
116+
117+
let constptr = self.patch.new_temp(ptr_ty, source_info.span);
118+
self.patch.add_assign(
119+
location,
120+
constptr.into(),
121+
Rvalue::Cast(CastKind::Transmute, mutptr_to_u8.clone(), ptr_ty),
122+
);
123+
124+
let nonnull = self.patch.new_temp(nonnull_ty, source_info.span);
125+
self.patch.add_assign(
126+
location,
127+
nonnull.into(),
128+
Rvalue::Aggregate(
129+
adt_kind(self.nonnull_def, args),
130+
indexvec![Operand::Move(constptr.into())],
131+
),
132+
);
133+
134+
let unique = self.patch.new_temp(unique_ty, source_info.span);
135+
let phantomdata_ty =
136+
self.unique_def.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, args);
137+
self.patch.add_assign(
138+
location,
139+
unique.into(),
140+
Rvalue::Aggregate(
141+
adt_kind(self.unique_def, args),
142+
indexvec![Operand::Move(nonnull.into()), zst(phantomdata_ty)],
143+
),
144+
);
145+
146+
let global_alloc_ty =
147+
box_adt.non_enum_variant().fields[FieldIdx::ONE].ty(tcx, box_args);
148+
*rvalue = Rvalue::Aggregate(
149+
adt_kind(*box_adt, box_args),
150+
indexvec![Operand::Move(unique.into()), zst(global_alloc_ty)],
151+
);
152+
}
153+
}
89154
}
90155

91156
pub(super) struct ElaborateBoxDerefs;
@@ -97,18 +162,22 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
97162

98163
let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did;
99164

100-
let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else {
165+
let Some(unique_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else {
101166
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
102167
};
103168

104-
let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did;
169+
let nonnull_did = unique_def.non_enum_variant().fields[FieldIdx::ZERO].did;
170+
171+
let Some(nonnull_def) = tcx.type_of(nonnull_did).instantiate_identity().ty_adt_def() else {
172+
span_bug!(tcx.def_span(nonnull_did), "expected Unique to contain Nonnull")
173+
};
105174

106175
let patch = MirPatch::new(body);
107176

108177
let local_decls = &mut body.local_decls;
109178

110179
let mut visitor =
111-
ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
180+
ElaborateBoxDerefVisitor { tcx, unique_def, nonnull_def, local_decls, patch };
112181

113182
for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
114183
visitor.visit_basic_block_data(block, data);
@@ -131,7 +200,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
131200
new_projections.get_or_insert_with(|| base.projection.to_vec());
132201

133202
let (unique_ty, nonnull_ty, ptr_ty) =
134-
build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
203+
build_ptr_tys(tcx, boxed_ty, unique_def, nonnull_def);
135204

136205
new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
137206
// While we can't project into `NonNull<_>` in a basic block

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,8 +1073,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10731073
}
10741074

10751075
// Unsupported values.
1076-
Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None,
1077-
Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"),
1076+
Rvalue::ThreadLocalRef(..) => return None,
1077+
Rvalue::CopyForDeref(_) | Rvalue::ShallowInitBox(..) => {
1078+
bug!("forbidden in runtime MIR: {rvalue:?}")
1079+
}
10781080
};
10791081
let ty = rvalue.ty(self.local_decls, self.tcx);
10801082
Some(self.insert(ty, value))

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
656656
) {
657657
match elem {
658658
ProjectionElem::Deref
659-
if self.body.phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
659+
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
660660
{
661661
let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty;
662662

@@ -1049,7 +1049,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
10491049
assert!(!adt_def.is_union());
10501050
let variant = &adt_def.variants()[idx];
10511051
if variant.fields.len() != fields.len() {
1052-
self.fail(location, "adt has the wrong number of initialized fields");
1052+
self.fail(location, format!(
1053+
"adt {def_id:?} has the wrong number of initialized fields, expected {}, found {}",
1054+
fields.len(),
1055+
variant.fields.len(),
1056+
));
10531057
}
10541058
for (src, dest) in std::iter::zip(fields, &variant.fields) {
10551059
let dest_ty = self
@@ -1252,6 +1256,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
12521256
}
12531257
}
12541258
Rvalue::ShallowInitBox(operand, _) => {
1259+
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) {
1260+
self.fail(location, format!("ShallowInitBox after ElaborateBoxDerefs"))
1261+
}
1262+
12551263
let a = operand.ty(&self.body.local_decls, self.tcx);
12561264
check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
12571265
}

tests/mir-opt/box_expr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
// EMIT_MIR box_expr.main.ElaborateDrops.diff
77
fn main() {
88
// CHECK-LABEL: fn main(
9-
// CHECK: [[box:_.*]] = ShallowInitBox(
9+
// CHECK: [[ptr:_.*]] = move {{_.*}} as *const S (Transmute);
10+
// CHECK: [[nonnull:_.*]] = NonNull::<S> { pointer: move [[ptr]] };
11+
// CHECK: [[unique:_.*]] = Unique::<S> { pointer: move [[nonnull]], _marker: const PhantomData::<S> };
12+
// CHECK: [[box:_.*]] = Box::<S>(move [[unique]], const std::alloc::Global);
1013
// CHECK: [[ptr:_.*]] = copy (([[box]].0: std::ptr::Unique<S>).0: std::ptr::NonNull<S>) as *const S (Transmute);
1114
// CHECK: (*[[ptr]]) = S::new() -> [return: [[ret:bb.*]], unwind: [[unwind:bb.*]]];
1215
// CHECK: [[ret]]: {

tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
let mut _6: *mut u8;
1212
let mut _7: std::boxed::Box<i32>;
1313
let mut _8: *const i32;
14-
let mut _9: *const i32;
14+
let mut _9: std::ptr::NonNull<i32>;
15+
let mut _10: std::ptr::Unique<i32>;
16+
let mut _11: *const i32;
17+
let mut _12: *const i32;
1518
scope 1 {
1619
debug x => _1;
1720
}
@@ -31,13 +34,21 @@
3134

3235
bb1: {
3336
StorageLive(_7);
34-
_7 = ShallowInitBox(move _6, i32);
35-
_8 = copy ((_7.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
36-
(*_8) = const 42_i32;
37+
- _8 = move _6 as *const i32 (Transmute);
38+
- _9 = NonNull::<i32> { pointer: move _8 };
39+
- _10 = Unique::<i32> { pointer: move _9, _marker: const PhantomData::<i32> };
40+
+ _8 = copy _6 as *const i32 (PtrToPtr);
41+
+ _9 = NonNull::<i32> { pointer: copy _8 };
42+
+ _10 = Unique::<i32> { pointer: copy _9, _marker: const PhantomData::<i32> };
43+
_7 = Box::<i32>(move _10, const std::alloc::Global);
44+
- _11 = copy ((_7.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
45+
- (*_11) = const 42_i32;
46+
+ _11 = copy _8;
47+
+ (*_8) = const 42_i32;
3748
_3 = move _7;
3849
StorageDead(_7);
39-
_9 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
40-
_2 = copy (*_9);
50+
_12 = copy ((_3.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>) as *const i32 (Transmute);
51+
_2 = copy (*_12);
4152
- _1 = Add(move _2, const 0_i32);
4253
- StorageDead(_2);
4354
+ _1 = copy _2;

0 commit comments

Comments
 (0)