diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 258428d77da10..03b8fdf1aa390 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -1291,6 +1291,53 @@ impl BodyOwnerKind { } } +/// The kind of an item that requires const-checking. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ConstContext { + /// A `const fn`. + ConstFn, + + /// A `static` or `static mut`. + Static(Mutability), + + /// A `const`, associated `const`, or other const context. + /// + /// Other contexts include: + /// - Array length expressions + /// - Enum discriminants + /// - Const generics + /// + /// For the most part, other contexts are treated just like a regular `const`, so they are + /// lumped into the same category. + Const, +} + +impl ConstContext { + /// A description of this const context that can appear between backticks in an error message. + /// + /// E.g. `const` or `static mut`. + pub fn keyword_name(self) -> &'static str { + match self { + Self::Const => "const", + Self::Static(Mutability::Not) => "static", + Self::Static(Mutability::Mut) => "static mut", + Self::ConstFn => "const fn", + } + } +} + +/// A colloquial, trivially pluralizable description of this const context for use in error +/// messages. +impl fmt::Display for ConstContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::Const => write!(f, "constant"), + Self::Static(_) => write!(f, "static"), + Self::ConstFn => write!(f, "constant function"), + } + } +} + /// A literal. pub type Lit = Spanned; diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs index 1c71fc57bea5a..235d1d80192c8 100644 --- a/src/librustc_middle/hir/map/mod.rs +++ b/src/librustc_middle/hir/map/mod.rs @@ -408,6 +408,9 @@ impl<'hir> Map<'hir> { }) } + /// Returns the `BodyOwnerKind` of this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind { match self.get(id) { Node::Item(&Item { kind: ItemKind::Const(..), .. }) @@ -424,6 +427,23 @@ impl<'hir> Map<'hir> { } } + /// Returns the `ConstContext` of the body associated with this `LocalDefId`. + /// + /// Panics if `LocalDefId` does not have an associated body. + pub fn body_const_context(&self, did: LocalDefId) -> Option { + let hir_id = self.local_def_id_to_hir_id(did); + let ccx = match self.body_owner_kind(hir_id) { + BodyOwnerKind::Const => ConstContext::Const, + BodyOwnerKind::Static(mt) => ConstContext::Static(mt), + + BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, + BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, + BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, + }; + + Some(ccx) + } + pub fn ty_param_owner(&self, id: HirId) -> HirId { match self.get(id) { Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => id, diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs index 46a46aa5ae953..7c439f80ef6ad 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -9,8 +9,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir; use rustc_middle::ty::{self, TyCtxt}; -use std::fmt; - pub use self::qualifs::Qualif; mod ops; @@ -25,7 +23,7 @@ pub struct ConstCx<'mir, 'tcx> { pub tcx: TyCtxt<'tcx>, pub def_id: DefId, pub param_env: ty::ParamEnv<'tcx>, - pub const_kind: Option, + pub const_kind: Option, } impl ConstCx<'mir, 'tcx> { @@ -40,78 +38,18 @@ impl ConstCx<'mir, 'tcx> { body: &'mir mir::Body<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Self { - let const_kind = ConstKind::for_item(tcx, def_id); - + let const_kind = tcx.hir().body_const_context(def_id); ConstCx { body, tcx, def_id: def_id.to_def_id(), param_env, const_kind } } /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.). /// /// Panics if this `Item` is not const. - pub fn const_kind(&self) -> ConstKind { + pub fn const_kind(&self) -> hir::ConstContext { self.const_kind.expect("`const_kind` must not be called on a non-const fn") } } -/// The kinds of items which require compile-time evaluation. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ConstKind { - /// A `static` item. - Static, - /// A `static mut` item. - StaticMut, - /// A `const fn` item. - ConstFn, - /// A `const` item or an anonymous constant (e.g. in array lengths). - Const, -} - -impl ConstKind { - /// Returns the validation mode for the item with the given `DefId`, or `None` if this item - /// does not require validation (e.g. a non-const `fn`). - pub fn for_item(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option { - use hir::BodyOwnerKind as HirKind; - - let hir_id = tcx.hir().as_local_hir_id(def_id); - - let mode = match tcx.hir().body_owner_kind(hir_id) { - HirKind::Closure => return None, - - // Note: this is deliberately checking for `is_const_fn_raw`, as the `is_const_fn` - // checks take into account the `rustc_const_unstable` attribute combined with enabled - // feature gates. Otherwise, const qualification would _not check_ whether this - // function body follows the `const fn` rules, as an unstable `const fn` would - // be considered "not const". More details are available in issue #67053. - HirKind::Fn if tcx.is_const_fn_raw(def_id) => ConstKind::ConstFn, - HirKind::Fn => return None, - - HirKind::Const => ConstKind::Const, - - HirKind::Static(hir::Mutability::Not) => ConstKind::Static, - HirKind::Static(hir::Mutability::Mut) => ConstKind::StaticMut, - }; - - Some(mode) - } - - pub fn is_static(self) -> bool { - match self { - ConstKind::Static | ConstKind::StaticMut => true, - ConstKind::ConstFn | ConstKind::Const => false, - } - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ConstKind::Const => write!(f, "constant"), - ConstKind::Static | ConstKind::StaticMut => write!(f, "static"), - ConstKind::ConstFn => write!(f, "constant function"), - } - } -} - /// 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 { Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn() diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs index fe20ceb47ee37..20277b09e3221 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -1,13 +1,14 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use rustc_errors::struct_span_err; +use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use super::{ConstCx, ConstKind}; +use super::ConstCx; /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { @@ -323,7 +324,7 @@ impl NonConstOp for RawPtrToIntCast { pub struct StaticAccess; impl NonConstOp for StaticAccess { fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { - ccx.const_kind().is_static() + matches!(ccx.const_kind(), hir::ConstContext::Static(_)) } fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { @@ -371,7 +372,7 @@ pub struct UnionAccess; impl NonConstOp for UnionAccess { fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { // Union accesses are stable in all contexts except `const fn`. - ccx.const_kind() != ConstKind::ConstFn + ccx.const_kind() != hir::ConstContext::ConstFn || ccx.tcx.features().enabled(Self::feature_gate().unwrap()) } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 3fd9131d5db76..af056bc0aaab9 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -1,7 +1,7 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. use rustc_errors::struct_span_err; -use rustc_hir::lang_items; +use rustc_hir::{self as hir, lang_items}; use rustc_hir::{def_id::DefId, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; @@ -18,7 +18,7 @@ use std::ops::Deref; use super::ops::{self, NonConstOp}; use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; -use super::{is_lang_panic_fn, ConstCx, ConstKind, Qualif}; +use super::{is_lang_panic_fn, ConstCx, Qualif}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::dataflow::MaybeMutBorrowedLocals; use crate::dataflow::{self, Analysis}; @@ -145,17 +145,13 @@ impl Qualifs<'mir, 'tcx> { // We don't care whether a `const fn` returns a value that is not structurally // matchable. Functions calls are opaque and always use type-based qualification, so // this value should never be used. - ConstKind::ConstFn => true, + hir::ConstContext::ConstFn => true, // If we know that all values of the return type are structurally matchable, there's no // need to run dataflow. - ConstKind::Const | ConstKind::Static | ConstKind::StaticMut - if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => - { - false - } + _ if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => false, - ConstKind::Const | ConstKind::Static | ConstKind::StaticMut => { + hir::ConstContext::Const | hir::ConstContext::Static(_) => { let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) .into_engine(ccx.tcx, &ccx.body, ccx.def_id) .iterate_to_fixpoint() @@ -198,7 +194,7 @@ impl Validator<'mir, 'tcx> { pub fn check_body(&mut self) { let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx; - let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn) + let use_min_const_fn_checks = (const_kind == Some(hir::ConstContext::ConstFn) && crate::const_eval::is_min_const_fn(tcx, def_id)) && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you; @@ -222,8 +218,9 @@ impl Validator<'mir, 'tcx> { self.visit_body(&body); // Ensure that the end result is `Sync` in a non-thread local `static`. - let should_check_for_sync = - const_kind == Some(ConstKind::Static) && !tcx.is_thread_local_static(def_id); + let should_check_for_sync = const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Not)) + && !tcx.is_thread_local_static(def_id); if should_check_for_sync { let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); @@ -351,7 +348,9 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { let ty = place.ty(self.body, self.tcx).ty; let is_allowed = match ty.kind { // Inside a `static mut`, `&mut [...]` is allowed. - ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut => { + ty::Array(..) | ty::Slice(_) + if self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut) => + { true } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 1d982d18eeb0d..02356a43699e3 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -182,7 +182,7 @@ pub fn run_passes( } fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { - let const_kind = check_consts::ConstKind::for_item(tcx, def_id.expect_local()); + let const_kind = tcx.hir().body_const_context(def_id.expect_local()); // No need to const-check a non-const `fn`. if const_kind.is_none() { diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index ad98920eb63b9..467e4188814aa 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -13,6 +13,7 @@ //! move analysis runs after promotion on broken MIR. use rustc_ast::ast::LitKind; +use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::traversal::ReversePostorder; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -30,7 +31,7 @@ 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, ConstKind}; +use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; use crate::transform::{MirPass, MirSource}; /// A `MirPass` for promotion. @@ -352,7 +353,9 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind + == Some(hir::ConstContext::Static(hir::Mutability::Mut)) + { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} @@ -517,7 +520,7 @@ impl<'tcx> Validator<'_, 'tcx> { if let Some(def_id) = c.check_static_ptr(self.tcx) { // Only allow statics (not consts) to refer to other statics. // FIXME(eddyb) does this matter at all for promotion? - let is_static = self.const_kind.map_or(false, |k| k.is_static()); + let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_))); if !is_static { return Err(Unpromotable); } @@ -607,7 +610,7 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] // is allowed right now, and only in functions. - if self.const_kind == Some(ConstKind::StaticMut) { + if self.const_kind == Some(hir::ConstContext::Static(hir::Mutability::Mut)) { // Inside a `static mut`, &mut [...] is also allowed. match ty.kind { ty::Array(..) | ty::Slice(_) => {} diff --git a/src/librustc_passes/check_const.rs b/src/librustc_passes/check_const.rs index c42f2fc264a80..94f9c619a3a26 100644 --- a/src/librustc_passes/check_const.rs +++ b/src/librustc_passes/check_const.rs @@ -7,7 +7,6 @@ //! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips //! through, but errors for structured control flow in a `const` should be emitted here. -use rustc_ast::ast::Mutability; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -19,8 +18,6 @@ use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; -use std::fmt; - /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] enum NonConstExpr { @@ -65,46 +62,6 @@ impl NonConstExpr { } } -#[derive(Copy, Clone)] -enum ConstKind { - Static, - StaticMut, - ConstFn, - Const, - AnonConst, -} - -impl ConstKind { - fn for_body(body: &hir::Body<'_>, tcx: TyCtxt<'_>) -> Option { - let owner = tcx.hir().body_owner(body.id()); - let const_kind = match tcx.hir().body_owner_kind(owner) { - hir::BodyOwnerKind::Const => Self::Const, - hir::BodyOwnerKind::Static(Mutability::Mut) => Self::StaticMut, - hir::BodyOwnerKind::Static(Mutability::Not) => Self::Static, - - hir::BodyOwnerKind::Fn if tcx.is_const_fn_raw(tcx.hir().local_def_id(owner)) => { - Self::ConstFn - } - hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => return None, - }; - - Some(const_kind) - } -} - -impl fmt::Display for ConstKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - Self::Static => "static", - Self::StaticMut => "static mut", - Self::Const | Self::AnonConst => "const", - Self::ConstFn => "const fn", - }; - - write!(f, "{}", s) - } -} - fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) { let mut vis = CheckConstVisitor::new(tcx); tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor()); @@ -117,7 +74,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { #[derive(Copy, Clone)] struct CheckConstVisitor<'tcx> { tcx: TyCtxt<'tcx>, - const_kind: Option, + const_kind: Option, } impl<'tcx> CheckConstVisitor<'tcx> { @@ -147,7 +104,8 @@ impl<'tcx> CheckConstVisitor<'tcx> { let const_kind = self .const_kind .expect("`const_check_violated` may only be called inside a const context"); - let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind); + + let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = @@ -191,7 +149,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { } /// Saves the parent `const_kind` before calling `f` and restores it afterwards. - fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { + fn recurse_into(&mut self, kind: Option, f: impl FnOnce(&mut Self)) { let parent_kind = self.const_kind; self.const_kind = kind; f(self); @@ -207,12 +165,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { } fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { - let kind = Some(ConstKind::AnonConst); + let kind = Some(hir::ConstContext::Const); self.recurse_into(kind, |this| intravisit::walk_anon_const(this, anon)); } fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - let kind = ConstKind::for_body(body, self.tcx); + let owner = self.tcx.hir().body_owner_def_id(body.id()); + let kind = self.tcx.hir().body_const_context(owner); self.recurse_into(kind, |this| intravisit::walk_body(this, body)); }