Auto merge of #89247 - fee1-dead:const-eval-select, r=oli-obk

Add `const_eval_select` intrinsic

Adds an intrinsic that calls a given function when evaluated at compiler time, but generates a call to another function when called at runtime.

See https://github.com/rust-lang/const-eval/issues/7 for previous discussion.

r? `@oli-obk.`
This commit is contained in:
bors 2021-10-14 10:06:30 +00:00
commit c34ac8747c
22 changed files with 372 additions and 39 deletions

View file

@ -0,0 +1,17 @@
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
#![feature(const_eval_select)]
use std::intrinsics::const_eval_select;
const fn foo(_: i32) -> i32 { 1 }
#[no_mangle]
pub fn hi(n: i32) -> i32 { n }
#[no_mangle]
pub unsafe fn hey() {
// CHECK: call i32 @hi(i32
const_eval_select((42,), foo, hi);
}

View file

@ -0,0 +1,36 @@
#![feature(const_eval_select)]
use std::intrinsics::const_eval_select;
const fn not_fn_items() {
const_eval_select((), || {}, || {});
//~^ ERROR expected a `FnOnce<()>` closure
const_eval_select((), 42, 0xDEADBEEF);
//~^ ERROR expected a `FnOnce<()>` closure
}
const fn foo(n: i32) -> i32 {
n
}
fn bar(n: i32) -> bool {
assert_eq!(n, 0, "{} must be equal to {}", n, 0);
n == 0
}
fn baz(n: bool) -> i32 {
assert!(n, "{} must be true", n);
n as i32
}
const fn return_ty_mismatch() {
const_eval_select((1,), foo, bar);
//~^ ERROR type mismatch
}
const fn args_ty_mismatch() {
const_eval_select((true,), foo, baz);
//~^ ERROR type mismatch
}
fn main() {}

View file

@ -0,0 +1,65 @@
error[E0277]: expected a `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:32]`
--> $DIR/const-eval-select-bad.rs:6:34
|
LL | const_eval_select((), || {}, || {});
| ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:32]`
| |
| required by a bound introduced by this call
|
= help: the trait `FnOnce<()>` is not implemented for `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:32]`
= note: wrap the `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:32]` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
LL | F: ~const FnOnce<ARG, Output = RET>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
--> $DIR/const-eval-select-bad.rs:8:31
|
LL | const_eval_select((), 42, 0xDEADBEEF);
| ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}`
| |
| required by a bound introduced by this call
|
= help: the trait `FnOnce<()>` is not implemented for `{integer}`
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
LL | F: ~const FnOnce<ARG, Output = RET>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
error[E0271]: type mismatch resolving `<fn(i32) -> bool {bar} as FnOnce<(i32,)>>::Output == i32`
--> $DIR/const-eval-select-bad.rs:27:5
|
LL | const_eval_select((1,), foo, bar);
| ^^^^^^^^^^^^^^^^^ expected `i32`, found `bool`
|
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
LL | G: FnOnce<ARG, Output = RET> + ~const Drop,
| ^^^^^^^^^^^^ required by this bound in `const_eval_select`
error[E0631]: type mismatch in function arguments
--> $DIR/const-eval-select-bad.rs:32:37
|
LL | const fn foo(n: i32) -> i32 {
| --------------------------- found signature of `fn(i32) -> _`
...
LL | const_eval_select((true,), foo, baz);
| ----------------- ^^^ expected signature of `fn(bool) -> _`
| |
| required by a bound introduced by this call
|
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
LL | F: ~const FnOnce<ARG, Output = RET>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0271, E0277, E0631.
For more information about an error, try `rustc --explain E0271`.

View file

@ -0,0 +1,20 @@
#![feature(staged_api)]
#![feature(const_eval_select)]
#![stable(since = "1.0", feature = "ui_test")]
use std::intrinsics::const_eval_select;
fn log() {
println!("HEY HEY HEY")
}
const fn nothing(){}
#[stable(since = "1.0", feature = "hey")]
#[rustc_const_stable(since = "1.0", feature = "const_hey")]
pub const unsafe fn hey() {
const_eval_select((), nothing, log);
//~^ ERROR `const_eval_select` is not yet stable as a const fn
}
fn main() {}

View file

@ -0,0 +1,10 @@
error: `const_eval_select` is not yet stable as a const fn
--> $DIR/const-eval-select-stability.rs:16:5
|
LL | const_eval_select((), nothing, log);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: const-stable functions can only call other const-stable functions
error: aborting due to previous error

View file

@ -0,0 +1,39 @@
// run-pass
// only-x86_64
#![feature(const_eval_select)]
use std::intrinsics::const_eval_select;
use std::arch::x86_64::*;
use std::mem::transmute;
const fn eq_ct(x: [i32; 4], y: [i32; 4]) -> bool {
x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3]
}
fn eq_rt(x: [i32; 4], y: [i32; 4]) -> bool {
unsafe {
let x = _mm_loadu_si128(&x as *const _ as *const _);
let y = _mm_loadu_si128(&y as *const _ as *const _);
let r = _mm_cmpeq_epi32(x, y);
let r = _mm_movemask_ps(transmute(r) );
r == 0b1111
}
}
const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
unsafe {
const_eval_select((x, y), eq_ct, eq_rt)
}
}
fn main() {
const X: bool = eq([0, 1, 2, 3], [0, 1, 2, 3]);
assert_eq!(X, true);
let x = eq([0, 1, 2, 3], [0, 1, 2, 3]);
assert_eq!(x, true);
const Y: bool = eq([0, 1, 2, 3], [0, 1, 3, 2]);
assert_eq!(Y, false);
let y = eq([0, 1, 2, 3], [0, 1, 3, 2]);
assert_eq!(y, false);
}

View file

@ -0,0 +1,26 @@
// run-pass
#![feature(const_eval_select)]
use std::intrinsics::const_eval_select;
const fn yes() -> bool {
true
}
fn no() -> bool {
false
}
// not a sound use case; testing only
const fn is_const_eval() -> bool {
unsafe { const_eval_select((), yes, no) }
}
fn main() {
const YES: bool = is_const_eval();
let no = is_const_eval();
assert_eq!(true, YES);
assert_eq!(false, no);
}