diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs deleted file mode 100644 index cc5131cb21795..0000000000000 --- a/src/librustc/ty/constness.rs +++ /dev/null @@ -1,156 +0,0 @@ -use crate::hir::map::blocks::FnLikeNode; -use crate::ty::query::Providers; -use crate::ty::TyCtxt; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_span::symbol::Symbol; -use rustc_target::spec::abi::Abi; -use syntax::attr; - -impl<'tcx> TyCtxt<'tcx> { - /// Whether the `def_id` counts as const fn in your current crate, considering all active - /// feature gates - pub fn is_const_fn(self, def_id: DefId) -> bool { - self.is_const_fn_raw(def_id) - && match self.is_unstable_const_fn(def_id) { - Some(feature_name) => { - // has a `rustc_const_unstable` attribute, check whether the user enabled the - // corresponding feature gate. - self.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(self, def_id: DefId) -> Option { - if self.is_const_fn_raw(def_id) { - let const_stab = self.lookup_const_stability(def_id)?; - if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } - } else { - None - } - } - - /// Returns `true` if this function must conform to `min_const_fn` - pub fn is_min_const_fn(self, def_id: DefId) -> bool { - // Bail out if the signature doesn't contain `const` - if !self.is_const_fn_raw(def_id) { - return false; - } - - if self.features().staged_api { - // In order for a libstd function to be considered min_const_fn - // it needs to be stable and have no `rustc_const_unstable` attribute. - match self.lookup_const_stability(def_id) { - // `rustc_const_unstable` functions don't need to conform. - Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false, - None => { - if let Some(stab) = self.lookup_stability(def_id) { - if stab.level.is_stable() { - self.sess.span_err( - self.def_span(def_id), - "stable const functions must have either `rustc_const_stable` or \ - `rustc_const_unstable` attribute", - ); - // While we errored above, because we don't know if we need to conform, we - // err on the "safe" side and require min_const_fn. - true - } else { - // Unstable functions need not conform to min_const_fn. - false - } - } else { - // Internal functions are forced to conform to min_const_fn. - // Annotate the internal function with a const stability attribute if - // you need to use unstable features. - // Note: this is an arbitrary choice that does not affect stability or const - // safety or anything, it just changes whether we need to annotate some - // internal functions with `rustc_const_stable` or with `rustc_const_unstable` - true - } - } - // Everything else needs to conform, because it would be callable from - // other `min_const_fn` functions. - _ => true, - } - } else { - // users enabling the `const_fn` feature gate can do what they want - !self.features().const_fn - } - } -} - -pub fn provide(providers: &mut Providers<'_>) { - /// Const evaluability whitelist is here to check evaluability at the - /// top level beforehand. - fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - match tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | Abi::PlatformIntrinsic => { - Some(tcx.lookup_const_stability(def_id).is_some()) - } - _ => None, - } - } - - /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether - /// said intrinsic is on the whitelist for being const callable. - fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let hir_id = tcx - .hir() - .as_local_hir_id(def_id) - .expect("Non-local call to local provider is_const_fn"); - - let node = tcx.hir().get(hir_id); - - if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) { - whitelisted - } else if let Some(fn_like) = FnLikeNode::from_node(node) { - fn_like.constness() == hir::Constness::Const - } else if let hir::Node::Ctor(_) = node { - true - } else { - false - } - } - - fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.is_const_fn(def_id) - && match tcx.lookup_const_stability(def_id) { - Some(stab) => { - if cfg!(debug_assertions) && stab.promotable { - let sig = tcx.fn_sig(def_id); - assert_eq!( - sig.unsafety(), - hir::Unsafety::Normal, - "don't mark const unsafe fns as promotable", - // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682 - ); - } - stab.promotable - } - None => false, - } - } - - fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.is_const_fn(def_id) - && tcx - .lookup_const_stability(def_id) - .map(|stab| stab.allow_const_fn_ptr) - .unwrap_or(false) - } - - *providers = Providers { - is_const_fn_raw, - is_promotable_const_fn, - const_fn_is_allowed_fn_ptr, - ..*providers - }; -} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 7cca12308e65f..747e6e8da99af 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -97,7 +97,6 @@ pub mod cast; #[macro_use] pub mod codec; pub mod _match; -mod constness; mod erase_regions; pub mod error; pub mod fast_reject; @@ -3318,7 +3317,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { context::provide(providers); erase_regions::provide(providers); layout::provide(providers); - constness::provide(providers); *providers = ty::query::Providers { asyncness, associated_item, diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index ac04ae285884b..7b2ce7f9ac7be 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -9,10 +9,12 @@ use crate::interpret::{intern_const_alloc_recursive, ConstValue, InterpCx}; mod error; mod eval_queries; +mod fn_queries; mod machine; pub use error::*; pub use eval_queries::*; +pub use fn_queries::*; pub use machine::*; /// Extracts a field of a (variant of a) const. diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs new file mode 100644 index 0000000000000..2443e1e91d378 --- /dev/null +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -0,0 +1,151 @@ +use rustc::hir::map::blocks::FnLikeNode; +use rustc::ty::query::Providers; +use rustc::ty::TyCtxt; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::Symbol; +use rustc_target::spec::abi::Abi; +use syntax::attr; + +/// 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) { + let const_stab = tcx.lookup_const_stability(def_id)?; + if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } + } else { + None + } +} + +/// Returns `true` if this function must conform to `min_const_fn` +pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // Bail out if the signature doesn't contain `const` + if !tcx.is_const_fn_raw(def_id) { + return false; + } + + if tcx.features().staged_api { + // In order for a libstd function to be considered min_const_fn + // it needs to be stable and have no `rustc_const_unstable` attribute. + match tcx.lookup_const_stability(def_id) { + // `rustc_const_unstable` functions don't need to conform. + Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false, + None => { + if let Some(stab) = tcx.lookup_stability(def_id) { + if stab.level.is_stable() { + tcx.sess.span_err( + tcx.def_span(def_id), + "stable const functions must have either `rustc_const_stable` or \ + `rustc_const_unstable` attribute", + ); + // While we errored above, because we don't know if we need to conform, we + // err on the "safe" side and require min_const_fn. + true + } else { + // Unstable functions need not conform to min_const_fn. + false + } + } else { + // Internal functions are forced to conform to min_const_fn. + // Annotate the internal function with a const stability attribute if + // you need to use unstable features. + // Note: this is an arbitrary choice that does not affect stability or const + // safety or anything, it just changes whether we need to annotate some + // internal functions with `rustc_const_stable` or with `rustc_const_unstable` + true + } + } + // Everything else needs to conform, because it would be callable from + // other `min_const_fn` functions. + _ => true, + } + } else { + // users enabling the `const_fn` feature gate can do what they want + !tcx.features().const_fn + } +} + +pub fn provide(providers: &mut Providers<'_>) { + /// Const evaluability whitelist is here to check evaluability at the + /// top level beforehand. + fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + match tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | Abi::PlatformIntrinsic => { + Some(tcx.lookup_const_stability(def_id).is_some()) + } + _ => None, + } + } + + /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether + /// said intrinsic is on the whitelist for being const callable. + fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let hir_id = tcx + .hir() + .as_local_hir_id(def_id) + .expect("Non-local call to local provider is_const_fn"); + + let node = tcx.hir().get(hir_id); + + if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) { + whitelisted + } else if let Some(fn_like) = FnLikeNode::from_node(node) { + fn_like.constness() == hir::Constness::Const + } else if let hir::Node::Ctor(_) = node { + true + } else { + false + } + } + + fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + is_const_fn(tcx, def_id) + && match tcx.lookup_const_stability(def_id) { + Some(stab) => { + if cfg!(debug_assertions) && stab.promotable { + let sig = tcx.fn_sig(def_id); + assert_eq!( + sig.unsafety(), + hir::Unsafety::Normal, + "don't mark const unsafe fns as promotable", + // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682 + ); + } + stab.promotable + } + None => false, + } + } + + fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + is_const_fn(tcx, def_id) + && tcx + .lookup_const_stability(def_id) + .map(|stab| stab.allow_const_fn_ptr) + .unwrap_or(false) + } + + *providers = Providers { + is_const_fn_raw, + is_promotable_const_fn, + const_fn_is_allowed_fn_ptr, + ..*providers + }; +} diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 9f70f1dd57688..36c6568029d5f 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -52,6 +52,7 @@ use rustc::ty::query::Providers; pub fn provide(providers: &mut Providers<'_>) { borrow_check::provide(providers); + const_eval::provide(providers); shim::provide(providers); transform::provide(providers); monomorphize::partitioning::provide(providers); diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 0aa42de538d2b..b818397247624 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -21,6 +21,7 @@ use super::ops::{self, NonConstOp}; use super::qualifs::{self, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{is_lang_panic_fn, ConstKind, Item, Qualif}; +use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::dataflow::{self as old_dataflow, generic as dataflow}; pub type IndirectlyMutableResults<'mir, 'tcx> = @@ -173,7 +174,7 @@ impl Validator<'a, 'mir, 'tcx> { let Item { tcx, body, def_id, const_kind, .. } = *self.item; let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn) - && tcx.is_min_const_fn(def_id)) + && crate::const_eval::is_min_const_fn(tcx, def_id)) && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; if use_min_const_fn_checks { @@ -560,13 +561,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { }; // At this point, we are calling a function whose `DefId` is known... - if self.tcx.is_const_fn(def_id) { + if is_const_fn(self.tcx, def_id) { return; } if is_lang_panic_fn(self.tcx, def_id) { self.check_op(ops::Panic); - } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { + } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) { // Exempt unstable const fns inside of macros with // `#[allow_internal_unstable]`. if !self.span.allows_unstable(feature) { diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 5cf5c54dd4878..d07e58b744771 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -14,6 +14,7 @@ use rustc_span::symbol::{sym, Symbol}; use std::ops::Bound; +use crate::const_eval::{is_const_fn, is_min_const_fn}; use crate::util; use rustc_error_codes::*; @@ -523,7 +524,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult let id = tcx.hir().as_local_hir_id(def_id).unwrap(); let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) { hir::BodyOwnerKind::Closure => (false, false), - hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)), + hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)), hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false), }; let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env); diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index b6656e720c5c3..00a39905c0232 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -29,6 +29,7 @@ use rustc_target::spec::abi::Abi; use std::cell::Cell; use std::{iter, mem, usize}; +use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstKind, Item}; use crate::transform::{MirPass, MirSource}; @@ -702,8 +703,8 @@ impl<'tcx> Validator<'_, 'tcx> { let is_const_fn = match fn_ty.kind { ty::FnDef(def_id, _) => { - self.tcx.is_const_fn(def_id) - || self.tcx.is_unstable_const_fn(def_id).is_some() + is_const_fn(self.tcx, def_id) + || is_unstable_const_fn(self.tcx, def_id).is_some() || is_lang_panic_fn(self.tcx, self.def_id) } _ => false, diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 9dea44e020010..fcdabb29cd0e2 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -327,7 +327,7 @@ fn check_terminator( TerminatorKind::Call { func, args, from_hir_call: _, destination: _, cleanup: _ } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(def_id, _) = fn_ty.kind { - if !tcx.is_min_const_fn(def_id) { + if !crate::const_eval::is_min_const_fn(tcx, def_id) { return Err(( span, format!( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 2400dded66668..c7e0f1e9e704b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -9,6 +9,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::Mutability; use rustc_metadata::creader::LoadedMacro; +use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::sym; use rustc_span::Span; @@ -212,7 +213,7 @@ fn build_external_function(cx: &DocContext<'_>, did: DefId) -> clean::Function { let sig = cx.tcx.fn_sig(did); let constness = - if cx.tcx.is_min_const_fn(did) { hir::Constness::Const } else { hir::Constness::NotConst }; + if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst }; let asyncness = cx.tcx.asyncness(did); let predicates = cx.tcx.predicates_of(did); let (generics, decl) = clean::enter_impl_trait(cx, || { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bb46a15258e0f..be9654612f504 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -21,6 +21,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_index::vec::{Idx, IndexVec}; +use rustc_mir::const_eval::is_min_const_fn; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym}; use rustc_span::{self, Pos}; @@ -895,7 +896,7 @@ impl Clean for doctree::Function<'_> { enter_impl_trait(cx, || (self.generics.clean(cx), (self.decl, self.body).clean(cx))); let did = cx.tcx.hir().local_def_id(self.id); - let constness = if cx.tcx.is_min_const_fn(did) { + let constness = if is_min_const_fn(cx.tcx, did) { hir::Constness::Const } else { hir::Constness::NotConst @@ -1187,7 +1188,7 @@ impl Clean for ty::AssocItem { }; let (all_types, ret_types) = get_all_types(&generics, &decl, cx); if provided { - let constness = if cx.tcx.is_min_const_fn(self.def_id) { + let constness = if is_min_const_fn(cx.tcx, self.def_id) { hir::Constness::Const } else { hir::Constness::NotConst diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index eeaac1d8c7431..32c93ee3b164d 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_interface; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_metadata; +extern crate rustc_mir; extern crate rustc_parse; extern crate rustc_resolve; extern crate rustc_span as rustc_span;