From 4fe1b281239f8721f9c53df52a57c86952f287c8 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Sun, 22 Sep 2019 11:41:33 +0200 Subject: [PATCH 1/2] Add is_const_eval intrinsic The `core::intrinsic::is_const_eval` returns `true` if it is evaluated at compile-time and false otherwise. --- src/libcore/intrinsics.rs | 20 ++++++++++++++++++++ src/librustc_codegen_llvm/intrinsic.rs | 3 +++ src/librustc_mir/interpret/intrinsics.rs | 6 +++++- src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_typeck/check/intrinsic.rs | 1 + src/test/codegen/intrinsics/is_const_eval.rs | 16 ++++++++++++++++ src/test/ui/intrinsics/is_const_eval.rs | 11 +++++++++++ 7 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/codegen/intrinsics/is_const_eval.rs create mode 100644 src/test/ui/intrinsics/is_const_eval.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index ecff40a75978d..d256b0e4c8c34 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -660,6 +660,26 @@ extern "rust-intrinsic" { /// Executes a breakpoint trap, for inspection by a debugger. pub fn breakpoint(); + /// Returns `true` during constant evaluation and `false` otherwise. + /// + /// # Safety + /// + /// This intrinsic allows breaking [referential transparency] in `const fn` + /// and is therefore `unsafe`. + /// + /// Code that uses this intrinsic must be extremely careful to ensure that + /// `const fn`s remain referentially-transparent independently of when they + /// are evaluated. + /// + /// The Rust compiler assumes that it is sound to replace a call to a `const + /// fn` with the result produced by evaluating it at compile-time. If + /// evaluating the function at run-time were to produce a different result, + /// or have any other observable side-effects, the behavior is undefined. + /// + /// [referential transparency]: https://en.wikipedia.org/wiki/Referential_transparency + #[cfg(not(boostrap_stdarch_ignore_this))] + pub fn is_const_eval() -> bool; + /// The size of a type in bytes. /// /// More specifically, this is the offset in bytes between successive diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 3f3c5ac1460a3..e70ccb5be9dd9 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -518,6 +518,9 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } }, + "is_const_eval" => { + self.const_bool(false) + }, "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { match float_type_width(arg_tys[0]) { Some(_width) => diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index ec09e69ec8537..6ce55cd058fac 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -46,8 +46,11 @@ crate fn eval_nullary_intrinsic<'tcx>( def_id: DefId, substs: SubstsRef<'tcx>, ) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> { - let tp_ty = substs.type_at(0); let name = &*tcx.item_name(def_id).as_str(); + if name == "is_const_eval" { + return Ok(ty::Const::from_bool(tcx, true)); + } + let tp_ty = substs.type_at(0); Ok(match name { "type_name" => { let alloc = type_name::alloc_type_name(tcx, tp_ty); @@ -94,6 +97,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { + "is_const_eval" | "min_align_of" | "pref_align_of" | "needs_drop" | diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 795721f3b3f28..9b91a15055e1c 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -557,8 +557,8 @@ impl Qualif for IsNotPromotable { | "saturating_add" | "saturating_sub" | "transmute" + | "is_const_eval" => return true, - _ => {} } } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index dfbf8bcd0f60f..3f84c03875bfc 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -145,6 +145,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) { "rustc_peek" => (1, vec![param(0)], param(0)), "panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()), "init" => (1, Vec::new(), param(0)), + "is_const_eval" => (0, Vec::new(), tcx.types.bool), "uninit" => (1, Vec::new(), param(0)), "forget" => (1, vec![param(0)], tcx.mk_unit()), "transmute" => (2, vec![ param(0) ], param(1)), diff --git a/src/test/codegen/intrinsics/is_const_eval.rs b/src/test/codegen/intrinsics/is_const_eval.rs new file mode 100644 index 0000000000000..f9c345b786858 --- /dev/null +++ b/src/test/codegen/intrinsics/is_const_eval.rs @@ -0,0 +1,16 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::is_const_eval; + +// CHECK-LABEL: @is_const_eval_test +#[no_mangle] +pub unsafe fn is_const_eval_test() -> bool { + // CHECK: %0 = alloca i8, align 1 + // CHECK: store i8 0, i8* %0, align 1 + // CHECK: %2 = trunc i8 %1 to i1 + // CHECK: ret i1 %2 + is_const_eval() +} diff --git a/src/test/ui/intrinsics/is_const_eval.rs b/src/test/ui/intrinsics/is_const_eval.rs new file mode 100644 index 0000000000000..9fc669af459dc --- /dev/null +++ b/src/test/ui/intrinsics/is_const_eval.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(core_intrinsics)] +use std::intrinsics::is_const_eval; + +fn main() { + const X: bool = unsafe { is_const_eval() }; + let y = unsafe { is_const_eval() }; + assert_eq!(X, true); + assert_eq!(y, false); +} From 223c832b3894fe6ce6d61d4f459f0aa827bec264 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Mon, 23 Sep 2019 10:17:01 +0200 Subject: [PATCH 2/2] Add a test using the intrinsic for something useful --- .../ui/intrinsics/is_const_eval_unleash.rs | 34 ++++++ .../intrinsics/is_const_eval_unleash.stderr | 114 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/test/ui/intrinsics/is_const_eval_unleash.rs create mode 100644 src/test/ui/intrinsics/is_const_eval_unleash.stderr diff --git a/src/test/ui/intrinsics/is_const_eval_unleash.rs b/src/test/ui/intrinsics/is_const_eval_unleash.rs new file mode 100644 index 0000000000000..b839c2c7f5ea6 --- /dev/null +++ b/src/test/ui/intrinsics/is_const_eval_unleash.rs @@ -0,0 +1,34 @@ +// run-pass +// only-x86_64 +// compile-flags: -Zunleash-the-miri-inside-of-you + +#![feature(core_intrinsics)] +use std::intrinsics::is_const_eval; +use std::arch::x86_64::*; +use std::mem::transmute; + +const fn eq(x: [i32; 4], y: [i32; 4]) -> bool { + if unsafe { is_const_eval() } { + x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3] + } else { + 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 + } + } +} + +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); +} diff --git a/src/test/ui/intrinsics/is_const_eval_unleash.stderr b/src/test/ui/intrinsics/is_const_eval_unleash.stderr new file mode 100644 index 0000000000000..2f9da195078f8 --- /dev/null +++ b/src/test/ui/intrinsics/is_const_eval_unleash.stderr @@ -0,0 +1,114 @@ +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:11:17 + | +LL | if unsafe { is_const_eval() } { + | ^^^^^^^^^^^^^^^ + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:11:5 + | +LL | / if unsafe { is_const_eval() } { +LL | | x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3] +LL | | } else { +LL | | unsafe { +... | +LL | | } +LL | | } + | |_____^ + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:26:5 + | +LL | assert_eq!(X, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:26:5 + | +LL | assert_eq!(X, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:26:5 + | +LL | assert_eq!(X, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:28:5 + | +LL | assert_eq!(x, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:28:5 + | +LL | assert_eq!(x, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:28:5 + | +LL | assert_eq!(x, true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:31:5 + | +LL | assert_eq!(Y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:31:5 + | +LL | assert_eq!(Y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:31:5 + | +LL | assert_eq!(Y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:33:5 + | +LL | assert_eq!(y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:33:5 + | +LL | assert_eq!(y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +warning: skipping const checks + --> $DIR/is_const_eval_unleash.rs:33:5 + | +LL | assert_eq!(y, false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) +