Skip to content

Commit 642e47e

Browse files
authored
Rollup merge of rust-lang#71500 - josephlr:offset, r=oli-obk,RalfJung
Make pointer offset methods/intrinsics const Implements rust-lang#71499 using [the implementations from miri](https://github.com/rust-lang/miri/blob/52f5d202bdcfe8986f0615845f8d1647ab8a2c6a/src/shims/intrinsics.rs#L96-L112). I added some tests what's allowed and what's UB. Let me know if any other cases should be added. CC: @RalfJung @oli-obk
2 parents 7823651 + 7d5415b commit 642e47e

File tree

13 files changed

+424
-49
lines changed

13 files changed

+424
-49
lines changed

src/libcore/intrinsics.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ extern "rust-intrinsic" {
13141314
/// The stabilized version of this intrinsic is
13151315
/// [`std::pointer::offset`](../../std/primitive.pointer.html#method.offset).
13161316
#[must_use = "returns a new pointer rather than modifying its argument"]
1317+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
13171318
pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
13181319

13191320
/// Calculates the offset from a pointer, potentially wrapping.
@@ -1331,6 +1332,7 @@ extern "rust-intrinsic" {
13311332
/// The stabilized version of this intrinsic is
13321333
/// [`std::pointer::wrapping_offset`](../../std/primitive.pointer.html#method.wrapping_offset).
13331334
#[must_use = "returns a new pointer rather than modifying its argument"]
1335+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
13341336
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
13351337

13361338
/// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#![feature(const_panic)]
8686
#![feature(const_fn_union)]
8787
#![feature(const_generics)]
88+
#![feature(const_ptr_offset)]
8889
#![feature(const_ptr_offset_from)]
8990
#![feature(const_result)]
9091
#![feature(const_slice_from_raw_parts)]

src/libcore/ptr/const_ptr.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ impl<T: ?Sized> *const T {
151151
/// ```
152152
#[stable(feature = "rust1", since = "1.0.0")]
153153
#[must_use = "returns a new pointer rather than modifying its argument"]
154+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
154155
#[inline]
155-
pub unsafe fn offset(self, count: isize) -> *const T
156+
pub const unsafe fn offset(self, count: isize) -> *const T
156157
where
157158
T: Sized,
158159
{
@@ -210,8 +211,9 @@ impl<T: ?Sized> *const T {
210211
/// ```
211212
#[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
212213
#[must_use = "returns a new pointer rather than modifying its argument"]
214+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
213215
#[inline]
214-
pub fn wrapping_offset(self, count: isize) -> *const T
216+
pub const fn wrapping_offset(self, count: isize) -> *const T
215217
where
216218
T: Sized,
217219
{
@@ -393,8 +395,9 @@ impl<T: ?Sized> *const T {
393395
/// ```
394396
#[stable(feature = "pointer_methods", since = "1.26.0")]
395397
#[must_use = "returns a new pointer rather than modifying its argument"]
398+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
396399
#[inline]
397-
pub unsafe fn add(self, count: usize) -> Self
400+
pub const unsafe fn add(self, count: usize) -> Self
398401
where
399402
T: Sized,
400403
{
@@ -455,8 +458,9 @@ impl<T: ?Sized> *const T {
455458
/// ```
456459
#[stable(feature = "pointer_methods", since = "1.26.0")]
457460
#[must_use = "returns a new pointer rather than modifying its argument"]
461+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
458462
#[inline]
459-
pub unsafe fn sub(self, count: usize) -> Self
463+
pub const unsafe fn sub(self, count: usize) -> Self
460464
where
461465
T: Sized,
462466
{
@@ -511,8 +515,9 @@ impl<T: ?Sized> *const T {
511515
/// ```
512516
#[stable(feature = "pointer_methods", since = "1.26.0")]
513517
#[must_use = "returns a new pointer rather than modifying its argument"]
518+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
514519
#[inline]
515-
pub fn wrapping_add(self, count: usize) -> Self
520+
pub const fn wrapping_add(self, count: usize) -> Self
516521
where
517522
T: Sized,
518523
{
@@ -567,8 +572,9 @@ impl<T: ?Sized> *const T {
567572
/// ```
568573
#[stable(feature = "pointer_methods", since = "1.26.0")]
569574
#[must_use = "returns a new pointer rather than modifying its argument"]
575+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
570576
#[inline]
571-
pub fn wrapping_sub(self, count: usize) -> Self
577+
pub const fn wrapping_sub(self, count: usize) -> Self
572578
where
573579
T: Sized,
574580
{

src/libcore/ptr/mut_ptr.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ impl<T: ?Sized> *mut T {
145145
/// ```
146146
#[stable(feature = "rust1", since = "1.0.0")]
147147
#[must_use = "returns a new pointer rather than modifying its argument"]
148+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
148149
#[inline]
149-
pub unsafe fn offset(self, count: isize) -> *mut T
150+
pub const unsafe fn offset(self, count: isize) -> *mut T
150151
where
151152
T: Sized,
152153
{
@@ -203,8 +204,9 @@ impl<T: ?Sized> *mut T {
203204
/// ```
204205
#[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
205206
#[must_use = "returns a new pointer rather than modifying its argument"]
207+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
206208
#[inline]
207-
pub fn wrapping_offset(self, count: isize) -> *mut T
209+
pub const fn wrapping_offset(self, count: isize) -> *mut T
208210
where
209211
T: Sized,
210212
{
@@ -439,8 +441,9 @@ impl<T: ?Sized> *mut T {
439441
/// ```
440442
#[stable(feature = "pointer_methods", since = "1.26.0")]
441443
#[must_use = "returns a new pointer rather than modifying its argument"]
444+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
442445
#[inline]
443-
pub unsafe fn add(self, count: usize) -> Self
446+
pub const unsafe fn add(self, count: usize) -> Self
444447
where
445448
T: Sized,
446449
{
@@ -501,8 +504,9 @@ impl<T: ?Sized> *mut T {
501504
/// ```
502505
#[stable(feature = "pointer_methods", since = "1.26.0")]
503506
#[must_use = "returns a new pointer rather than modifying its argument"]
507+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
504508
#[inline]
505-
pub unsafe fn sub(self, count: usize) -> Self
509+
pub const unsafe fn sub(self, count: usize) -> Self
506510
where
507511
T: Sized,
508512
{
@@ -557,8 +561,9 @@ impl<T: ?Sized> *mut T {
557561
/// ```
558562
#[stable(feature = "pointer_methods", since = "1.26.0")]
559563
#[must_use = "returns a new pointer rather than modifying its argument"]
564+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
560565
#[inline]
561-
pub fn wrapping_add(self, count: usize) -> Self
566+
pub const fn wrapping_add(self, count: usize) -> Self
562567
where
563568
T: Sized,
564569
{
@@ -613,8 +618,9 @@ impl<T: ?Sized> *mut T {
613618
/// ```
614619
#[stable(feature = "pointer_methods", since = "1.26.0")]
615620
#[must_use = "returns a new pointer rather than modifying its argument"]
621+
#[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")]
616622
#[inline]
617-
pub fn wrapping_sub(self, count: usize) -> Self
623+
pub const fn wrapping_sub(self, count: usize) -> Self
618624
where
619625
T: Sized,
620626
{

src/librustc_middle/mir/interpret/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,12 @@ pub fn truncate(value: u128, size: Size) -> u128 {
598598
// Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
599599
(value << shift) >> shift
600600
}
601+
602+
/// Computes the unsigned absolute value without wrapping or panicking.
603+
#[inline]
604+
pub fn uabs(value: i64) -> u64 {
605+
// The only tricky part here is if value == i64::MIN. In that case,
606+
// wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64
607+
// gives 2^63, the correct value.
608+
value.wrapping_abs() as u64
609+
}

src/librustc_middle/mir/interpret/pointer.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{AllocId, InterpResult};
1+
use super::{uabs, AllocId, InterpResult};
22

33
use rustc_macros::HashStable;
44
use rustc_target::abi::{HasDataLayout, Size};
@@ -24,6 +24,12 @@ pub trait PointerArithmetic: HasDataLayout {
2424
u64::try_from(max_usize_plus_1 - 1).unwrap()
2525
}
2626

27+
#[inline]
28+
fn machine_isize_min(&self) -> i64 {
29+
let max_isize_plus_1 = 1i128 << (self.pointer_size().bits() - 1);
30+
i64::try_from(-max_isize_plus_1).unwrap()
31+
}
32+
2733
#[inline]
2834
fn machine_isize_max(&self) -> i64 {
2935
let max_isize_plus_1 = 1u128 << (self.pointer_size().bits() - 1);
@@ -42,21 +48,23 @@ pub trait PointerArithmetic: HasDataLayout {
4248

4349
#[inline]
4450
fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
51+
// We do not need to check if i fits in a machine usize. If it doesn't,
52+
// either the wrapping_add will wrap or res will not fit in a pointer.
4553
let res = val.overflowing_add(i);
4654
self.truncate_to_ptr(res)
4755
}
4856

4957
#[inline]
5058
fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
51-
if i < 0 {
52-
// Trickery to ensure that `i64::MIN` works fine: compute `n = -i`.
53-
// This formula only works for true negative values; it overflows for zero!
54-
let n = u64::MAX - (i as u64) + 1;
55-
let res = val.overflowing_sub(n);
56-
self.truncate_to_ptr(res)
59+
// We need to make sure that i fits in a machine isize.
60+
let n = uabs(i);
61+
if i >= 0 {
62+
let (val, over) = self.overflowing_offset(val, n);
63+
(val, over || i > self.machine_isize_max())
5764
} else {
58-
// `i >= 0`, so the cast is safe.
59-
self.overflowing_offset(val, i as u64)
65+
let res = val.overflowing_sub(n);
66+
let (val, over) = self.truncate_to_ptr(res);
67+
(val, over || i < self.machine_isize_min())
6068
}
6169
}
6270

src/librustc_mir/interpret/intrinsics.rs

+54-3
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
33
//! and miri.
44
5+
use std::convert::TryFrom;
6+
57
use rustc_hir::def_id::DefId;
68
use rustc_middle::mir::{
79
self,
8-
interpret::{ConstValue, GlobalId, InterpResult, Scalar},
10+
interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar},
911
BinOp,
1012
};
1113
use rustc_middle::ty;
1214
use rustc_middle::ty::subst::SubstsRef;
13-
use rustc_middle::ty::TyCtxt;
15+
use rustc_middle::ty::{Ty, TyCtxt};
1416
use rustc_span::symbol::{sym, Symbol};
1517
use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size};
1618

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

1921
mod caller_location;
2022
mod type_name;
@@ -279,7 +281,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279281
let result = Scalar::from_uint(truncated_bits, layout.size);
280282
self.write_scalar(result, dest)?;
281283
}
284+
sym::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 offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
290+
self.write_scalar(offset_ptr, dest)?;
291+
}
292+
sym::arith_offset => {
293+
let ptr = self.read_scalar(args[0])?.not_undef()?;
294+
let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?;
295+
let pointee_ty = substs.type_at(0);
282296

297+
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
298+
let offset_bytes = offset_count.wrapping_mul(pointee_size);
299+
let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self);
300+
self.write_scalar(offset_ptr, dest)?;
301+
}
283302
sym::ptr_offset_from => {
284303
let a = self.read_immediate(args[0])?.to_scalar()?;
285304
let b = self.read_immediate(args[1])?.to_scalar()?;
@@ -409,4 +428,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
409428
// `Rem` says this is all right, so we can let `Div` do its job.
410429
self.binop_ignore_overflow(BinOp::Div, a, b, dest)
411430
}
431+
432+
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
433+
/// allocation. For integer pointers, we consider each of them their own tiny allocation of size
434+
/// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value.
435+
pub fn ptr_offset_inbounds(
436+
&self,
437+
ptr: Scalar<M::PointerTag>,
438+
pointee_ty: Ty<'tcx>,
439+
offset_count: i64,
440+
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
441+
// We cannot overflow i64 as a type's size must be <= isize::MAX.
442+
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
443+
// The computed offset, in bytes, cannot overflow an isize.
444+
let offset_bytes =
445+
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
446+
// The offset being in bounds cannot rely on "wrapping around" the address space.
447+
// So, first rule out overflows in the pointer arithmetic.
448+
let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?;
449+
// ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
450+
// memory between these pointers must be accessible. Note that we do not require the
451+
// pointers to be properly aligned (unlike a read/write operation).
452+
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
453+
let size: u64 = uabs(offset_bytes);
454+
// This call handles checking for integer/NULL pointers.
455+
self.memory.check_ptr_access_align(
456+
min_ptr,
457+
Size::from_bytes(size),
458+
None,
459+
CheckInAllocMsg::InboundsTest,
460+
)?;
461+
Ok(offset_ptr)
462+
}
412463
}

src/librustc_span/symbol.rs

+2
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,
@@ -516,6 +517,7 @@ symbols! {
516517
not,
517518
note,
518519
object_safe_for_dispatch,
520+
offset,
519521
Ok,
520522
omit_gdb_pretty_printer_section,
521523
on,

src/test/ui/consts/miri_unleashed/ptr_arith.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
#![feature(core_intrinsics)]
33
#![allow(const_err)]
44

5-
// A test demonstrating that we prevent doing even trivial
6-
// pointer arithmetic or comparison during CTFE.
5+
// During CTFE, we prevent pointer comparison and pointer-to-int casts.
76

87
static CMP: () = {
98
let x = &0 as *const _;
@@ -19,11 +18,4 @@ static INT_PTR_ARITH: () = unsafe {
1918
//~| NOTE pointer-to-integer cast
2019
};
2120

22-
static PTR_ARITH: () = unsafe {
23-
let x = &0 as *const _;
24-
let _v = core::intrinsics::offset(x, 0);
25-
//~^ ERROR could not evaluate static initializer
26-
//~| NOTE calling intrinsic `offset`
27-
};
28-
2921
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,28 @@
11
error[E0080]: could not evaluate static initializer
2-
--> $DIR/ptr_arith.rs:10:14
2+
--> $DIR/ptr_arith.rs:9:14
33
|
44
LL | let _v = x == x;
55
| ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants
66

77
error[E0080]: could not evaluate static initializer
8-
--> $DIR/ptr_arith.rs:17:14
8+
--> $DIR/ptr_arith.rs:16:14
99
|
1010
LL | let _v = x + 0;
1111
| ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants
1212

13-
error[E0080]: could not evaluate static initializer
14-
--> $DIR/ptr_arith.rs:24:14
15-
|
16-
LL | let _v = core::intrinsics::offset(x, 0);
17-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "calling intrinsic `offset`" needs an rfc before being allowed inside constants
18-
1913
warning: skipping const checks
2014
|
2115
help: skipping check for `const_compare_raw_pointers` feature
22-
--> $DIR/ptr_arith.rs:10:14
16+
--> $DIR/ptr_arith.rs:9:14
2317
|
2418
LL | let _v = x == x;
2519
| ^^^^^^
2620
help: skipping check that does not even have a feature gate
27-
--> $DIR/ptr_arith.rs:16:20
21+
--> $DIR/ptr_arith.rs:15:20
2822
|
2923
LL | let x: usize = std::mem::transmute(&0);
3024
| ^^^^^^^^^^^^^^^^^^^^^^^
31-
help: skipping check that does not even have a feature gate
32-
--> $DIR/ptr_arith.rs:24:14
33-
|
34-
LL | let _v = core::intrinsics::offset(x, 0);
35-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3625

37-
error: aborting due to 3 previous errors; 1 warning emitted
26+
error: aborting due to 2 previous errors; 1 warning emitted
3827

3928
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)