@@ -1969,6 +1969,40 @@ extern "rust-intrinsic" {
1969
1969
// (`transmute` also falls into this category, but it cannot be wrapped due to the
1970
1970
// check that `T` and `U` have the same size.)
1971
1971
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
+
1972
2006
/// Checks whether `ptr` is properly aligned with respect to
1973
2007
/// `align_of::<T>()`.
1974
2008
pub ( 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 {
1977
2011
1978
2012
/// Checks whether the regions of memory starting at `src` and `dst` of size
1979
2013
/// `count * size_of::<T>()` do *not* overlap.
1980
- #[ cfg( debug_assertions) ]
1981
2014
pub ( crate ) fn is_nonoverlapping < T > ( src : * const T , dst : * const T , count : usize ) -> bool {
1982
2015
let src_usize = src. addr ( ) ;
1983
2016
let dst_usize = dst. addr ( ) ;
@@ -2079,28 +2112,16 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2079
2112
pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
2080
2113
}
2081
2114
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
-
2101
2115
// SAFETY: the safety contract for `copy_nonoverlapping` must be
2102
2116
// 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
+ }
2104
2125
}
2105
2126
2106
2127
/// 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) {
2173
2194
fn copy < T > ( src : * const T , dst : * mut T , count : usize ) ;
2174
2195
}
2175
2196
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.
2188
2198
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)
2190
2201
}
2191
-
2192
- // SAFETY: the safety contract for `copy` must be upheld by the caller.
2193
- unsafe { copy ( src, dst, count) }
2194
2202
}
2195
2203
2196
2204
/// 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) {
2274
2282
fn write_bytes < T > ( dst : * mut T , val : u8 , count : usize ) ;
2275
2283
}
2276
2284
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.
2289
2286
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)
2291
2289
}
2292
-
2293
- // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
2294
- unsafe { write_bytes ( dst, val, count) }
2295
2290
}
2296
2291
2297
2292
/// Selects which function to call depending on the context.
0 commit comments