Skip to content

Commit 3fa48ec

Browse files
authored
Rollup merge of rust-lang#122647 - RalfJung:box-to-raw-retag, r=oli-obk
add_retag: ensure box-to-raw-ptr casts are preserved for Miri In rust-lang#122233 I added `retag_box_to_raw` not realizing that we can already do `addr_of_mut!(*bx)` to turn a box into a raw pointer without an intermediate reference. We just need to ensure this information is preserved past the ElaborateBoxDerefs pass. r? ``@oli-obk``
2 parents acc45eb + bcf8015 commit 3fa48ec

File tree

16 files changed

+92
-104
lines changed

16 files changed

+92
-104
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
341341
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
342342
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
343343
// seem to fail to set their `MirPhase` correctly.
344-
if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
344+
if matches!(kind, RetagKind::TwoPhase) {
345345
self.fail(location, format!("explicit `{kind:?}` is forbidden"));
346346
}
347347
}
@@ -1272,7 +1272,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
12721272
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
12731273
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
12741274
// seem to fail to set their `MirPhase` correctly.
1275-
if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
1275+
if matches!(kind, RetagKind::TwoPhase) {
12761276
self.fail(location, format!("explicit `{kind:?}` is forbidden"));
12771277
}
12781278
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

-4
Original file line numberDiff line numberDiff line change
@@ -658,10 +658,6 @@ pub fn check_intrinsic_type(
658658
sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
659659
sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
660660

661-
sym::retag_box_to_raw => {
662-
(2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0)))
663-
}
664-
665661
other => {
666662
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
667663
return;

compiler/rustc_mir_transform/src/add_retag.rs

+30-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
2424
| ty::Str
2525
| ty::FnDef(..)
2626
| ty::Never => false,
27-
// References
27+
// References and Boxes (`noalias` sources)
2828
ty::Ref(..) => true,
2929
ty::Adt(..) if ty.is_box() => true,
3030
ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true,
@@ -125,15 +125,39 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
125125
for i in (0..block_data.statements.len()).rev() {
126126
let (retag_kind, place) = match block_data.statements[i].kind {
127127
// Retag after assignments of reference type.
128-
StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
128+
StatementKind::Assign(box (ref place, ref rvalue)) => {
129129
let add_retag = match rvalue {
130130
// Ptr-creating operations already do their own internal retagging, no
131131
// need to also add a retag statement.
132-
Rvalue::Ref(..) | Rvalue::AddressOf(..) => false,
133-
_ => true,
132+
// *Except* if we are deref'ing a Box, because those get desugared to directly working
133+
// with the inner raw pointer! That's relevant for `AddressOf` as Miri otherwise makes it
134+
// a NOP when the original pointer is already raw.
135+
Rvalue::AddressOf(_mutbl, place) => {
136+
// Using `is_box_global` here is a bit sketchy: if this code is
137+
// generic over the allocator, we'll not add a retag! This is a hack
138+
// to make Stacked Borrows compatible with custom allocator code.
139+
// Long-term, we'll want to move to an aliasing model where "cast to
140+
// raw pointer" is a complete NOP, and then this will no longer be
141+
// an issue.
142+
if place.is_indirect_first_projection()
143+
&& body.local_decls[place.local].ty.is_box_global(tcx)
144+
{
145+
Some(RetagKind::Raw)
146+
} else {
147+
None
148+
}
149+
}
150+
Rvalue::Ref(..) => None,
151+
_ => {
152+
if needs_retag(place) {
153+
Some(RetagKind::Default)
154+
} else {
155+
None
156+
}
157+
}
134158
};
135-
if add_retag {
136-
(RetagKind::Default, *place)
159+
if let Some(kind) = add_retag {
160+
(kind, *place)
137161
} else {
138162
continue;
139163
}

compiler/rustc_mir_transform/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,11 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
529529
// AddMovesForPackedDrops needs to run after drop
530530
// elaboration.
531531
&add_moves_for_packed_drops::AddMovesForPackedDrops,
532-
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
532+
// `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. Otherwise it should run fairly late,
533533
// but before optimizations begin.
534+
&add_retag::AddRetag,
534535
&elaborate_box_derefs::ElaborateBoxDerefs,
535536
&coroutine::StateTransform,
536-
&add_retag::AddRetag,
537537
&Lint(known_panics_lint::KnownPanicsLint),
538538
];
539539
pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));

compiler/rustc_span/src/symbol.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,6 @@ symbols! {
14631463
residual,
14641464
result,
14651465
resume,
1466-
retag_box_to_raw,
14671466
return_position_impl_trait_in_trait,
14681467
return_type_notation,
14691468
rhs,

library/alloc/src/boxed.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ use core::error::Error;
155155
use core::fmt;
156156
use core::future::Future;
157157
use core::hash::{Hash, Hasher};
158-
use core::intrinsics::retag_box_to_raw;
159158
use core::iter::FusedIterator;
160159
use core::marker::Tuple;
161160
use core::marker::Unsize;
@@ -165,7 +164,7 @@ use core::ops::{
165164
CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver,
166165
};
167166
use core::pin::Pin;
168-
use core::ptr::{self, NonNull, Unique};
167+
use core::ptr::{self, addr_of_mut, NonNull, Unique};
169168
use core::task::{Context, Poll};
170169

171170
#[cfg(not(no_global_oom_handling))]
@@ -1111,16 +1110,12 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
11111110
#[unstable(feature = "allocator_api", issue = "32838")]
11121111
#[inline]
11131112
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
1114-
// This is the transition point from `Box` to raw pointers. For Stacked Borrows, these casts
1115-
// are relevant -- if this is a global allocator Box and we just get the pointer from `b.0`,
1116-
// it will have `Unique` permission, which is not what we want from a raw pointer. We could
1117-
// fix that by going through `&mut`, but then if this is *not* a global allocator Box, we'd
1118-
// be adding uniqueness assertions that we do not want. So for Miri's sake we pass this
1119-
// pointer through an intrinsic for box-to-raw casts, which can do the right thing wrt the
1120-
// aliasing model.
1121-
let b = mem::ManuallyDrop::new(b);
1113+
let mut b = mem::ManuallyDrop::new(b);
1114+
// We carefully get the raw pointer out in a way that Miri's aliasing model understands what
1115+
// is happening: using the primitive "deref" of `Box`.
1116+
let ptr = addr_of_mut!(**b);
11221117
let alloc = unsafe { ptr::read(&b.1) };
1123-
(unsafe { retag_box_to_raw::<T, A>(b.0.as_ptr()) }, alloc)
1118+
(ptr, alloc)
11241119
}
11251120

11261121
#[unstable(

library/core/src/intrinsics.rs

-13
Original file line numberDiff line numberDiff line change
@@ -2709,19 +2709,6 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
27092709
unreachable!()
27102710
}
27112711

2712-
/// Retag a box pointer as part of casting it to a raw pointer. This is the `Box` equivalent of
2713-
/// `(x: &mut T) as *mut T`. The input pointer must be the pointer of a `Box` (passed as raw pointer
2714-
/// to avoid all questions around move semantics and custom allocators), and `A` must be the `Box`'s
2715-
/// allocator.
2716-
#[unstable(feature = "core_intrinsics", issue = "none")]
2717-
#[rustc_nounwind]
2718-
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
2719-
#[cfg_attr(bootstrap, inline)]
2720-
pub unsafe fn retag_box_to_raw<T: ?Sized, A>(ptr: *mut T) -> *mut T {
2721-
// Miri needs to adjust the provenance, but for regular codegen this is not needed
2722-
ptr
2723-
}
2724-
27252712
// Some functions are defined here because they accidentally got made
27262713
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
27272714
// (`transmute` also falls into this category, but it cannot be wrapped due to the

src/tools/miri/src/borrow_tracker/mod.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::num::NonZero;
55
use smallvec::SmallVec;
66

77
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8-
use rustc_middle::{mir::RetagKind, ty::Ty};
8+
use rustc_middle::mir::RetagKind;
99
use rustc_target::abi::Size;
1010

1111
use crate::*;
@@ -291,19 +291,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
291291
}
292292
}
293293

294-
fn retag_box_to_raw(
295-
&mut self,
296-
val: &ImmTy<'tcx, Provenance>,
297-
alloc_ty: Ty<'tcx>,
298-
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
299-
let this = self.eval_context_mut();
300-
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
301-
match method {
302-
BorrowTrackerMethod::StackedBorrows => this.sb_retag_box_to_raw(val, alloc_ty),
303-
BorrowTrackerMethod::TreeBorrows => this.tb_retag_box_to_raw(val, alloc_ty),
304-
}
305-
}
306-
307294
fn retag_place_contents(
308295
&mut self,
309296
kind: RetagKind,

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+10-28
Original file line numberDiff line numberDiff line change
@@ -865,24 +865,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
865865
this.sb_retag_reference(val, new_perm, RetagInfo { cause, in_field: false })
866866
}
867867

868-
fn sb_retag_box_to_raw(
869-
&mut self,
870-
val: &ImmTy<'tcx, Provenance>,
871-
alloc_ty: Ty<'tcx>,
872-
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
873-
let this = self.eval_context_mut();
874-
let is_global_alloc = alloc_ty.ty_adt_def().is_some_and(|adt| {
875-
let global_alloc = this.tcx.require_lang_item(rustc_hir::LangItem::GlobalAlloc, None);
876-
adt.did() == global_alloc
877-
});
878-
if is_global_alloc {
879-
// Retag this as-if it was a mutable reference.
880-
this.sb_retag_ptr_value(RetagKind::Raw, val)
881-
} else {
882-
Ok(val.clone())
883-
}
884-
}
885-
886868
fn sb_retag_place_contents(
887869
&mut self,
888870
kind: RetagKind,
@@ -891,9 +873,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
891873
let this = self.eval_context_mut();
892874
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
893875
let retag_cause = match kind {
894-
RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(), // these can only happen in `retag_ptr_value`
876+
RetagKind::TwoPhase { .. } => unreachable!(), // can only happen in `retag_ptr_value`
895877
RetagKind::FnEntry => RetagCause::FnEntry,
896-
RetagKind::Default => RetagCause::Normal,
878+
RetagKind::Default | RetagKind::Raw => RetagCause::Normal,
897879
};
898880
let mut visitor =
899881
RetagVisitor { ecx: this, kind, retag_cause, retag_fields, in_field: false };
@@ -959,14 +941,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
959941

960942
// Check the type of this value to see what to do with it (retag, or recurse).
961943
match place.layout.ty.kind() {
962-
ty::Ref(..) => {
963-
let new_perm =
964-
NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx);
965-
self.retag_ptr_inplace(place, new_perm)?;
966-
}
967-
ty::RawPtr(..) => {
968-
// We do *not* want to recurse into raw pointers -- wide raw pointers have
969-
// fields, and for dyn Trait pointees those can have reference type!
944+
ty::Ref(..) | ty::RawPtr(..) => {
945+
if matches!(place.layout.ty.kind(), ty::Ref(..))
946+
|| self.kind == RetagKind::Raw
947+
{
948+
let new_perm =
949+
NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx);
950+
self.retag_ptr_inplace(place, new_perm)?;
951+
}
970952
}
971953
ty::Adt(adt, _) if adt.is_box() => {
972954
// Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

-9
Original file line numberDiff line numberDiff line change
@@ -392,15 +392,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
392392
}
393393
}
394394

395-
fn tb_retag_box_to_raw(
396-
&mut self,
397-
val: &ImmTy<'tcx, Provenance>,
398-
_alloc_ty: Ty<'tcx>,
399-
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
400-
// Casts to raw pointers are NOPs in Tree Borrows.
401-
Ok(val.clone())
402-
}
403-
404395
/// Retag all pointers that are stored in this place.
405396
fn tb_retag_place_contents(
406397
&mut self,

src/tools/miri/src/shims/intrinsics/mod.rs

-12
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
140140

141141
this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
142142
}
143-
"retag_box_to_raw" => {
144-
let [ptr] = check_arg_count(args)?;
145-
let alloc_ty = generic_args[1].expect_ty();
146-
147-
let val = this.read_immediate(ptr)?;
148-
let new_val = if this.machine.borrow_tracker.is_some() {
149-
this.retag_box_to_raw(&val, alloc_ty)?
150-
} else {
151-
val
152-
};
153-
this.write_immediate(*new_val, dest)?;
154-
}
155143

156144
// We want to return either `true` or `false` at random, or else something like
157145
// ```

src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
66
|
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9-
help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4]
9+
help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
1010
--> $DIR/newtype_pair_retagging.rs:LL:CC
1111
|
1212
LL | let ptr = Box::into_raw(Box::new(0i32));

src/tools/miri/tests/fail/both_borrows/newtype_retagging.stack.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc)
66
|
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9-
help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4]
9+
help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
1010
--> $DIR/newtype_retagging.rs:LL:CC
1111
|
1212
LL | let ptr = Box::into_raw(Box::new(0i32));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// MIR for `box_to_raw_mut` after SimplifyCfg-pre-optimizations
2+
3+
fn box_to_raw_mut(_1: &mut Box<i32>) -> *mut i32 {
4+
debug x => _1;
5+
let mut _0: *mut i32;
6+
let mut _2: std::boxed::Box<i32>;
7+
let mut _3: *const i32;
8+
9+
bb0: {
10+
Retag([fn entry] _1);
11+
_2 = deref_copy (*_1);
12+
_3 = (((_2.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32);
13+
_0 = &raw mut (*_3);
14+
Retag([raw] _0);
15+
return;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// MIR for `box_to_raw_mut` after SimplifyCfg-pre-optimizations
2+
3+
fn box_to_raw_mut(_1: &mut Box<i32>) -> *mut i32 {
4+
debug x => _1;
5+
let mut _0: *mut i32;
6+
let mut _2: std::boxed::Box<i32>;
7+
let mut _3: *const i32;
8+
9+
bb0: {
10+
Retag([fn entry] _1);
11+
_2 = deref_copy (*_1);
12+
_3 = (((_2.0: std::ptr::Unique<i32>).0: std::ptr::NonNull<i32>).0: *const i32);
13+
_0 = &raw mut (*_3);
14+
Retag([raw] _0);
15+
return;
16+
}
17+
}

tests/mir-opt/retag.rs

+5
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ fn array_casts() {
6565
let p = &x as *const usize;
6666
assert_eq!(unsafe { *p.add(1) }, 1);
6767
}
68+
69+
// EMIT_MIR retag.box_to_raw_mut.SimplifyCfg-pre-optimizations.after.mir
70+
fn box_to_raw_mut(x: &mut Box<i32>) -> *mut i32 {
71+
std::ptr::addr_of_mut!(**x)
72+
}

0 commit comments

Comments
 (0)