1
Fork 0

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) { pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
debug!("illegal_op: op={:?}", op); debug!("illegal_op: op={:?}", op);
if op.is_allowed_in_item(ccx) { let gate = match op.status_in_item(ccx) {
return; 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 { 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; return;
} }
op.emit_error(ccx, span); op.emit_error(ccx, span);
} }
pub enum Status {
Allowed,
Unstable(Symbol),
Forbidden,
}
/// An operation that is not *always* allowed in a const context. /// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug { pub trait NonConstOp: std::fmt::Debug {
/// Returns the `Symbol` corresponding to the feature gate that would enable this operation, /// Returns an enum indicating whether this operation is allowed within the given item.
/// or `None` if such a feature gate does not exist. fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
fn feature_gate() -> Option<Symbol> { Status::Forbidden
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))
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -53,9 +50,13 @@ pub trait NonConstOp: std::fmt::Debug {
"{} contains unimplemented expression type", "{} contains unimplemented expression type",
ccx.const_kind() 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()) { if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note( err.note(
"A function call isn't allowed in the const's initialization expression \ "A function call isn't allowed in the const's initialization expression \
@ -182,14 +183,13 @@ impl NonConstOp for CellBorrow {
#[derive(Debug)] #[derive(Debug)]
pub struct MutBorrow; pub struct MutBorrow;
impl NonConstOp for MutBorrow { impl NonConstOp for MutBorrow {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
// Forbid everywhere except in const fn // Forbid everywhere except in const fn with a feature gate
ccx.const_kind() == hir::ConstContext::ConstFn if ccx.const_kind() == hir::ConstContext::ConstFn {
&& ccx.tcx.features().enabled(Self::feature_gate().unwrap()) Status::Unstable(sym::const_mut_refs)
} } else {
Status::Forbidden
fn feature_gate() -> Option<Symbol> { }
Some(sym::const_mut_refs)
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { 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()), &format!("mutable references are not allowed in {}s", ccx.const_kind()),
) )
} else { } else {
struct_span_err!( let mut err = struct_span_err!(
ccx.tcx.sess, ccx.tcx.sess,
span, span,
E0764, E0764,
"mutable references are not allowed in {}s", "mutable references are not allowed in {}s",
ccx.const_kind(), 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()) { if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
err.note( err.note(
"References in statics and constants may only refer \ "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)] #[derive(Debug)]
pub struct MutAddressOf; pub struct MutAddressOf;
impl NonConstOp for MutAddressOf { impl NonConstOp for MutAddressOf {
fn feature_gate() -> Option<Symbol> { fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
Some(sym::const_mut_refs) // 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) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -247,16 +254,16 @@ impl NonConstOp for MutAddressOf {
#[derive(Debug)] #[derive(Debug)]
pub struct MutDeref; pub struct MutDeref;
impl NonConstOp for MutDeref { impl NonConstOp for MutDeref {
fn feature_gate() -> Option<Symbol> { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Some(sym::const_mut_refs) Status::Unstable(sym::const_mut_refs)
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Panic; pub struct Panic;
impl NonConstOp for Panic { impl NonConstOp for Panic {
fn feature_gate() -> Option<Symbol> { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Some(sym::const_panic) Status::Unstable(sym::const_panic)
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -289,8 +296,8 @@ impl NonConstOp for RawPtrComparison {
#[derive(Debug)] #[derive(Debug)]
pub struct RawPtrDeref; pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref { impl NonConstOp for RawPtrDeref {
fn feature_gate() -> Option<Symbol> { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Some(sym::const_raw_ptr_deref) Status::Unstable(sym::const_raw_ptr_deref)
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -307,8 +314,8 @@ impl NonConstOp for RawPtrDeref {
#[derive(Debug)] #[derive(Debug)]
pub struct RawPtrToIntCast; pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast { impl NonConstOp for RawPtrToIntCast {
fn feature_gate() -> Option<Symbol> { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Some(sym::const_raw_ptr_to_usize_cast) Status::Unstable(sym::const_raw_ptr_to_usize_cast)
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -326,8 +333,12 @@ impl NonConstOp for RawPtrToIntCast {
#[derive(Debug)] #[derive(Debug)]
pub struct StaticAccess; pub struct StaticAccess;
impl NonConstOp for StaticAccess { impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
matches!(ccx.const_kind(), hir::ConstContext::Static(_)) if let hir::ConstContext::Static(_) = ccx.const_kind() {
Status::Allowed
} else {
Status::Forbidden
}
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {
@ -371,14 +382,13 @@ impl NonConstOp for ThreadLocalAccess {
#[derive(Debug)] #[derive(Debug)]
pub struct UnionAccess; pub struct UnionAccess;
impl NonConstOp for 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`. // Union accesses are stable in all contexts except `const fn`.
ccx.const_kind() != hir::ConstContext::ConstFn if ccx.const_kind() != hir::ConstContext::ConstFn {
|| ccx.tcx.features().enabled(Self::feature_gate().unwrap()) Status::Allowed
} } else {
Status::Unstable(sym::const_fn_union)
fn feature_gate() -> Option<Symbol> { }
Some(sym::const_fn_union)
} }
fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) {