Make const_eval_select
a rustc_intrinsic
This commit is contained in:
parent
196ff446d2
commit
7f9830b16c
5 changed files with 95 additions and 71 deletions
|
@ -579,7 +579,7 @@ pub fn check_intrinsic_type(
|
||||||
|
|
||||||
sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool),
|
sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool),
|
||||||
|
|
||||||
sym::const_eval_select => (4, 0, vec![param(0), param(1), param(2)], param(3)),
|
sym::const_eval_select => (4, 1, vec![param(0), param(1), param(2)], param(3)),
|
||||||
|
|
||||||
sym::vtable_size | sym::vtable_align => {
|
sym::vtable_size | sym::vtable_align => {
|
||||||
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
|
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
|
||||||
|
|
|
@ -1704,8 +1704,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||||
{
|
{
|
||||||
for &local_def_id in tcx.mir_keys(()) {
|
for &local_def_id in tcx.mir_keys(()) {
|
||||||
if let DefKind::AssocFn | DefKind::Fn = tcx.def_kind(local_def_id) {
|
if let DefKind::AssocFn | DefKind::Fn = tcx.def_kind(local_def_id) {
|
||||||
record_array!(self.tables.deduced_param_attrs[local_def_id.to_def_id()] <-
|
if tcx.intrinsic(local_def_id).map_or(true, |i| !i.must_be_overridden) {
|
||||||
self.tcx.deduced_param_attrs(local_def_id.to_def_id()));
|
record_array!(self.tables.deduced_param_attrs[local_def_id.to_def_id()] <-
|
||||||
|
self.tcx.deduced_param_attrs(local_def_id.to_def_id()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1365,7 +1365,9 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
|
||||||
|
|
||||||
fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
|
fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
|
||||||
if associated_body(Node::ImplItem(item)).is_some() {
|
if associated_body(Node::ImplItem(item)).is_some() {
|
||||||
self.body_owners.push(item.owner_id.def_id);
|
if !self.tcx.has_attr(item.owner_id.def_id, sym::rustc_intrinsic_must_be_overridden) {
|
||||||
|
self.body_owners.push(item.owner_id.def_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.impl_items.push(item.impl_item_id());
|
self.impl_items.push(item.impl_item_id());
|
||||||
|
|
|
@ -2508,62 +2508,7 @@ extern "rust-intrinsic" {
|
||||||
#[rustc_nounwind]
|
#[rustc_nounwind]
|
||||||
pub fn vtable_align(ptr: *const ()) -> usize;
|
pub fn vtable_align(ptr: *const ()) -> usize;
|
||||||
|
|
||||||
/// Selects which function to call depending on the context.
|
#[cfg(bootstrap)]
|
||||||
///
|
|
||||||
/// If this function is evaluated at compile-time, then a call to this
|
|
||||||
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
|
||||||
/// replaced with a call to `called_at_rt` otherwise.
|
|
||||||
///
|
|
||||||
/// This function is safe to call, but note the stability concerns below.
|
|
||||||
///
|
|
||||||
/// # Type Requirements
|
|
||||||
///
|
|
||||||
/// The two functions must be both function items. They cannot be function
|
|
||||||
/// pointers or closures. The first function must be a `const fn`.
|
|
||||||
///
|
|
||||||
/// `arg` will be the tupled arguments that will be passed to either one of
|
|
||||||
/// the two functions, therefore, both functions must accept the same type of
|
|
||||||
/// arguments. Both functions must return RET.
|
|
||||||
///
|
|
||||||
/// # Stability concerns
|
|
||||||
///
|
|
||||||
/// Rust has not yet decided that `const fn` are allowed to tell whether
|
|
||||||
/// they run at compile-time or at runtime. Therefore, when using this
|
|
||||||
/// intrinsic anywhere that can be reached from stable, it is crucial that
|
|
||||||
/// the end-to-end behavior of the stable `const fn` is the same for both
|
|
||||||
/// 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:
|
|
||||||
/// ```no_run
|
|
||||||
/// #![feature(const_eval_select)]
|
|
||||||
/// #![feature(core_intrinsics)]
|
|
||||||
/// # #![allow(internal_features)]
|
|
||||||
/// # #![cfg_attr(bootstrap, allow(unused))]
|
|
||||||
/// use std::intrinsics::const_eval_select;
|
|
||||||
///
|
|
||||||
/// // Standard library
|
|
||||||
/// # #[cfg(not(bootstrap))]
|
|
||||||
/// pub const fn inconsistent() -> i32 {
|
|
||||||
/// fn runtime() -> i32 { 1 }
|
|
||||||
/// const fn compiletime() -> i32 { 2 }
|
|
||||||
///
|
|
||||||
// // ⚠ This code violates the required equivalence of `compiletime`
|
|
||||||
/// // and `runtime`.
|
|
||||||
/// const_eval_select((), compiletime, runtime)
|
|
||||||
/// }
|
|
||||||
/// # #[cfg(bootstrap)]
|
|
||||||
/// # pub const fn inconsistent() -> i32 { 0 }
|
|
||||||
///
|
|
||||||
/// // User Crate
|
|
||||||
/// const X: i32 = inconsistent();
|
|
||||||
/// let x = inconsistent();
|
|
||||||
/// assert_eq!(x, X);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Currently such an assertion would always succeed; until Rust decides
|
|
||||||
/// otherwise, that principle should not be violated.
|
|
||||||
#[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)]
|
#[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>(
|
||||||
|
@ -2576,6 +2521,79 @@ extern "rust-intrinsic" {
|
||||||
F: FnOnce<ARG, Output = RET>;
|
F: FnOnce<ARG, Output = RET>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Selects which function to call depending on the context.
|
||||||
|
///
|
||||||
|
/// If this function is evaluated at compile-time, then a call to this
|
||||||
|
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
||||||
|
/// replaced with a call to `called_at_rt` otherwise.
|
||||||
|
///
|
||||||
|
/// This function is safe to call, but note the stability concerns below.
|
||||||
|
///
|
||||||
|
/// # Type Requirements
|
||||||
|
///
|
||||||
|
/// The two functions must be both function items. They cannot be function
|
||||||
|
/// pointers or closures. The first function must be a `const fn`.
|
||||||
|
///
|
||||||
|
/// `arg` will be the tupled arguments that will be passed to either one of
|
||||||
|
/// the two functions, therefore, both functions must accept the same type of
|
||||||
|
/// arguments. Both functions must return RET.
|
||||||
|
///
|
||||||
|
/// # Stability concerns
|
||||||
|
///
|
||||||
|
/// Rust has not yet decided that `const fn` are allowed to tell whether
|
||||||
|
/// they run at compile-time or at runtime. Therefore, when using this
|
||||||
|
/// intrinsic anywhere that can be reached from stable, it is crucial that
|
||||||
|
/// the end-to-end behavior of the stable `const fn` is the same for both
|
||||||
|
/// 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:
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(const_eval_select)]
|
||||||
|
/// #![feature(core_intrinsics)]
|
||||||
|
/// # #![allow(internal_features)]
|
||||||
|
/// # #![cfg_attr(bootstrap, allow(unused))]
|
||||||
|
/// use std::intrinsics::const_eval_select;
|
||||||
|
///
|
||||||
|
/// // Standard library
|
||||||
|
/// # #[cfg(not(bootstrap))]
|
||||||
|
/// pub const fn inconsistent() -> i32 {
|
||||||
|
/// fn runtime() -> i32 { 1 }
|
||||||
|
/// const fn compiletime() -> i32 { 2 }
|
||||||
|
///
|
||||||
|
// // ⚠ This code violates the required equivalence of `compiletime`
|
||||||
|
/// // and `runtime`.
|
||||||
|
/// const_eval_select((), compiletime, runtime)
|
||||||
|
/// }
|
||||||
|
/// # #[cfg(bootstrap)]
|
||||||
|
/// # pub const fn inconsistent() -> i32 { 0 }
|
||||||
|
///
|
||||||
|
/// // User Crate
|
||||||
|
/// const X: i32 = inconsistent();
|
||||||
|
/// let x = inconsistent();
|
||||||
|
/// assert_eq!(x, X);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Currently such an assertion would always succeed; until Rust decides
|
||||||
|
/// otherwise, that principle should not be violated.
|
||||||
|
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
|
||||||
|
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[rustc_intrinsic]
|
||||||
|
#[rustc_intrinsic_must_be_overridden]
|
||||||
|
pub const fn const_eval_select<ARG: Tuple, F, G, RET>(
|
||||||
|
_arg: ARG,
|
||||||
|
_called_in_const: F,
|
||||||
|
_called_at_rt: G,
|
||||||
|
) -> RET
|
||||||
|
where
|
||||||
|
G: FnOnce<ARG, Output = RET>,
|
||||||
|
F: FnOnce<ARG, Output = RET>,
|
||||||
|
{
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the argument's value is statically known at
|
/// Returns whether the argument's value is statically known at
|
||||||
/// compile-time.
|
/// compile-time.
|
||||||
///
|
///
|
||||||
|
|
|
@ -509,17 +509,19 @@ trait StructuralPartialEq {}
|
||||||
|
|
||||||
const fn drop<T: ~const Destruct>(_: T) {}
|
const fn drop<T: ~const Destruct>(_: T) {}
|
||||||
|
|
||||||
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_intrinsic_must_be_overridden]
|
||||||
#[rustc_safe_intrinsic]
|
#[rustc_intrinsic]
|
||||||
fn const_eval_select<ARG: Tuple, F, G, RET>(
|
const fn const_eval_select<ARG: Tuple, F, G, RET>(
|
||||||
arg: ARG,
|
arg: ARG,
|
||||||
called_in_const: F,
|
called_in_const: F,
|
||||||
called_at_rt: G,
|
called_at_rt: G,
|
||||||
) -> RET
|
) -> RET
|
||||||
where
|
where
|
||||||
F: const FnOnce<ARG, Output = RET>,
|
F: const FnOnce<ARG, Output = RET>,
|
||||||
G: FnOnce<ARG, Output = RET>;
|
G: FnOnce<ARG, Output = RET>,
|
||||||
|
{
|
||||||
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_const_eval_select() {
|
fn test_const_eval_select() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue