66
66
use crate :: marker:: DiscriminantKind ;
67
67
use crate :: marker:: Tuple ;
68
68
use crate :: mem:: align_of;
69
+ use crate :: ub_checks;
69
70
70
71
pub mod mir;
71
72
pub mod simd;
@@ -2727,147 +2728,6 @@ pub unsafe fn retag_box_to_raw<T: ?Sized, A>(ptr: *mut T) -> *mut T {
2727
2728
// (`transmute` also falls into this category, but it cannot be wrapped due to the
2728
2729
// check that `T` and `U` have the same size.)
2729
2730
2730
- /// Check that the preconditions of an unsafe function are followed. The check is enabled at
2731
- /// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
2732
- /// checks implemented with this macro for language UB are always ignored.
2733
- ///
2734
- /// This macro should be called as
2735
- /// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
2736
- /// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
2737
- /// those arguments are passed to a function with the body `check_expr`.
2738
- /// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
2739
- /// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
2740
- /// of a documented library precondition that does not *immediately* lead to language UB.
2741
- ///
2742
- /// If `check_library_ub` is used but the check is actually guarding language UB, the check will
2743
- /// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
2744
- /// diagnostic, but our ability to detect UB is unchanged.
2745
- /// But if `check_language_ub` is used when the check is actually for library UB, the check is
2746
- /// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
2747
- /// library UB, the backtrace Miri reports may be far removed from original cause.
2748
- ///
2749
- /// These checks are behind a condition which is evaluated at codegen time, not expansion time like
2750
- /// [`debug_assert`]. This means that a standard library built with optimizations and debug
2751
- /// assertions disabled will have these checks optimized out of its monomorphizations, but if a
2752
- /// caller of the standard library has debug assertions enabled and monomorphizes an expansion of
2753
- /// this macro, that monomorphization will contain the check.
2754
- ///
2755
- /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
2756
- /// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
2757
- /// this structure:
2758
- /// ```ignore (pseudocode)
2759
- /// if ::core::intrinsics::check_language_ub() {
2760
- /// precondition_check(args)
2761
- /// }
2762
- /// ```
2763
- /// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
2764
- /// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
2765
- /// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
2766
- /// MIR, but *can* be inlined and fully optimized by a codegen backend.
2767
- ///
2768
- /// Callers should avoid introducing any other `let` bindings or any code outside this macro in
2769
- /// order to call it. Since the precompiled standard library is built with full debuginfo and these
2770
- /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
2771
- /// debuginfo to have a measurable compile-time impact on debug builds.
2772
- #[ allow_internal_unstable( ub_checks) ] // permit this to be called in stably-const fn
2773
- macro_rules! assert_unsafe_precondition {
2774
- ( $kind: ident, $message: expr, ( $( $name: ident: $ty: ty = $arg: expr) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2775
- {
2776
- // #[cfg(bootstrap)] (this comment)
2777
- // When the standard library is compiled with debug assertions, we want the check to inline for better performance.
2778
- // This is important when working on the compiler, which is compiled with debug assertions locally.
2779
- // When not compiled with debug assertions (so the precompiled std) we outline the check to minimize the compile
2780
- // time impact when debug assertions are disabled.
2781
- // The proper solution to this is the `#[rustc_no_mir_inline]` below, but we still want decent performance for cfg(bootstrap).
2782
- #[ cfg_attr( all( debug_assertions, bootstrap) , inline( always) ) ]
2783
- #[ cfg_attr( all( not( debug_assertions) , bootstrap) , inline( never) ) ]
2784
-
2785
- // This check is inlineable, but not by the MIR inliner.
2786
- // The reason for this is that the MIR inliner is in an exceptionally bad position
2787
- // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
2788
- // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
2789
- // would be bad for compile times.
2790
- //
2791
- // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
2792
- // inlining the check. If it's `true`, it can inline it and get significantly better performance.
2793
- #[ cfg_attr( not( bootstrap) , rustc_no_mir_inline) ]
2794
- #[ cfg_attr( not( bootstrap) , inline) ]
2795
- #[ rustc_nounwind]
2796
- #[ rustc_const_unstable( feature = "ub_checks" , issue = "none" ) ]
2797
- const fn precondition_check( $( $name: $ty) ,* ) {
2798
- if !$e {
2799
- :: core:: panicking:: panic_nounwind(
2800
- concat!( "unsafe precondition(s) violated: " , $message)
2801
- ) ;
2802
- }
2803
- }
2804
-
2805
- if :: core:: intrinsics:: $kind( ) {
2806
- precondition_check( $( $arg, ) * ) ;
2807
- }
2808
- }
2809
- } ;
2810
- }
2811
- pub ( crate ) use assert_unsafe_precondition;
2812
-
2813
- /// Checks whether `ptr` is properly aligned with respect to
2814
- /// `align_of::<T>()`.
2815
- ///
2816
- /// In `const` this is approximate and can fail spuriously. It is primarily intended
2817
- /// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
2818
- /// check is anyway not executed in `const`.
2819
- #[ inline]
2820
- pub ( crate ) const fn is_aligned_and_not_null ( ptr : * const ( ) , align : usize ) -> bool {
2821
- !ptr. is_null ( ) && ptr. is_aligned_to ( align)
2822
- }
2823
-
2824
- #[ inline]
2825
- pub ( crate ) const fn is_valid_allocation_size ( size : usize , len : usize ) -> bool {
2826
- let max_len = if size == 0 { usize:: MAX } else { isize:: MAX as usize / size } ;
2827
- len <= max_len
2828
- }
2829
-
2830
- /// Checks whether the regions of memory starting at `src` and `dst` of size
2831
- /// `count * size` do *not* overlap.
2832
- ///
2833
- /// Note that in const-eval this function just returns `true` and therefore must
2834
- /// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
2835
- #[ inline]
2836
- pub ( crate ) const fn is_nonoverlapping (
2837
- src : * const ( ) ,
2838
- dst : * const ( ) ,
2839
- size : usize ,
2840
- count : usize ,
2841
- ) -> bool {
2842
- #[ inline]
2843
- fn runtime ( src : * const ( ) , dst : * const ( ) , size : usize , count : usize ) -> bool {
2844
- let src_usize = src. addr ( ) ;
2845
- let dst_usize = dst. addr ( ) ;
2846
- let Some ( size) = size. checked_mul ( count) else {
2847
- crate :: panicking:: panic_nounwind (
2848
- "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2849
- )
2850
- } ;
2851
- let diff = src_usize. abs_diff ( dst_usize) ;
2852
- // If the absolute distance between the ptrs is at least as big as the size of the buffer,
2853
- // they do not overlap.
2854
- diff >= size
2855
- }
2856
-
2857
- #[ inline]
2858
- const fn comptime ( _: * const ( ) , _: * const ( ) , _: usize , _: usize ) -> bool {
2859
- true
2860
- }
2861
-
2862
- #[ cfg_attr( not( bootstrap) , allow( unused_unsafe) ) ] // on bootstrap bump, remove unsafe block
2863
- // SAFETY: This function's precondition is equivalent to that of `const_eval_select`.
2864
- // Programs which do not execute UB will only see this function return `true`, which makes the
2865
- // const and runtime implementation indistinguishable.
2866
- unsafe {
2867
- const_eval_select ( ( src, dst, size, count) , comptime, runtime)
2868
- }
2869
- }
2870
-
2871
2731
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
2872
2732
/// and destination must *not* overlap.
2873
2733
///
@@ -2966,7 +2826,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2966
2826
pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
2967
2827
}
2968
2828
2969
- assert_unsafe_precondition ! (
2829
+ ub_checks :: assert_unsafe_precondition!(
2970
2830
check_language_ub,
2971
2831
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2972
2832
and the specified memory ranges do not overlap",
@@ -2977,9 +2837,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2977
2837
align: usize = align_of:: <T >( ) ,
2978
2838
count: usize = count,
2979
2839
) =>
2980
- is_aligned_and_not_null( src, align)
2981
- && is_aligned_and_not_null( dst, align)
2982
- && is_nonoverlapping( src, dst, size, count)
2840
+ ub_checks :: is_aligned_and_not_null( src, align)
2841
+ && ub_checks :: is_aligned_and_not_null( dst, align)
2842
+ && ub_checks :: is_nonoverlapping( src, dst, size, count)
2983
2843
) ;
2984
2844
2985
2845
// SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3070,7 +2930,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3070
2930
3071
2931
// SAFETY: the safety contract for `copy` must be upheld by the caller.
3072
2932
unsafe {
3073
- assert_unsafe_precondition ! (
2933
+ ub_checks :: assert_unsafe_precondition!(
3074
2934
check_language_ub,
3075
2935
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
3076
2936
and the specified memory ranges do not overlap",
@@ -3079,8 +2939,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3079
2939
dst: * mut ( ) = dst as * mut ( ) ,
3080
2940
align: usize = align_of:: <T >( ) ,
3081
2941
) =>
3082
- is_aligned_and_not_null( src, align)
3083
- && is_aligned_and_not_null( dst, align)
2942
+ ub_checks :: is_aligned_and_not_null( src, align)
2943
+ && ub_checks :: is_aligned_and_not_null( dst, align)
3084
2944
) ;
3085
2945
copy ( src, dst, count)
3086
2946
}
@@ -3151,13 +3011,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
3151
3011
3152
3012
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
3153
3013
unsafe {
3154
- assert_unsafe_precondition ! (
3014
+ ub_checks :: assert_unsafe_precondition!(
3155
3015
check_language_ub,
3156
3016
"ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
3157
3017
(
3158
3018
addr: * const ( ) = dst as * const ( ) ,
3159
3019
align: usize = align_of:: <T >( ) ,
3160
- ) => is_aligned_and_not_null( addr, align)
3020
+ ) => ub_checks :: is_aligned_and_not_null( addr, align)
3161
3021
) ;
3162
3022
write_bytes ( dst, val, count)
3163
3023
}
0 commit comments