Use enum for status of non-const ops

This commit is contained in:
Dylan MacKenzie 2020-09-02 13:25:19 -07:00
parent 9b4154193e
commit ed6c7efd87

View file

@ -14,35 +14,32 @@ use super::ConstCx;
pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
debug!("illegal_op: op={:?}", op);
if op.is_allowed_in_item(ccx) {
return;
}
let gate = match op.status_in_item(ccx) {
Status::Allowed => return,
Status::Unstable(gate) if ccx.tcx.features().enabled(gate) => return,
Status::Unstable(gate) => Some(gate),
Status::Forbidden => None,
};
if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
ccx.tcx.sess.miri_unleashed_feature(span, gate);
return;
}
op.emit_error(ccx, span);
}
pub enum Status {
Allowed,
Unstable(Symbol),
Forbidden,
}
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
/// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
/// or `None` if such a feature gate does not exist.
fn feature_gate() -> Option<Symbol> {
None
}
/// Returns `true` if this operation is allowed in the given item.
///
/// This check should assume that we are not in a non-const `fn`, where all operations are
/// legal.
///
/// By default, it returns `true` if and only if this operation has a corresponding feature
/// gate and that gate is enabled.
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
Self::feature_gate().map_or(false, |gate| ccx.tcx.features().enabled(gate))
/// Returns an enum indicating whether this operation is allowed within the given item.
fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
Status::Forbidden
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -53,9 +50,13 @@ pub trait NonConstOp: std::fmt::Debug {
"{} contains unimplemented expression type",
ccx.const_kind()
);
if let Some(feat) = Self::feature_gate() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feat));
if let Status::Unstable(gate) = self.status_in_item(ccx) {
if !ccx.tcx.features().enabled(gate) && nightly_options::is_nightly_build() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", gate));
}
}
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"A function call isn't allowed in the const's initialization expression \
@ -182,14 +183,13 @@ impl NonConstOp for CellBorrow {
#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
// Forbid everywhere except in const fn
ccx.const_kind() == hir::ConstContext::ConstFn
&& ccx.tcx.features().enabled(Self::feature_gate().unwrap())
}
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// Forbid everywhere except in const fn with a feature gate
if ccx.const_kind() == hir::ConstContext::ConstFn {
Status::Unstable(sym::const_mut_refs)
} else {
Status::Forbidden
}
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -201,15 +201,16 @@ impl NonConstOp for MutBorrow {
&format!("mutable references are not allowed in {}s", ccx.const_kind()),
)
} else {
struct_span_err!(
let mut err = struct_span_err!(
ccx.tcx.sess,
span,
E0764,
"mutable references are not allowed in {}s",
ccx.const_kind(),
)
);
err.span_label(span, format!("`&mut` is only allowed in `const fn`"));
err
};
err.span_label(span, "`&mut` is only allowed in `const fn`".to_string());
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"References in statics and constants may only refer \
@ -226,11 +227,17 @@ impl NonConstOp for MutBorrow {
}
}
// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues.
#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// Forbid everywhere except in const fn with a feature gate
if ccx.const_kind() == hir::ConstContext::ConstFn {
Status::Unstable(sym::const_mut_refs)
} else {
Status::Forbidden
}
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -247,16 +254,16 @@ impl NonConstOp for MutAddressOf {
#[derive(Debug)]
pub struct MutDeref;
impl NonConstOp for MutDeref {
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
}
#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
fn feature_gate() -> Option<Symbol> {
Some(sym::const_panic)
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_panic)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -289,8 +296,8 @@ impl NonConstOp for RawPtrComparison {
#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
fn feature_gate() -> Option<Symbol> {
Some(sym::const_raw_ptr_deref)
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_raw_ptr_deref)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -307,8 +314,8 @@ impl NonConstOp for RawPtrDeref {
#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
fn feature_gate() -> Option<Symbol> {
Some(sym::const_raw_ptr_to_usize_cast)
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_raw_ptr_to_usize_cast)
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -326,8 +333,12 @@ impl NonConstOp for RawPtrToIntCast {
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
matches!(ccx.const_kind(), hir::ConstContext::Static(_))
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
if let hir::ConstContext::Static(_) = ccx.const_kind() {
Status::Allowed
} else {
Status::Forbidden
}
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -371,14 +382,13 @@ impl NonConstOp for ThreadLocalAccess {
#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool {
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// Union accesses are stable in all contexts except `const fn`.
ccx.const_kind() != hir::ConstContext::ConstFn
|| ccx.tcx.features().enabled(Self::feature_gate().unwrap())
}
fn feature_gate() -> Option<Symbol> {
Some(sym::const_fn_union)
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_union)
}
}
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {