Skip to content

Commit

Permalink
Rollup merge of #71989 - ecstatic-morse:const-context-enum, r=oli-obk
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Dylan-DPC authored May 8, 2020
2 parents a51e004 + e356d5c commit 1e6c199
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 134 deletions.
47 changes: 47 additions & 0 deletions src/librustc_hir/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down
20 changes: 20 additions & 0 deletions src/librustc_middle/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..), .. })
Expand All @@ -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,
Expand Down
68 changes: 3 additions & 65 deletions src/librustc_mir/transform/check_consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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> {
Expand All @@ -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()
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_mir/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
}

Expand Down
25 changes: 12 additions & 13 deletions src/librustc_mir/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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};
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;

Expand All @@ -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());
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
11 changes: 7 additions & 4 deletions src/librustc_mir/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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.
Expand Down Expand Up @@ -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(_) => {}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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(_) => {}
Expand Down
Loading

0 comments on commit 1e6c199

Please sign in to comment.