diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 10afd9560fa95..df4cc295fac5f 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -6,23 +6,6 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_target::spec::abi::Abi; -/// Whether the `def_id` counts as const fn in your current crate, considering all active -/// feature gates -pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.is_const_fn_raw(def_id) - && match is_unstable_const_fn(tcx, def_id) { - Some(feature_name) => { - // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate. - tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_name) - } - // functions without const stability are either stable user written - // const fn or the user is using feature gates and we thus don't - // care what they do - None => true, - } -} - /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if tcx.is_const_fn_raw(def_id) { @@ -77,7 +60,7 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - is_const_fn(tcx, def_id) + tcx.is_const_fn(def_id) && match tcx.lookup_const_stability(def_id) { Some(stab) => { if cfg!(debug_assertions) && stab.promotable { diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 52d04cb4ff1e0..c1cb5326ca35a 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -26,7 +26,6 @@ use rustc_index::vec::{Idx, IndexVec}; use std::cell::Cell; use std::{cmp, iter, mem}; -use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; use crate::transform::MirPass; @@ -656,9 +655,7 @@ impl<'tcx> Validator<'_, 'tcx> { let is_const_fn = match *fn_ty.kind() { ty::FnDef(def_id, _) => { - is_const_fn(self.tcx, def_id) - || is_unstable_const_fn(self.tcx, def_id).is_some() - || is_lang_panic_fn(self.tcx, def_id) + self.tcx.is_const_fn_raw(def_id) || is_lang_panic_fn(self.tcx, def_id) } _ => false, }; @@ -1079,7 +1076,7 @@ pub fn is_const_fn_in_array_repeat_expression<'tcx>( if let ty::FnDef(def_id, _) = *literal.ty().kind() { if let Some((destination_place, _)) = destination { if destination_place == place { - if is_const_fn(ccx.tcx, def_id) { + if ccx.tcx.is_const_fn(def_id) { return true; } } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 3c0fedb360827..e236c4712c883 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -120,7 +120,9 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous /// types generated for a fn pointer type (e.g., `fn(int) -> int`) - FnPointerCandidate, + FnPointerCandidate { + is_const: bool, + }, /// Builtin implementation of `DiscriminantKind`. DiscriminantKindCandidate, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1f5057d1da22f..8cbae81f67348 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2741,6 +2741,29 @@ impl<'tcx> TyCtxt<'tcx> { pub fn lifetime_scope(self, id: HirId) -> Option { self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id)) } + + /// Whether the `def_id` counts as const fn in the current crate, considering all active + /// feature gates + pub fn is_const_fn(self, def_id: DefId) -> bool { + if self.is_const_fn_raw(def_id) { + match self.lookup_const_stability(def_id) { + Some(stability) if stability.level.is_unstable() => { + // has a `rustc_const_unstable` attribute, check whether the user enabled the + // corresponding feature gate. + self.features() + .declared_lib_features + .iter() + .any(|&(sym, _)| sym == stability.feature) + } + // functions without const stability are either stable user written + // const fn or the user is using feature gates and we thus don't + // care what they do + _ => true, + } + } else { + false + } + } } impl TyCtxtAt<'tcx> { 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 6d64dc8254bb4..d31ae216d3a57 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -475,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .. } = self_ty.fn_sig(self.tcx()).skip_binder() { - candidates.vec.push(FnPointerCandidate); + candidates.vec.push(FnPointerCandidate { is_const: false }); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). @@ -488,7 +488,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } = self_ty.fn_sig(self.tcx()).skip_binder() { if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { - candidates.vec.push(FnPointerCandidate); + candidates + .vec + .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3b6555de912e9..b3ebd1cb1c440 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -92,7 +92,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::Generator(vtable_generator)) } - FnPointerCandidate => { + FnPointerCandidate { .. } => { let data = self.confirm_fn_pointer_candidate(obligation)?; Ok(ImplSource::FnPointer(data)) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7b948a0939fff..8dfd71e9cfb65 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1100,6 +1100,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // generator, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} + // FnDef where the function is const + FnPointerCandidate { is_const: true } => {} ConstDropCandidate => {} _ => { // reject all other types of candidates @@ -1513,6 +1515,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + // Drop otherwise equivalent non-const fn pointer candidates + (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + // Global bounds from the where clause should be ignored // here (see issue #50825). Otherwise, we have a where // clause so don't go around looking for impls. @@ -1523,7 +1528,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) @@ -1541,7 +1546,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) @@ -1571,7 +1576,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) @@ -1583,7 +1588,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) @@ -1664,7 +1669,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) @@ -1673,7 +1678,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate - | FnPointerCandidate + | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e281bbc59c255..ae7e818f106a6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -11,7 +11,7 @@ crate mod utils; use rustc_ast as ast; use rustc_attr as attr; -use rustc_const_eval::const_eval::{is_const_fn, is_unstable_const_fn}; +use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -787,7 +787,7 @@ fn clean_fn_or_proc_macro( let mut func = (sig, generics, body_id).clean(cx); let def_id = item.def_id.to_def_id(); func.header.constness = - if is_const_fn(cx.tcx, def_id) && is_unstable_const_fn(cx.tcx, def_id).is_none() { + if cx.tcx.is_const_fn(def_id) && is_unstable_const_fn(cx.tcx, def_id).is_none() { hir::Constness::Const } else { hir::Constness::NotConst diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-closures.rs b/src/test/ui/rfc-2632-const-trait-impl/const-closures.rs new file mode 100644 index 0000000000000..99e608797ff65 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-closures.rs @@ -0,0 +1,31 @@ +// run-pass + +#![feature(const_trait_impl)] +#![feature(const_fn_trait_bound)] + +const fn answer_p1(f: &F) -> u8 + where + F: ~const FnOnce() -> u8, + F: ~const FnMut() -> u8, + F: ~const Fn() -> u8, +{ + f() * 7 +} + +const fn three() -> u8 { + 3 +} + +const fn answer_p2() -> u8 { + answer_p1(&three) +} + +const fn answer u8>(f: &F) -> u8 { + f() + f() +} + +const ANSWER: u8 = answer(&answer_p2); + +fn main() { + assert_eq!(ANSWER, 42) +} diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 3a94f47298390..beddc6f09be81 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -18,7 +18,6 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index e9a9895cb746f..67dda33e9daaf 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -364,7 +364,7 @@ fn check_terminator( } fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { - rustc_const_eval::const_eval::is_const_fn(tcx, def_id) + tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire