use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
self,
- interpret::{ConstValue, GlobalId, InterpResult, Scalar},
+ interpret::{ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar},
BinOp,
};
use rustc_middle::ty;
mod caller_location;
mod type_name;
-fn numeric_intrinsic<Tag>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Tag> {
+fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
let size = match kind {
Primitive::Int(integer, _) => integer.size(),
_ => bug!("invalid `{}` argument: {:?}", name, bits),
pub fn emulate_intrinsic(
&mut self,
instance: ty::Instance<'tcx>,
- args: &[OpTy<'tcx, M::PointerTag>],
- dest: &PlaceTy<'tcx, M::PointerTag>,
+ args: &[OpTy<'tcx, M::Provenance>],
+ dest: &PlaceTy<'tcx, M::Provenance>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx, bool> {
let substs = instance.substs;
// We managed to find a valid allocation for one pointer, but not the other.
// That means they are definitely not pointing to the same allocation.
throw_ub_format!(
- "{} called on pointers into different allocations",
+ "`{}` called on pointers into different allocations",
intrinsic_name
);
}
// Found allocation for both. They must be into the same allocation.
if a_alloc_id != b_alloc_id {
throw_ub_format!(
- "{} called on pointers into different allocations",
+ "`{}` called on pointers into different allocations",
intrinsic_name
);
}
};
// Compute distance.
- let distance = {
- // The subtraction is always done in `isize` to enforce
- // the "no more than `isize::MAX` apart" requirement.
- let a_offset = ImmTy::from_uint(a_offset, isize_layout);
- let b_offset = ImmTy::from_uint(b_offset, isize_layout);
- let (val, overflowed, _ty) =
- self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?;
+ let dist = {
+ // Addresses are unsigned, so this is a `usize` computation. We have to do the
+ // overflow check separately anyway.
+ let (val, overflowed, _ty) = {
+ let a_offset = ImmTy::from_uint(a_offset, usize_layout);
+ let b_offset = ImmTy::from_uint(b_offset, usize_layout);
+ self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?
+ };
if overflowed {
- throw_ub_format!("pointers were too far apart for {}", intrinsic_name);
+ // a < b
+ if intrinsic_name == sym::ptr_offset_from_unsigned {
+ throw_ub_format!(
+ "`{}` called when first pointer has smaller offset than second: {} < {}",
+ intrinsic_name,
+ a_offset,
+ b_offset,
+ );
+ }
+ // The signed form of the intrinsic allows this. If we interpret the
+ // difference as isize, we'll get the proper signed difference. If that
+ // seems *positive*, they were more than isize::MAX apart.
+ let dist = val.to_machine_isize(self)?;
+ if dist >= 0 {
+ throw_ub_format!(
+ "`{}` called when first pointer is too far before second",
+ intrinsic_name
+ );
+ }
+ dist
+ } else {
+ // b >= a
+ let dist = val.to_machine_isize(self)?;
+ // If converting to isize produced a *negative* result, we had an overflow
+ // because they were more than isize::MAX apart.
+ if dist < 0 {
+ throw_ub_format!(
+ "`{}` called when first pointer is too far ahead of second",
+ intrinsic_name
+ );
+ }
+ dist
}
- val.to_machine_isize(self)?
};
// Check that the range between them is dereferenceable ("in-bounds or one past the
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
- let min_ptr = if distance >= 0 { b } else { a };
+ let min_ptr = if dist >= 0 { b } else { a };
self.check_ptr_access_align(
min_ptr,
- Size::from_bytes(distance.unsigned_abs()),
+ Size::from_bytes(dist.unsigned_abs()),
Align::ONE,
CheckInAllocMsg::OffsetFromTest,
)?;
- if intrinsic_name == sym::ptr_offset_from_unsigned && distance < 0 {
- throw_ub_format!(
- "{} called when first pointer has smaller offset than second: {} < {}",
- intrinsic_name,
- a_offset,
- b_offset,
- );
- }
-
// Perform division by size to compute return value.
let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
+ assert!(0 <= dist && dist <= self.machine_isize_max());
usize_layout
} else {
+ assert!(self.machine_isize_min() <= dist && dist <= self.machine_isize_max());
isize_layout
};
let pointee_layout = self.layout_of(substs.type_at(0))?;
// If ret_layout is unsigned, we checked that so is the distance, so we are good.
- let val = ImmTy::from_int(distance, ret_layout);
+ let val = ImmTy::from_int(dist, ret_layout);
let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
self.exact_div(&val, &size, dest)?;
}
for i in 0..dest_len {
let place = self.mplace_index(&dest, i)?;
- let value =
- if i == index { *elem } else { self.mplace_index(&input, i)?.into() };
+ let value = if i == index {
+ elem.clone()
+ } else {
+ self.mplace_index(&input, i)?.into()
+ };
self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?;
}
}
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
self.write_scalar(result, dest)?;
}
+
+ sym::vtable_size => {
+ let ptr = self.read_pointer(&args[0])?;
+ let (size, _align) = self.get_vtable_size_and_align(ptr)?;
+ self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?;
+ }
+ sym::vtable_align => {
+ let ptr = self.read_pointer(&args[0])?;
+ let (_size, align) = self.get_vtable_size_and_align(ptr)?;
+ self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?;
+ }
+
_ => return Ok(false),
}
pub fn exact_div(
&mut self,
- a: &ImmTy<'tcx, M::PointerTag>,
- b: &ImmTy<'tcx, M::PointerTag>,
- dest: &PlaceTy<'tcx, M::PointerTag>,
+ a: &ImmTy<'tcx, M::Provenance>,
+ b: &ImmTy<'tcx, M::Provenance>,
+ dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
// Performs an exact division, resulting in undefined behavior where
// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`.
pub fn saturating_arith(
&self,
mir_op: BinOp,
- l: &ImmTy<'tcx, M::PointerTag>,
- r: &ImmTy<'tcx, M::PointerTag>,
- ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+ l: &ImmTy<'tcx, M::Provenance>,
+ r: &ImmTy<'tcx, M::Provenance>,
+ ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
let (val, overflowed, _ty) = self.overflowing_binary_op(mir_op, l, r)?;
Ok(if overflowed {
/// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value.
pub fn ptr_offset_inbounds(
&self,
- ptr: Pointer<Option<M::PointerTag>>,
+ ptr: Pointer<Option<M::Provenance>>,
pointee_ty: Ty<'tcx>,
offset_count: i64,
- ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
+ ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
// We cannot overflow i64 as a type's size must be <= isize::MAX.
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
// The computed offset, in bytes, must not overflow an isize.
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
pub(crate) fn copy_intrinsic(
&mut self,
- src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+ src: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
let count = self.read_scalar(&count)?.to_machine_usize(self)?;
pub(crate) fn write_bytes_intrinsic(
&mut self,
- dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
+ dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ byte: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
) -> InterpResult<'tcx> {
let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?;
pub(crate) fn raw_eq_intrinsic(
&mut self,
- lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::PointerTag>,
- ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
+ lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
+ ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?;
assert!(!layout.is_unsized());