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:
Dylan DPC 2020-05-08 14:11:44 +02:00 committed by GitHub
commit 1e6c199653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 101 additions and 134 deletions

View File

@ -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>;

View File

@ -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,

View File

@ -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()

View File

@ -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())
}

View File

@ -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
}

View File

@ -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() {

View File

@ -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(_) => {}

View File

@ -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));
}