Skip to content

Commit 8c72503

Browse files
committed
Make const_eval_select a rustc_intrinsic
1 parent c7beecf commit 8c72503

File tree

4 files changed

+104
-85
lines changed

4 files changed

+104
-85
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 6 additions & 2 deletions
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

Lines changed: 71 additions & 53 deletions
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

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const fn bar() {
3737
let x = 42_i32 + 43_i32;
3838
}
3939

40-
4140
#[lang = "Try"]
4241
#[const_trait]
4342
trait Try: FromResidual<Self::Residual> {
@@ -53,7 +52,7 @@ trait Try: FromResidual<Self::Residual> {
5352

5453
// FIXME
5554
// #[const_trait]
56-
trait FromResidual<R = <Self as /* FIXME: ~const */ Try>::Residual> {
55+
trait FromResidual<R = <Self as Try>::Residual> {
5756
#[lang = "from_residual"]
5857
fn from_residual(residual: R) -> Self;
5958
}
@@ -190,7 +189,6 @@ trait Index<Idx: ?Sized> {
190189
fn index(&self, index: Idx) -> &Self::Output;
191190
}
192191

193-
194192
#[const_trait]
195193
unsafe trait SliceIndex<T: ?Sized> {
196194
type Output: ?Sized;
@@ -224,16 +222,13 @@ where
224222
*/
225223

226224
#[lang = "unsize"]
227-
trait Unsize<T: ?Sized> {
228-
}
225+
trait Unsize<T: ?Sized> {}
229226

230227
#[lang = "coerce_unsized"]
231-
trait CoerceUnsized<T: ?Sized> {
232-
}
228+
trait CoerceUnsized<T: ?Sized> {}
233229

234230
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
235231

236-
237232
#[lang = "deref"]
238233
// #[const_trait] FIXME
239234
trait Deref {
@@ -243,16 +238,15 @@ trait Deref {
243238
fn deref(&self) -> &Self::Target;
244239
}
245240

246-
247-
impl<T: ?Sized> /* const */ Deref for &T {
241+
impl<T: ?Sized> Deref for &T {
248242
type Target = T;
249243

250244
fn deref(&self) -> &T {
251245
*self
252246
}
253247
}
254248

255-
impl<T: ?Sized> /* const */ Deref for &mut T {
249+
impl<T: ?Sized> Deref for &mut T {
256250
type Target = T;
257251

258252
fn deref(&self) -> &T {
@@ -360,7 +354,6 @@ impl PartialEq for str {
360354
}
361355
}
362356

363-
364357
#[lang = "not"]
365358
#[const_trait]
366359
trait Not {
@@ -398,17 +391,16 @@ impl<'a, T: ?Sized> Pin<&'a T> {
398391
}
399392
}
400393

401-
402394
impl<P: Deref> Pin<P> {
403-
/* const */ fn as_ref(&self) -> Pin<&P::Target>
395+
/* const */
396+
fn as_ref(&self) -> Pin<&P::Target>
404397
where
405-
P: /* ~const */ Deref,
398+
P: Deref,
406399
{
407400
unsafe { Pin::new_unchecked(&*self.pointer) }
408401
}
409402
}
410403

411-
412404
impl<'a, T: ?Sized> Pin<&'a mut T> {
413405
const unsafe fn get_unchecked_mut(self) -> &'a mut T {
414406
self.pointer
@@ -434,14 +426,14 @@ impl<T> Option<T> {
434426
}
435427
*/
436428

437-
impl<P: /* ~const */ Deref> /* const */ Deref for Pin<P> {
429+
impl<P: Deref> Deref for Pin<P> {
438430
type Target = P::Target;
439431
fn deref(&self) -> &P::Target {
440432
Pin::get_ref(Pin::as_ref(self))
441433
}
442434
}
443435

444-
impl<T> /* const */ Deref for Option<T> {
436+
impl<T> Deref for Option<T> {
445437
type Target = T;
446438
fn deref(&self) -> &T {
447439
loop {}
@@ -509,21 +501,26 @@ trait StructuralPartialEq {}
509501

510502
const fn drop<T: ~const Destruct>(_: T) {}
511503

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>;
504+
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
505+
#[rustc_intrinsic_must_be_overridden]
506+
#[rustc_intrinsic]
507+
const unsafe fn const_eval_select<ARG: Tuple, F, G, RET>(
508+
arg: ARG,
509+
called_in_const: F,
510+
called_at_rt: G,
511+
) -> RET
512+
where
513+
F: const FnOnce<ARG, Output = RET>,
514+
G: FnOnce<ARG, Output = RET>,
515+
{
516+
loop {}
522517
}
523518

524519
fn test_const_eval_select() {
525520
const fn const_fn() {}
526521
fn rt_fn() {}
527522

528-
unsafe { const_eval_select((), const_fn, rt_fn); }
523+
unsafe {
524+
const_eval_select((), const_fn, rt_fn);
525+
}
529526
}

0 commit comments

Comments
 (0)