@@ -67,6 +67,7 @@ use crate::marker::DiscriminantKind;
67
67
use crate :: marker:: Tuple ;
68
68
use crate :: mem:: align_of;
69
69
use crate :: ptr;
70
+ use crate :: ub_checks;
70
71
71
72
pub mod mir;
72
73
pub mod simd;
@@ -2755,132 +2756,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
2755
2756
// (`transmute` also falls into this category, but it cannot be wrapped due to the
2756
2757
// check that `T` and `U` have the same size.)
2757
2758
2758
- /// Check that the preconditions of an unsafe function are followed. The check is enabled at
2759
- /// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
2760
- /// checks implemented with this macro for language UB are always ignored.
2761
- ///
2762
- /// This macro should be called as
2763
- /// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
2764
- /// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
2765
- /// those arguments are passed to a function with the body `check_expr`.
2766
- /// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
2767
- /// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
2768
- /// of a documented library precondition that does not *immediately* lead to language UB.
2769
- ///
2770
- /// If `check_library_ub` is used but the check is actually guarding language UB, the check will
2771
- /// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
2772
- /// diagnostic, but our ability to detect UB is unchanged.
2773
- /// But if `check_language_ub` is used when the check is actually for library UB, the check is
2774
- /// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
2775
- /// library UB, the backtrace Miri reports may be far removed from original cause.
2776
- ///
2777
- /// These checks are behind a condition which is evaluated at codegen time, not expansion time like
2778
- /// [`debug_assert`]. This means that a standard library built with optimizations and debug
2779
- /// assertions disabled will have these checks optimized out of its monomorphizations, but if a
2780
- /// caller of the standard library has debug assertions enabled and monomorphizes an expansion of
2781
- /// this macro, that monomorphization will contain the check.
2782
- ///
2783
- /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
2784
- /// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
2785
- /// this structure:
2786
- /// ```ignore (pseudocode)
2787
- /// if ::core::intrinsics::check_language_ub() {
2788
- /// precondition_check(args)
2789
- /// }
2790
- /// ```
2791
- /// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
2792
- /// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
2793
- /// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
2794
- /// MIR, but *can* be inlined and fully optimized by a codegen backend.
2795
- ///
2796
- /// Callers should avoid introducing any other `let` bindings or any code outside this macro in
2797
- /// order to call it. Since the precompiled standard library is built with full debuginfo and these
2798
- /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
2799
- /// debuginfo to have a measurable compile-time impact on debug builds.
2800
- #[ allow_internal_unstable( ub_checks) ] // permit this to be called in stably-const fn
2801
- macro_rules! assert_unsafe_precondition {
2802
- ( $kind: ident, $message: expr, ( $( $name: ident: $ty: ty = $arg: expr) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2803
- {
2804
- // This check is inlineable, but not by the MIR inliner.
2805
- // The reason for this is that the MIR inliner is in an exceptionally bad position
2806
- // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
2807
- // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
2808
- // would be bad for compile times.
2809
- //
2810
- // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
2811
- // inlining the check. If it's `true`, it can inline it and get significantly better performance.
2812
- #[ rustc_no_mir_inline]
2813
- #[ inline]
2814
- #[ rustc_nounwind]
2815
- #[ rustc_const_unstable( feature = "ub_checks" , issue = "none" ) ]
2816
- const fn precondition_check( $( $name: $ty) ,* ) {
2817
- if !$e {
2818
- :: core:: panicking:: panic_nounwind(
2819
- concat!( "unsafe precondition(s) violated: " , $message)
2820
- ) ;
2821
- }
2822
- }
2823
-
2824
- if :: core:: intrinsics:: $kind( ) {
2825
- precondition_check( $( $arg, ) * ) ;
2826
- }
2827
- }
2828
- } ;
2829
- }
2830
- pub ( crate ) use assert_unsafe_precondition;
2831
-
2832
- /// Checks whether `ptr` is properly aligned with respect to
2833
- /// `align_of::<T>()`.
2834
- ///
2835
- /// In `const` this is approximate and can fail spuriously. It is primarily intended
2836
- /// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
2837
- /// check is anyway not executed in `const`.
2838
- #[ inline]
2839
- pub ( crate ) const fn is_aligned_and_not_null ( ptr : * const ( ) , align : usize ) -> bool {
2840
- !ptr. is_null ( ) && ptr. is_aligned_to ( align)
2841
- }
2842
-
2843
- #[ inline]
2844
- pub ( crate ) const fn is_valid_allocation_size ( size : usize , len : usize ) -> bool {
2845
- let max_len = if size == 0 { usize:: MAX } else { isize:: MAX as usize / size } ;
2846
- len <= max_len
2847
- }
2848
-
2849
- /// Checks whether the regions of memory starting at `src` and `dst` of size
2850
- /// `count * size` do *not* overlap.
2851
- ///
2852
- /// Note that in const-eval this function just returns `true` and therefore must
2853
- /// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
2854
- #[ inline]
2855
- pub ( crate ) const fn is_nonoverlapping (
2856
- src : * const ( ) ,
2857
- dst : * const ( ) ,
2858
- size : usize ,
2859
- count : usize ,
2860
- ) -> bool {
2861
- #[ inline]
2862
- fn runtime ( src : * const ( ) , dst : * const ( ) , size : usize , count : usize ) -> bool {
2863
- let src_usize = src. addr ( ) ;
2864
- let dst_usize = dst. addr ( ) ;
2865
- let Some ( size) = size. checked_mul ( count) else {
2866
- crate :: panicking:: panic_nounwind (
2867
- "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2868
- )
2869
- } ;
2870
- let diff = src_usize. abs_diff ( dst_usize) ;
2871
- // If the absolute distance between the ptrs is at least as big as the size of the buffer,
2872
- // they do not overlap.
2873
- diff >= size
2874
- }
2875
-
2876
- #[ inline]
2877
- const fn comptime ( _: * const ( ) , _: * const ( ) , _: usize , _: usize ) -> bool {
2878
- true
2879
- }
2880
-
2881
- const_eval_select ( ( src, dst, size, count) , comptime, runtime)
2882
- }
2883
-
2884
2759
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
2885
2760
/// and destination must *not* overlap.
2886
2761
///
@@ -2979,7 +2854,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2979
2854
pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
2980
2855
}
2981
2856
2982
- assert_unsafe_precondition ! (
2857
+ ub_checks :: assert_unsafe_precondition!(
2983
2858
check_language_ub,
2984
2859
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2985
2860
and the specified memory ranges do not overlap",
@@ -2990,9 +2865,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2990
2865
align: usize = align_of:: <T >( ) ,
2991
2866
count: usize = count,
2992
2867
) =>
2993
- is_aligned_and_not_null( src, align)
2994
- && is_aligned_and_not_null( dst, align)
2995
- && is_nonoverlapping( src, dst, size, count)
2868
+ ub_checks :: is_aligned_and_not_null( src, align)
2869
+ && ub_checks :: is_aligned_and_not_null( dst, align)
2870
+ && ub_checks :: is_nonoverlapping( src, dst, size, count)
2996
2871
) ;
2997
2872
2998
2873
// SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3083,7 +2958,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3083
2958
3084
2959
// SAFETY: the safety contract for `copy` must be upheld by the caller.
3085
2960
unsafe {
3086
- assert_unsafe_precondition ! (
2961
+ ub_checks :: assert_unsafe_precondition!(
3087
2962
check_language_ub,
3088
2963
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
3089
2964
and the specified memory ranges do not overlap",
@@ -3092,8 +2967,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3092
2967
dst: * mut ( ) = dst as * mut ( ) ,
3093
2968
align: usize = align_of:: <T >( ) ,
3094
2969
) =>
3095
- is_aligned_and_not_null( src, align)
3096
- && is_aligned_and_not_null( dst, align)
2970
+ ub_checks :: is_aligned_and_not_null( src, align)
2971
+ && ub_checks :: is_aligned_and_not_null( dst, align)
3097
2972
) ;
3098
2973
copy ( src, dst, count)
3099
2974
}
@@ -3164,13 +3039,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
3164
3039
3165
3040
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
3166
3041
unsafe {
3167
- assert_unsafe_precondition ! (
3042
+ ub_checks :: assert_unsafe_precondition!(
3168
3043
check_language_ub,
3169
3044
"ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
3170
3045
(
3171
3046
addr: * const ( ) = dst as * const ( ) ,
3172
3047
align: usize = align_of:: <T >( ) ,
3173
- ) => is_aligned_and_not_null( addr, align)
3048
+ ) => ub_checks :: is_aligned_and_not_null( addr, align)
3174
3049
) ;
3175
3050
write_bytes ( dst, val, count)
3176
3051
}
0 commit comments