56
56
57
57
use crate :: marker:: DiscriminantKind ;
58
58
use crate :: marker:: Tuple ;
59
- use crate :: mem;
59
+ use crate :: mem:: { self , align_of } ;
60
60
61
61
pub mod mir;
62
62
pub mod simd;
@@ -2569,6 +2569,17 @@ extern "rust-intrinsic" {
2569
2569
#[ rustc_nounwind]
2570
2570
#[ cfg( not( bootstrap) ) ]
2571
2571
pub fn is_val_statically_known < T : Copy > ( arg : T ) -> bool ;
2572
+
2573
+ #[ rustc_const_unstable( feature = "delayed_debug_assertions" , issue = "none" ) ]
2574
+ #[ rustc_safe_intrinsic]
2575
+ #[ cfg( not( bootstrap) ) ]
2576
+ pub ( crate ) fn debug_assertions ( ) -> bool ;
2577
+ }
2578
+
2579
+ #[ cfg( bootstrap) ]
2580
+ #[ rustc_const_unstable( feature = "delayed_debug_assertions" , issue = "none" ) ]
2581
+ pub ( crate ) const fn debug_assertions ( ) -> bool {
2582
+ cfg ! ( debug_assertions)
2572
2583
}
2573
2584
2574
2585
// FIXME: Seems using `unstable` here completely ignores `rustc_allow_const_fn_unstable`
@@ -2587,10 +2598,27 @@ pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
2587
2598
/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
2588
2599
/// and only at runtime.
2589
2600
///
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.
2601
+ /// This macro should be called as
2602
+ /// `assert_unsafe_precondition!((expr => name: Type, expr => name: Type) => Expression)`
2603
+ /// where each `expr` will be evaluated and passed in as function argument `name: Type`. Then all
2604
+ /// those arguments are passed to a function via [`const_eval_select`].
2605
+ ///
2606
+ /// These checks are behind a condition which is evaluated at codegen time, not expansion time like
2607
+ /// [`debug_assert`]. This means that a standard library built with optimizations and debug
2608
+ /// assertions disabled will have these checks optimized out of its monomorphizations, but if a
2609
+ /// a caller of the standard library has debug assertions enabled and monomorphizes an expansion of
2610
+ /// this macro, that monomorphization will contain the check.
2611
+ ///
2612
+ /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
2613
+ /// implementation to mitigate their compile-time overhead. The runtime function that we
2614
+ /// [`const_eval_select`] to is monomorphic, `#[inline(never)]`, and `#[rustc_nounwind]`. That
2615
+ /// combination of properties ensures that the code for the checks is only compiled once, and has a
2616
+ /// minimal impact on the caller's code size.
2617
+ ///
2618
+ /// Caller should also introducing any other `let` bindings or any code outside this macro in order
2619
+ /// to call it. Since the precompiled standard library is built with full debuginfo and these
2620
+ /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
2621
+ /// debuginfo to have a measurable compile-time impact on debug builds.
2594
2622
///
2595
2623
/// # Safety
2596
2624
///
@@ -2604,26 +2632,24 @@ pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
2604
2632
///
2605
2633
/// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make
2606
2634
/// 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
2635
+ #[ allow_internal_unstable( const_eval_select, delayed_debug_assertions ) ] // permit this to be called in stably-const fn
2608
2636
macro_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) ,* ) {
2637
+ ( $message: expr, ( $( $name: ident: $ty: ty = $arg: expr) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2638
+ {
2639
+ #[ inline( never) ]
2640
+ #[ rustc_nounwind]
2641
+ fn precondition_check( $( $name: $ty) ,* ) {
2615
2642
if !$e {
2616
- // don't unwind to reduce impact on code size
2617
2643
:: core:: panicking:: panic_nounwind(
2618
- concat!( "unsafe precondition(s) violated: " , $name )
2644
+ concat!( "unsafe precondition(s) violated: " , $message )
2619
2645
) ;
2620
2646
}
2621
2647
}
2622
- #[ allow( non_snake_case) ]
2623
- #[ inline]
2624
- const fn comptime$( <$( $tt) * >) ?( $( _: $ty) ,* ) { }
2648
+ const fn comptime( $( _: $ty) ,* ) { }
2625
2649
2626
- :: core:: intrinsics:: const_eval_select( ( $( $i, ) * ) , comptime, runtime) ;
2650
+ if :: core:: intrinsics:: debug_assertions( ) {
2651
+ :: core:: intrinsics:: const_eval_select( ( $( $arg, ) * ) , comptime, precondition_check) ;
2652
+ }
2627
2653
}
2628
2654
} ;
2629
2655
}
@@ -2632,30 +2658,47 @@ pub(crate) use assert_unsafe_precondition;
2632
2658
/// Checks whether `ptr` is properly aligned with respect to
2633
2659
/// `align_of::<T>()`.
2634
2660
#[ inline]
2635
- pub ( crate ) fn is_aligned_and_not_null < T > ( ptr : * const T ) -> bool {
2636
- !ptr. is_null ( ) && ptr. is_aligned ( )
2661
+ pub ( crate ) fn is_aligned_and_not_null ( ptr : * const ( ) , align : usize ) -> bool {
2662
+ !ptr. is_null ( ) && ptr. is_aligned_to ( align )
2637
2663
}
2638
2664
2639
- /// Checks whether an allocation of `len` instances of `T` exceeds
2640
- /// the maximum allowed allocation size.
2641
2665
#[ 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
- } ;
2666
+ pub ( crate ) fn is_valid_allocation_size ( size : usize , len : usize ) -> bool {
2667
+ let max_len = if size == 0 { usize:: MAX } else { isize:: MAX as usize / size } ;
2647
2668
len <= max_len
2648
2669
}
2649
2670
2671
+ pub ( crate ) fn is_nonoverlapping_mono (
2672
+ src : * const ( ) ,
2673
+ dst : * const ( ) ,
2674
+ size : usize ,
2675
+ count : usize ,
2676
+ ) -> bool {
2677
+ let src_usize = src. addr ( ) ;
2678
+ let dst_usize = dst. addr ( ) ;
2679
+ let Some ( size) = size. checked_mul ( count) else {
2680
+ crate :: panicking:: panic_nounwind (
2681
+ "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2682
+ )
2683
+ } ;
2684
+ let diff = src_usize. abs_diff ( dst_usize) ;
2685
+ // If the absolute distance between the ptrs is at least as big as the size of the buffer,
2686
+ // they do not overlap.
2687
+ diff >= size
2688
+ }
2689
+
2650
2690
/// Checks whether the regions of memory starting at `src` and `dst` of size
2651
2691
/// `count * size_of::<T>()` do *not* overlap.
2652
2692
#[ inline]
2653
2693
pub ( crate ) fn is_nonoverlapping < T > ( src : * const T , dst : * const T , count : usize ) -> bool {
2654
2694
let src_usize = src. addr ( ) ;
2655
2695
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" ) ;
2696
+ let Some ( size) = mem:: size_of :: < T > ( ) . checked_mul ( count) else {
2697
+ // Use panic_nounwind instead of Option::expect, so that this function is nounwind.
2698
+ crate :: panicking:: panic_nounwind (
2699
+ "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2700
+ )
2701
+ } ;
2659
2702
let diff = src_usize. abs_diff ( dst_usize) ;
2660
2703
// If the absolute distance between the ptrs is at least as big as the size of the buffer,
2661
2704
// they do not overlap.
@@ -2766,10 +2809,16 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2766
2809
assert_unsafe_precondition ! (
2767
2810
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2768
2811
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)
2812
+ (
2813
+ src: * const ( ) = src as * const ( ) ,
2814
+ dst: * mut ( ) = dst as * mut ( ) ,
2815
+ size: usize = size_of:: <T >( ) ,
2816
+ align: usize = align_of:: <T >( ) ,
2817
+ count: usize = count,
2818
+ ) =>
2819
+ is_aligned_and_not_null( src, align)
2820
+ && is_aligned_and_not_null( dst, align)
2821
+ && is_nonoverlapping_mono( src, dst, size, count)
2773
2822
) ;
2774
2823
copy_nonoverlapping ( src, dst, count)
2775
2824
}
@@ -2859,9 +2908,15 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
2859
2908
// SAFETY: the safety contract for `copy` must be upheld by the caller.
2860
2909
unsafe {
2861
2910
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)
2911
+ "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2912
+ and the specified memory ranges do not overlap",
2913
+ (
2914
+ src: * const ( ) = src as * const ( ) ,
2915
+ dst: * mut ( ) = dst as * mut ( ) ,
2916
+ align: usize = align_of:: <T >( ) ,
2917
+ ) =>
2918
+ is_aligned_and_not_null( src, align)
2919
+ && is_aligned_and_not_null( dst, align)
2865
2920
) ;
2866
2921
copy ( src, dst, count)
2867
2922
}
@@ -2934,7 +2989,10 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
2934
2989
unsafe {
2935
2990
assert_unsafe_precondition ! (
2936
2991
"ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
2937
- [ T ] ( dst: * mut T ) => is_aligned_and_not_null( dst)
2992
+ (
2993
+ addr: * const ( ) = dst as * const ( ) ,
2994
+ align: usize = align_of:: <T >( ) ,
2995
+ ) => is_aligned_and_not_null( addr, align)
2938
2996
) ;
2939
2997
write_bytes ( dst, val, count)
2940
2998
}
0 commit comments