-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Make <*const/mut T>::offset_from const fn
#63810
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
88b5e94
4a51801
94a6d4b
1c9d889
3956c48
bf83ecf
b93f48f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ use rustc::mir::BinOp; | |
use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue}; | ||
|
||
use super::{ | ||
Machine, PlaceTy, OpTy, InterpCx, | ||
Machine, PlaceTy, OpTy, InterpCx, ImmTy, | ||
}; | ||
|
||
mod type_name; | ||
|
@@ -236,6 +236,29 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
let result = Scalar::from_uint(truncated_bits, layout.size); | ||
self.write_scalar(result, dest)?; | ||
} | ||
|
||
"ptr_offset_from" => { | ||
let a = self.read_immediate(args[0])?.to_scalar()?.to_ptr()?; | ||
let b = self.read_immediate(args[1])?.to_scalar()?.to_ptr()?; | ||
Comment on lines
+241
to
+242
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I just realized this is wrong... we need to support integers here as well. This caused a regression in https://github.com/RalfJung/miri-test-libstd. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #66083 fixes that. |
||
if a.alloc_id != b.alloc_id { | ||
throw_ub_format!( | ||
"ptr_offset_from cannot compute offset of pointers into different \ | ||
allocations.", | ||
); | ||
} | ||
let usize_layout = self.layout_of(self.tcx.types.usize)?; | ||
let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); | ||
let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); | ||
let (val, _overflowed, _ty) = self.overflowing_binary_op( | ||
BinOp::Sub, a_offset, b_offset, | ||
)?; | ||
let pointee_layout = self.layout_of(substs.type_at(0))?; | ||
let isize_layout = self.layout_of(self.tcx.types.isize)?; | ||
let val = ImmTy::from_scalar(val, isize_layout); | ||
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); | ||
self.exact_div(val, size, dest)?; | ||
} | ||
|
||
"transmute" => { | ||
self.copy_op_transmute(args[0], dest)?; | ||
} | ||
|
@@ -340,4 +363,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
return Ok(false); | ||
} | ||
} | ||
|
||
pub fn exact_div( | ||
&mut self, | ||
a: ImmTy<'tcx, M::PointerTag>, | ||
b: ImmTy<'tcx, M::PointerTag>, | ||
dest: PlaceTy<'tcx, M::PointerTag>, | ||
) -> InterpResult<'tcx> { | ||
// Performs an exact division, resulting in undefined behavior where | ||
// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`. | ||
// First, check x % y != 0. | ||
if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 { | ||
// Then, check if `b` is -1, which is the "min_value / -1" case. | ||
let minus1 = Scalar::from_int(-1, dest.layout.size); | ||
let b = b.to_scalar().unwrap(); | ||
if b == minus1 { | ||
throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") | ||
} else { | ||
throw_ub_format!( | ||
"exact_div: {} cannot be divided by {} without remainder", | ||
a.to_scalar().unwrap(), | ||
b, | ||
) | ||
} | ||
} | ||
self.binop_ignore_overflow(BinOp::Div, a, b, dest) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// run-pass | ||
|
||
#![feature(const_raw_ptr_deref)] | ||
#![feature(const_ptr_offset_from)] | ||
#![feature(ptr_offset_from)] | ||
|
||
struct Struct { | ||
field: (), | ||
} | ||
|
||
#[repr(C)] | ||
struct Struct2 { | ||
data: u8, | ||
field: u8, | ||
} | ||
|
||
pub const OFFSET: usize = { | ||
let uninit = std::mem::MaybeUninit::<Struct>::uninit(); | ||
let base_ptr: *const Struct = &uninit as *const _ as *const Struct; | ||
// The following statement is UB (taking the address of an uninitialized field). | ||
// Const eval doesn't detect this right now, but it may stop compiling at some point | ||
// in the future. | ||
let field_ptr = unsafe { &(*base_ptr).field as *const () as *const u8 }; | ||
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u8) }; | ||
offset as usize | ||
}; | ||
|
||
pub const OFFSET_2: usize = { | ||
let uninit = std::mem::MaybeUninit::<Struct2>::uninit(); | ||
let base_ptr: *const Struct2 = &uninit as *const _ as *const Struct2; | ||
let field_ptr = unsafe { &(*base_ptr).field as *const u8 }; | ||
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u8) }; | ||
offset as usize | ||
}; | ||
|
||
pub const OVERFLOW: isize = { | ||
let uninit = std::mem::MaybeUninit::<Struct2>::uninit(); | ||
let base_ptr: *const Struct2 = &uninit as *const _ as *const Struct2; | ||
let field_ptr = unsafe { &(*base_ptr).field as *const u8 }; | ||
unsafe { (base_ptr as *const u8).offset_from(field_ptr) } | ||
}; | ||
|
||
fn main() { | ||
assert_eq!(OFFSET, 0); | ||
assert_eq!(OFFSET_2, 1); | ||
assert_eq!(OVERFLOW, -1); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// ignore-x86 FIXME: missing sysroot spans (#53081) | ||
|
||
#![feature(const_raw_ptr_deref)] | ||
#![feature(const_ptr_offset_from)] | ||
#![feature(ptr_offset_from)] | ||
|
||
#[repr(C)] | ||
struct Struct { | ||
data: u8, | ||
field: u8, | ||
} | ||
|
||
pub const DIFFERENT_ALLOC: usize = { | ||
//~^ NOTE | ||
let uninit = std::mem::MaybeUninit::<Struct>::uninit(); | ||
let base_ptr: *const Struct = &uninit as *const _ as *const Struct; | ||
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit(); | ||
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; | ||
let offset = unsafe { field_ptr.offset_from(base_ptr) }; | ||
offset as usize | ||
}; | ||
|
||
pub const NOT_PTR: usize = { | ||
//~^ NOTE | ||
unsafe { (42 as *const u8).offset_from(&5u8) as usize } | ||
}; | ||
|
||
pub const NOT_MULTIPLE_OF_SIZE: usize = { | ||
//~^ NOTE | ||
let data = [5u8, 6, 7]; | ||
let base_ptr = data.as_ptr(); | ||
let field_ptr = &data[1] as *const u8 as *const u16; | ||
let offset = unsafe { field_ptr.offset_from(base_ptr as *const u16) }; | ||
offset as usize | ||
}; | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
error: any use of this value will cause an error | ||
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL | ||
| | ||
LL | intrinsics::ptr_offset_from(self, origin) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | | ||
| ptr_offset_from cannot compute offset of pointers into different allocations. | ||
| inside call to `std::ptr::<impl *const Struct>::offset_from` at $DIR/offset_from_ub.rs:19:27 | ||
| | ||
::: $DIR/offset_from_ub.rs:13:1 | ||
| | ||
LL | / pub const DIFFERENT_ALLOC: usize = { | ||
LL | | | ||
LL | | let uninit = std::mem::MaybeUninit::<Struct>::uninit(); | ||
LL | | let base_ptr: *const Struct = &uninit as *const _ as *const Struct; | ||
... | | ||
LL | | offset as usize | ||
LL | | }; | ||
| |__- | ||
| | ||
= note: `#[deny(const_err)]` on by default | ||
|
||
error: any use of this value will cause an error | ||
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL | ||
| | ||
LL | intrinsics::ptr_offset_from(self, origin) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | | ||
| a memory access tried to interpret some bytes as a pointer | ||
| inside call to `std::ptr::<impl *const u8>::offset_from` at $DIR/offset_from_ub.rs:25:14 | ||
| | ||
::: $DIR/offset_from_ub.rs:23:1 | ||
| | ||
LL | / pub const NOT_PTR: usize = { | ||
LL | | | ||
LL | | unsafe { (42 as *const u8).offset_from(&5u8) as usize } | ||
LL | | }; | ||
| |__- | ||
|
||
error: any use of this value will cause an error | ||
--> $SRC_DIR/libcore/ptr/mod.rs:LL:COL | ||
| | ||
LL | intrinsics::ptr_offset_from(self, origin) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | | ||
| exact_div: 1 cannot be divided by 2 without remainder | ||
| inside call to `std::ptr::<impl *const u16>::offset_from` at $DIR/offset_from_ub.rs:33:27 | ||
| | ||
::: $DIR/offset_from_ub.rs:28:1 | ||
| | ||
LL | / pub const NOT_MULTIPLE_OF_SIZE: usize = { | ||
LL | | | ||
LL | | let data = [5u8, 6, 7]; | ||
LL | | let base_ptr = data.as_ptr(); | ||
... | | ||
LL | | offset as usize | ||
LL | | }; | ||
| |__- | ||
|
||
error: aborting due to 3 previous errors | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// run-pass | ||
|
||
#![feature(ptr_offset_from)] | ||
|
||
fn main() { | ||
let mut a = [0; 5]; | ||
let ptr1: *mut i32 = &mut a[1]; | ||
let ptr2: *mut i32 = &mut a[3]; | ||
unsafe { | ||
assert_eq!(ptr2.offset_from(ptr1), 2); | ||
assert_eq!(ptr1.offset_from(ptr2), -2); | ||
assert_eq!(ptr1.offset(2), ptr2); | ||
assert_eq!(ptr2.offset(-2), ptr1); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.