-
- /// Raises an error if the offset moves the pointer outside of its allocation.
- /// We consider ZSTs their own huge allocation that doesn't overlap with anything (and nothing
- /// moves in there because the size is 0). We also consider the NULL pointer its own separate
- /// allocation, and all the remaining integers pointers their own allocation.
- fn pointer_offset_inbounds(
- &self,
- ptr: Scalar<Tag>,
- pointee_ty: Ty<'tcx>,
- offset: i64,
- ) -> InterpResult<'tcx, Scalar<Tag>> {
- let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
- let offset = offset
- .checked_mul(pointee_size)
- .ok_or_else(|| err_panic!(Overflow(mir::BinOp::Mul)))?;
- // Now let's see what kind of pointer this is.
- let ptr = if offset == 0 {
- match ptr {
- Scalar::Ptr(ptr) => ptr,
- Scalar::Raw { .. } => {
- // Offset 0 on an integer. We accept that, pretending there is
- // a little zero-sized allocation here.
- return Ok(ptr);
- }
- }
- } else {
- // Offset > 0. We *require* a pointer.
- self.force_ptr(ptr)?
- };
- // Both old and new pointer must be in-bounds of a *live* allocation.
- // (Of the same allocation, but that part is trivial with our representation.)
- self.pointer_inbounds(ptr)?;
- let ptr = ptr.signed_offset(offset, self)?;
- self.pointer_inbounds(ptr)?;
- Ok(Scalar::Ptr(ptr))
- }