From 5387b6542f1d72d332c84e8325309f0c9de7b6a0 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 12 Oct 2021 05:06:37 +0000 Subject: [PATCH 1/4] Add const_eval_select intrinsic --- .../rustc_codegen_cranelift/src/abi/mod.rs | 4 +- .../src/intrinsics/mod.rs | 4 +- .../src/const_eval/machine.rs | 59 +++++++++++----- .../src/interpret/terminator.rs | 2 +- .../src/transform/check_consts/check.rs | 8 ++- .../src/transform/check_consts/mod.rs | 12 +++- .../check_consts/post_drop_elaboration.rs | 6 +- .../src/transform/promote_consts.rs | 4 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 + compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_span/src/symbol.rs | 3 + .../src/traits/select/candidate_assembly.rs | 8 ++- compiler/rustc_typeck/src/check/callee.rs | 6 +- compiler/rustc_typeck/src/check/intrinsic.rs | 2 + library/core/src/intrinsics.rs | 69 +++++++++++++++++++ .../codegen/intrinsics/const_eval_select.rs | 17 +++++ .../ui/intrinsics/const-eval-select-bad.rs | 36 ++++++++++ .../intrinsics/const-eval-select-bad.stderr | 63 +++++++++++++++++ .../ui/intrinsics/const-eval-select-x86_64.rs | 39 +++++++++++ src/test/ui/intrinsics/const-eval-select.rs | 26 +++++++ 20 files changed, 334 insertions(+), 38 deletions(-) create mode 100644 src/test/codegen/intrinsics/const_eval_select.rs create mode 100644 src/test/ui/intrinsics/const-eval-select-bad.rs create mode 100644 src/test/ui/intrinsics/const-eval-select-bad.stderr create mode 100644 src/test/ui/intrinsics/const-eval-select-x86_64.rs create mode 100644 src/test/ui/intrinsics/const-eval-select.rs diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 15bb90678059d..78fdf9c02d06a 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -309,13 +309,13 @@ pub(crate) fn codegen_terminator_call<'tcx>( span: Span, func: &Operand<'tcx>, args: &[Operand<'tcx>], - destination: Option<(Place<'tcx>, BasicBlock)>, + mir_dest: Option<(Place<'tcx>, BasicBlock)>, ) { let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx)); let fn_sig = fx.tcx.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); - let destination = destination.map(|(place, bb)| (codegen_place(fx, place), bb)); + let destination = mir_dest.map(|(place, bb)| (codegen_place(fx, place), bb)); // Handle special calls like instrinsics and empty drop glue. let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 48183b2d4f634..313b62c5770b6 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -407,11 +407,9 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( destination: Option<(CPlace<'tcx>, BasicBlock)>, span: Span, ) { - let def_id = instance.def_id(); + let intrinsic = fx.tcx.item_name(instance.def_id()); let substs = instance.substs; - let intrinsic = fx.tcx.item_name(def_id); - let ret = match destination { Some((place, _)) => place, None => { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index ae20f6f97b212..202c9cad8eb53 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -26,14 +26,35 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { /// "Intercept" a function call to a panic-related function /// because we have something special to do for it. /// If this returns successfully (`Ok`), the function should just be evaluated normally. - fn hook_panic_fn( + fn hook_special_const_fn( &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], + is_const_fn: bool, ) -> InterpResult<'tcx, Option>> { // The list of functions we handle here must be in sync with - // `is_lang_panic_fn` in `transform/check_consts/mod.rs`. + // `is_lang_special_const_fn` in `transform/check_consts/mod.rs`. let def_id = instance.def_id(); + + if is_const_fn { + if Some(def_id) == self.tcx.lang_items().const_eval_select() { + // redirect to const_eval_select_ct + if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() { + return Ok(Some( + ty::Instance::resolve( + *self.tcx, + ty::ParamEnv::reveal_all(), + const_eval_select, + instance.substs, + ) + .unwrap() + .unwrap(), + )); + } + } + return Ok(None); + } + if Some(def_id) == self.tcx.lang_items().panic_fn() || Some(def_id) == self.tcx.lang_items().panic_str() || Some(def_id) == self.tcx.lang_items().panic_display() @@ -255,31 +276,31 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // Only check non-glue functions if let ty::InstanceDef::Item(def) = instance.def { + let mut is_const_fn = true; + // Execution might have wandered off into other crates, so we cannot do a stability- // sensitive check here. But we can at least rule out functions that are not const // at all. if !ecx.tcx.is_const_fn_raw(def.did) { // allow calling functions marked with #[default_method_body_is_const]. if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) { - // Some functions we support even if they are non-const -- but avoid testing - // that for const fn! - if let Some(new_instance) = ecx.hook_panic_fn(instance, args)? { - // We call another const fn instead. - return Self::find_mir_or_eval_fn( - ecx, - new_instance, - _abi, - args, - _ret, - _unwind, - ); - } else { - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) - } + is_const_fn = false; } } + + // Some functions we support even if they are non-const -- but avoid testing + // that for const fn! + // `const_eval_select` is a const fn because it must use const trait bounds. + if let Some(new_instance) = ecx.hook_special_const_fn(instance, args, is_const_fn)? { + // We call another const fn instead. + return Self::find_mir_or_eval_fn(ecx, new_instance, _abi, args, _ret, _unwind); + } + + if !is_const_fn { + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) + } } // This is a const fn. Call it. Ok(Some(ecx.load_mir(instance.def, None)?)) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index a06903aedf649..8d3544d434acf 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -231,7 +231,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } /// Call this function -- pushing the stack frame and initializing the arguments. - fn eval_fn_call( + pub(crate) fn eval_fn_call( &mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>, caller_abi: Abi, diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index fd5cd269a3a9e..22f14ffbd583a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -24,7 +24,7 @@ use std::ops::Deref; use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_panic_fn, ConstCx, Qualif}; +use super::{is_lang_special_const_fn, ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated @@ -259,7 +259,9 @@ impl Checker<'mir, 'tcx> { self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE); } - self.visit_body(&body); + if !tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { + self.visit_body(&body); + } // Ensure that the end result is `Sync` in a non-thread local `static`. let should_check_for_sync = self.const_kind() @@ -886,7 +888,7 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { } // At this point, we are calling a function, `callee`, whose `DefId` is known... - if is_lang_panic_fn(tcx, callee) { + if is_lang_special_const_fn(tcx, callee) { // `begin_panic` and `panic_display` are generic functions that accept // types other than str. Check to enforce that only str can be used in // const-eval. diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index d1fd3ceaa589a..0a852282f8f26 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -74,9 +74,6 @@ impl ConstCx<'mir, 'tcx> { /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - // We can allow calls to these functions because `hook_panic_fn` in - // `const_eval/machine.rs` ensures the calls are handled specially. - // Keep in sync with what that function handles! Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().panic_str() || Some(def_id) == tcx.lang_items().panic_display() @@ -85,6 +82,15 @@ pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { || Some(def_id) == tcx.lang_items().begin_panic_fmt() } +/// Returns `true` if this `DefId` points to one of the lang items that will be handled differently +/// in const_eval. +pub fn is_lang_special_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + // We can allow calls to these functions because `hook_special_const_fn` in + // `const_eval/machine.rs` ensures the calls are handled specially. + // Keep in sync with what that function handles! + is_lang_panic_fn(tcx, def_id) || Some(def_id) == tcx.lang_items().const_eval_select() +} + pub fn rustc_allow_const_fn_unstable( tcx: TyCtxt<'tcx>, def_id: DefId, diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index f2ba5a1ebb19b..1a8c8b1c78d08 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -1,7 +1,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_span::{symbol::sym, Span}; use super::check::Qualifs; use super::ops::{self, NonConstOp}; @@ -30,6 +30,10 @@ pub fn check_live_drops(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) { return; } + if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { + return; + } + let ccx = ConstCx { body, tcx, const_kind, param_env: tcx.param_env(def_id) }; if !checking_enabled(&ccx) { return; diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index be1b827f2356f..7cfe3d7f809e9 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -26,7 +26,7 @@ use rustc_index::vec::{Idx, IndexVec}; use std::cell::Cell; use std::{cmp, iter, mem}; -use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; +use crate::transform::check_consts::{is_lang_special_const_fn, qualifs, ConstCx}; use crate::transform::MirPass; /// A `MirPass` for promotion. @@ -657,7 +657,7 @@ impl<'tcx> Validator<'_, 'tcx> { let is_const_fn = match *fn_ty.kind() { ty::FnDef(def_id, _) => { - self.tcx.is_const_fn_raw(def_id) || is_lang_panic_fn(self.tcx, def_id) + self.tcx.is_const_fn_raw(def_id) || is_lang_special_const_fn(self.tcx, def_id) } _ => false, }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index f3eaf2645f50a..85b0db468d125 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -467,6 +467,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_promotable, Normal, template!(Word), IMPL_DETAIL), rustc_attr!(rustc_legacy_const_generics, Normal, template!(List: "N"), INTERNAL_UNSTABLE), + // Do not const-check this function's body. It will always get replaced during CTFE. + rustc_attr!(rustc_do_not_const_check, Normal, template!(Word), INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Layout related: diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 814054c551878..f35353dbfb581 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -299,6 +299,8 @@ language_item_table! { DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); Oom, sym::oom, oom, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; + ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4); + ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4); Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0e30e154ee57c..63bd58a3bec38 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -441,6 +441,8 @@ symbols! { const_compare_raw_pointers, const_constructor, const_eval_limit, + const_eval_select, + const_eval_select_ct, const_evaluatable_checked, const_extern_fn, const_fn, @@ -1095,6 +1097,7 @@ symbols! { rustc_diagnostic_item, rustc_diagnostic_macros, rustc_dirty, + rustc_do_not_const_check, rustc_dummy, rustc_dump_env_program_clauses, rustc_dump_program_clauses, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index d68ae07907734..b78c227c364c1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -973,12 +973,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Tuple(_) => stack.extend(ty.tuple_fields().map(|t| (t, depth + 1))), ty::Closure(_, substs) => { - stack.extend(substs.as_closure().upvar_tys().map(|t| (t, depth + 1))) + let substs = substs.as_closure(); + let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); + stack.push((ty, depth + 1)); } ty::Generator(_, substs, _) => { let substs = substs.as_generator(); - stack.extend(substs.upvar_tys().map(|t| (t, depth + 1))); + let ty = self.infcx.shallow_resolve(substs.tupled_upvars_ty()); + + stack.push((ty, depth + 1)); stack.push((substs.witness(), depth + 1)); } diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 51bbcbebcdc0b..06c42098791ef 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -17,7 +17,7 @@ use rustc_infer::{ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; -use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -317,6 +317,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { ty::FnDef(def_id, subst) => { + let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst); + // Unit testing: function items annotated with // `#[rustc_evaluate_where_clauses]` trigger special output // to let us test the trait evaluation system. @@ -342,7 +344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } - (callee_ty.fn_sig(self.tcx), Some(def_id)) + (fn_sig, Some(def_id)) } ty::FnPtr(sig) => (sig, None), ref t => { diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index ff7a26853b188..b0cb8443bfbc1 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -390,6 +390,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::black_box => (1, vec![param(0)], param(0)), + sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)), + other => { tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3e26d46ddcaf9..da0591418f072 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2221,3 +2221,72 @@ pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { write_bytes(dst, val, count) } } + +/// 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. +/// +/// # Type Requirements +/// +/// The two functions must be both function items. They cannot be function +/// pointers or closures. +/// +/// `arg` will be the 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. +/// +/// # 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(bootstrap))] +#[unstable( + feature = "const_eval_select", + issue = "none", + reason = "const_eval_select will never be stable" +)] +#[lang = "const_eval_select"] +#[rustc_do_not_const_check] +pub const unsafe fn const_eval_select( + arg: ARG, + _called_in_const: F, + called_at_rt: G, +) -> RET +where + F: ~const FnOnce(ARG) -> RET, + G: FnOnce(ARG) -> RET + ~const Drop, +{ + called_at_rt(arg) +} + +#[cfg(not(bootstrap))] +#[unstable( + feature = "const_eval_select", + issue = "none", + reason = "const_eval_select will never be stable" +)] +#[lang = "const_eval_select_ct"] +pub const unsafe fn const_eval_select_ct( + arg: ARG, + called_in_const: F, + _called_at_rt: G, +) -> RET +where + F: ~const FnOnce(ARG) -> RET, + G: FnOnce(ARG) -> RET + ~const Drop, +{ + called_in_const(arg) +} diff --git a/src/test/codegen/intrinsics/const_eval_select.rs b/src/test/codegen/intrinsics/const_eval_select.rs new file mode 100644 index 0000000000000..84777cac7c390 --- /dev/null +++ b/src/test/codegen/intrinsics/const_eval_select.rs @@ -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); +} diff --git a/src/test/ui/intrinsics/const-eval-select-bad.rs b/src/test/ui/intrinsics/const-eval-select-bad.rs new file mode 100644 index 0000000000000..1e5bf7a70b8c4 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-bad.rs @@ -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() {} diff --git a/src/test/ui/intrinsics/const-eval-select-bad.stderr b/src/test/ui/intrinsics/const-eval-select-bad.stderr new file mode 100644 index 0000000000000..a38eb627e341d --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-bad.stderr @@ -0,0 +1,63 @@ +error[E0277]: expected a `FnOnce<((),)>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:34]` + --> $DIR/const-eval-select-bad.rs:6:36 + | +LL | const_eval_select((), |()| {}, |()| {}); + | ----------------- ^^^^^^^ expected an `FnOnce<((),)>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:34]` + | | + | 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:34]` +note: required by a bound in `const_eval_select` + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | +LL | F: ~const FnOnce(ARG) -> 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: required by a bound in `const_eval_select` + --> $SRC_DIR/core/src/intrinsics.rs:LL:COL + | +LL | F: ~const FnOnce(ARG) -> RET, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` + +error[E0271]: type mismatch resolving ` 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) -> 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) -> 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`. diff --git a/src/test/ui/intrinsics/const-eval-select-x86_64.rs b/src/test/ui/intrinsics/const-eval-select-x86_64.rs new file mode 100644 index 0000000000000..807db65bc72d9 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-x86_64.rs @@ -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, y): ([i32; 4], [i32; 4])) -> bool { + x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3] +} + +fn eq_rt((x, y): ([i32; 4], [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); +} diff --git a/src/test/ui/intrinsics/const-eval-select.rs b/src/test/ui/intrinsics/const-eval-select.rs new file mode 100644 index 0000000000000..0b2c2f6ed4f06 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select.rs @@ -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); +} From 6770dbd4b5729677bcca6a4c73d3335e523a7ac9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 14 Oct 2021 06:18:53 +0000 Subject: [PATCH 2/4] Avoid tupling at the callee --- library/core/src/intrinsics.rs | 12 +++--- .../ui/intrinsics/const-eval-select-bad.rs | 12 +++--- .../intrinsics/const-eval-select-bad.stderr | 42 ++++++++++--------- .../ui/intrinsics/const-eval-select-x86_64.rs | 4 +- src/test/ui/intrinsics/const-eval-select.rs | 4 +- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index da0591418f072..54d0805550ac5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2266,10 +2266,10 @@ pub const unsafe fn const_eval_select( called_at_rt: G, ) -> RET where - F: ~const FnOnce(ARG) -> RET, - G: FnOnce(ARG) -> RET + ~const Drop, + F: ~const FnOnce, + G: FnOnce + ~const Drop, { - called_at_rt(arg) + called_at_rt.call_once(arg) } #[cfg(not(bootstrap))] @@ -2285,8 +2285,8 @@ pub const unsafe fn const_eval_select_ct( _called_at_rt: G, ) -> RET where - F: ~const FnOnce(ARG) -> RET, - G: FnOnce(ARG) -> RET + ~const Drop, + F: ~const FnOnce, + G: FnOnce + ~const Drop, { - called_in_const(arg) + called_in_const.call_once(arg) } diff --git a/src/test/ui/intrinsics/const-eval-select-bad.rs b/src/test/ui/intrinsics/const-eval-select-bad.rs index 1e5bf7a70b8c4..8fbdc0c39c6aa 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.rs +++ b/src/test/ui/intrinsics/const-eval-select-bad.rs @@ -3,22 +3,22 @@ use std::intrinsics::const_eval_select; const fn not_fn_items() { - const_eval_select((), |()| {}, |()| {}); - //~^ ERROR expected a `FnOnce<((),)>` closure + const_eval_select((), || {}, || {}); + //~^ ERROR expected a `FnOnce<()>` closure const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR expected a `FnOnce<((),)>` closure + //~^ ERROR expected a `FnOnce<()>` closure } -const fn foo((n,): (i32,)) -> i32 { +const fn foo(n: i32) -> i32 { n } -fn bar((n,): (i32,)) -> bool { +fn bar(n: i32) -> bool { assert_eq!(n, 0, "{} must be equal to {}", n, 0); n == 0 } -fn baz((n,): (bool,)) -> i32 { +fn baz(n: bool) -> i32 { assert!(n, "{} must be true", n); n as i32 } diff --git a/src/test/ui/intrinsics/const-eval-select-bad.stderr b/src/test/ui/intrinsics/const-eval-select-bad.stderr index a38eb627e341d..78647e92138c2 100644 --- a/src/test/ui/intrinsics/const-eval-select-bad.stderr +++ b/src/test/ui/intrinsics/const-eval-select-bad.stderr @@ -1,34 +1,36 @@ -error[E0277]: expected a `FnOnce<((),)>` closure, found `[closure@$DIR/const-eval-select-bad.rs:6:27: 6:34]` - --> $DIR/const-eval-select-bad.rs:6:36 +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: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:34]` + = 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) -> RET, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: ~const FnOnce, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` -error[E0277]: expected a `FnOnce<((),)>` closure, found `{integer}` +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}` + | ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}` | | | required by a bound introduced by this call | - = help: the trait `FnOnce<((),)>` is not implemented for `{integer}` + = 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) -> RET, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: ~const FnOnce, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` -error[E0271]: type mismatch resolving ` bool {bar} as FnOnce<((i32,),)>>::Output == i32` +error[E0271]: type mismatch resolving ` bool {bar} as FnOnce<(i32,)>>::Output == i32` --> $DIR/const-eval-select-bad.rs:27:5 | LL | const_eval_select((1,), foo, bar); @@ -37,25 +39,25 @@ LL | const_eval_select((1,), foo, bar); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics.rs:LL:COL | -LL | G: FnOnce(ARG) -> RET + ~const Drop, - | ^^^ required by this bound in `const_eval_select` +LL | G: FnOnce + ~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 fn foo(n: i32) -> i32 { + | --------------------------- found signature of `fn(i32) -> _` ... LL | const_eval_select((true,), foo, baz); - | ----------------- ^^^ expected signature of `fn((bool,)) -> _` + | ----------------- ^^^ 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) -> RET, - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` +LL | F: ~const FnOnce, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select` error: aborting due to 4 previous errors diff --git a/src/test/ui/intrinsics/const-eval-select-x86_64.rs b/src/test/ui/intrinsics/const-eval-select-x86_64.rs index 807db65bc72d9..afec8e054bb76 100644 --- a/src/test/ui/intrinsics/const-eval-select-x86_64.rs +++ b/src/test/ui/intrinsics/const-eval-select-x86_64.rs @@ -6,11 +6,11 @@ use std::intrinsics::const_eval_select; use std::arch::x86_64::*; use std::mem::transmute; -const fn eq_ct((x, y): ([i32; 4], [i32; 4])) -> bool { +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, y): ([i32; 4], [i32; 4])) -> bool { +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 _); diff --git a/src/test/ui/intrinsics/const-eval-select.rs b/src/test/ui/intrinsics/const-eval-select.rs index 0b2c2f6ed4f06..744db2f15b056 100644 --- a/src/test/ui/intrinsics/const-eval-select.rs +++ b/src/test/ui/intrinsics/const-eval-select.rs @@ -4,11 +4,11 @@ use std::intrinsics::const_eval_select; -const fn yes(_: ()) -> bool { +const fn yes() -> bool { true } -fn no(_: ()) -> bool { +fn no() -> bool { false } From 26b78ccd317d7950e0aa9861c7c8e643d92d77cf Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 14 Oct 2021 06:53:20 +0000 Subject: [PATCH 3/4] Fix const stability --- .../src/transform/check_consts/check.rs | 7 +++++-- library/core/src/intrinsics.rs | 2 ++ .../intrinsics/const-eval-select-stability.rs | 20 +++++++++++++++++++ .../const-eval-select-stability.stderr | 10 ++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/intrinsics/const-eval-select-stability.rs create mode 100644 src/test/ui/intrinsics/const-eval-select-stability.stderr diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 22f14ffbd583a..d704c4335c754 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -24,7 +24,7 @@ use std::ops::Deref; use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_special_const_fn, ConstCx, Qualif}; +use super::{is_lang_panic_fn, is_lang_special_const_fn, ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated @@ -910,7 +910,10 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { } } - return; + if is_lang_panic_fn(tcx, callee) { + // run stability check on non-panic special const fns. + return; + } } if Some(callee) == tcx.lang_items().exchange_malloc_fn() { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 54d0805550ac5..886ace193c428 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2258,6 +2258,7 @@ pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { issue = "none", reason = "const_eval_select will never be stable" )] +#[rustc_const_unstable(feature = "const_eval_select", issue = "none")] #[lang = "const_eval_select"] #[rustc_do_not_const_check] pub const unsafe fn const_eval_select( @@ -2278,6 +2279,7 @@ where issue = "none", reason = "const_eval_select will never be stable" )] +#[rustc_const_unstable(feature = "const_eval_select", issue = "none")] #[lang = "const_eval_select_ct"] pub const unsafe fn const_eval_select_ct( arg: ARG, diff --git a/src/test/ui/intrinsics/const-eval-select-stability.rs b/src/test/ui/intrinsics/const-eval-select-stability.rs new file mode 100644 index 0000000000000..db2462aee5922 --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-stability.rs @@ -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() {} diff --git a/src/test/ui/intrinsics/const-eval-select-stability.stderr b/src/test/ui/intrinsics/const-eval-select-stability.stderr new file mode 100644 index 0000000000000..79641bbb46abe --- /dev/null +++ b/src/test/ui/intrinsics/const-eval-select-stability.stderr @@ -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 + From 11fac09eadc3a60982e46e2fed177d6b0a686041 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 14 Oct 2021 07:35:35 +0000 Subject: [PATCH 4/4] fix codegen test --- src/test/codegen/intrinsics/const_eval_select.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/codegen/intrinsics/const_eval_select.rs b/src/test/codegen/intrinsics/const_eval_select.rs index 84777cac7c390..34e653b4b9dd4 100644 --- a/src/test/codegen/intrinsics/const_eval_select.rs +++ b/src/test/codegen/intrinsics/const_eval_select.rs @@ -5,10 +5,10 @@ use std::intrinsics::const_eval_select; -const fn foo(_: (i32,)) -> i32 { 1 } +const fn foo(_: i32) -> i32 { 1 } #[no_mangle] -pub fn hi((n,): (i32,)) -> i32 { n } +pub fn hi(n: i32) -> i32 { n } #[no_mangle] pub unsafe fn hey() {