Skip to content

Commit 24bfee2

Browse files
authored
Rollup merge of rust-lang#63810 - oli-obk:const_offset_from, r=RalfJung,nikic
Make <*const/mut T>::offset_from `const fn` This reenables offset_of cc @mjbshaw after rust-lang#63075 broke it
2 parents 23f890f + bf83ecf commit 24bfee2

File tree

10 files changed

+253
-3
lines changed

10 files changed

+253
-3
lines changed

src/libcore/intrinsics.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,10 @@ extern "rust-intrinsic" {
13391339
/// Emits a `!nontemporal` store according to LLVM (see their docs).
13401340
/// Probably will never become stable.
13411341
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
1342+
1343+
/// See documentation of `<*const T>::offset_from` for details.
1344+
#[cfg(not(bootstrap))]
1345+
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
13421346
}
13431347

13441348
// 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
@@ -12,7 +12,7 @@ use rustc::mir::BinOp;
1212
use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
1313

1414
use super::{
15-
Machine, PlaceTy, OpTy, InterpCx,
15+
Machine, PlaceTy, OpTy, InterpCx, ImmTy,
1616
};
1717

1818
mod type_name;
@@ -236,6 +236,29 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236236
let result = Scalar::from_uint(truncated_bits, layout.size);
237237
self.write_scalar(result, dest)?;
238238
}
239+
240+
"ptr_offset_from" => {
241+
let a = self.read_immediate(args[0])?.to_scalar()?.to_ptr()?;
242+
let b = self.read_immediate(args[1])?.to_scalar()?.to_ptr()?;
243+
if a.alloc_id != b.alloc_id {
244+
throw_ub_format!(
245+
"ptr_offset_from cannot compute offset of pointers into different \
246+
allocations.",
247+
);
248+
}
249+
let usize_layout = self.layout_of(self.tcx.types.usize)?;
250+
let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
251+
let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
252+
let (val, _overflowed, _ty) = self.overflowing_binary_op(
253+
BinOp::Sub, a_offset, b_offset,
254+
)?;
255+
let pointee_layout = self.layout_of(substs.type_at(0))?;
256+
let isize_layout = self.layout_of(self.tcx.types.isize)?;
257+
let val = ImmTy::from_scalar(val, isize_layout);
258+
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
259+
self.exact_div(val, size, dest)?;
260+
}
261+
239262
"transmute" => {
240263
self.copy_op_transmute(args[0], dest)?;
241264
}
@@ -340,4 +363,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
340363
return Ok(false);
341364
}
342365
}
366+
367+
pub fn exact_div(
368+
&mut self,
369+
a: ImmTy<'tcx, M::PointerTag>,
370+
b: ImmTy<'tcx, M::PointerTag>,
371+
dest: PlaceTy<'tcx, M::PointerTag>,
372+
) -> InterpResult<'tcx> {
373+
// Performs an exact division, resulting in undefined behavior where
374+
// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`.
375+
// First, check x % y != 0.
376+
if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
377+
// Then, check if `b` is -1, which is the "min_value / -1" case.
378+
let minus1 = Scalar::from_int(-1, dest.layout.size);
379+
let b = b.to_scalar().unwrap();
380+
if b == minus1 {
381+
throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
382+
} else {
383+
throw_ub_format!(
384+
"exact_div: {} cannot be divided by {} without remainder",
385+
a.to_scalar().unwrap(),
386+
b,
387+
)
388+
}
389+
}
390+
self.binop_ignore_overflow(BinOp::Div, a, b, dest)
391+
}
343392
}

src/librustc_mir/transform/qualify_consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ impl Qualif for IsNotPromotable {
560560
| "transmute"
561561
| "simd_insert"
562562
| "simd_extract"
563+
| "ptr_offset_from"
563564
=> return true,
564565

565566
_ => {}

src/librustc_typeck/check/intrinsic.rs

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

310+
"ptr_offset_from" =>
311+
(1, vec![ tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0)) ], tcx.types.isize),
310312
"unchecked_div" | "unchecked_rem" | "exact_div" =>
311313
(1, vec![param(0), param(0)], param(0)),
312314
"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

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// ingoring on musl because it's ui output hides libcore backtraces
2+
// ignore-musl
3+
4+
#![feature(const_raw_ptr_deref)]
5+
#![feature(const_ptr_offset_from)]
6+
#![feature(ptr_offset_from)]
7+
8+
#[repr(C)]
9+
struct Struct {
10+
data: u8,
11+
field: u8,
12+
}
13+
14+
pub const DIFFERENT_ALLOC: usize = {
15+
//~^ NOTE
16+
let uninit = std::mem::MaybeUninit::<Struct>::uninit();
17+
let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
18+
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
19+
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
20+
let offset = unsafe { field_ptr.offset_from(base_ptr) };
21+
offset as usize
22+
};
23+
24+
pub const NOT_PTR: usize = {
25+
//~^ NOTE
26+
unsafe { (42 as *const u8).offset_from(&5u8) as usize }
27+
};
28+
29+
pub const NOT_MULTIPLE_OF_SIZE: usize = {
30+
//~^ NOTE
31+
let data = [5u8, 6, 7];
32+
let base_ptr = data.as_ptr();
33+
let field_ptr = &data[1] as *const u8 as *const u16;
34+
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u16) };
35+
offset as usize
36+
};
37+
38+
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:20:27
9+
|
10+
::: $DIR/offset_from_ub.rs:14: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:26:14
31+
|
32+
::: $DIR/offset_from_ub.rs:24: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:34:27
48+
|
49+
::: $DIR/offset_from_ub.rs:29: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)