@@ -2514,6 +2514,8 @@ extern "rust-intrinsic" {
2514
2514
/// intrinsic will be replaced with a call to `called_in_const`. It gets
2515
2515
/// replaced with a call to `called_at_rt` otherwise.
2516
2516
///
2517
+ /// This function is safe to call, but note the stability concerns below.
2518
+ ///
2517
2519
/// # Type Requirements
2518
2520
///
2519
2521
/// The two functions must be both function items. They cannot be function
@@ -2523,45 +2525,47 @@ extern "rust-intrinsic" {
2523
2525
/// the two functions, therefore, both functions must accept the same type of
2524
2526
/// arguments. Both functions must return RET.
2525
2527
///
2526
- /// # Safety
2528
+ /// # Stability concerns
2527
2529
///
2528
- /// The two functions must behave observably equivalent. Safe code in other
2529
- /// crates may assume that calling a `const fn` at compile-time and at run-time
2530
- /// produces the same result. A function that produces a different result when
2531
- /// evaluated at run-time, or has any other observable side-effects, is
2532
- /// *unsound*.
2530
+ /// Rust has not yet decided that `const fn` are allowed to tell whether
2531
+ /// they run at compile-time or at runtime. Therefore, when using this
2532
+ /// intrinsic anywhere that can be reached from stable, it is crucial that
2533
+ /// the end-to-end behavior of the stable `const fn` is the same for both
2534
+ /// modes of execution. (Here, Undefined Behavior is considered "the same"
2535
+ /// as any other behavior, so if the function exhibits UB at runtime then
2536
+ /// it may do whatever it wants at compile-time.)
2533
2537
///
2534
2538
/// Here is an example of how this could cause a problem:
2535
2539
/// ```no_run
2536
2540
/// #![feature(const_eval_select)]
2537
2541
/// #![feature(core_intrinsics)]
2538
2542
/// # #![allow(internal_features)]
2539
- /// use std::hint::unreachable_unchecked;
2543
+ /// # #![cfg_attr(bootstrap, allow(unused))]
2540
2544
/// use std::intrinsics::const_eval_select;
2541
2545
///
2542
- /// // Crate A
2546
+ /// // Standard library
2547
+ /// # #[cfg(not(bootstrap))]
2543
2548
/// pub const fn inconsistent() -> i32 {
2544
2549
/// fn runtime() -> i32 { 1 }
2545
2550
/// const fn compiletime() -> i32 { 2 }
2546
2551
///
2547
- /// unsafe {
2548
- // // ⚠ This code violates the required equivalence of `compiletime`
2549
- /// // and `runtime`.
2550
- /// const_eval_select((), compiletime, runtime)
2551
- /// }
2552
+ // // ⚠ This code violates the required equivalence of `compiletime`
2553
+ /// // and `runtime`.
2554
+ /// const_eval_select((), compiletime, runtime)
2552
2555
/// }
2556
+ /// # #[cfg(bootstrap)]
2557
+ /// # pub const fn inconsistent() -> i32 { 0 }
2553
2558
///
2554
- /// // Crate B
2559
+ /// // User Crate
2555
2560
/// const X: i32 = inconsistent();
2556
2561
/// let x = inconsistent();
2557
- /// if x != X { unsafe { unreachable_unchecked(); }}
2562
+ /// assert_eq!(x, X);
2558
2563
/// ```
2559
2564
///
2560
- /// This code causes Undefined Behavior when being run, since the
2561
- /// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
2562
- /// which violates the principle that a `const fn` must behave the same at
2563
- /// compile-time and at run-time. The unsafe code in crate B is fine.
2565
+ /// Currently such an assertion would always succeed; until Rust decides
2566
+ /// otherwise, that principle should not be violated.
2564
2567
#[ rustc_const_unstable( feature = "const_eval_select" , issue = "none" ) ]
2568
+ #[ cfg_attr( not( bootstrap) , rustc_safe_intrinsic) ]
2565
2569
pub fn const_eval_select < ARG : Tuple , F , G , RET > (
2566
2570
arg : ARG ,
2567
2571
called_in_const : F ,
0 commit comments