mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
Rollup merge of #133116 - RalfJung:const-null-ptr, r=dtolnay
stabilize const_ptr_is_null FCP passed in https://github.com/rust-lang/rust/issues/74939. The second commit cleans up const stability around UB checks a bit, now that everything they need (except for `const_eval_select`) is stable. Fixes https://github.com/rust-lang/rust/issues/74939
This commit is contained in:
commit
af1c8be400
@ -263,6 +263,12 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
|
||||
}
|
||||
|
||||
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
|
||||
/// Returns `2` if the result is unknown.
|
||||
/// Returns `1` if the pointers are guaranteed equal.
|
||||
/// Returns `0` if the pointers are guaranteed inequal.
|
||||
///
|
||||
/// Note that this intrinsic is exposed on stable for comparison with null. In other words, any
|
||||
/// change to this function that affects comparison with null is insta-stable!
|
||||
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
|
||||
interp_ok(match (a, b) {
|
||||
// Comparisons between integers are always known.
|
||||
|
@ -3292,8 +3292,8 @@ pub const unsafe fn ptr_offset_from_unsigned<T>(_ptr: *const T, _base: *const T)
|
||||
|
||||
/// See documentation of `<*const T>::guaranteed_eq` for details.
|
||||
/// Returns `2` if the result is unknown.
|
||||
/// Returns `1` if the pointers are guaranteed equal
|
||||
/// Returns `0` if the pointers are guaranteed inequal
|
||||
/// Returns `1` if the pointers are guaranteed equal.
|
||||
/// Returns `0` if the pointers are guaranteed inequal.
|
||||
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020"))]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
@ -4014,9 +4014,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
|
||||
count: usize = count,
|
||||
) => {
|
||||
let zero_size = count == 0 || size == 0;
|
||||
ub_checks::is_aligned_and_not_null(src, align, zero_size)
|
||||
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
|
||||
&& ub_checks::is_nonoverlapping(src, dst, size, count)
|
||||
ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size)
|
||||
&& ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size)
|
||||
&& ub_checks::maybe_is_nonoverlapping(src, dst, size, count)
|
||||
}
|
||||
);
|
||||
|
||||
@ -4120,8 +4120,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
||||
align: usize = align_of::<T>(),
|
||||
zero_size: bool = T::IS_ZST || count == 0,
|
||||
) =>
|
||||
ub_checks::is_aligned_and_not_null(src, align, zero_size)
|
||||
&& ub_checks::is_aligned_and_not_null(dst, align, zero_size)
|
||||
ub_checks::maybe_is_aligned_and_not_null(src, align, zero_size)
|
||||
&& ub_checks::maybe_is_aligned_and_not_null(dst, align, zero_size)
|
||||
);
|
||||
copy(src, dst, count)
|
||||
}
|
||||
@ -4202,7 +4202,7 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
|
||||
addr: *const () = dst as *const (),
|
||||
align: usize = align_of::<T>(),
|
||||
zero_size: bool = T::IS_ZST || count == 0,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, zero_size)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, zero_size)
|
||||
);
|
||||
write_bytes(dst, val, count)
|
||||
}
|
||||
|
@ -109,6 +109,7 @@
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(const_exact_div))]
|
||||
#![cfg_attr(bootstrap, feature(const_fmt_arguments_new))]
|
||||
#![cfg_attr(bootstrap, feature(const_ub_checks))]
|
||||
#![feature(array_ptr_get)]
|
||||
#![feature(asm_experimental_arch)]
|
||||
#![feature(const_align_of_val)]
|
||||
@ -121,7 +122,6 @@
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_nonnull_new)]
|
||||
#![feature(const_pin_2)]
|
||||
#![feature(const_ptr_is_null)]
|
||||
#![feature(const_ptr_sub_ptr)]
|
||||
#![feature(const_raw_ptr_comparison)]
|
||||
#![feature(const_size_of_val)]
|
||||
@ -132,7 +132,6 @@
|
||||
#![feature(const_type_id)]
|
||||
#![feature(const_type_name)]
|
||||
#![feature(const_typed_swap)]
|
||||
#![feature(const_ub_checks)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(coverage_attribute)]
|
||||
#![feature(do_not_recommend)]
|
||||
|
@ -29,16 +29,18 @@ impl<T: ?Sized> *const T {
|
||||
/// assert!(!ptr.is_null());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
|
||||
#[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "ptr_const_is_null"]
|
||||
#[inline]
|
||||
#[rustc_allow_const_fn_unstable(const_eval_select)]
|
||||
pub const fn is_null(self) -> bool {
|
||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||
// considering their "data" part for null-ness.
|
||||
let ptr = self as *const u8;
|
||||
const_eval_select!(
|
||||
@capture { ptr: *const u8 } -> bool:
|
||||
if const #[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")] {
|
||||
// This use of `const_raw_ptr_comparison` has been explicitly blessed by t-lang.
|
||||
if const #[rustc_allow_const_fn_unstable(const_raw_ptr_comparison)] {
|
||||
match (ptr).guaranteed_eq(null_mut()) {
|
||||
Some(res) => res,
|
||||
// To remain maximally convervative, we stop execution when we don't
|
||||
@ -280,7 +282,7 @@ impl<T: ?Sized> *const T {
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
|
||||
#[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline]
|
||||
pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> {
|
||||
// SAFETY: the caller must guarantee that `self` is valid
|
||||
|
@ -1103,9 +1103,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
||||
count: usize = count,
|
||||
) => {
|
||||
let zero_size = size == 0 || count == 0;
|
||||
ub_checks::is_aligned_and_not_null(x, align, zero_size)
|
||||
&& ub_checks::is_aligned_and_not_null(y, align, zero_size)
|
||||
&& ub_checks::is_nonoverlapping(x, y, size, count)
|
||||
ub_checks::maybe_is_aligned_and_not_null(x, align, zero_size)
|
||||
&& ub_checks::maybe_is_aligned_and_not_null(y, align, zero_size)
|
||||
&& ub_checks::maybe_is_nonoverlapping(x, y, size, count)
|
||||
}
|
||||
);
|
||||
|
||||
@ -1216,7 +1216,7 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
|
||||
addr: *const () = dst as *const (),
|
||||
align: usize = align_of::<T>(),
|
||||
is_zst: bool = T::IS_ZST,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
|
||||
);
|
||||
mem::replace(&mut *dst, src)
|
||||
}
|
||||
@ -1369,7 +1369,7 @@ pub const unsafe fn read<T>(src: *const T) -> T {
|
||||
addr: *const () = src as *const (),
|
||||
align: usize = align_of::<T>(),
|
||||
is_zst: bool = T::IS_ZST,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
|
||||
);
|
||||
crate::intrinsics::read_via_copy(src)
|
||||
}
|
||||
@ -1573,7 +1573,7 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
||||
addr: *mut () = dst as *mut (),
|
||||
align: usize = align_of::<T>(),
|
||||
is_zst: bool = T::IS_ZST,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
|
||||
);
|
||||
intrinsics::write_via_move(dst, src)
|
||||
}
|
||||
@ -1745,7 +1745,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
addr: *const () = src as *const (),
|
||||
align: usize = align_of::<T>(),
|
||||
is_zst: bool = T::IS_ZST,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
|
||||
);
|
||||
intrinsics::volatile_load(src)
|
||||
}
|
||||
@ -1825,7 +1825,7 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||
addr: *mut () = dst as *mut (),
|
||||
align: usize = align_of::<T>(),
|
||||
is_zst: bool = T::IS_ZST,
|
||||
) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
|
||||
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
|
||||
);
|
||||
intrinsics::volatile_store(dst, src);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// assert!(!ptr.is_null());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
|
||||
#[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "ptr_is_null"]
|
||||
#[inline]
|
||||
pub const fn is_null(self) -> bool {
|
||||
@ -271,7 +271,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
|
||||
#[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline]
|
||||
pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> {
|
||||
// SAFETY: the caller must guarantee that `self` is valid for a
|
||||
@ -619,7 +619,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// println!("{s:?}"); // It'll print: "[4, 2, 3]".
|
||||
/// ```
|
||||
#[stable(feature = "ptr_as_ref", since = "1.9.0")]
|
||||
#[rustc_const_unstable(feature = "const_ptr_is_null", issue = "74939")]
|
||||
#[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline]
|
||||
pub const unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
|
||||
// SAFETY: the caller must guarantee that `self` is be valid for
|
||||
|
@ -132,7 +132,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
|
||||
align: usize = align_of::<T>(),
|
||||
len: usize = len,
|
||||
) =>
|
||||
ub_checks::is_aligned_and_not_null(data, align, false)
|
||||
ub_checks::maybe_is_aligned_and_not_null(data, align, false)
|
||||
&& ub_checks::is_valid_allocation_size(size, len)
|
||||
);
|
||||
&*ptr::slice_from_raw_parts(data, len)
|
||||
@ -186,7 +186,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
|
||||
align: usize = align_of::<T>(),
|
||||
len: usize = len,
|
||||
) =>
|
||||
ub_checks::is_aligned_and_not_null(data, align, false)
|
||||
ub_checks::maybe_is_aligned_and_not_null(data, align, false)
|
||||
&& ub_checks::is_valid_allocation_size(size, len)
|
||||
);
|
||||
&mut *ptr::slice_from_raw_parts_mut(data, len)
|
||||
|
@ -64,8 +64,6 @@ macro_rules! assert_unsafe_precondition {
|
||||
#[rustc_no_mir_inline]
|
||||
#[inline]
|
||||
#[rustc_nounwind]
|
||||
#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ub_checks", issue = "none"))]
|
||||
#[rustc_allow_const_fn_unstable(const_ptr_is_null, const_ub_checks)] // only for UB checks
|
||||
const fn precondition_check($($name:$ty),*) {
|
||||
if !$e {
|
||||
::core::panicking::panic_nounwind(
|
||||
@ -116,12 +114,16 @@ pub(crate) const fn check_language_ub() -> bool {
|
||||
/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
|
||||
/// check is anyway not executed in `const`.
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
|
||||
pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
|
||||
#[rustc_allow_const_fn_unstable(const_eval_select)]
|
||||
pub(crate) const fn maybe_is_aligned_and_not_null(
|
||||
ptr: *const (),
|
||||
align: usize,
|
||||
is_zst: bool,
|
||||
) -> bool {
|
||||
// This is just for safety checks so we can const_eval_select.
|
||||
const_eval_select!(
|
||||
@capture { ptr: *const (), align: usize, is_zst: bool } -> bool:
|
||||
if const #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] {
|
||||
if const {
|
||||
is_zst || !ptr.is_null()
|
||||
} else {
|
||||
ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
|
||||
@ -141,8 +143,8 @@ pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool {
|
||||
/// Note that in const-eval this function just returns `true` and therefore must
|
||||
/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
|
||||
pub(crate) const fn is_nonoverlapping(
|
||||
#[rustc_allow_const_fn_unstable(const_eval_select)]
|
||||
pub(crate) const fn maybe_is_nonoverlapping(
|
||||
src: *const (),
|
||||
dst: *const (),
|
||||
size: usize,
|
||||
|
@ -1,4 +1,3 @@
|
||||
#![feature(const_ptr_is_null)]
|
||||
use std::ptr;
|
||||
|
||||
const IS_NULL: () = {
|
||||
|
@ -8,7 +8,7 @@ note: inside `std::ptr::const_ptr::<impl *const T>::is_null::compiletime`
|
||||
note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
|
||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||
note: inside `MAYBE_NULL`
|
||||
--> $DIR/const-ptr-is-null.rs:17:14
|
||||
--> $DIR/const-ptr-is-null.rs:16:14
|
||||
|
|
||||
LL | assert!(!ptr.wrapping_sub(512).is_null());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,7 +1,6 @@
|
||||
//@ compile-flags: --crate-type=lib
|
||||
//@ check-pass
|
||||
|
||||
#![feature(const_ptr_is_null)]
|
||||
#![allow(useless_ptr_null_checks)]
|
||||
|
||||
const FOO: &usize = &42;
|
||||
|
Loading…
Reference in New Issue
Block a user