Skip to content

Commit 60a9d5a

Browse files
committed
Re-enable copy[_nonoverlapping]() runtime checks
This commit re-enables the debug checks for valid usages of the two functions `copy()` and `copy_nonoverlapping()`. Those checks were com- mented out in #79684 in order to make the functions const. All that's been left was a FIXME, that could not be resolved until there is was way to only do the checks at runtime. Since #89247 there is such a way: `const_eval_select()`. This commit uses that new intrinsic in order to either do nothing (at compile time) or to do the old checks (at runtime). The change itself is rather small: in order to make the checks usable with `const_eval_select`, they are moved into a local function (one for `copy` and one for `copy_nonoverlapping` to keep symmetry). The change does not break referential transparency, as there is nothing you can do at compile time, which you cannot do on runtime without get- ting undefined behavior. The CTFE-engine won't allow missuses. The other way round is also fine.
1 parent 60952bc commit 60a9d5a

File tree

2 files changed

+47
-15
lines changed

2 files changed

+47
-15
lines changed

library/core/src/intrinsics.rs

+46-14
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,19 @@ pub(crate) fn is_aligned_and_not_null<T>(ptr: *const T) -> bool {
19511951
!ptr.is_null() && ptr as usize % mem::align_of::<T>() == 0
19521952
}
19531953

1954+
/// Checks whether the regions of memory starting at `src` and `dst` of size
1955+
/// `count * size_of::<T>()` do *not* overlap.
1956+
#[cfg(debug_assertions)]
1957+
pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) -> bool {
1958+
let src_usize = src as usize;
1959+
let dst_usize = dst as usize;
1960+
let size = mem::size_of::<T>().checked_mul(count).unwrap();
1961+
let diff = if src_usize > dst_usize { src_usize - dst_usize } else { dst_usize - src_usize };
1962+
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
1963+
// they do not overlap.
1964+
diff >= size
1965+
}
1966+
19541967
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
19551968
/// and destination must *not* overlap.
19561969
///
@@ -2042,15 +2055,24 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
20422055
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
20432056
}
20442057

2045-
// FIXME: Perform these checks only at run time
2046-
/*if cfg!(debug_assertions)
2047-
&& !(is_aligned_and_not_null(src)
2048-
&& is_aligned_and_not_null(dst)
2049-
&& is_nonoverlapping(src, dst, count))
2050-
{
2051-
// Not panicking to keep codegen impact smaller.
2052-
abort();
2053-
}*/
2058+
#[cfg(debug_assertions)]
2059+
fn runtime_check<T>(src: *const T, dst: *mut T, count: usize) {
2060+
if !is_aligned_and_not_null(src)
2061+
|| !is_aligned_and_not_null(dst)
2062+
|| !is_nonoverlapping(src, dst, count)
2063+
{
2064+
// Not panicking to keep codegen impact smaller.
2065+
abort();
2066+
}
2067+
}
2068+
#[cfg(debug_assertions)]
2069+
const fn compiletime_check<T>(_src: *const T, _dst: *mut T, _count: usize) {}
2070+
#[cfg(debug_assertions)]
2071+
// SAFETY: runtime debug-assertions are a best-effort basis; it's fine to
2072+
// not do them during compile time
2073+
unsafe {
2074+
const_eval_select((src, dst, count), compiletime_check, runtime_check);
2075+
}
20542076

20552077
// SAFETY: the safety contract for `copy_nonoverlapping` must be
20562078
// upheld by the caller.
@@ -2127,11 +2149,21 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
21272149
fn copy<T>(src: *const T, dst: *mut T, count: usize);
21282150
}
21292151

2130-
// FIXME: Perform these checks only at run time
2131-
/*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) {
2132-
// Not panicking to keep codegen impact smaller.
2133-
abort();
2134-
}*/
2152+
#[cfg(debug_assertions)]
2153+
fn runtime_check<T>(src: *const T, dst: *mut T) {
2154+
if !is_aligned_and_not_null(src) || !is_aligned_and_not_null(dst) {
2155+
// Not panicking to keep codegen impact smaller.
2156+
abort();
2157+
}
2158+
}
2159+
#[cfg(debug_assertions)]
2160+
const fn compiletime_check<T>(_src: *const T, _dst: *mut T) {}
2161+
#[cfg(debug_assertions)]
2162+
// SAFETY: runtime debug-assertions are a best-effort basis; it's fine to
2163+
// not do them during compile time
2164+
unsafe {
2165+
const_eval_select((src, dst), compiletime_check, runtime_check);
2166+
}
21352167

21362168
// SAFETY: the safety contract for `copy` must be upheld by the caller.
21372169
unsafe { copy(src, dst, count) }

library/core/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
#![feature(const_caller_location)]
106106
#![feature(const_cell_into_inner)]
107107
#![feature(const_discriminant)]
108-
#![cfg_attr(not(bootstrap), feature(const_eval_select))]
108+
#![feature(const_eval_select)]
109109
#![feature(const_float_bits_conv)]
110110
#![feature(const_float_classify)]
111111
#![feature(const_fmt_arguments_new)]

0 commit comments

Comments
 (0)