@@ -67,6 +67,7 @@ use crate::marker::DiscriminantKind;
6767use crate :: marker:: Tuple ;
6868use crate :: mem:: align_of;
6969use crate :: ptr;
70+ use crate :: ub_checks;
7071
7172pub mod mir;
7273pub mod simd;
@@ -2660,38 +2661,22 @@ pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
26602661 unsafe { ptr:: swap_nonoverlapping ( x, y, 1 ) } ;
26612662}
26622663
2663- /// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)`
2664- /// during monomorphization.
2665- ///
2666- /// This intrinsic is evaluated after monomorphization, and therefore branching on this value can
2667- /// be used to implement debug assertions that are included in the precompiled standard library,
2668- /// but can be optimized out by builds that monomorphize the standard library code with debug
2669- /// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
2670- ///
2671- /// We have separate intrinsics for library UB and language UB because checkers like the const-eval
2672- /// interpreter and Miri already implement checks for language UB. Since such checkers do not know
2673- /// about library preconditions, checks guarded by this intrinsic let them find more UB.
2674- #[ rustc_const_unstable( feature = "ub_checks" , issue = "none" ) ]
2675- #[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
2676- #[ inline( always) ]
2677- #[ rustc_intrinsic]
2678- pub ( crate ) const fn check_library_ub ( ) -> bool {
2679- cfg ! ( debug_assertions)
2680- }
2681-
2682- /// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)`
2683- /// during monomorphization.
2684- ///
2685- /// Since checks implemented at the source level must come strictly before the operation that
2686- /// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the
2687- /// interpreter's improved diagnostics for the cases that our source-level checks catch.
2688- ///
2689- /// See `check_library_ub` for more information.
2690- #[ rustc_const_unstable( feature = "ub_checks" , issue = "none" ) ]
2664+ /// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of
2665+ /// `cfg!(debug_assertions)` during monomorphization.
2666+ ///
2667+ /// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates
2668+ /// compiled with and without debug_assertions. The common case here is a user program built with
2669+ /// debug_assertions linked against the distributed sysroot which is built without debug_assertions.
2670+ /// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
2671+ /// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that
2672+ /// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the
2673+ /// user has debug assertions disabled, the checks will still get optimized out. This intrinsic is
2674+ /// primarily used by [`ub_checks::assert_unsafe_precondition`].
2675+ #[ rustc_const_unstable( feature = "const_ub_checks" , issue = "none" ) ]
26912676#[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
26922677#[ inline( always) ]
2693- #[ rustc_intrinsic]
2694- pub ( crate ) const fn check_language_ub ( ) -> bool {
2678+ #[ cfg_attr ( not ( bootstrap ) , rustc_intrinsic) ] // just make it a regular fn in bootstrap
2679+ pub ( crate ) const fn ub_checks ( ) -> bool {
26952680 cfg ! ( debug_assertions)
26962681}
26972682
@@ -2755,132 +2740,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
27552740// (`transmute` also falls into this category, but it cannot be wrapped due to the
27562741// check that `T` and `U` have the same size.)
27572742
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-
28842743/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
28852744/// and destination must *not* overlap.
28862745///
@@ -2979,7 +2838,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
29792838 pub fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) ;
29802839 }
29812840
2982- assert_unsafe_precondition ! (
2841+ ub_checks :: assert_unsafe_precondition!(
29832842 check_language_ub,
29842843 "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
29852844 and the specified memory ranges do not overlap",
@@ -2990,9 +2849,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
29902849 align: usize = align_of:: <T >( ) ,
29912850 count: usize = count,
29922851 ) =>
2993- is_aligned_and_not_null( src, align)
2994- && is_aligned_and_not_null( dst, align)
2995- && is_nonoverlapping( src, dst, size, count)
2852+ ub_checks :: is_aligned_and_not_null( src, align)
2853+ && ub_checks :: is_aligned_and_not_null( dst, align)
2854+ && ub_checks :: is_nonoverlapping( src, dst, size, count)
29962855 ) ;
29972856
29982857 // SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3083,7 +2942,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30832942
30842943 // SAFETY: the safety contract for `copy` must be upheld by the caller.
30852944 unsafe {
3086- assert_unsafe_precondition ! (
2945+ ub_checks :: assert_unsafe_precondition!(
30872946 check_language_ub,
30882947 "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
30892948 and the specified memory ranges do not overlap",
@@ -3092,8 +2951,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30922951 dst: * mut ( ) = dst as * mut ( ) ,
30932952 align: usize = align_of:: <T >( ) ,
30942953 ) =>
3095- is_aligned_and_not_null( src, align)
3096- && is_aligned_and_not_null( dst, align)
2954+ ub_checks :: is_aligned_and_not_null( src, align)
2955+ && ub_checks :: is_aligned_and_not_null( dst, align)
30972956 ) ;
30982957 copy ( src, dst, count)
30992958 }
@@ -3164,13 +3023,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
31643023
31653024 // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
31663025 unsafe {
3167- assert_unsafe_precondition ! (
3026+ ub_checks :: assert_unsafe_precondition!(
31683027 check_language_ub,
31693028 "ptr::write_bytes requires that the destination pointer is aligned and non-null" ,
31703029 (
31713030 addr: * const ( ) = dst as * const ( ) ,
31723031 align: usize = align_of:: <T >( ) ,
3173- ) => is_aligned_and_not_null( addr, align)
3032+ ) => ub_checks :: is_aligned_and_not_null( addr, align)
31743033 ) ;
31753034 write_bytes ( dst, val, count)
31763035 }
0 commit comments