@@ -1969,6 +1969,40 @@ extern "rust-intrinsic" {
19691969// (`transmute` also falls into this category, but it cannot be wrapped due to the
19701970// check that `T` and `U` have the same size.)
19711971
1972+ /// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
1973+ /// and only at runtime.
1974+ ///
1975+ /// # Safety
1976+ ///
1977+ /// Invoking this macro is only sound if the following code is already UB when the passed
1978+ /// expression evaluates to false.
1979+ ///
1980+ /// This macro expands to a check at runtime if debug_assertions is set. It has no effect at
1981+ /// compile time, but the semantics of the contained `const_eval_select` must be the same at
1982+ /// runtime and at compile time. Thus if the expression evaluates to false, this macro produces
1983+ /// different behavior at compile time and at runtime, and invoking it is incorrect.
1984+ ///
1985+ /// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make
1986+ /// the occasional mistake, and this check should help them figure things out.
1987+ #[ allow_internal_unstable( const_eval_select) ] // permit this to be called in stably-const fn
1988+ macro_rules! assert_unsafe_precondition {
1989+ ( $e: expr) => {
1990+ if cfg!( debug_assertions) {
1991+ // Use a closure so that we can capture arbitrary expressions from the invocation
1992+ let runtime = || {
1993+ if !$e {
1994+ // abort instead of panicking to reduce impact on code size
1995+ :: core:: intrinsics:: abort( ) ;
1996+ }
1997+ } ;
1998+ const fn comptime( ) { }
1999+
2000+ :: core:: intrinsics:: const_eval_select( ( ) , comptime, runtime) ;
2001+ }
2002+ } ;
2003+ }
2004+ pub ( crate ) use assert_unsafe_precondition;
2005+
19722006/// Checks whether `ptr` is properly aligned with respect to
19732007/// `align_of::<T>()`.
19742008pub ( crate ) fn is_aligned_and_not_null < T > ( ptr : * const T ) -> bool {
@@ -1977,7 +2011,6 @@ pub(crate) fn is_aligned_and_not_null<T>(ptr: *const T) -> bool {
19772011
19782012/// Checks whether the regions of memory starting at `src` and `dst` of size
19792013/// `count * size_of::<T>()` do *not* overlap.
1980- #[ cfg( debug_assertions) ]
19812014pub ( crate ) fn is_nonoverlapping < T > ( src : * const T , dst : * const T , count : usize ) -> bool {
19822015 let src_usize = src as usize ;
19832016 let dst_usize = dst as usize ;
@@ -2079,28 +2112,16 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
20792112 pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
20802113 }
20812114
2082- #[ cfg( debug_assertions) ]
2083- fn runtime_check < T > ( src : * const T , dst : * mut T , count : usize ) {
2084- if !is_aligned_and_not_null ( src)
2085- || !is_aligned_and_not_null ( dst)
2086- || !is_nonoverlapping ( src, dst, count)
2087- {
2088- // Not panicking to keep codegen impact smaller.
2089- abort ( ) ;
2090- }
2091- }
2092- #[ cfg( debug_assertions) ]
2093- const fn compiletime_check < T > ( _src : * const T , _dst : * mut T , _count : usize ) { }
2094- #[ cfg( debug_assertions) ]
2095- // SAFETY: As per our safety precondition, we may assume that the `abort` above is never reached.
2096- // Therefore, compiletime_check and runtime_check are observably equivalent.
2097- unsafe {
2098- const_eval_select ( ( src, dst, count) , compiletime_check, runtime_check) ;
2099- }
2100-
21012115 // SAFETY: the safety contract for `copy_nonoverlapping` must be
21022116 // upheld by the caller.
2103- unsafe { copy_nonoverlapping ( src, dst, count) }
2117+ unsafe {
2118+ assert_unsafe_precondition ! (
2119+ is_aligned_and_not_null( src)
2120+ && is_aligned_and_not_null( dst)
2121+ && is_nonoverlapping( src, dst, count)
2122+ ) ;
2123+ copy_nonoverlapping ( src, dst, count)
2124+ }
21042125}
21052126
21062127/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
@@ -2173,24 +2194,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
21732194 fn copy < T > ( src : * const T , dst : * mut T , count : usize ) ;
21742195 }
21752196
2176- #[ cfg( debug_assertions) ]
2177- fn runtime_check < T > ( src : * const T , dst : * mut T ) {
2178- if !is_aligned_and_not_null ( src) || !is_aligned_and_not_null ( dst) {
2179- // Not panicking to keep codegen impact smaller.
2180- abort ( ) ;
2181- }
2182- }
2183- #[ cfg( debug_assertions) ]
2184- const fn compiletime_check < T > ( _src : * const T , _dst : * mut T ) { }
2185- #[ cfg( debug_assertions) ]
2186- // SAFETY: As per our safety precondition, we may assume that the `abort` above is never reached.
2187- // Therefore, compiletime_check and runtime_check are observably equivalent.
2197+ // SAFETY: the safety contract for `copy` must be upheld by the caller.
21882198 unsafe {
2189- const_eval_select ( ( src, dst) , compiletime_check, runtime_check) ;
2199+ assert_unsafe_precondition ! ( is_aligned_and_not_null( src) && is_aligned_and_not_null( dst) ) ;
2200+ copy ( src, dst, count)
21902201 }
2191-
2192- // SAFETY: the safety contract for `copy` must be upheld by the caller.
2193- unsafe { copy ( src, dst, count) }
21942202}
21952203
21962204/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
@@ -2274,24 +2282,11 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
22742282 fn write_bytes < T > ( dst : * mut T , val : u8 , count : usize ) ;
22752283 }
22762284
2277- #[ cfg( debug_assertions) ]
2278- fn runtime_check < T > ( ptr : * mut T ) {
2279- debug_assert ! (
2280- is_aligned_and_not_null( ptr) ,
2281- "attempt to write to unaligned or null pointer"
2282- ) ;
2283- }
2284- #[ cfg( debug_assertions) ]
2285- const fn compiletime_check < T > ( _ptr : * mut T ) { }
2286- #[ cfg( debug_assertions) ]
2287- // SAFETY: runtime debug-assertions are a best-effort basis; it's fine to
2288- // not do them during compile time
2285+ // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
22892286 unsafe {
2290- const_eval_select ( ( dst, ) , compiletime_check, runtime_check) ;
2287+ assert_unsafe_precondition ! ( is_aligned_and_not_null( dst) ) ;
2288+ write_bytes ( dst, val, count)
22912289 }
2292-
2293- // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
2294- unsafe { write_bytes ( dst, val, count) }
22952290}
22962291
22972292/// Selects which function to call depending on the context.
0 commit comments