6666use crate :: marker:: DiscriminantKind ;
6767use crate :: marker:: Tuple ;
6868use crate :: mem:: align_of;
69+ use crate :: ub_checks;
6970
7071pub mod mir;
7172pub mod simd;
@@ -2733,132 +2734,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
27332734// (`transmute` also falls into this category, but it cannot be wrapped due to the
27342735// check that `T` and `U` have the same size.)
27352736
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-
28622737/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
28632738/// and destination must *not* overlap.
28642739///
@@ -2957,7 +2832,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
29572832 pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
29582833 }
29592834
2960- assert_unsafe_precondition ! (
2835+ ub_checks :: assert_unsafe_precondition!(
29612836 check_language_ub,
29622837 "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
29632838 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
29682843 align: usize = align_of:: <T >( ) ,
29692844 count: usize = count,
29702845 ) =>
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)
29742849 ) ;
29752850
29762851 // 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) {
30612936
30622937 // SAFETY: the safety contract for `copy` must be upheld by the caller.
30632938 unsafe {
3064- assert_unsafe_precondition ! (
2939+ ub_checks :: assert_unsafe_precondition!(
30652940 check_language_ub,
30662941 "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
30672942 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) {
30702945 dst: * mut ( ) = dst as * mut ( ) ,
30712946 align: usize = align_of:: <T >( ) ,
30722947 ) =>
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)
30752950 ) ;
30762951 copy ( src, dst, count)
30772952 }
@@ -3142,13 +3017,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
31423017
31433018 // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
31443019 unsafe {
3145- assert_unsafe_precondition ! (
3020+ ub_checks :: assert_unsafe_precondition!(
31463021 check_language_ub,
31473022 "ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
31483023 (
31493024 addr: * const ( ) = dst as * const ( ) ,
31503025 align: usize = align_of:: <T >( ) ,
3151- ) => is_aligned_and_not_null( addr, align)
3026+ ) => ub_checks :: is_aligned_and_not_null( addr, align)
31523027 ) ;
31533028 write_bytes ( dst, val, count)
31543029 }
0 commit comments