Skip to content

Commit 392fbde

Browse files
authored
Rollup merge of rust-lang#98847 - RalfJung:box-is-special, r=oli-obk
fix interpreter validity check on Box Follow-up to rust-lang#98554: avoid walking over parts of the value twice. And then move all that logic into the general visitor so not each visitor implementation has to deal with it...
2 parents 0fe8ac3 + d7edf66 commit 392fbde

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

compiler/rustc_const_eval/src/interpret/validity.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -593,16 +593,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
593593
self.check_safe_pointer(value, "reference")?;
594594
Ok(true)
595595
}
596-
ty::Adt(def, ..) if def.is_box() => {
597-
let unique = self.ecx.operand_field(value, 0)?;
598-
let nonnull = self.ecx.operand_field(&unique, 0)?;
599-
let ptr = self.ecx.operand_field(&nonnull, 0)?;
600-
self.check_safe_pointer(&ptr, "box")?;
601-
602-
// Check other fields of Box
603-
self.walk_value(value)?;
604-
Ok(true)
605-
}
606596
ty::FnPtr(_sig) => {
607597
let value = try_validation!(
608598
self.ecx.read_scalar(value).and_then(|v| v.check_init()),
@@ -813,6 +803,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
813803
Ok(())
814804
}
815805

806+
#[inline]
807+
fn visit_box(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
808+
self.check_safe_pointer(op, "box")?;
809+
Ok(())
810+
}
811+
816812
#[inline]
817813
fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> {
818814
trace!("visit_value: {:?}, {:?}", *op, op.layout);
@@ -821,8 +817,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
821817
if self.try_visit_primitive(op)? {
822818
return Ok(());
823819
}
824-
// Sanity check: `builtin_deref` does not know any pointers that are not primitive.
825-
assert!(op.layout.ty.builtin_deref(true).is_none());
826820

827821
// Special check preventing `UnsafeCell` in the inner part of constants
828822
if let Some(def) = op.layout.ty.ty_adt_def() {

compiler/rustc_const_eval/src/interpret/visitor.rs

+49
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ macro_rules! make_value_visitor {
151151
{
152152
Ok(())
153153
}
154+
/// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
155+
/// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
156+
/// pointee type is the actual `T`.
157+
#[inline(always)]
158+
fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx>
159+
{
160+
Ok(())
161+
}
154162
/// Visits this value as an aggregate, you are getting an iterator yielding
155163
/// all the fields (still in an `InterpResult`, you have to do error handling yourself).
156164
/// Recurses into the fields.
@@ -221,6 +229,47 @@ macro_rules! make_value_visitor {
221229
// Slices do not need special handling here: they have `Array` field
222230
// placement with length 0, so we enter the `Array` case below which
223231
// indirectly uses the metadata to determine the actual length.
232+
233+
// However, `Box`... let's talk about `Box`.
234+
ty::Adt(def, ..) if def.is_box() => {
235+
// `Box` is a hybrid primitive-library-defined type that one the one hand is
236+
// a dereferenceable pointer, on the other hand has *basically arbitrary
237+
// user-defined layout* since the user controls the 'allocator' field. So it
238+
// cannot be treated like a normal pointer, since it does not fit into an
239+
// `Immediate`. Yeah, it is quite terrible. But many visitors want to do
240+
// something with "all boxed pointers", so we handle this mess for them.
241+
//
242+
// When we hit a `Box`, we do not do the usual `visit_aggregate`; instead,
243+
// we (a) call `visit_box` on the pointer value, and (b) recurse on the
244+
// allocator field. We also assert tons of things to ensure we do not miss
245+
// any other fields.
246+
247+
// `Box` has two fields: the pointer we care about, and the allocator.
248+
assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
249+
let (unique_ptr, alloc) =
250+
(v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?);
251+
// Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
252+
// (which means another 2 fields, the second of which is a `PhantomData`)
253+
assert_eq!(unique_ptr.layout().fields.count(), 2);
254+
let (nonnull_ptr, phantom) = (
255+
unique_ptr.project_field(self.ecx(), 0)?,
256+
unique_ptr.project_field(self.ecx(), 1)?,
257+
);
258+
assert!(
259+
phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
260+
"2nd field of `Unique` should be PhantomData but is {:?}",
261+
phantom.layout().ty,
262+
);
263+
// ... that contains a `NonNull`... (gladly, only a single field here)
264+
assert_eq!(nonnull_ptr.layout().fields.count(), 1);
265+
let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr
266+
// ... whose only field finally is a raw ptr we can dereference.
267+
self.visit_box(&raw_ptr)?;
268+
269+
// The second `Box` field is the allocator, which we recursively check for validity
270+
// like in regular structs.
271+
self.visit_field(v, 1, &alloc)?;
272+
}
224273
_ => {},
225274
};
226275

compiler/rustc_const_eval/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Rust MIR: a lowered representation of Rust.
2121
#![feature(trusted_step)]
2222
#![feature(try_blocks)]
2323
#![feature(yeet_expr)]
24+
#![feature(is_some_with)]
2425
#![recursion_limit = "256"]
2526
#![allow(rustc::potential_query_instability)]
2627

0 commit comments

Comments
 (0)