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;
@@ -2733,132 +2734,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
2733
2734
// (`transmute` also falls into this category, but it cannot be wrapped due to the
2734
2735
// check that `T` and `U` have the same size.)
2735
2736
2736
- /// Check that the preconditions of an unsafe function are followed. The check is enabled at
2737
- /// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
2738
- /// checks implemented with this macro for language UB are always ignored.
2739
- ///
2740
- /// This macro should be called as
2741
- /// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
2742
- /// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
2743
- /// those arguments are passed to a function with the body `check_expr`.
2744
- /// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
2745
- /// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
2746
- /// of a documented library precondition that does not *immediately* lead to language UB.
2747
- ///
2748
- /// If `check_library_ub` is used but the check is actually guarding language UB, the check will
2749
- /// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
2750
- /// diagnostic, but our ability to detect UB is unchanged.
2751
- /// But if `check_language_ub` is used when the check is actually for library UB, the check is
2752
- /// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
2753
- /// library UB, the backtrace Miri reports may be far removed from original cause.
2754
- ///
2755
- /// These checks are behind a condition which is evaluated at codegen time, not expansion time like
2756
- /// [`debug_assert`]. This means that a standard library built with optimizations and debug
2757
- /// assertions disabled will have these checks optimized out of its monomorphizations, but if a
2758
- /// caller of the standard library has debug assertions enabled and monomorphizes an expansion of
2759
- /// this macro, that monomorphization will contain the check.
2760
- ///
2761
- /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
2762
- /// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
2763
- /// this structure:
2764
- /// ```ignore (pseudocode)
2765
- /// if ::core::intrinsics::check_language_ub() {
2766
- /// precondition_check(args)
2767
- /// }
2768
- /// ```
2769
- /// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
2770
- /// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
2771
- /// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
2772
- /// MIR, but *can* be inlined and fully optimized by a codegen backend.
2773
- ///
2774
- /// Callers should avoid introducing any other `let` bindings or any code outside this macro in
2775
- /// order to call it. Since the precompiled standard library is built with full debuginfo and these
2776
- /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
2777
- /// debuginfo to have a measurable compile-time impact on debug builds.
2778
- #[ allow_internal_unstable( ub_checks) ] // permit this to be called in stably-const fn
2779
- macro_rules! assert_unsafe_precondition {
2780
- ( $kind: ident, $message: expr, ( $( $name: ident: $ty: ty = $arg: expr) ,* $( , ) ?) => $e: expr $( , ) ?) => {
2781
- {
2782
- // This check is inlineable, but not by the MIR inliner.
2783
- // The reason for this is that the MIR inliner is in an exceptionally bad position
2784
- // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
2785
- // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
2786
- // would be bad for compile times.
2787
- //
2788
- // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
2789
- // inlining the check. If it's `true`, it can inline it and get significantly better performance.
2790
- #[ rustc_no_mir_inline]
2791
- #[ inline]
2792
- #[ rustc_nounwind]
2793
- #[ rustc_const_unstable( feature = "ub_checks" , issue = "none" ) ]
2794
- const fn precondition_check( $( $name: $ty) ,* ) {
2795
- if !$e {
2796
- :: core:: panicking:: panic_nounwind(
2797
- concat!( "unsafe precondition(s) violated: " , $message)
2798
- ) ;
2799
- }
2800
- }
2801
-
2802
- if :: core:: intrinsics:: $kind( ) {
2803
- precondition_check( $( $arg, ) * ) ;
2804
- }
2805
- }
2806
- } ;
2807
- }
2808
- pub ( crate ) use assert_unsafe_precondition;
2809
-
2810
- /// Checks whether `ptr` is properly aligned with respect to
2811
- /// `align_of::<T>()`.
2812
- ///
2813
- /// In `const` this is approximate and can fail spuriously. It is primarily intended
2814
- /// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
2815
- /// check is anyway not executed in `const`.
2816
- #[ inline]
2817
- pub ( crate ) const fn is_aligned_and_not_null ( ptr : * const ( ) , align : usize ) -> bool {
2818
- !ptr. is_null ( ) && ptr. is_aligned_to ( align)
2819
- }
2820
-
2821
- #[ inline]
2822
- pub ( crate ) const fn is_valid_allocation_size ( size : usize , len : usize ) -> bool {
2823
- let max_len = if size == 0 { usize:: MAX } else { isize:: MAX as usize / size } ;
2824
- len <= max_len
2825
- }
2826
-
2827
- /// Checks whether the regions of memory starting at `src` and `dst` of size
2828
- /// `count * size` do *not* overlap.
2829
- ///
2830
- /// Note that in const-eval this function just returns `true` and therefore must
2831
- /// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
2832
- #[ inline]
2833
- pub ( crate ) const fn is_nonoverlapping (
2834
- src : * const ( ) ,
2835
- dst : * const ( ) ,
2836
- size : usize ,
2837
- count : usize ,
2838
- ) -> bool {
2839
- #[ inline]
2840
- fn runtime ( src : * const ( ) , dst : * const ( ) , size : usize , count : usize ) -> bool {
2841
- let src_usize = src. addr ( ) ;
2842
- let dst_usize = dst. addr ( ) ;
2843
- let Some ( size) = size. checked_mul ( count) else {
2844
- crate :: panicking:: panic_nounwind (
2845
- "is_nonoverlapping: `size_of::<T>() * count` overflows a usize" ,
2846
- )
2847
- } ;
2848
- let diff = src_usize. abs_diff ( dst_usize) ;
2849
- // If the absolute distance between the ptrs is at least as big as the size of the buffer,
2850
- // they do not overlap.
2851
- diff >= size
2852
- }
2853
-
2854
- #[ inline]
2855
- const fn comptime ( _: * const ( ) , _: * const ( ) , _: usize , _: usize ) -> bool {
2856
- true
2857
- }
2858
-
2859
- const_eval_select ( ( src, dst, size, count) , comptime, runtime)
2860
- }
2861
-
2862
2737
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
2863
2738
/// and destination must *not* overlap.
2864
2739
///
@@ -2957,7 +2832,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2957
2832
pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
2958
2833
}
2959
2834
2960
- assert_unsafe_precondition ! (
2835
+ ub_checks :: assert_unsafe_precondition!(
2961
2836
check_language_ub,
2962
2837
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
2963
2838
and the specified memory ranges do not overlap",
@@ -2968,9 +2843,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
2968
2843
align: usize = align_of:: <T >( ) ,
2969
2844
count: usize = count,
2970
2845
) =>
2971
- is_aligned_and_not_null( src, align)
2972
- && is_aligned_and_not_null( dst, align)
2973
- && is_nonoverlapping( src, dst, size, count)
2846
+ ub_checks :: is_aligned_and_not_null( src, align)
2847
+ && ub_checks :: is_aligned_and_not_null( dst, align)
2848
+ && ub_checks :: is_nonoverlapping( src, dst, size, count)
2974
2849
) ;
2975
2850
2976
2851
// SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3061,7 +2936,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3061
2936
3062
2937
// SAFETY: the safety contract for `copy` must be upheld by the caller.
3063
2938
unsafe {
3064
- assert_unsafe_precondition ! (
2939
+ ub_checks :: assert_unsafe_precondition!(
3065
2940
check_language_ub,
3066
2941
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
3067
2942
and the specified memory ranges do not overlap",
@@ -3070,8 +2945,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
3070
2945
dst: * mut ( ) = dst as * mut ( ) ,
3071
2946
align: usize = align_of:: <T >( ) ,
3072
2947
) =>
3073
- is_aligned_and_not_null( src, align)
3074
- && is_aligned_and_not_null( dst, align)
2948
+ ub_checks :: is_aligned_and_not_null( src, align)
2949
+ && ub_checks :: is_aligned_and_not_null( dst, align)
3075
2950
) ;
3076
2951
copy ( src, dst, count)
3077
2952
}
@@ -3142,13 +3017,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
3142
3017
3143
3018
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
3144
3019
unsafe {
3145
- assert_unsafe_precondition ! (
3020
+ ub_checks :: assert_unsafe_precondition!(
3146
3021
check_language_ub,
3147
3022
"ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
3148
3023
(
3149
3024
addr: * const ( ) = dst as * const ( ) ,
3150
3025
align: usize = align_of:: <T >( ) ,
3151
- ) => is_aligned_and_not_null( addr, align)
3026
+ ) => ub_checks :: is_aligned_and_not_null( addr, align)
3152
3027
) ;
3153
3028
write_bytes ( dst, val, count)
3154
3029
}
0 commit comments