Skip to content

Commit 6c1b220

Browse files
committed
Auto merge of #63810 - oli-obk:const_offset_from, r=RalfJung,nikic
Make <*const/mut T>::offset_from `const fn` This reenables offset_of cc @mjbshaw after #63075 broke it
2 parents 91fd628 + b93f48f commit 6c1b220

File tree

10 files changed

+252
-3
lines changed

10 files changed

+252
-3
lines changed

src/libcore/intrinsics.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,10 @@ extern "rust-intrinsic" {
13441344
/// Emits a `!nontemporal` store according to LLVM (see their docs).
13451345
/// Probably will never become stable.
13461346
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
1347+
1348+
/// See documentation of `<*const T>::offset_from` for details.
1349+
#[cfg(not(bootstrap))]
1350+
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
13471351
}
13481352

13491353
// Some functions are defined here because they accidentally got made

src/libcore/ptr/mod.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -1286,7 +1286,22 @@ impl<T: ?Sized> *const T {
12861286
/// }
12871287
/// ```
12881288
#[unstable(feature = "ptr_offset_from", issue = "41079")]
1289+
#[cfg(not(bootstrap))]
1290+
#[rustc_const_unstable(feature = "const_ptr_offset_from")]
12891291
#[inline]
1292+
pub const unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
1293+
let pointee_size = mem::size_of::<T>();
1294+
let ok = 0 < pointee_size && pointee_size <= isize::max_value() as usize;
1295+
// assert that the pointee size is valid in a const eval compatible way
1296+
// FIXME: do this with a real assert at some point
1297+
[()][(!ok) as usize];
1298+
intrinsics::ptr_offset_from(self, origin)
1299+
}
1300+
1301+
#[unstable(feature = "ptr_offset_from", issue = "41079")]
1302+
#[inline]
1303+
#[cfg(bootstrap)]
1304+
/// bootstrap
12901305
pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
12911306
let pointee_size = mem::size_of::<T>();
12921307
assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize);
@@ -2013,8 +2028,9 @@ impl<T: ?Sized> *mut T {
20132028
/// }
20142029
/// ```
20152030
#[unstable(feature = "ptr_offset_from", issue = "41079")]
2031+
#[rustc_const_unstable(feature = "const_ptr_offset_from")]
20162032
#[inline]
2017-
pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
2033+
pub const unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
20182034
(self as *const T).offset_from(origin)
20192035
}
20202036

src/librustc_codegen_llvm/intrinsic.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc::mir::interpret::GlobalId;
1919
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
2020
use rustc::hir;
2121
use syntax::ast::{self, FloatTy};
22+
use rustc_target::abi::HasDataLayout;
2223

2324
use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
2425
use rustc_codegen_ssa::traits::*;
@@ -694,6 +695,23 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
694695
return;
695696
}
696697

698+
"ptr_offset_from" => {
699+
let ty = substs.type_at(0);
700+
let pointee_size = self.size_of(ty);
701+
702+
// This is the same sequence that Clang emits for pointer subtraction.
703+
// It can be neither `nsw` nor `nuw` because the input is treated as
704+
// unsigned but then the output is treated as signed, so neither works.
705+
let a = args[0].immediate();
706+
let b = args[1].immediate();
707+
let a = self.ptrtoint(a, self.type_isize());
708+
let b = self.ptrtoint(b, self.type_isize());
709+
let d = self.sub(a, b);
710+
let pointee_size = self.const_usize(pointee_size.bytes());
711+
// this is where the signed magic happens (notice the `s` in `exactsdiv`)
712+
self.exactsdiv(d, pointee_size)
713+
}
714+
697715
_ => bug!("unknown intrinsic '{}'", name),
698716
};
699717

@@ -1224,7 +1242,6 @@ fn generic_simd_intrinsic(
12241242
// The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
12251243
// vector mask and returns an unsigned integer containing the most
12261244
// significant bit (MSB) of each lane.
1227-
use rustc_target::abi::HasDataLayout;
12281245

12291246
// If the vector has less than 8 lanes, an u8 is returned with zeroed
12301247
// trailing bits.

src/librustc_mir/interpret/intrinsics.rs

+50-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc::mir::BinOp;
1313
use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
1414

1515
use super::{
16-
Machine, PlaceTy, OpTy, InterpCx,
16+
Machine, PlaceTy, OpTy, InterpCx, ImmTy,
1717
};
1818

1919
mod caller_location;
@@ -249,6 +249,29 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
249249
let result = Scalar::from_uint(truncated_bits, layout.size);
250250
self.write_scalar(result, dest)?;
251251
}
252+
253+
"ptr_offset_from" => {
254+
let a = self.read_immediate(args[0])?.to_scalar()?.to_ptr()?;
255+
let b = self.read_immediate(args[1])?.to_scalar()?.to_ptr()?;
256+
if a.alloc_id != b.alloc_id {
257+
throw_ub_format!(
258+
"ptr_offset_from cannot compute offset of pointers into different \
259+
allocations.",
260+
);
261+
}
262+
let usize_layout = self.layout_of(self.tcx.types.usize)?;
263+
let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
264+
let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
265+
let (val, _overflowed, _ty) = self.overflowing_binary_op(
266+
BinOp::Sub, a_offset, b_offset,
267+
)?;
268+
let pointee_layout = self.layout_of(substs.type_at(0))?;
269+
let isize_layout = self.layout_of(self.tcx.types.isize)?;
270+
let val = ImmTy::from_scalar(val, isize_layout);
271+
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
272+
self.exact_div(val, size, dest)?;
273+
}
274+
252275
"transmute" => {
253276
self.copy_op_transmute(args[0], dest)?;
254277
}
@@ -354,4 +377,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
354377
return Ok(false);
355378
}
356379
}
380+
381+
pub fn exact_div(
382+
&mut self,
383+
a: ImmTy<'tcx, M::PointerTag>,
384+
b: ImmTy<'tcx, M::PointerTag>,
385+
dest: PlaceTy<'tcx, M::PointerTag>,
386+
) -> InterpResult<'tcx> {
387+
// Performs an exact division, resulting in undefined behavior where
388+
// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`.
389+
// First, check x % y != 0.
390+
if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
391+
// Then, check if `b` is -1, which is the "min_value / -1" case.
392+
let minus1 = Scalar::from_int(-1, dest.layout.size);
393+
let b = b.to_scalar().unwrap();
394+
if b == minus1 {
395+
throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
396+
} else {
397+
throw_ub_format!(
398+
"exact_div: {} cannot be divided by {} without remainder",
399+
a.to_scalar().unwrap(),
400+
b,
401+
)
402+
}
403+
}
404+
self.binop_ignore_overflow(BinOp::Div, a, b, dest)
405+
}
357406
}

src/librustc_mir/transform/qualify_consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ impl Qualif for IsNotPromotable {
564564
| "transmute"
565565
| "simd_insert"
566566
| "simd_extract"
567+
| "ptr_offset_from"
567568
=> return true,
568569

569570
_ => {}

src/librustc_typeck/check/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
317317
(1, vec![param(0), param(0)],
318318
tcx.intern_tup(&[param(0), tcx.types.bool])),
319319

320+
"ptr_offset_from" =>
321+
(1, vec![ tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0)) ], tcx.types.isize),
320322
"unchecked_div" | "unchecked_rem" | "exact_div" =>
321323
(1, vec![param(0), param(0)], param(0)),
322324
"unchecked_shl" | "unchecked_shr" |

src/test/ui/consts/offset_from.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// run-pass
2+
3+
#![feature(const_raw_ptr_deref)]
4+
#![feature(const_ptr_offset_from)]
5+
#![feature(ptr_offset_from)]
6+
7+
struct Struct {
8+
field: (),
9+
}
10+
11+
#[repr(C)]
12+
struct Struct2 {
13+
data: u8,
14+
field: u8,
15+
}
16+
17+
pub const OFFSET: usize = {
18+
let uninit = std::mem::MaybeUninit::<Struct>::uninit();
19+
let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
20+
// The following statement is UB (taking the address of an uninitialized field).
21+
// Const eval doesn't detect this right now, but it may stop compiling at some point
22+
// in the future.
23+
let field_ptr = unsafe { &(*base_ptr).field as *const () as *const u8 };
24+
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u8) };
25+
offset as usize
26+
};
27+
28+
pub const OFFSET_2: usize = {
29+
let uninit = std::mem::MaybeUninit::<Struct2>::uninit();
30+
let base_ptr: *const Struct2 = &uninit as *const _ as *const Struct2;
31+
let field_ptr = unsafe { &(*base_ptr).field as *const u8 };
32+
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u8) };
33+
offset as usize
34+
};
35+
36+
pub const OVERFLOW: isize = {
37+
let uninit = std::mem::MaybeUninit::<Struct2>::uninit();
38+
let base_ptr: *const Struct2 = &uninit as *const _ as *const Struct2;
39+
let field_ptr = unsafe { &(*base_ptr).field as *const u8 };
40+
unsafe { (base_ptr as *const u8).offset_from(field_ptr) }
41+
};
42+
43+
fn main() {
44+
assert_eq!(OFFSET, 0);
45+
assert_eq!(OFFSET_2, 1);
46+
assert_eq!(OVERFLOW, -1);
47+
}

src/test/ui/consts/offset_from_ub.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// ignore-x86 FIXME: missing sysroot spans (#53081)
2+
3+
#![feature(const_raw_ptr_deref)]
4+
#![feature(const_ptr_offset_from)]
5+
#![feature(ptr_offset_from)]
6+
7+
#[repr(C)]
8+
struct Struct {
9+
data: u8,
10+
field: u8,
11+
}
12+
13+
pub const DIFFERENT_ALLOC: usize = {
14+
//~^ NOTE
15+
let uninit = std::mem::MaybeUninit::<Struct>::uninit();
16+
let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
17+
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
18+
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
19+
let offset = unsafe { field_ptr.offset_from(base_ptr) };
20+
offset as usize
21+
};
22+
23+
pub const NOT_PTR: usize = {
24+
//~^ NOTE
25+
unsafe { (42 as *const u8).offset_from(&5u8) as usize }
26+
};
27+
28+
pub const NOT_MULTIPLE_OF_SIZE: usize = {
29+
//~^ NOTE
30+
let data = [5u8, 6, 7];
31+
let base_ptr = data.as_ptr();
32+
let field_ptr = &data[1] as *const u8 as *const u16;
33+
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u16) };
34+
offset as usize
35+
};
36+
37+
fn main() {}
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
error: any use of this value will cause an error
2+
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL
3+
|
4+
LL | intrinsics::ptr_offset_from(self, origin)
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| ptr_offset_from cannot compute offset of pointers into different allocations.
8+
| inside call to `std::ptr::<impl *const Struct>::offset_from` at $DIR/offset_from_ub.rs:19:27
9+
|
10+
::: $DIR/offset_from_ub.rs:13:1
11+
|
12+
LL | / pub const DIFFERENT_ALLOC: usize = {
13+
LL | |
14+
LL | | let uninit = std::mem::MaybeUninit::<Struct>::uninit();
15+
LL | | let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
16+
... |
17+
LL | | offset as usize
18+
LL | | };
19+
| |__-
20+
|
21+
= note: `#[deny(const_err)]` on by default
22+
23+
error: any use of this value will cause an error
24+
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL
25+
|
26+
LL | intrinsics::ptr_offset_from(self, origin)
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
| |
29+
| a memory access tried to interpret some bytes as a pointer
30+
| inside call to `std::ptr::<impl *const u8>::offset_from` at $DIR/offset_from_ub.rs:25:14
31+
|
32+
::: $DIR/offset_from_ub.rs:23:1
33+
|
34+
LL | / pub const NOT_PTR: usize = {
35+
LL | |
36+
LL | | unsafe { (42 as *const u8).offset_from(&5u8) as usize }
37+
LL | | };
38+
| |__-
39+
40+
error: any use of this value will cause an error
41+
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL
42+
|
43+
LL | intrinsics::ptr_offset_from(self, origin)
44+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45+
| |
46+
| exact_div: 1 cannot be divided by 2 without remainder
47+
| inside call to `std::ptr::<impl *const u16>::offset_from` at $DIR/offset_from_ub.rs:33:27
48+
|
49+
::: $DIR/offset_from_ub.rs:28:1
50+
|
51+
LL | / pub const NOT_MULTIPLE_OF_SIZE: usize = {
52+
LL | |
53+
LL | | let data = [5u8, 6, 7];
54+
LL | | let base_ptr = data.as_ptr();
55+
... |
56+
LL | | offset as usize
57+
LL | | };
58+
| |__-
59+
60+
error: aborting due to 3 previous errors
61+

src/test/ui/offset_from.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-pass
2+
3+
#![feature(ptr_offset_from)]
4+
5+
fn main() {
6+
let mut a = [0; 5];
7+
let ptr1: *mut i32 = &mut a[1];
8+
let ptr2: *mut i32 = &mut a[3];
9+
unsafe {
10+
assert_eq!(ptr2.offset_from(ptr1), 2);
11+
assert_eq!(ptr1.offset_from(ptr2), -2);
12+
assert_eq!(ptr1.offset(2), ptr2);
13+
assert_eq!(ptr2.offset(-2), ptr1);
14+
}
15+
}

0 commit comments

Comments
 (0)