make read_immediate error immediately on uninit, so ImmTy can carry initialized Scalar
This commit is contained in:
parent
2e52fe01cf
commit
30fa931f92
51 changed files with 491 additions and 747 deletions
|
@ -3,7 +3,7 @@ use crate::interpret::eval_nullary_intrinsic;
|
|||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
|
||||
Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
|
||||
ScalarMaybeUninit, StackPopCleanup,
|
||||
StackPopCleanup,
|
||||
};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
|
@ -170,10 +170,7 @@ pub(super) fn op_to_const<'tcx>(
|
|||
// see comment on `let try_as_immediate` above
|
||||
Err(imm) => match *imm {
|
||||
_ if imm.layout.is_zst() => ConstValue::ZeroSized,
|
||||
Immediate::Scalar(x) => match x {
|
||||
ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s),
|
||||
ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()),
|
||||
},
|
||||
Immediate::Scalar(x) => ConstValue::Scalar(x),
|
||||
Immediate::ScalarPair(a, b) => {
|
||||
debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
|
||||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
|
|
|
@ -347,8 +347,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
};
|
||||
match intrinsic_name {
|
||||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
||||
let a = ecx.read_immediate(&args[0])?.to_scalar()?;
|
||||
let b = ecx.read_immediate(&args[1])?.to_scalar()?;
|
||||
let a = ecx.read_scalar(&args[0])?;
|
||||
let b = ecx.read_scalar(&args[1])?;
|
||||
let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
|
||||
ecx.guaranteed_eq(a, b)?
|
||||
} else {
|
||||
|
|
|
@ -3,7 +3,7 @@ use super::machine::CompileTimeEvalContext;
|
|||
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
|
||||
MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit,
|
||||
MemoryKind, PlaceTy, Scalar,
|
||||
};
|
||||
use crate::interpret::{MPlaceTy, Value};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
|
@ -90,7 +90,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||
let Ok(val) = ecx.read_immediate(&place.into()) else {
|
||||
return Err(ValTreeCreationError::Other);
|
||||
};
|
||||
let val = val.to_scalar().unwrap();
|
||||
let val = val.to_scalar();
|
||||
*num_nodes += 1;
|
||||
|
||||
Ok(ty::ValTree::Leaf(val.assert_int()))
|
||||
|
@ -349,11 +349,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
|
||||
let scalar_int = valtree.unwrap_leaf();
|
||||
debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place);
|
||||
ecx.write_immediate(
|
||||
Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar_int.into())),
|
||||
&place.into(),
|
||||
)
|
||||
.unwrap();
|
||||
ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap();
|
||||
}
|
||||
ty::Ref(_, inner_ty, _) => {
|
||||
let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
|
||||
|
@ -366,11 +362,10 @@ fn valtree_into_mplace<'tcx>(
|
|||
let imm = match inner_ty.kind() {
|
||||
ty::Slice(_) | ty::Str => {
|
||||
let len = valtree.unwrap_branch().len();
|
||||
let len_scalar =
|
||||
ScalarMaybeUninit::Scalar(Scalar::from_machine_usize(len as u64, &tcx));
|
||||
let len_scalar = Scalar::from_machine_usize(len as u64, &tcx);
|
||||
|
||||
Immediate::ScalarPair(
|
||||
ScalarMaybeUninit::from_maybe_pointer((*pointee_place).ptr, &tcx),
|
||||
Scalar::from_maybe_pointer((*pointee_place).ptr, &tcx),
|
||||
len_scalar,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -123,10 +123,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
match src.layout.ty.kind() {
|
||||
// Floating point
|
||||
Float(FloatTy::F32) => {
|
||||
return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into());
|
||||
return Ok(self.cast_from_float(src.to_scalar().to_f32()?, cast_ty).into());
|
||||
}
|
||||
Float(FloatTy::F64) => {
|
||||
return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
|
||||
return Ok(self.cast_from_float(src.to_scalar().to_f64()?, cast_ty).into());
|
||||
}
|
||||
// The rest is integer/pointer-"like", including fn ptr casts
|
||||
_ => assert!(
|
||||
|
@ -153,7 +153,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert_eq!(dest_layout.size, self.pointer_size());
|
||||
assert!(src.layout.ty.is_unsafe_ptr());
|
||||
return match **src {
|
||||
Immediate::ScalarPair(data, _) => Ok(data.check_init()?.into()),
|
||||
Immediate::ScalarPair(data, _) => Ok(data.into()),
|
||||
Immediate::Scalar(..) => span_bug!(
|
||||
self.cur_span(),
|
||||
"{:?} input to a fat-to-thin cast ({:?} -> {:?})",
|
||||
|
@ -167,7 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
// # The remaining source values are scalar and "int-like".
|
||||
let scalar = src.to_scalar()?;
|
||||
let scalar = src.to_scalar();
|
||||
Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_));
|
||||
assert!(cast_ty.is_integral());
|
||||
|
||||
let scalar = src.to_scalar()?;
|
||||
let scalar = src.to_scalar();
|
||||
let ptr = scalar.to_pointer(self)?;
|
||||
match ptr.into_pointer_or_addr() {
|
||||
Ok(ptr) => M::expose_ptr(self, ptr)?,
|
||||
|
@ -197,7 +197,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert_matches!(cast_ty.kind(), ty::RawPtr(_));
|
||||
|
||||
// First cast to usize.
|
||||
let scalar = src.to_scalar()?;
|
||||
let scalar = src.to_scalar();
|
||||
let addr = self.cast_from_int_like(scalar, src.layout, self.tcx.types.usize)?;
|
||||
let addr = addr.to_machine_usize(self)?;
|
||||
|
||||
|
@ -291,7 +291,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
|
||||
(&ty::Array(_, length), &ty::Slice(_)) => {
|
||||
let ptr = self.read_immediate(src)?.to_scalar()?;
|
||||
let ptr = self.read_scalar(src)?;
|
||||
// u64 cast is from usize to u64, which is always good
|
||||
let val =
|
||||
Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
|
||||
|
@ -303,7 +303,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
|
||||
return self.write_immediate(*val, dest);
|
||||
}
|
||||
let (old_data, old_vptr) = val.to_scalar_pair()?;
|
||||
let (old_data, old_vptr) = val.to_scalar_pair();
|
||||
let old_vptr = old_vptr.to_pointer(self)?;
|
||||
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
|
||||
if old_trait != data_a.principal() {
|
||||
|
@ -315,7 +315,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
(_, &ty::Dynamic(ref data, _)) => {
|
||||
// Initial cast from sized to dyn trait
|
||||
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
|
||||
let ptr = self.read_immediate(src)?.to_scalar()?;
|
||||
let ptr = self.read_scalar(src)?;
|
||||
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
|
||||
self.write_immediate(val, dest)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou
|
|||
use super::{
|
||||
AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
|
||||
Scalar, ScalarMaybeUninit, StackPopJump,
|
||||
Scalar, StackPopJump,
|
||||
};
|
||||
use crate::transform::validate::equal_up_to_regions;
|
||||
|
||||
|
@ -991,16 +991,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
|
|||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => {
|
||||
write!(fmt, " {:?}", val)?;
|
||||
if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val {
|
||||
if let Scalar::Ptr(ptr, _size) = val {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => {
|
||||
write!(fmt, " ({:?}, {:?})", val1, val2)?;
|
||||
if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val1 {
|
||||
if let Scalar::Ptr(ptr, _size) = val1 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val2 {
|
||||
if let Scalar::Ptr(ptr, _size) = val2 {
|
||||
allocs.push(ptr.provenance.get_alloc_id());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
| sym::bitreverse => {
|
||||
let ty = substs.type_at(0);
|
||||
let layout_of = self.layout_of(ty)?;
|
||||
let val = self.read_scalar(&args[0])?.check_init()?;
|
||||
let val = self.read_scalar(&args[0])?;
|
||||
let bits = val.to_bits(layout_of.size)?;
|
||||
let kind = match layout_of.abi {
|
||||
Abi::Scalar(scalar) => scalar.primitive(),
|
||||
|
@ -256,7 +256,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
|
||||
if overflowed {
|
||||
let layout = self.layout_of(substs.type_at(0))?;
|
||||
let r_val = r.to_scalar()?.to_bits(layout.size)?;
|
||||
let r_val = r.to_scalar().to_bits(layout.size)?;
|
||||
if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
|
||||
throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name);
|
||||
} else {
|
||||
|
@ -269,9 +269,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
|
||||
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
|
||||
let layout = self.layout_of(substs.type_at(0))?;
|
||||
let val = self.read_scalar(&args[0])?.check_init()?;
|
||||
let val = self.read_scalar(&args[0])?;
|
||||
let val_bits = val.to_bits(layout.size)?;
|
||||
let raw_shift = self.read_scalar(&args[1])?.check_init()?;
|
||||
let raw_shift = self.read_scalar(&args[1])?;
|
||||
let raw_shift_bits = raw_shift.to_bits(layout.size)?;
|
||||
let width_bits = u128::from(layout.size.bits());
|
||||
let shift_bits = raw_shift_bits % width_bits;
|
||||
|
@ -507,7 +507,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
self.copy_op(&args[0], dest, /*allow_transmute*/ false)?;
|
||||
}
|
||||
sym::assume => {
|
||||
let cond = self.read_scalar(&args[0])?.check_init()?.to_bool()?;
|
||||
let cond = self.read_scalar(&args[0])?.to_bool()?;
|
||||
if !cond {
|
||||
throw_ub_format!("`assume` intrinsic called with `false`");
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// term since the sign of the second term can be inferred from this and
|
||||
// the fact that the operation has overflowed (if either is 0 no
|
||||
// overflow can occur)
|
||||
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
|
||||
let first_term: u128 = l.to_scalar().to_bits(l.layout.size)?;
|
||||
let first_term_positive = first_term & (1 << (num_bits - 1)) == 0;
|
||||
if first_term_positive {
|
||||
// Negative overflow not possible since the positive first term
|
||||
|
|
|
@ -21,7 +21,6 @@ use rustc_target::abi::{Align, HasDataLayout, Size};
|
|||
use super::{
|
||||
alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx,
|
||||
InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar,
|
||||
ScalarMaybeUninit,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
@ -901,11 +900,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
|
|||
/// Reading and writing.
|
||||
impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> {
|
||||
/// `range` is relative to this allocation reference, not the base of the allocation.
|
||||
pub fn write_scalar(
|
||||
&mut self,
|
||||
range: AllocRange,
|
||||
val: ScalarMaybeUninit<Prov>,
|
||||
) -> InterpResult<'tcx> {
|
||||
pub fn write_scalar(&mut self, range: AllocRange, val: Scalar<Prov>) -> InterpResult<'tcx> {
|
||||
let range = self.range.subrange(range);
|
||||
debug!("write_scalar at {:?}{range:?}: {val:?}", self.alloc_id);
|
||||
Ok(self
|
||||
|
@ -915,11 +910,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> {
|
|||
}
|
||||
|
||||
/// `offset` is relative to this allocation reference, not the base of the allocation.
|
||||
pub fn write_ptr_sized(
|
||||
&mut self,
|
||||
offset: Size,
|
||||
val: ScalarMaybeUninit<Prov>,
|
||||
) -> InterpResult<'tcx> {
|
||||
pub fn write_ptr_sized(&mut self, offset: Size, val: Scalar<Prov>) -> InterpResult<'tcx> {
|
||||
self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
|
||||
}
|
||||
|
||||
|
@ -938,7 +929,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> {
|
|||
&self,
|
||||
range: AllocRange,
|
||||
read_provenance: bool,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
|
||||
) -> InterpResult<'tcx, Scalar<Prov>> {
|
||||
let range = self.range.subrange(range);
|
||||
let res = self
|
||||
.alloc
|
||||
|
@ -949,12 +940,12 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> {
|
|||
}
|
||||
|
||||
/// `range` is relative to this allocation reference, not the base of the allocation.
|
||||
pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
|
||||
pub fn read_integer(&self, range: AllocRange) -> InterpResult<'tcx, Scalar<Prov>> {
|
||||
self.read_scalar(range, /*read_provenance*/ false)
|
||||
}
|
||||
|
||||
/// `offset` is relative to this allocation reference, not the base of the allocation.
|
||||
pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Prov>> {
|
||||
pub fn read_pointer(&self, offset: Size) -> InterpResult<'tcx, Scalar<Prov>> {
|
||||
self.read_scalar(
|
||||
alloc_range(offset, self.tcx.data_layout().pointer_size),
|
||||
/*read_provenance*/ true,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
//! Functions concerning immediate values and operands, and reading from operands.
|
||||
//! All high-level functions to read from memory work on operands as sources.
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
|
||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer};
|
||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, DelaySpanBugEmitted, Ty};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding};
|
||||
|
@ -14,7 +12,7 @@ use rustc_target::abi::{VariantIdx, Variants};
|
|||
use super::{
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
|
||||
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
|
||||
Provenance, Scalar, ScalarMaybeUninit,
|
||||
Provenance, Scalar,
|
||||
};
|
||||
|
||||
/// An `Immediate` represents a single immediate self-contained Rust value.
|
||||
|
@ -27,23 +25,14 @@ use super::{
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Immediate<Prov: Provenance = AllocId> {
|
||||
/// A single scalar value (must have *initialized* `Scalar` ABI).
|
||||
/// FIXME: we also currently often use this for ZST.
|
||||
/// `ScalarMaybeUninit` should reject ZST, and we should use `Uninit` for them instead.
|
||||
Scalar(ScalarMaybeUninit<Prov>),
|
||||
Scalar(Scalar<Prov>),
|
||||
/// A pair of two scalar value (must have `ScalarPair` ABI where both fields are
|
||||
/// `Scalar::Initialized`).
|
||||
ScalarPair(ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>),
|
||||
ScalarPair(Scalar<Prov>, Scalar<Prov>),
|
||||
/// A value of fully uninitialized memory. Can have and size and layout.
|
||||
Uninit,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> From<ScalarMaybeUninit<Prov>> for Immediate<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: ScalarMaybeUninit<Prov>) -> Self {
|
||||
Immediate::Scalar(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: Scalar<Prov>) -> Self {
|
||||
|
@ -51,13 +40,13 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Immediate<Prov> {
|
||||
impl<Prov: Provenance> Immediate<Prov> {
|
||||
pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx))
|
||||
Immediate::Scalar(Scalar::from_pointer(p, cx))
|
||||
}
|
||||
|
||||
pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(ScalarMaybeUninit::from_maybe_pointer(p, cx))
|
||||
Immediate::Scalar(Scalar::from_maybe_pointer(p, cx))
|
||||
}
|
||||
|
||||
pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self {
|
||||
|
@ -69,41 +58,28 @@ impl<'tcx, Prov: Provenance> Immediate<Prov> {
|
|||
vtable: Pointer<Option<Prov>>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> Self {
|
||||
Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx))
|
||||
Immediate::ScalarPair(val.into(), Scalar::from_maybe_pointer(vtable, cx))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Prov> {
|
||||
pub fn to_scalar(self) -> Scalar<Prov> {
|
||||
match self {
|
||||
Immediate::Scalar(val) => val,
|
||||
Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"),
|
||||
Immediate::Uninit => ScalarMaybeUninit::Uninit,
|
||||
Immediate::Uninit => bug!("Got uninit where a scalar was expected"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Prov>> {
|
||||
self.to_scalar_or_uninit().check_init()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Prov>, ScalarMaybeUninit<Prov>) {
|
||||
pub fn to_scalar_pair(self) -> (Scalar<Prov>, Scalar<Prov>) {
|
||||
match self {
|
||||
Immediate::ScalarPair(val1, val2) => (val1, val2),
|
||||
Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"),
|
||||
Immediate::Uninit => (ScalarMaybeUninit::Uninit, ScalarMaybeUninit::Uninit),
|
||||
Immediate::Uninit => bug!("Got uninit where a scalar pair was expected"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Prov>, Scalar<Prov>)> {
|
||||
let (val1, val2) = self.to_scalar_or_uninit_pair();
|
||||
Ok((val1.check_init()?, val2.check_init()?))
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
|
||||
|
@ -119,27 +95,17 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
|
|||
/// Helper function for printing a scalar to a FmtPrinter
|
||||
fn p<'a, 'tcx, Prov: Provenance>(
|
||||
cx: FmtPrinter<'a, 'tcx>,
|
||||
s: ScalarMaybeUninit<Prov>,
|
||||
s: Scalar<Prov>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> {
|
||||
match s {
|
||||
ScalarMaybeUninit::Scalar(Scalar::Int(int)) => {
|
||||
cx.pretty_print_const_scalar_int(int, ty, true)
|
||||
}
|
||||
ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _sz)) => {
|
||||
Scalar::Int(int) => cx.pretty_print_const_scalar_int(int, ty, true),
|
||||
Scalar::Ptr(ptr, _sz) => {
|
||||
// Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to
|
||||
// print what is points to, which would fail since it has no access to the local
|
||||
// memory.
|
||||
cx.pretty_print_const_pointer(ptr, ty, true)
|
||||
}
|
||||
ScalarMaybeUninit::Uninit => cx.typed_value(
|
||||
|mut this| {
|
||||
this.write_str("uninit ")?;
|
||||
Ok(this)
|
||||
},
|
||||
|this| this.print_type(ty),
|
||||
" ",
|
||||
),
|
||||
}
|
||||
}
|
||||
ty::tls::with(|tcx| {
|
||||
|
@ -269,7 +235,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||
#[inline]
|
||||
pub fn to_const_int(self) -> ConstInt {
|
||||
assert!(self.layout.ty.is_integral());
|
||||
let int = self.to_scalar().expect("to_const_int doesn't work on scalar pairs").assert_int();
|
||||
let int = self.to_scalar().assert_int();
|
||||
ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +293,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
fn read_immediate_from_mplace_raw(
|
||||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
force: bool,
|
||||
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> {
|
||||
if mplace.layout.is_unsized() {
|
||||
// Don't touch unsized
|
||||
|
@ -345,47 +310,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// case where some of the bytes are initialized and others are not. So, we need an extra
|
||||
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
|
||||
// like a `Scalar` (or `ScalarPair`).
|
||||
let scalar_layout = match mplace.layout.abi {
|
||||
// `if` does not work nested inside patterns, making this a bit awkward to express.
|
||||
Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => Some(s),
|
||||
Abi::Scalar(s) if force => Some(s.primitive()),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(s) = scalar_layout {
|
||||
let size = s.size(self);
|
||||
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
|
||||
let scalar = alloc
|
||||
.read_scalar(alloc_range(Size::ZERO, size), /*read_provenance*/ s.is_ptr())?;
|
||||
return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
|
||||
}
|
||||
let scalar_pair_layout = match mplace.layout.abi {
|
||||
Ok(match mplace.layout.abi {
|
||||
Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => {
|
||||
let size = s.size(self);
|
||||
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
|
||||
let scalar = alloc.read_scalar(
|
||||
alloc_range(Size::ZERO, size),
|
||||
/*read_provenance*/ s.is_ptr(),
|
||||
)?;
|
||||
Some(ImmTy { imm: scalar.into(), layout: mplace.layout })
|
||||
}
|
||||
Abi::ScalarPair(
|
||||
abi::Scalar::Initialized { value: a, .. },
|
||||
abi::Scalar::Initialized { value: b, .. },
|
||||
) => Some((a, b)),
|
||||
Abi::ScalarPair(a, b) if force => Some((a.primitive(), b.primitive())),
|
||||
_ => None,
|
||||
};
|
||||
if let Some((a, b)) = scalar_pair_layout {
|
||||
// We checked `ptr_align` above, so all fields will have the alignment they need.
|
||||
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
|
||||
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
|
||||
let (a_size, b_size) = (a.size(self), b.size(self));
|
||||
let b_offset = a_size.align_to(b.align(self).abi);
|
||||
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
|
||||
let a_val = alloc.read_scalar(
|
||||
alloc_range(Size::ZERO, a_size),
|
||||
/*read_provenance*/ a.is_ptr(),
|
||||
)?;
|
||||
let b_val = alloc
|
||||
.read_scalar(alloc_range(b_offset, b_size), /*read_provenance*/ b.is_ptr())?;
|
||||
return Ok(Some(ImmTy {
|
||||
imm: Immediate::ScalarPair(a_val, b_val),
|
||||
layout: mplace.layout,
|
||||
}));
|
||||
}
|
||||
// Neither a scalar nor scalar pair.
|
||||
return Ok(None);
|
||||
) => {
|
||||
// We checked `ptr_align` above, so all fields will have the alignment they need.
|
||||
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
|
||||
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
|
||||
let (a_size, b_size) = (a.size(self), b.size(self));
|
||||
let b_offset = a_size.align_to(b.align(self).abi);
|
||||
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
|
||||
let a_val = alloc.read_scalar(
|
||||
alloc_range(Size::ZERO, a_size),
|
||||
/*read_provenance*/ a.is_ptr(),
|
||||
)?;
|
||||
let b_val = alloc.read_scalar(
|
||||
alloc_range(b_offset, b_size),
|
||||
/*read_provenance*/ b.is_ptr(),
|
||||
)?;
|
||||
Some(ImmTy {
|
||||
imm: Immediate::ScalarPair(a_val.into(), b_val.into()),
|
||||
layout: mplace.layout,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// Neither a scalar nor scalar pair.
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Try returning an immediate for the operand. If the layout does not permit loading this as an
|
||||
|
@ -394,20 +356,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// succeed! Whether it succeeds depends on whether the layout can be represented
|
||||
/// in an `Immediate`, not on which data is stored there currently.
|
||||
///
|
||||
/// If `force` is `true`, then even scalars with fields that can be ununit will be
|
||||
/// read. This means the load is lossy and should not be written back!
|
||||
/// This flag exists only for validity checking.
|
||||
///
|
||||
/// This is an internal function that should not usually be used; call `read_immediate` instead.
|
||||
/// ConstProp needs it, though.
|
||||
pub fn read_immediate_raw(
|
||||
&self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
force: bool,
|
||||
) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::Provenance>, MPlaceTy<'tcx, M::Provenance>>> {
|
||||
Ok(match src.try_as_mplace() {
|
||||
Ok(ref mplace) => {
|
||||
if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? {
|
||||
if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? {
|
||||
Ok(val)
|
||||
} else {
|
||||
Err(*mplace)
|
||||
|
@ -418,24 +375,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Read an immediate from a place, asserting that that is possible with the given layout.
|
||||
///
|
||||
/// If this suceeds, the `ImmTy` is never `Uninit`.
|
||||
#[inline(always)]
|
||||
pub fn read_immediate(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? {
|
||||
Ok(imm)
|
||||
} else {
|
||||
span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty);
|
||||
if !matches!(
|
||||
op.layout.abi,
|
||||
Abi::Scalar(abi::Scalar::Initialized { .. })
|
||||
| Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. })
|
||||
) {
|
||||
span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty);
|
||||
}
|
||||
let imm = self.read_immediate_raw(op)?.unwrap();
|
||||
if matches!(*imm, Immediate::Uninit) {
|
||||
throw_ub!(InvalidUninitBytes(None));
|
||||
}
|
||||
Ok(imm)
|
||||
}
|
||||
|
||||
/// Read a scalar from a place
|
||||
pub fn read_scalar(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> {
|
||||
Ok(self.read_immediate(op)?.to_scalar_or_uninit())
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
Ok(self.read_immediate(op)?.to_scalar())
|
||||
}
|
||||
|
||||
/// Read a pointer from a place.
|
||||
|
@ -727,7 +693,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// Figure out which discriminant and variant this corresponds to.
|
||||
Ok(match *tag_encoding {
|
||||
TagEncoding::Direct => {
|
||||
let scalar = tag_val.to_scalar()?;
|
||||
let scalar = tag_val.to_scalar();
|
||||
// Generate a specific error if `tag_val` is not an integer.
|
||||
// (`tag_bits` itself is only used for error messages below.)
|
||||
let tag_bits = scalar
|
||||
|
@ -758,7 +724,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
(discr_val, index.0)
|
||||
}
|
||||
TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => {
|
||||
let tag_val = tag_val.to_scalar()?;
|
||||
let tag_val = tag_val.to_scalar();
|
||||
// Compute the variant this niche value/"tag" corresponds to. With niche layout,
|
||||
// discriminant (encoded in niche/tag) and variant index are the same.
|
||||
let variants_start = niche_variants.start().as_u32();
|
||||
|
@ -785,9 +751,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
|
||||
let variant_index_relative_val =
|
||||
self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
|
||||
let variant_index_relative = variant_index_relative_val
|
||||
.to_scalar()?
|
||||
.assert_bits(tag_val.layout.size);
|
||||
let variant_index_relative =
|
||||
variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
|
||||
// Check if this is in the range that indicates an actual discriminant.
|
||||
if variant_index_relative <= u128::from(variants_end - variants_start) {
|
||||
let variant_index_relative = u32::try_from(variant_index_relative)
|
||||
|
|
|
@ -329,21 +329,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
match left.layout.ty.kind() {
|
||||
ty::Char => {
|
||||
assert_eq!(left.layout.ty, right.layout.ty);
|
||||
let left = left.to_scalar()?;
|
||||
let right = right.to_scalar()?;
|
||||
let left = left.to_scalar();
|
||||
let right = right.to_scalar();
|
||||
Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
|
||||
}
|
||||
ty::Bool => {
|
||||
assert_eq!(left.layout.ty, right.layout.ty);
|
||||
let left = left.to_scalar()?;
|
||||
let right = right.to_scalar()?;
|
||||
let left = left.to_scalar();
|
||||
let right = right.to_scalar();
|
||||
Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
|
||||
}
|
||||
ty::Float(fty) => {
|
||||
assert_eq!(left.layout.ty, right.layout.ty);
|
||||
let ty = left.layout.ty;
|
||||
let left = left.to_scalar()?;
|
||||
let right = right.to_scalar()?;
|
||||
let left = left.to_scalar();
|
||||
let right = right.to_scalar();
|
||||
Ok(match fty {
|
||||
FloatTy::F32 => {
|
||||
self.binary_float_op(bin_op, ty, left.to_f32()?, right.to_f32()?)
|
||||
|
@ -363,8 +363,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
right.layout.ty
|
||||
);
|
||||
|
||||
let l = left.to_scalar()?.to_bits(left.layout.size)?;
|
||||
let r = right.to_scalar()?.to_bits(right.layout.size)?;
|
||||
let l = left.to_scalar().to_bits(left.layout.size)?;
|
||||
let r = right.to_scalar().to_bits(right.layout.size)?;
|
||||
self.binary_int_op(bin_op, l, left.layout, r, right.layout)
|
||||
}
|
||||
_ if left.layout.ty.is_any_ptr() => {
|
||||
|
@ -410,7 +410,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
use rustc_middle::mir::UnOp::*;
|
||||
|
||||
let layout = val.layout;
|
||||
let val = val.to_scalar()?;
|
||||
let val = val.to_scalar();
|
||||
trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
|
||||
|
||||
match layout.ty.kind() {
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, Vari
|
|||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
|
||||
ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
|
||||
Pointer, Provenance, Scalar, ScalarMaybeUninit,
|
||||
Pointer, Provenance, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
|
@ -254,8 +254,6 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
|||
// These are defined here because they produce a place.
|
||||
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
/// Note: do not call `as_ref` on the resulting place. This function should only be used to
|
||||
/// read from the resulting mplace, not to get its address back.
|
||||
pub fn try_as_mplace(&self) -> Result<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
match **self {
|
||||
Operand::Indirect(mplace) => {
|
||||
|
@ -267,8 +265,6 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
|
||||
#[inline(always)]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
/// Note: do not call `as_ref` on the resulting place. This function should only be used to
|
||||
/// read from the resulting mplace, not to get its address back.
|
||||
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
|
||||
self.try_as_mplace().unwrap()
|
||||
}
|
||||
|
@ -312,7 +308,7 @@ where
|
|||
let layout = self.layout_of(pointee_type)?;
|
||||
let (ptr, meta) = match **val {
|
||||
Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None),
|
||||
Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta.check_init()?)),
|
||||
Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta)),
|
||||
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
|
||||
};
|
||||
|
||||
|
@ -467,7 +463,7 @@ where
|
|||
#[inline(always)]
|
||||
pub fn write_scalar(
|
||||
&mut self,
|
||||
val: impl Into<ScalarMaybeUninit<M::Provenance>>,
|
||||
val: impl Into<Scalar<M::Provenance>>,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.write_immediate(Immediate::Scalar(val.into()), dest)
|
||||
|
@ -644,7 +640,7 @@ where
|
|||
|
||||
// Let us see if the layout is simple so we take a shortcut,
|
||||
// avoid force_allocation.
|
||||
let src = match self.read_immediate_raw(src, /*force*/ false)? {
|
||||
let src = match self.read_immediate_raw(src)? {
|
||||
Ok(src_val) => {
|
||||
assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
|
||||
assert!(
|
||||
|
|
|
@ -129,8 +129,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
Assert { ref cond, expected, ref msg, target, cleanup } => {
|
||||
let cond_val =
|
||||
self.read_immediate(&self.eval_operand(cond, None)?)?.to_scalar()?.to_bool()?;
|
||||
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
|
||||
if expected == cond_val {
|
||||
self.go_to_block(target);
|
||||
} else {
|
||||
|
|
|
@ -20,8 +20,8 @@ use rustc_target::abi::{Abi, Scalar as ScalarAbi, Size, VariantIdx, Variants, Wr
|
|||
use std::hash::Hash;
|
||||
|
||||
use super::{
|
||||
alloc_range, CheckInAllocMsg, GlobalAlloc, Immediate, InterpCx, InterpResult, MPlaceTy,
|
||||
Machine, MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
|
||||
alloc_range, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
|
||||
Machine, MemPlaceMeta, OpTy, Scalar, ValueVisitor,
|
||||
};
|
||||
|
||||
macro_rules! throw_validation_failure {
|
||||
|
@ -304,6 +304,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
Ok(r)
|
||||
}
|
||||
|
||||
fn read_immediate(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
expected: &str,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
Ok(try_validation!(
|
||||
self.ecx.read_immediate(op),
|
||||
self.path,
|
||||
err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "{expected}" },
|
||||
err_ub!(InvalidUninitBytes(None)) => { "uninitialized memory" } expected { "{expected}" }
|
||||
))
|
||||
}
|
||||
|
||||
fn read_scalar(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
expected: &str,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
Ok(self.read_immediate(op, expected)?.to_scalar())
|
||||
}
|
||||
|
||||
fn check_wide_ptr_meta(
|
||||
&mut self,
|
||||
meta: MemPlaceMeta<M::Provenance>,
|
||||
|
@ -348,18 +369,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
value: &OpTy<'tcx, M::Provenance>,
|
||||
kind: &str,
|
||||
) -> InterpResult<'tcx> {
|
||||
let value = try_validation!(
|
||||
self.ecx.read_immediate(value),
|
||||
self.path,
|
||||
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||
);
|
||||
let place = self.ecx.ref_to_mplace(&self.read_immediate(value, &format!("a {kind}"))?)?;
|
||||
// Handle wide pointers.
|
||||
// Check metadata early, for better diagnostics
|
||||
let place = try_validation!(
|
||||
self.ecx.ref_to_mplace(&value),
|
||||
self.path,
|
||||
err_ub!(InvalidUninitBytes(None)) => { "uninitialized {}", kind },
|
||||
);
|
||||
if place.layout.is_unsized() {
|
||||
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
||||
}
|
||||
|
@ -454,28 +466,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn read_scalar(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<M::Provenance>> {
|
||||
Ok(try_validation!(
|
||||
self.ecx.read_scalar(op),
|
||||
self.path,
|
||||
err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
|
||||
))
|
||||
}
|
||||
|
||||
fn read_immediate_forced(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Immediate<M::Provenance>> {
|
||||
Ok(*try_validation!(
|
||||
self.ecx.read_immediate_raw(op, /*force*/ true),
|
||||
self.path,
|
||||
err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
|
||||
).unwrap())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn try_visit_primitive(
|
||||
|
@ -486,39 +476,39 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
let ty = value.layout.ty;
|
||||
match ty.kind() {
|
||||
ty::Bool => {
|
||||
let value = self.read_scalar(value)?;
|
||||
let value = self.read_scalar(value, "a boolean")?;
|
||||
try_validation!(
|
||||
value.to_bool(),
|
||||
self.path,
|
||||
err_ub!(InvalidBool(..)) | err_ub!(InvalidUninitBytes(None)) =>
|
||||
err_ub!(InvalidBool(..)) =>
|
||||
{ "{:x}", value } expected { "a boolean" },
|
||||
);
|
||||
Ok(true)
|
||||
}
|
||||
ty::Char => {
|
||||
let value = self.read_scalar(value)?;
|
||||
let value = self.read_scalar(value, "a unicode scalar value")?;
|
||||
try_validation!(
|
||||
value.to_char(),
|
||||
self.path,
|
||||
err_ub!(InvalidChar(..)) | err_ub!(InvalidUninitBytes(None)) =>
|
||||
err_ub!(InvalidChar(..)) =>
|
||||
{ "{:x}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" },
|
||||
);
|
||||
Ok(true)
|
||||
}
|
||||
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
|
||||
let value = self.read_scalar(value)?;
|
||||
// NOTE: Keep this in sync with the array optimization for int/float
|
||||
// types below!
|
||||
try_validation!(
|
||||
value.check_init(),
|
||||
self.path,
|
||||
err_ub!(InvalidUninitBytes(..)) =>
|
||||
{ "{:x}", value } expected { "initialized bytes" }
|
||||
);
|
||||
let value = self.read_scalar(
|
||||
value,
|
||||
if matches!(ty.kind(), ty::Float(..)) {
|
||||
"a floating point number"
|
||||
} else {
|
||||
"an integer"
|
||||
},
|
||||
)?;
|
||||
// As a special exception we *do* match on a `Scalar` here, since we truly want
|
||||
// to know its underlying representation (and *not* cast it to an integer).
|
||||
let is_ptr = value.check_init().map_or(false, |v| matches!(v, Scalar::Ptr(..)));
|
||||
if is_ptr {
|
||||
if matches!(value, Scalar::Ptr(..)) {
|
||||
throw_validation_failure!(self.path,
|
||||
{ "{:x}", value } expected { "plain (non-pointer) bytes" }
|
||||
)
|
||||
|
@ -529,12 +519,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
// We are conservative with uninit for integers, but try to
|
||||
// actually enforce the strict rules for raw pointers (mostly because
|
||||
// that lets us re-use `ref_to_mplace`).
|
||||
let place = try_validation!(
|
||||
self.ecx.read_immediate(value).and_then(|ref i| self.ecx.ref_to_mplace(i)),
|
||||
self.path,
|
||||
err_ub!(InvalidUninitBytes(None)) => { "uninitialized raw pointer" },
|
||||
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||
);
|
||||
let place =
|
||||
self.ecx.ref_to_mplace(&self.read_immediate(value, "a raw pointer")?)?;
|
||||
if place.layout.is_unsized() {
|
||||
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
||||
}
|
||||
|
@ -555,12 +541,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
Ok(true)
|
||||
}
|
||||
ty::FnPtr(_sig) => {
|
||||
let value = try_validation!(
|
||||
self.ecx.read_scalar(value).and_then(|v| v.check_init()),
|
||||
self.path,
|
||||
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||
err_ub!(InvalidUninitBytes(None)) => { "uninitialized bytes" } expected { "a proper pointer or integer value" },
|
||||
);
|
||||
let value = self.read_scalar(value, "a function pointer")?;
|
||||
|
||||
// If we check references recursively, also check that this points to a function.
|
||||
if let Some(_) = self.ref_tracking {
|
||||
|
@ -611,34 +592,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
|
||||
fn visit_scalar(
|
||||
&mut self,
|
||||
scalar: ScalarMaybeUninit<M::Provenance>,
|
||||
scalar: Scalar<M::Provenance>,
|
||||
scalar_layout: ScalarAbi,
|
||||
) -> InterpResult<'tcx> {
|
||||
// We check `is_full_range` in a slightly complicated way because *if* we are checking
|
||||
// number validity, then we want to ensure that `Scalar::Initialized` is indeed initialized,
|
||||
// i.e. that we go over the `check_init` below.
|
||||
let size = scalar_layout.size(self.ecx);
|
||||
let is_full_range = match scalar_layout {
|
||||
ScalarAbi::Initialized { .. } => false, // not "full" since uninit is not valid
|
||||
ScalarAbi::Union { .. } => true,
|
||||
};
|
||||
if is_full_range {
|
||||
// Nothing to check. Cruciall we don't even `read_scalar` until here, since that would
|
||||
// fail for `Union` scalars!
|
||||
return Ok(());
|
||||
}
|
||||
// We have something to check: it must at least be initialized.
|
||||
let valid_range = scalar_layout.valid_range(self.ecx);
|
||||
let WrappingRange { start, end } = valid_range;
|
||||
let max_value = size.unsigned_int_max();
|
||||
assert!(end <= max_value);
|
||||
let value = try_validation!(
|
||||
scalar.check_init(),
|
||||
self.path,
|
||||
err_ub!(InvalidUninitBytes(None)) => { "{:x}", scalar }
|
||||
expected { "something {}", wrapping_range_format(valid_range, max_value) },
|
||||
);
|
||||
let bits = match value.try_to_int() {
|
||||
let bits = match scalar.try_to_int() {
|
||||
Ok(int) => int.assert_bits(size),
|
||||
Err(_) => {
|
||||
// So this is a pointer then, and casting to an int failed.
|
||||
|
@ -646,7 +608,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
// We support 2 kinds of ranges here: full range, and excluding zero.
|
||||
if start == 1 && end == max_value {
|
||||
// Only null is the niche. So make sure the ptr is NOT null.
|
||||
if self.ecx.scalar_may_be_null(value)? {
|
||||
if self.ecx.scalar_may_be_null(scalar)? {
|
||||
throw_validation_failure!(self.path,
|
||||
{ "a potentially null pointer" }
|
||||
expected {
|
||||
|
@ -800,10 +762,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
);
|
||||
}
|
||||
Abi::Scalar(scalar_layout) => {
|
||||
// We use a 'forced' read because we always need a `Immediate` here
|
||||
// and treating "partially uninit" as "fully uninit" is fine for us.
|
||||
let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
|
||||
self.visit_scalar(scalar, scalar_layout)?;
|
||||
if !scalar_layout.is_uninit_valid() {
|
||||
// There is something to check here.
|
||||
let scalar = self.read_scalar(op, "initiailized scalar value")?;
|
||||
self.visit_scalar(scalar, scalar_layout)?;
|
||||
}
|
||||
}
|
||||
Abi::ScalarPair(a_layout, b_layout) => {
|
||||
// There is no `rustc_layout_scalar_valid_range_start` for pairs, so
|
||||
|
@ -811,10 +774,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
// but that can miss bugs in layout computation. Layout computation
|
||||
// is subtle due to enums having ScalarPair layout, where one field
|
||||
// is the discriminant.
|
||||
if cfg!(debug_assertions) {
|
||||
// We use a 'forced' read because we always need a `Immediate` here
|
||||
// and treating "partially uninit" as "fully uninit" is fine for us.
|
||||
let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
|
||||
if cfg!(debug_assertions)
|
||||
&& !a_layout.is_uninit_valid()
|
||||
&& !b_layout.is_uninit_valid()
|
||||
{
|
||||
// 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.
|
||||
let (a, b) =
|
||||
self.read_immediate(op, "initiailized scalar value")?.to_scalar_pair();
|
||||
self.visit_scalar(a, a_layout)?;
|
||||
self.visit_scalar(b, b_layout)?;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue