Skip to content

Commit 0c3685d

Browse files
committed
Make const_eval_select a rustc_intrinsic
1 parent c7beecf commit 0c3685d

File tree

4 files changed

+91
-66
lines changed

4 files changed

+91
-66
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ pub fn check_intrinsic_type(
578578

579579
sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool),
580580

581-
sym::const_eval_select => (4, 0, vec![param(0), param(1), param(2)], param(3)),
581+
sym::const_eval_select => (4, 1, vec![param(0), param(1), param(2)], param(3)),
582582

583583
sym::vtable_size | sym::vtable_align => {
584584
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)

compiler/rustc_middle/src/hir/map/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,9 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
13231323

13241324
fn visit_item(&mut self, item: &'hir Item<'hir>) {
13251325
if associated_body(Node::Item(item)).is_some() {
1326-
self.body_owners.push(item.owner_id.def_id);
1326+
if !self.tcx.has_attr(item.owner_id.def_id, sym::rustc_intrinsic_must_be_overridden) {
1327+
self.body_owners.push(item.owner_id.def_id);
1328+
}
13271329
}
13281330

13291331
self.items.push(item.item_id());
@@ -1373,7 +1375,9 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
13731375

13741376
fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
13751377
if associated_body(Node::ImplItem(item)).is_some() {
1376-
self.body_owners.push(item.owner_id.def_id);
1378+
if !self.tcx.has_attr(item.owner_id.def_id, sym::rustc_intrinsic_must_be_overridden) {
1379+
self.body_owners.push(item.owner_id.def_id);
1380+
}
13771381
}
13781382

13791383
self.impl_items.push(item.impl_item_id());

library/core/src/intrinsics.rs

+71-53
Original file line numberDiff line numberDiff line change
@@ -2508,59 +2508,7 @@ extern "rust-intrinsic" {
25082508
#[rustc_nounwind]
25092509
pub fn vtable_align(ptr: *const ()) -> usize;
25102510

2511-
/// Selects which function to call depending on the context.
2512-
///
2513-
/// If this function is evaluated at compile-time, then a call to this
2514-
/// intrinsic will be replaced with a call to `called_in_const`. It gets
2515-
/// replaced with a call to `called_at_rt` otherwise.
2516-
///
2517-
/// # Type Requirements
2518-
///
2519-
/// The two functions must be both function items. They cannot be function
2520-
/// pointers or closures. The first function must be a `const fn`.
2521-
///
2522-
/// `arg` will be the tupled arguments that will be passed to either one of
2523-
/// the two functions, therefore, both functions must accept the same type of
2524-
/// arguments. Both functions must return RET.
2525-
///
2526-
/// # Safety
2527-
///
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*.
2533-
///
2534-
/// Here is an example of how this could cause a problem:
2535-
/// ```no_run
2536-
/// #![feature(const_eval_select)]
2537-
/// #![feature(core_intrinsics)]
2538-
/// # #![allow(internal_features)]
2539-
/// use std::hint::unreachable_unchecked;
2540-
/// use std::intrinsics::const_eval_select;
2541-
///
2542-
/// // Crate A
2543-
/// pub const fn inconsistent() -> i32 {
2544-
/// fn runtime() -> i32 { 1 }
2545-
/// const fn compiletime() -> i32 { 2 }
2546-
///
2547-
/// unsafe {
2548-
// // ⚠ This code violates the required equivalence of `compiletime`
2549-
/// // and `runtime`.
2550-
/// const_eval_select((), compiletime, runtime)
2551-
/// }
2552-
/// }
2553-
///
2554-
/// // Crate B
2555-
/// const X: i32 = inconsistent();
2556-
/// let x = inconsistent();
2557-
/// if x != X { unsafe { unreachable_unchecked(); }}
2558-
/// ```
2559-
///
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.
2511+
#[cfg(bootstrap)]
25642512
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
25652513
pub fn const_eval_select<ARG: Tuple, F, G, RET>(
25662514
arg: ARG,
@@ -2572,6 +2520,76 @@ extern "rust-intrinsic" {
25722520
F: FnOnce<ARG, Output = RET>;
25732521
}
25742522

2523+
/// Selects which function to call depending on the context.
2524+
///
2525+
/// If this function is evaluated at compile-time, then a call to this
2526+
/// intrinsic will be replaced with a call to `called_in_const`. It gets
2527+
/// replaced with a call to `called_at_rt` otherwise.
2528+
///
2529+
/// # Type Requirements
2530+
///
2531+
/// The two functions must be both function items. They cannot be function
2532+
/// pointers or closures. The first function must be a `const fn`.
2533+
///
2534+
/// `arg` will be the tupled arguments that will be passed to either one of
2535+
/// the two functions, therefore, both functions must accept the same type of
2536+
/// arguments. Both functions must return RET.
2537+
///
2538+
/// # Safety
2539+
///
2540+
/// The two functions must behave observably equivalent. Safe code in other
2541+
/// crates may assume that calling a `const fn` at compile-time and at run-time
2542+
/// produces the same result. A function that produces a different result when
2543+
/// evaluated at run-time, or has any other observable side-effects, is
2544+
/// *unsound*.
2545+
///
2546+
/// Here is an example of how this could cause a problem:
2547+
/// ```no_run
2548+
/// #![feature(const_eval_select)]
2549+
/// #![feature(core_intrinsics)]
2550+
/// # #![allow(internal_features)]
2551+
/// use std::hint::unreachable_unchecked;
2552+
/// use std::intrinsics::const_eval_select;
2553+
///
2554+
/// // Crate A
2555+
/// pub const fn inconsistent() -> i32 {
2556+
/// fn runtime() -> i32 { 1 }
2557+
/// const fn compiletime() -> i32 { 2 }
2558+
///
2559+
/// unsafe {
2560+
// // ⚠ This code violates the required equivalence of `compiletime`
2561+
/// // and `runtime`.
2562+
/// const_eval_select((), compiletime, runtime)
2563+
/// }
2564+
/// }
2565+
///
2566+
/// // Crate B
2567+
/// const X: i32 = inconsistent();
2568+
/// let x = inconsistent();
2569+
/// if x != X { unsafe { unreachable_unchecked(); }}
2570+
/// ```
2571+
///
2572+
/// This code causes Undefined Behavior when being run, since the
2573+
/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
2574+
/// which violates the principle that a `const fn` must behave the same at
2575+
/// compile-time and at run-time. The unsafe code in crate B is fine.
2576+
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
2577+
#[unstable(feature = "core_intrinsics", issue = "none")]
2578+
#[cfg(not(bootstrap))]
2579+
#[rustc_intrinsic]
2580+
#[rustc_intrinsic_must_be_overridden]
2581+
pub const unsafe fn const_eval_select<ARG: Tuple, F, G, RET>(
2582+
_arg: ARG,
2583+
_called_in_const: F,
2584+
_called_at_rt: G,
2585+
) -> RET
2586+
where
2587+
G: FnOnce<ARG, Output = RET>,
2588+
F: FnOnce<ARG, Output = RET>,
2589+
{
2590+
unreachable!()
2591+
}
2592+
25752593
/// Returns whether the argument's value is statically known at
25762594
/// compile-time.
25772595
///

tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -509,16 +509,19 @@ trait StructuralPartialEq {}
509509

510510
const fn drop<T: ~const Destruct>(_: T) {}
511511

512-
extern "rust-intrinsic" {
513-
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
514-
fn const_eval_select<ARG: Tuple, F, G, RET>(
515-
arg: ARG,
516-
called_in_const: F,
517-
called_at_rt: G,
518-
) -> RET
519-
where
520-
F: const FnOnce<ARG, Output = RET>,
521-
G: FnOnce<ARG, Output = RET>;
512+
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
513+
#[rustc_intrinsic_must_be_overridden]
514+
#[rustc_intrinsic]
515+
const unsafe fn const_eval_select<ARG: Tuple, F, G, RET>(
516+
arg: ARG,
517+
called_in_const: F,
518+
called_at_rt: G,
519+
) -> RET
520+
where
521+
F: const FnOnce<ARG, Output = RET>,
522+
G: FnOnce<ARG, Output = RET>,
523+
{
524+
loop {}
522525
}
523526

524527
fn test_const_eval_select() {

0 commit comments

Comments
 (0)