interpret: refactor projection code to work on a common trait, and use that for visitors
This commit is contained in:
parent
a593de4fab
commit
a2bcafa500
44 changed files with 863 additions and 1210 deletions
|
@ -29,7 +29,7 @@ use std::hash::Hash;
|
|||
use super::UndefinedBehaviorInfo::*;
|
||||
use super::{
|
||||
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
|
||||
Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
|
||||
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
|
||||
};
|
||||
|
||||
macro_rules! throw_validation_failure {
|
||||
|
@ -462,6 +462,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
|
||||
/// Check if this is a value of primitive type, and if yes check the validity of the value
|
||||
/// at that type. Return `true` if the type is indeed primitive.
|
||||
///
|
||||
/// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references.
|
||||
fn try_visit_primitive(
|
||||
&mut self,
|
||||
value: &OpTy<'tcx, M::Provenance>,
|
||||
|
@ -655,15 +657,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
) -> InterpResult<'tcx, VariantIdx> {
|
||||
self.with_elem(PathElem::EnumTag, move |this| {
|
||||
Ok(try_validation!(
|
||||
this.ecx.read_discriminant(op),
|
||||
this.ecx.read_discriminant(op).map(|(_, idx)| idx),
|
||||
this.path,
|
||||
InvalidTag(val) => InvalidEnumTag {
|
||||
value: format!("{val:x}"),
|
||||
},
|
||||
|
||||
UninhabitedEnumVariantRead(_) => UninhabitedEnumTag,
|
||||
InvalidUninitBytes(None) => UninitEnumTag,
|
||||
)
|
||||
.1)
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -733,60 +734,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
}
|
||||
}
|
||||
|
||||
// Recursively walk the value at its type.
|
||||
self.walk_value(op)?;
|
||||
|
||||
// *After* all of this, check the ABI. We need to check the ABI to handle
|
||||
// types like `NonNull` where the `Scalar` info is more restrictive than what
|
||||
// the fields say (`rustc_layout_scalar_valid_range_start`).
|
||||
// But in most cases, this will just propagate what the fields say,
|
||||
// and then we want the error to point at the field -- so, first recurse,
|
||||
// then check ABI.
|
||||
//
|
||||
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
|
||||
// scalars, we do the same check on every "level" (e.g., first we check
|
||||
// MyNewtype and then the scalar in there).
|
||||
match op.layout.abi {
|
||||
Abi::Uninhabited => {
|
||||
let ty = op.layout.ty;
|
||||
throw_validation_failure!(self.path, UninhabitedVal { ty });
|
||||
}
|
||||
Abi::Scalar(scalar_layout) => {
|
||||
if !scalar_layout.is_uninit_valid() {
|
||||
// There is something to check here.
|
||||
let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
|
||||
self.visit_scalar(scalar, scalar_layout)?;
|
||||
}
|
||||
}
|
||||
Abi::ScalarPair(a_layout, b_layout) => {
|
||||
// We can only proceed if *both* scalars need to be initialized.
|
||||
// FIXME: find a way to also check ScalarPair when one side can be uninit but
|
||||
// the other must be init.
|
||||
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
|
||||
let (a, b) =
|
||||
self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
|
||||
self.visit_scalar(a, a_layout)?;
|
||||
self.visit_scalar(b, b_layout)?;
|
||||
}
|
||||
}
|
||||
Abi::Vector { .. } => {
|
||||
// No checks here, we assume layout computation gets this right.
|
||||
// (This is harder to check since Miri does not represent these as `Immediate`. We
|
||||
// also cannot use field projections since this might be a newtype around a vector.)
|
||||
}
|
||||
Abi::Aggregate { .. } => {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_aggregate(
|
||||
&mut self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Recursively walk the value at its type. Apply optimizations for some large types.
|
||||
match op.layout.ty.kind() {
|
||||
ty::Str => {
|
||||
let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
|
||||
|
@ -874,12 +822,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
// ZST type, so either validation fails for all elements or none.
|
||||
ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => {
|
||||
// Validate just the first element (if any).
|
||||
self.walk_aggregate(op, fields.take(1))?
|
||||
if op.len(self.ecx)? > 0 {
|
||||
self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.walk_aggregate(op, fields)? // default handler
|
||||
self.walk_value(op)?; // default handler
|
||||
}
|
||||
}
|
||||
|
||||
// *After* all of this, check the ABI. We need to check the ABI to handle
|
||||
// types like `NonNull` where the `Scalar` info is more restrictive than what
|
||||
// the fields say (`rustc_layout_scalar_valid_range_start`).
|
||||
// But in most cases, this will just propagate what the fields say,
|
||||
// and then we want the error to point at the field -- so, first recurse,
|
||||
// then check ABI.
|
||||
//
|
||||
// FIXME: We could avoid some redundant checks here. For newtypes wrapping
|
||||
// scalars, we do the same check on every "level" (e.g., first we check
|
||||
// MyNewtype and then the scalar in there).
|
||||
match op.layout.abi {
|
||||
Abi::Uninhabited => {
|
||||
let ty = op.layout.ty;
|
||||
throw_validation_failure!(self.path, UninhabitedVal { ty });
|
||||
}
|
||||
Abi::Scalar(scalar_layout) => {
|
||||
if !scalar_layout.is_uninit_valid() {
|
||||
// There is something to check here.
|
||||
let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
|
||||
self.visit_scalar(scalar, scalar_layout)?;
|
||||
}
|
||||
}
|
||||
Abi::ScalarPair(a_layout, b_layout) => {
|
||||
// We can only proceed if *both* scalars need to be initialized.
|
||||
// FIXME: find a way to also check ScalarPair when one side can be uninit but
|
||||
// the other must be init.
|
||||
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
|
||||
let (a, b) =
|
||||
self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
|
||||
self.visit_scalar(a, a_layout)?;
|
||||
self.visit_scalar(b, b_layout)?;
|
||||
}
|
||||
}
|
||||
Abi::Vector { .. } => {
|
||||
// No checks here, we assume layout computation gets this right.
|
||||
// (This is harder to check since Miri does not represent these as `Immediate`. We
|
||||
// also cannot use field projections since this might be a newtype around a vector.)
|
||||
}
|
||||
Abi::Aggregate { .. } => {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue