mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 18:23:49 +00:00
Rollup merge of #121894 - RalfJung:const_eval_select, r=oli-obk
const_eval_select: make it safe but be careful with what we expose on stable for now As this is all still nightly-only I think `````@rust-lang/wg-const-eval````` can do that without involving t-lang. r? `````@oli-obk````` Cc `````@Nilstrieb````` -- the updated version of your RFC would basically say that we can remove these comments about not making behavior differences visible in stable `const fn`
This commit is contained in:
commit
327842b4ab
@ -132,7 +132,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
|||||||
| sym::fsub_algebraic
|
| sym::fsub_algebraic
|
||||||
| sym::fmul_algebraic
|
| sym::fmul_algebraic
|
||||||
| sym::fdiv_algebraic
|
| sym::fdiv_algebraic
|
||||||
| sym::frem_algebraic => hir::Unsafety::Normal,
|
| sym::frem_algebraic
|
||||||
|
| sym::const_eval_select => hir::Unsafety::Normal,
|
||||||
_ => hir::Unsafety::Unsafe,
|
_ => hir::Unsafety::Unsafe,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -385,6 +385,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "panic_immediate_abort"))]
|
#[cfg(not(feature = "panic_immediate_abort"))]
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::const_eval_select((layout,), ct_error, rt_error)
|
core::intrinsics::const_eval_select((layout,), ct_error, rt_error)
|
||||||
}
|
}
|
||||||
|
@ -428,10 +428,13 @@ impl CStr {
|
|||||||
unsafe { &*(bytes as *const [u8] as *const CStr) }
|
unsafe { &*(bytes as *const [u8] as *const CStr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The const and runtime versions have identical behavior
|
// SAFETY: The const and runtime versions have identical behavior
|
||||||
// unless the safety contract of `from_bytes_with_nul_unchecked` is
|
// unless the safety contract of `from_bytes_with_nul_unchecked` is
|
||||||
// violated, which is UB.
|
// violated, which is UB.
|
||||||
unsafe { intrinsics::const_eval_select((bytes,), const_impl, rt_impl) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((bytes,), const_impl, rt_impl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the inner pointer to this C string.
|
/// Returns the inner pointer to this C string.
|
||||||
@ -719,6 +722,9 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize {
|
|||||||
unsafe { strlen(s) }
|
unsafe { strlen(s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: the two functions always provide equivalent functionality
|
// SAFETY: the two functions always provide equivalent functionality
|
||||||
unsafe { intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2514,6 +2514,8 @@ extern "rust-intrinsic" {
|
|||||||
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
||||||
/// replaced with a call to `called_at_rt` otherwise.
|
/// replaced with a call to `called_at_rt` otherwise.
|
||||||
///
|
///
|
||||||
|
/// This function is safe to call, but note the stability concerns below.
|
||||||
|
///
|
||||||
/// # Type Requirements
|
/// # Type Requirements
|
||||||
///
|
///
|
||||||
/// The two functions must be both function items. They cannot be function
|
/// The two functions must be both function items. They cannot be function
|
||||||
@ -2523,45 +2525,47 @@ extern "rust-intrinsic" {
|
|||||||
/// the two functions, therefore, both functions must accept the same type of
|
/// the two functions, therefore, both functions must accept the same type of
|
||||||
/// arguments. Both functions must return RET.
|
/// arguments. Both functions must return RET.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Stability concerns
|
||||||
///
|
///
|
||||||
/// The two functions must behave observably equivalent. Safe code in other
|
/// Rust has not yet decided that `const fn` are allowed to tell whether
|
||||||
/// crates may assume that calling a `const fn` at compile-time and at run-time
|
/// they run at compile-time or at runtime. Therefore, when using this
|
||||||
/// produces the same result. A function that produces a different result when
|
/// intrinsic anywhere that can be reached from stable, it is crucial that
|
||||||
/// evaluated at run-time, or has any other observable side-effects, is
|
/// the end-to-end behavior of the stable `const fn` is the same for both
|
||||||
/// *unsound*.
|
/// modes of execution. (Here, Undefined Behavior is considered "the same"
|
||||||
|
/// as any other behavior, so if the function exhibits UB at runtime then
|
||||||
|
/// it may do whatever it wants at compile-time.)
|
||||||
///
|
///
|
||||||
/// Here is an example of how this could cause a problem:
|
/// Here is an example of how this could cause a problem:
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// #![feature(const_eval_select)]
|
/// #![feature(const_eval_select)]
|
||||||
/// #![feature(core_intrinsics)]
|
/// #![feature(core_intrinsics)]
|
||||||
/// # #![allow(internal_features)]
|
/// # #![allow(internal_features)]
|
||||||
/// use std::hint::unreachable_unchecked;
|
/// # #![cfg_attr(bootstrap, allow(unused))]
|
||||||
/// use std::intrinsics::const_eval_select;
|
/// use std::intrinsics::const_eval_select;
|
||||||
///
|
///
|
||||||
/// // Crate A
|
/// // Standard library
|
||||||
|
/// # #[cfg(not(bootstrap))]
|
||||||
/// pub const fn inconsistent() -> i32 {
|
/// pub const fn inconsistent() -> i32 {
|
||||||
/// fn runtime() -> i32 { 1 }
|
/// fn runtime() -> i32 { 1 }
|
||||||
/// const fn compiletime() -> i32 { 2 }
|
/// const fn compiletime() -> i32 { 2 }
|
||||||
///
|
///
|
||||||
/// unsafe {
|
// // ⚠ This code violates the required equivalence of `compiletime`
|
||||||
// // ⚠ This code violates the required equivalence of `compiletime`
|
/// // and `runtime`.
|
||||||
/// // and `runtime`.
|
/// const_eval_select((), compiletime, runtime)
|
||||||
/// const_eval_select((), compiletime, runtime)
|
|
||||||
/// }
|
|
||||||
/// }
|
/// }
|
||||||
|
/// # #[cfg(bootstrap)]
|
||||||
|
/// # pub const fn inconsistent() -> i32 { 0 }
|
||||||
///
|
///
|
||||||
/// // Crate B
|
/// // User Crate
|
||||||
/// const X: i32 = inconsistent();
|
/// const X: i32 = inconsistent();
|
||||||
/// let x = inconsistent();
|
/// let x = inconsistent();
|
||||||
/// if x != X { unsafe { unreachable_unchecked(); }}
|
/// assert_eq!(x, X);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This code causes Undefined Behavior when being run, since the
|
/// Currently such an assertion would always succeed; until Rust decides
|
||||||
/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
|
/// otherwise, that principle should not be violated.
|
||||||
/// which violates the principle that a `const fn` must behave the same at
|
|
||||||
/// compile-time and at run-time. The unsafe code in crate B is fine.
|
|
||||||
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
|
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
|
||||||
|
#[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
|
||||||
pub fn const_eval_select<ARG: Tuple, F, G, RET>(
|
pub fn const_eval_select<ARG: Tuple, F, G, RET>(
|
||||||
arg: ARG,
|
arg: ARG,
|
||||||
called_in_const: F,
|
called_in_const: F,
|
||||||
|
@ -1153,8 +1153,11 @@ impl f32 {
|
|||||||
// Stability concerns.
|
// Stability concerns.
|
||||||
unsafe { mem::transmute(x) }
|
unsafe { mem::transmute(x) }
|
||||||
}
|
}
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raw transmutation from `u32`.
|
/// Raw transmutation from `u32`.
|
||||||
@ -1245,8 +1248,11 @@ impl f32 {
|
|||||||
// Stability concerns.
|
// Stability concerns.
|
||||||
unsafe { mem::transmute(x) }
|
unsafe { mem::transmute(x) }
|
||||||
}
|
}
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the memory representation of this floating point number as a byte array in
|
/// Return the memory representation of this floating point number as a byte array in
|
||||||
|
@ -1146,8 +1146,11 @@ impl f64 {
|
|||||||
// Stability concerns.
|
// Stability concerns.
|
||||||
unsafe { mem::transmute::<f64, u64>(rt) }
|
unsafe { mem::transmute::<f64, u64>(rt) }
|
||||||
}
|
}
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raw transmutation from `u64`.
|
/// Raw transmutation from `u64`.
|
||||||
@ -1243,8 +1246,11 @@ impl f64 {
|
|||||||
// Stability concerns.
|
// Stability concerns.
|
||||||
unsafe { mem::transmute::<u64, f64>(rt) }
|
unsafe { mem::transmute::<u64, f64>(rt) }
|
||||||
}
|
}
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
|
unsafe {
|
||||||
|
intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the memory representation of this floating point number as a byte array in
|
/// Return the memory representation of this floating point number as a byte array in
|
||||||
|
@ -117,6 +117,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo
|
|||||||
panic_fmt(fmt);
|
panic_fmt(fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: const panic does not care about unwinding
|
// SAFETY: const panic does not care about unwinding
|
||||||
unsafe {
|
unsafe {
|
||||||
super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime);
|
super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime);
|
||||||
|
@ -48,8 +48,11 @@ impl<T: ?Sized> *const T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The two versions are equivalent at runtime.
|
// SAFETY: The two versions are equivalent at runtime.
|
||||||
unsafe { const_eval_select((self as *const u8,), const_impl, runtime_impl) }
|
unsafe {
|
||||||
|
const_eval_select((self as *const u8,), const_impl, runtime_impl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Casts to a pointer of another type.
|
/// Casts to a pointer of another type.
|
||||||
@ -806,6 +809,7 @@ impl<T: ?Sized> *const T {
|
|||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The comparison has no side-effects, and the intrinsic
|
// SAFETY: The comparison has no side-effects, and the intrinsic
|
||||||
// does this check internally in the CTFE implementation.
|
// does this check internally in the CTFE implementation.
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -1623,8 +1627,11 @@ impl<T: ?Sized> *const T {
|
|||||||
ptr.align_offset(align) == 0
|
ptr.align_offset(align) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The two versions are equivalent at runtime.
|
// SAFETY: The two versions are equivalent at runtime.
|
||||||
unsafe { const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) }
|
unsafe {
|
||||||
|
const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,6 +995,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: the caller must guarantee that `x` and `y` are
|
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||||
// valid for writes and properly aligned.
|
// valid for writes and properly aligned.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -48,8 +48,11 @@ impl<T: ?Sized> *mut T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The two versions are equivalent at runtime.
|
// SAFETY: The two versions are equivalent at runtime.
|
||||||
unsafe { const_eval_select((self as *mut u8,), const_impl, runtime_impl) }
|
unsafe {
|
||||||
|
const_eval_select((self as *mut u8,), const_impl, runtime_impl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Casts to a pointer of another type.
|
/// Casts to a pointer of another type.
|
||||||
@ -1896,8 +1899,11 @@ impl<T: ?Sized> *mut T {
|
|||||||
ptr.align_offset(align) == 0
|
ptr.align_offset(align) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: The two versions are equivalent at runtime.
|
// SAFETY: The two versions are equivalent at runtime.
|
||||||
unsafe { const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) }
|
unsafe {
|
||||||
|
const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ where
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
||||||
const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
|
const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: we are just panicking here
|
// SAFETY: we are just panicking here
|
||||||
unsafe {
|
unsafe {
|
||||||
const_eval_select(
|
const_eval_select(
|
||||||
@ -63,6 +64,7 @@ const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
||||||
const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
|
const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: we are just panicking here
|
// SAFETY: we are just panicking here
|
||||||
unsafe {
|
unsafe {
|
||||||
const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt)
|
const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt)
|
||||||
@ -87,8 +89,11 @@ const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
#[rustc_const_unstable(feature = "const_slice_index", issue = "none")]
|
||||||
const fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
const fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: we are just panicking here
|
// SAFETY: we are just panicking here
|
||||||
unsafe { const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt) }
|
unsafe {
|
||||||
|
const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME const-hack
|
// FIXME const-hack
|
||||||
|
@ -86,6 +86,7 @@ use iter::{MatchesInternal, SplitNInternal};
|
|||||||
#[rustc_allow_const_fn_unstable(const_eval_select)]
|
#[rustc_allow_const_fn_unstable(const_eval_select)]
|
||||||
#[cfg(not(feature = "panic_immediate_abort"))]
|
#[cfg(not(feature = "panic_immediate_abort"))]
|
||||||
const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
|
const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
|
||||||
|
#[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block
|
||||||
// SAFETY: panics for both branches
|
// SAFETY: panics for both branches
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::intrinsics::const_eval_select(
|
crate::intrinsics::const_eval_select(
|
||||||
|
@ -12,8 +12,5 @@ fn uhoh() {
|
|||||||
const fn c() {}
|
const fn c() {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// safety: this is unsound and just used to test
|
std::intrinsics::const_eval_select((), c, uhoh);
|
||||||
unsafe {
|
|
||||||
std::intrinsics::const_eval_select((), c, uhoh);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
thread 'main' panicked at $DIR/const-eval-select-backtrace.rs:17:9:
|
thread 'main' panicked at $DIR/const-eval-select-backtrace.rs:15:5:
|
||||||
Aaah!
|
Aaah!
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
@ -13,7 +13,7 @@ const fn nothing(){}
|
|||||||
|
|
||||||
#[stable(since = "1.0", feature = "hey")]
|
#[stable(since = "1.0", feature = "hey")]
|
||||||
#[rustc_const_stable(since = "1.0", feature = "const_hey")]
|
#[rustc_const_stable(since = "1.0", feature = "const_hey")]
|
||||||
pub const unsafe fn hey() {
|
pub const fn hey() {
|
||||||
const_eval_select((), nothing, log);
|
const_eval_select((), nothing, log);
|
||||||
//~^ ERROR `const_eval_select` is not yet stable as a const fn
|
//~^ ERROR `const_eval_select` is not yet stable as a const fn
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,7 @@ fn eq_rt(x: [i32; 4], y: [i32; 4]) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
|
const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
|
||||||
unsafe {
|
const_eval_select((x, y), eq_ct, eq_rt)
|
||||||
const_eval_select((x, y), eq_ct, eq_rt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -13,9 +13,9 @@ fn no() -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// not a sound use case; testing only
|
// not allowed on stable; testing only
|
||||||
const fn is_const_eval() -> bool {
|
const fn is_const_eval() -> bool {
|
||||||
unsafe { const_eval_select((), yes, no) }
|
const_eval_select((), yes, no)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -511,6 +511,7 @@ const fn drop<T: ~const Destruct>(_: T) {}
|
|||||||
|
|
||||||
extern "rust-intrinsic" {
|
extern "rust-intrinsic" {
|
||||||
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
|
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
|
||||||
|
#[rustc_safe_intrinsic]
|
||||||
fn const_eval_select<ARG: Tuple, F, G, RET>(
|
fn const_eval_select<ARG: Tuple, F, G, RET>(
|
||||||
arg: ARG,
|
arg: ARG,
|
||||||
called_in_const: F,
|
called_in_const: F,
|
||||||
@ -525,5 +526,5 @@ fn test_const_eval_select() {
|
|||||||
const fn const_fn() {}
|
const fn const_fn() {}
|
||||||
fn rt_fn() {}
|
fn rt_fn() {}
|
||||||
|
|
||||||
unsafe { const_eval_select((), const_fn, rt_fn); }
|
const_eval_select((), const_fn, rt_fn);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user