5656
5757use crate :: marker:: DiscriminantKind ;
5858use crate :: marker:: Tuple ;
59- use crate :: mem;
59+ use crate :: mem:: { self , align_of } ;
6060
6161pub mod mir;
6262pub mod simd;
@@ -947,7 +947,7 @@ extern "rust-intrinsic" {
947947 /// own, or if it does not enable any significant optimizations.
948948 ///
949949 /// This intrinsic does not have a stable counterpart.
950- #[ rustc_const_stable( feature = "const_assume" , since = "CURRENT_RUSTC_VERSION " ) ]
950+ #[ rustc_const_stable( feature = "const_assume" , since = "1.77.0 " ) ]
951951 #[ rustc_nounwind]
952952 pub fn assume ( b : bool ) ;
953953
@@ -2567,16 +2567,18 @@ extern "rust-intrinsic" {
25672567 /// matter what*.
25682568 #[ rustc_const_unstable( feature = "is_val_statically_known" , issue = "none" ) ]
25692569 #[ rustc_nounwind]
2570- #[ cfg( not( bootstrap) ) ]
25712570 pub fn is_val_statically_known < T : Copy > ( arg : T ) -> bool ;
2571+
2572+ #[ rustc_const_unstable( feature = "delayed_debug_assertions" , issue = "none" ) ]
2573+ #[ rustc_safe_intrinsic]
2574+ #[ cfg( not( bootstrap) ) ]
2575+ pub ( crate ) fn debug_assertions ( ) -> bool ;
25722576}
25732577
2574- // FIXME: Seems using `unstable` here completely ignores `rustc_allow_const_fn_unstable`
2575- // and thus compiling stage0 core doesn't work.
2576- #[ rustc_const_stable( feature = "is_val_statically_known" , since = "0.0.0" ) ]
25772578#[ cfg( bootstrap) ]
2578- pub const unsafe fn is_val_statically_known < T : Copy > ( _arg : T ) -> bool {
2579- false
2579+ #[ rustc_const_unstable( feature = "delayed_debug_assertions" , issue = "none" ) ]
2580+ pub ( crate ) const fn debug_assertions ( ) -> bool {
2581+ cfg ! ( debug_assertions)
25802582}
25812583
25822584// Some functions are defined here because they accidentally got made
@@ -2587,10 +2589,27 @@ pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
25872589/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
25882590/// and only at runtime.
25892591///
2590- /// This macro should be called as `assert_unsafe_precondition!([Generics](name: Type) => Expression)`
2591- /// where the names specified will be moved into the macro as captured variables, and defines an item
2592- /// to call `const_eval_select` on. The tokens inside the square brackets are used to denote generics
2593- /// for the function declarations and can be omitted if there is no generics.
2592+ /// This macro should be called as
2593+ /// `assert_unsafe_precondition!((expr => name: Type, expr => name: Type) => Expression)`
2594+ /// where each `expr` will be evaluated and passed in as function argument `name: Type`. Then all
2595+ /// those arguments are passed to a function via [`const_eval_select`].
2596+ ///
2597+ /// These checks are behind a condition which is evaluated at codegen time, not expansion time like
2598+ /// [`debug_assert`]. This means that a standard library built with optimizations and debug
2599+ /// assertions disabled will have these checks optimized out of its monomorphizations, but if a
2600+ /// a caller of the standard library has debug assertions enabled and monomorphizes an expansion of
2601+ /// this macro, that monomorphization will contain the check.
2602+ ///
2603+ /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
2604+ /// implementation to mitigate their compile-time overhead. The runtime function that we
2605+ /// [`const_eval_select`] to is monomorphic, `#[inline(never)]`, and `#[rustc_nounwind]`. That
2606+ /// combination of properties ensures that the code for the checks is only compiled once, and has a
2607+ /// minimal impact on the caller's code size.
2608+ ///
2609+ /// Caller should also introducing any other `let` bindings or any code outside this macro in order
2610+ /// to call it. Since the precompiled standard library is built with full debuginfo and these
2611+ /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
2612+ /// debuginfo to have a measurable compile-time impact on debug builds.
25942613///
25952614/// # Safety
25962615///
@@ -2604,26 +2623,24 @@ pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
26042623///
26052624/// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make
26062625/// the occasional mistake, and this check should help them figure things out.
2607- #[ allow_internal_unstable( const_eval_select) ] // permit this to be called in stably-const fn
2626+ #[ allow_internal_unstable( const_eval_select, delayed_debug_assertions ) ] // permit this to be called in stably-const fn
26082627macro_rules! assert_unsafe_precondition {
2609- ( $name: expr, $( [ $( $tt: tt) * ] ) ?( $( $i: ident: $ty: ty) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2610- if cfg!( debug_assertions) {
2611- // allow non_snake_case to allow capturing const generics
2612- #[ allow( non_snake_case) ]
2613- #[ inline( always) ]
2614- fn runtime$( <$( $tt) * >) ?( $( $i: $ty) ,* ) {
2628+ ( $message: expr, ( $( $name: ident: $ty: ty = $arg: expr) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2629+ {
2630+ #[ inline( never) ]
2631+ #[ rustc_nounwind]
2632+ fn precondition_check( $( $name: $ty) ,* ) {
26152633 if !$e {
2616- // don't unwind to reduce impact on code size
26172634 :: core:: panicking:: panic_nounwind(
2618- concat!( "unsafe precondition(s) violated: " , $name )
2635+ concat!( "unsafe precondition(s) violated: " , $message )
26192636 ) ;
26202637 }
26212638 }
2622- #[ allow( non_snake_case) ]
2623- #[ inline]
2624- const fn comptime$( <$( $tt) * >) ?( $( _: $ty) ,* ) { }
2639+ const fn comptime( $( _: $ty) ,* ) { }
26252640
2626- :: core:: intrinsics:: const_eval_select( ( $( $i, ) * ) , comptime, runtime) ;
2641+ if :: core:: intrinsics:: debug_assertions( ) {
2642+ :: core:: intrinsics:: const_eval_select( ( $( $arg, ) * ) , comptime, precondition_check) ;
2643+ }
26272644 }
26282645 } ;
26292646}
@@ -2632,30 +2649,47 @@ pub(crate) use assert_unsafe_precondition;
26322649/// Checks whether `ptr` is properly aligned with respect to
26332650/// `align_of::<T>()`.
26342651#[ inline]
2635- pub ( crate ) fn is_aligned_and_not_null < T > ( ptr : * const T ) -> bool {
2636- !ptr. is_null ( ) && ptr. is_aligned ( )
2652+ pub ( crate ) fn is_aligned_and_not_null ( ptr : * const ( ) , align : usize ) -> bool {
2653+ !ptr. is_null ( ) && ptr. is_aligned_to ( align )
26372654}
26382655
2639- /// Checks whether an allocation of `len` instances of `T` exceeds
2640- /// the maximum allowed allocation size.
26412656#[ inline]
2642- pub ( crate ) fn is_valid_allocation_size < T > ( len : usize ) -> bool {
2643- let max_len = const {
2644- let size = crate :: mem:: size_of :: < T > ( ) ;
2645- if size == 0 { usize:: MAX } else { isize:: MAX as usize / size }
2646- } ;
2657+ pub ( crate ) fn is_valid_allocation_size ( size : usize , len : usize ) -> bool {
2658+ let max_len = if size == 0 { usize:: MAX } else { isize:: MAX as usize / size } ;
26472659 len <= max_len
26482660}
26492661
2662+ pub ( crate ) fn is_nonoverlapping_mono (
2663+ src : * const ( ) ,
2664+ dst : * const ( ) ,
2665+ size : usize ,
2666+ count : usize ,
2667+ ) -> bool {
2668+ let src_usize = src. addr ( ) ;
2669+ let dst_usize = dst. addr ( ) ;
2670+ let Some ( size) = size. checked_mul ( count) else {
2671+ crate :: panicking:: panic_nounwind (
2672+ "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2673+ )
2674+ } ;
2675+ let diff = src_usize. abs_diff ( dst_usize) ;
2676+ // If the absolute distance between the ptrs is at least as big as the size of the buffer,
2677+ // they do not overlap.
2678+ diff >= size
2679+ }
2680+
26502681/// Checks whether the regions of memory starting at `src` and `dst` of size
26512682/// `count * size_of::<T>()` do *not* overlap.
26522683#[ inline]
26532684pub ( crate ) fn is_nonoverlapping < T > ( src : * const T , dst : * const T , count : usize ) -> bool {
26542685 let src_usize = src. addr ( ) ;
26552686 let dst_usize = dst. addr ( ) ;
2656- let size = mem:: size_of :: < T > ( )
2657- . checked_mul ( count)
2658- . expect ( "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ) ;
2687+ let Some ( size) = mem:: size_of :: < T > ( ) . checked_mul ( count) else {
2688+ // Use panic_nounwind instead of Option::expect, so that this function is nounwind.
2689+ crate :: panicking:: panic_nounwind (
2690+ "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2691+ )
2692+ } ;
26592693 let diff = src_usize. abs_diff ( dst_usize) ;
26602694 // If the absolute distance between the ptrs is at least as big as the size of the buffer,
26612695 // they do not overlap.
@@ -2766,10 +2800,16 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
27662800 assert_unsafe_precondition ! (
27672801 "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
27682802 and the specified memory ranges do not overlap",
2769- [ T ] ( src: * const T , dst: * mut T , count: usize ) =>
2770- is_aligned_and_not_null( src)
2771- && is_aligned_and_not_null( dst)
2772- && is_nonoverlapping( src, dst, count)
2803+ (
2804+ src: * const ( ) = src as * const ( ) ,
2805+ dst: * mut ( ) = dst as * mut ( ) ,
2806+ size: usize = size_of:: <T >( ) ,
2807+ align: usize = align_of:: <T >( ) ,
2808+ count: usize = count,
2809+ ) =>
2810+ is_aligned_and_not_null( src, align)
2811+ && is_aligned_and_not_null( dst, align)
2812+ && is_nonoverlapping_mono( src, dst, size, count)
27732813 ) ;
27742814 copy_nonoverlapping ( src, dst, count)
27752815 }
@@ -2859,9 +2899,15 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
28592899 // SAFETY: the safety contract for `copy` must be upheld by the caller.
28602900 unsafe {
28612901 assert_unsafe_precondition ! (
2862- "ptr::copy requires that both pointer arguments are aligned and non-null" ,
2863- [ T ] ( src: * const T , dst: * mut T ) =>
2864- is_aligned_and_not_null( src) && is_aligned_and_not_null( dst)
2902+ "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2903+ and the specified memory ranges do not overlap",
2904+ (
2905+ src: * const ( ) = src as * const ( ) ,
2906+ dst: * mut ( ) = dst as * mut ( ) ,
2907+ align: usize = align_of:: <T >( ) ,
2908+ ) =>
2909+ is_aligned_and_not_null( src, align)
2910+ && is_aligned_and_not_null( dst, align)
28652911 ) ;
28662912 copy ( src, dst, count)
28672913 }
@@ -2934,7 +2980,10 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
29342980 unsafe {
29352981 assert_unsafe_precondition ! (
29362982 "ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
2937- [ T ] ( dst: * mut T ) => is_aligned_and_not_null( dst)
2983+ (
2984+ addr: * const ( ) = dst as * const ( ) ,
2985+ align: usize = align_of:: <T >( ) ,
2986+ ) => is_aligned_and_not_null( addr, align)
29382987 ) ;
29392988 write_bytes ( dst, val, count)
29402989 }
0 commit comments