Rollup merge of #71989 - ecstatic-morse:const-context-enum, r=oli-obk
Use a single enum for the kind of a const context This adds a `ConstContext` enum to the `rustc_hir` crate and method that can be called via `tcx.hir()` to get the `ConstContext` for a given body owner. This arose from discussion in #71824. r? @oli-obk
This commit is contained in:
commit
1e6c199653
@ -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<LitKind>;
|
||||
|
||||
|
@ -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<ConstContext> {
|
||||
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,
|
||||
|
@ -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<ConstKind>,
|
||||
pub const_kind: Option<hir::ConstContext>,
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
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()
|
||||
|
@ -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 {
|
||||
@ -326,7 +327,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) {
|
||||
@ -374,7 +375,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())
|
||||
}
|
||||
|
||||
|
@ -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::impls::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
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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(_) => {}
|
||||
|
@ -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<Self> {
|
||||
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<ConstKind>,
|
||||
const_kind: Option<hir::ConstContext>,
|
||||
}
|
||||
|
||||
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<ConstKind>, f: impl FnOnce(&mut Self)) {
|
||||
fn recurse_into(&mut self, kind: Option<hir::ConstContext>, 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));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user