Skip to content

Commit 7d2f64b

Browse files
committed
librustc_mir: Add support for const fn offset/arith_offset
Miri's pointer_offset_inbounds implementation has been moved into librustc_mir as ptr_offset_inbounds (to avoid breaking miri on a nightly update). The comments have been slightly reworked to better match `offset`'s external documentation about what causes UB. The intrinsic implementations are taken directly from miri. Signed-off-by: Joe Richey <joerichey@google.com>
1 parent 9e2a6a2 commit 7d2f64b

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

src/librustc_mir/interpret/intrinsics.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ use rustc_middle::mir::{
1212
};
1313
use rustc_middle::ty;
1414
use rustc_middle::ty::subst::SubstsRef;
15-
use rustc_middle::ty::TyCtxt;
15+
use rustc_middle::ty::{Ty, TyCtxt};
1616
use rustc_span::symbol::{sym, Symbol};
1717
use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size};
1818

19-
use super::{ImmTy, InterpCx, Machine, OpTy, PlaceTy};
19+
use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy};
2020

2121
mod caller_location;
2222
mod type_name;
@@ -273,7 +273,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
273273
let result = Scalar::from_uint(truncated_bits, layout.size);
274274
self.write_scalar(result, dest)?;
275275
}
276+
sym::offset => {
277+
let ptr = self.read_scalar(args[0])?.not_undef()?;
278+
let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?;
279+
let pointee_ty = substs.type_at(0);
276280

281+
let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
282+
self.write_scalar(offset_ptr, dest)?;
283+
}
284+
sym::arith_offset => {
285+
let ptr = self.read_scalar(args[0])?.not_undef()?;
286+
let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?;
287+
let pointee_ty = substs.type_at(0);
288+
289+
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
290+
let offset_bytes = offset_count.wrapping_mul(pointee_size);
291+
let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self);
292+
self.write_scalar(offset_ptr, dest)?;
293+
}
277294
sym::ptr_offset_from => {
278295
let a = self.read_immediate(args[0])?.to_scalar()?;
279296
let b = self.read_immediate(args[1])?.to_scalar()?;
@@ -403,4 +420,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
403420
// `Rem` says this is all right, so we can let `Div` do its job.
404421
self.binop_ignore_overflow(BinOp::Div, a, b, dest)
405422
}
423+
424+
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
425+
/// allocation. For integer pointers, we consider each of them their own tiny allocation of size
426+
/// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value.
427+
pub fn ptr_offset_inbounds(
428+
&self,
429+
ptr: Scalar<M::PointerTag>,
430+
pointee_ty: Ty<'tcx>,
431+
offset_count: i64,
432+
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
433+
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
434+
// The computed offset, in bytes, cannot overflow an isize.
435+
let offset_bytes = offset_count
436+
.checked_mul(pointee_size)
437+
.ok_or(err_ub_format!("inbounds pointer arithmetic: overflow computing offset"))?;
438+
// The offset being in bounds cannot rely on "wrapping around" the address space.
439+
// So, first rule out overflows in the pointer arithmetic.
440+
let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?;
441+
// ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
442+
// memory between these pointers must be accessible. Note that we do not require the
443+
// pointers to be properly aligned (unlike a read/write operation).
444+
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
445+
let size = offset_bytes.checked_abs().unwrap();
446+
// This call handles checking for integer/NULL pointers.
447+
self.memory.check_ptr_access_align(
448+
min_ptr,
449+
Size::from_bytes(size),
450+
None,
451+
CheckInAllocMsg::InboundsTest,
452+
)?;
453+
Ok(offset_ptr)
454+
}
406455
}

src/librustc_span/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ symbols! {
147147
Arc,
148148
Arguments,
149149
ArgumentV1,
150+
arith_offset,
150151
arm_target_feature,
151152
asm,
152153
assert,
@@ -507,6 +508,7 @@ symbols! {
507508
not,
508509
note,
509510
object_safe_for_dispatch,
511+
offset,
510512
Ok,
511513
omit_gdb_pretty_printer_section,
512514
on,

0 commit comments

Comments
 (0)