about summary refs log tree commit diff
path: root/library/core/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-03-17 10:29:02 +0100
committerRalf Jung <post@ralfj.de>2024-03-23 18:45:05 +0100
commit61775304200826b412f18fc87218161fb8a0dc5c (patch)
tree43a2a658f42b2e50f03f8f09d1f970ebeef564a3 /library/core/src
parent987ef4c9221e792614a015a39b78f7d6a156c2f2 (diff)
downloadrust-61775304200826b412f18fc87218161fb8a0dc5c.tar.gz
rust-61775304200826b412f18fc87218161fb8a0dc5c.zip
refactor check_{lang,library}_ub: use a single intrinsic, put policy into library
Diffstat (limited to 'library/core/src')
-rw-r--r--library/core/src/intrinsics.rs44
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/ub_checks.rs35
3 files changed, 46 insertions, 34 deletions
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index af83a8d8fa4..76e387d54d8 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2661,38 +2661,22 @@ pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
     unsafe { ptr::swap_nonoverlapping(x, y, 1) };
 }
 
-/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)`
-/// during monomorphization.
-///
-/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can
-/// be used to implement debug assertions that are included in the precompiled standard library,
-/// but can be optimized out by builds that monomorphize the standard library code with debug
-/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
-///
-/// We have separate intrinsics for library UB and language UB because checkers like the const-eval
-/// interpreter and Miri already implement checks for language UB. Since such checkers do not know
-/// about library preconditions, checks guarded by this intrinsic let them find more UB.
-#[rustc_const_unstable(feature = "ub_checks", issue = "none")]
+/// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of
+/// `cfg!(debug_assertions)` during monomorphization.
+///
+/// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates
+/// compiled with and without debug_assertions. The common case here is a user program built with
+/// debug_assertions linked against the distributed sysroot which is built without debug_assertions.
+/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
+/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that
+/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the
+/// user has debug assertions disabled, the checks will still get optimized out. This intrinsic is
+/// primarily used by [`ub_checks::assert_unsafe_precondition`].
+#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[inline(always)]
-#[rustc_intrinsic]
-pub(crate) const fn check_library_ub() -> bool {
-    cfg!(debug_assertions)
-}
-
-/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)`
-/// during monomorphization.
-///
-/// Since checks implemented at the source level must come strictly before the operation that
-/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the
-/// interpreter's improved diagnostics for the cases that our source-level checks catch.
-///
-/// See `check_library_ub` for more information.
-#[rustc_const_unstable(feature = "ub_checks", issue = "none")]
-#[unstable(feature = "core_intrinsics", issue = "none")]
-#[inline(always)]
-#[rustc_intrinsic]
-pub(crate) const fn check_language_ub() -> bool {
+#[cfg_attr(not(bootstrap), rustc_intrinsic)] // just make it a regular fn in bootstrap
+pub(crate) const fn ub_checks() -> bool {
     cfg!(debug_assertions)
 }
 
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 8f292bf1551..0c95ecafc7c 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -171,6 +171,7 @@
 #![feature(const_type_id)]
 #![feature(const_type_name)]
 #![feature(const_typed_swap)]
+#![feature(const_ub_checks)]
 #![feature(const_unicode_case_lookup)]
 #![feature(const_unsafecell_get_mut)]
 #![feature(const_waker)]
diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs
index 2c5b4699806..ff6b2d30539 100644
--- a/library/core/src/ub_checks.rs
+++ b/library/core/src/ub_checks.rs
@@ -1,7 +1,7 @@
 //! Provides the [`assert_unsafe_precondition`] macro as well as some utility functions that cover
 //! common preconditions.
 
-use crate::intrinsics::const_eval_select;
+use crate::intrinsics::{self, const_eval_select};
 
 /// Check that the preconditions of an unsafe function are followed. The check is enabled at
 /// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
@@ -45,7 +45,7 @@ use crate::intrinsics::const_eval_select;
 /// order to call it. Since the precompiled standard library is built with full debuginfo and these
 /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
 /// debuginfo to have a measurable compile-time impact on debug builds.
-#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn
+#[allow_internal_unstable(const_ub_checks)] // permit this to be called in stably-const fn
 macro_rules! assert_unsafe_precondition {
     ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
         {
@@ -60,7 +60,7 @@ macro_rules! assert_unsafe_precondition {
             #[rustc_no_mir_inline]
             #[inline]
             #[rustc_nounwind]
-            #[rustc_const_unstable(feature = "ub_checks", issue = "none")]
+            #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
             const fn precondition_check($($name:$ty),*) {
                 if !$e {
                     ::core::panicking::panic_nounwind(
@@ -69,7 +69,7 @@ macro_rules! assert_unsafe_precondition {
                 }
             }
 
-            if ::core::intrinsics::$kind() {
+            if ::core::ub_checks::$kind() {
                 precondition_check($($arg,)*);
             }
         }
@@ -77,6 +77,33 @@ macro_rules! assert_unsafe_precondition {
 }
 pub(crate) use assert_unsafe_precondition;
 
+/// Checking library UB is always enabled when UB-checking is done
+/// (and we use a reexport so that there is no unnecessary wrapper function).
+pub(crate) use intrinsics::ub_checks as check_library_ub;
+
+/// Determines whether we should check for language UB.
+///
+/// The intention is to not do that when running in the interpreter, as that one has its own
+/// language UB checks which generally produce better errors.
+#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")]
+#[inline]
+pub(crate) const fn check_language_ub() -> bool {
+    #[inline]
+    fn runtime() -> bool {
+        // Disable UB checks in Miri.
+        !cfg!(miri)
+    }
+
+    #[inline]
+    const fn comptime() -> bool {
+        // Always disable UB checks.
+        false
+    }
+
+    // Only used for UB checks so we may const_eval_select.
+    intrinsics::ub_checks() && const_eval_select((), comptime, runtime)
+}
+
 /// Checks whether `ptr` is properly aligned with respect to
 /// `align_of::<T>()`.
 ///