Skip to content

Commit 7594067

Browse files
committed
Auto merge of #90041 - jfrimmel:rt_copy_checks, r=Mark-Simulacrum
Re-enable `copy[_nonoverlapping]()` debug-checks This commit re-enables the debug checks for valid usages of the two functions `copy()` and `copy_nonoverlapping()`. Those checks were commented 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 getting undefined behavior. The CTFE-engine won't allow missuses. The other way round is also fine. I've refactored the code to use `#[cfg(debug_assertions)]` on the new items. If that is not desired, the second commit can be dropped. I haven't added any checks, as I currently don't know, how to test this properly. Closes #90012. cc `@rust-lang/lang,` `@rust-lang/libs` and `@rust-lang/wg-const-eval` (as those teams are linked in the issue above).
2 parents 032dfe4 + 60a9d5a commit 7594067

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)