rust/src/librustc_mir/transform/check_consts/ops.rs

401 lines
12 KiB
Rust
Raw Normal View History

//! Concrete error types for all operations which may be invalid in a certain const context.
use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
use rustc_session::config::nightly_options;
use rustc_session::parse::feature_err;
2020-01-01 19:30:57 +01:00
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
use super::{ConstKind, Item};
/// An operation that is not *always* allowed in a const context.
pub trait NonConstOp: std::fmt::Debug {
/// Whether this operation can be evaluated by miri.
const IS_SUPPORTED_IN_MIRI: bool = true;
2020-03-14 15:59:10 -07:00
/// 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.
2020-03-14 15:59:10 -07:00
///
/// 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, item: &Item<'_, '_>) -> bool {
2020-03-14 15:59:10 -07:00
Self::feature_gate().map_or(false, |gate| item.tcx.features().enabled(gate))
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
span,
E0019,
"{} contains unimplemented expression type",
item.const_kind()
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
2019-12-22 17:42:04 -05:00
err.note(
"A function call isn't allowed in the const's initialization expression \
because the expression's value must be known at compile-time.",
);
err.note(
"Remember: you can't use a function call inside a const's initialization \
expression! However, you can use it anywhere else.",
);
}
err.emit();
}
}
/// A `Downcast` projection.
#[derive(Debug)]
pub struct Downcast;
impl NonConstOp for Downcast {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_if_match)
}
}
/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
impl NonConstOp for FnCallIndirect {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
2020-02-27 13:34:08 +01:00
let mut err =
item.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
err.emit();
}
}
/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
pub struct FnCallNonConst(pub DefId);
impl NonConstOp for FnCallNonConst {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = struct_span_err!(
item.tcx.sess,
span,
E0015,
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
item.const_kind(),
);
err.emit();
}
}
/// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
///
/// Contains the name of the feature that would allow the use of this function.
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Symbol);
impl NonConstOp for FnCallUnstable {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let FnCallUnstable(def_id, feature) = *self;
2019-12-22 17:42:04 -05:00
let mut err = item.tcx.sess.struct_span_err(
span,
&format!("`{}` is not yet stable as a const fn", item.tcx.def_path_str(def_id)),
);
if nightly_options::is_nightly_build() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
}
err.emit();
}
}
#[derive(Debug)]
pub struct HeapAllocation;
impl NonConstOp for HeapAllocation {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
2019-12-22 17:42:04 -05:00
let mut err = struct_span_err!(
item.tcx.sess,
span,
E0010,
"allocations are not allowed in {}s",
item.const_kind()
);
err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"The value of statics and constants must be known at compile time, \
and they live for the entire lifetime of a program. Creating a boxed \
value allocates memory on the heap at runtime, and therefore cannot \
2019-12-22 17:42:04 -05:00
be done at compile time.",
);
}
err.emit();
}
}
#[derive(Debug)]
pub struct IfOrMatch;
impl NonConstOp for IfOrMatch {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_if_match)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
// This should be caught by the HIR const-checker.
2019-12-22 17:42:04 -05:00
item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
}
}
#[derive(Debug)]
pub struct InlineAsm;
impl NonConstOp for InlineAsm {}
#[derive(Debug)]
pub struct LiveDrop;
impl NonConstOp for LiveDrop {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
2019-12-22 17:42:04 -05:00
struct_span_err!(
item.tcx.sess,
span,
E0493,
"destructors cannot be evaluated at compile-time"
)
.span_label(span, format!("{}s cannot evaluate destructors", item.const_kind()))
.emit();
}
}
#[derive(Debug)]
pub struct Loop;
impl NonConstOp for Loop {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_loop)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
// This should be caught by the HIR const-checker.
2019-12-22 17:42:04 -05:00
item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
}
}
#[derive(Debug)]
2019-11-26 10:00:41 -05:00
pub struct CellBorrow;
impl NonConstOp for CellBorrow {
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
struct_span_err!(
2019-12-22 17:42:04 -05:00
item.tcx.sess,
span,
E0492,
2019-11-26 10:00:41 -05:00
"cannot borrow a constant which may contain \
2019-12-22 17:42:04 -05:00
interior mutability, create a static instead"
)
.emit();
2019-11-26 10:00:41 -05:00
}
}
#[derive(Debug)]
pub struct MutBorrow;
impl NonConstOp for MutBorrow {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
let mut err = feature_err(
&item.tcx.sess.parse_sess,
sym::const_mut_refs,
span,
2019-12-22 17:42:04 -05:00
&format!(
"references in {}s may only refer \
to immutable values",
item.const_kind()
),
);
2019-12-22 17:42:04 -05:00
err.span_label(span, format!("{}s require immutable values", item.const_kind()));
2019-11-26 10:00:41 -05:00
if item.tcx.sess.teach(&err.get_code().unwrap()) {
2019-12-22 17:42:04 -05:00
err.note(
"References in statics and constants may only refer \
2019-11-26 10:00:41 -05:00
to immutable values.\n\n\
Statics are shared everywhere, and if they refer to \
mutable data one might violate memory safety since \
holding multiple mutable references to shared data \
is not allowed.\n\n\
If you really want global mutable state, try using \
2019-12-22 17:42:04 -05:00
static mut or a global UnsafeCell.",
);
}
2019-11-26 10:00:41 -05:00
err.emit();
}
}
#[derive(Debug)]
pub struct MutAddressOf;
impl NonConstOp for MutAddressOf {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
sym::const_mut_refs,
span,
2019-12-22 17:42:04 -05:00
&format!("`&raw mut` is not allowed in {}s", item.const_kind()),
)
.emit();
}
}
#[derive(Debug)]
pub struct MutDeref;
2019-11-22 19:59:34 -05:00
impl NonConstOp for MutDeref {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_mut_refs)
2019-11-22 19:59:34 -05:00
}
}
#[derive(Debug)]
pub struct Panic;
impl NonConstOp for Panic {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_panic)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
sym::const_panic,
span,
&format!("panicking in {}s is unstable", item.const_kind()),
)
.emit();
}
}
#[derive(Debug)]
pub struct RawPtrComparison;
impl NonConstOp for RawPtrComparison {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_compare_raw_pointers)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
&item.tcx.sess.parse_sess,
sym::const_compare_raw_pointers,
span,
&format!("comparing raw pointers inside {}", item.const_kind()),
)
.emit();
}
}
#[derive(Debug)]
pub struct RawPtrDeref;
impl NonConstOp for RawPtrDeref {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_raw_ptr_deref)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
2019-12-22 17:42:04 -05:00
&item.tcx.sess.parse_sess,
sym::const_raw_ptr_deref,
span,
&format!("dereferencing raw pointers in {}s is unstable", item.const_kind(),),
)
.emit();
}
}
#[derive(Debug)]
pub struct RawPtrToIntCast;
impl NonConstOp for RawPtrToIntCast {
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_raw_ptr_to_usize_cast)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
2019-12-22 17:42:04 -05:00
&item.tcx.sess.parse_sess,
sym::const_raw_ptr_to_usize_cast,
span,
&format!("casting pointers to integers in {}s is unstable", item.const_kind(),),
)
.emit();
}
}
/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
impl NonConstOp for StaticAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
item.const_kind().is_static()
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
2019-12-22 17:42:04 -05:00
let mut err = struct_span_err!(
item.tcx.sess,
span,
E0013,
2020-01-10 13:31:36 +00:00
"{}s cannot refer to statics",
2019-12-22 17:42:04 -05:00
item.const_kind()
);
2020-01-10 13:31:36 +00:00
err.help(
"consider extracting the value of the `static` to a `const`, and referring to that",
);
if item.tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
2020-01-10 13:31:36 +00:00
"`static` and `const` variables can refer to other `const` variables. \
A `const` variable, however, cannot refer to a `static` variable.",
);
2020-01-10 13:31:36 +00:00
err.help("To fix this, the value can be extracted to a `const` and then used.");
}
err.emit();
}
}
/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
impl NonConstOp for ThreadLocalAccess {
const IS_SUPPORTED_IN_MIRI: bool = false;
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
struct_span_err!(
2019-12-22 17:42:04 -05:00
item.tcx.sess,
span,
E0625,
"thread-local statics cannot be \
2019-12-22 17:42:04 -05:00
accessed at compile-time"
)
.emit();
}
}
#[derive(Debug)]
pub struct UnionAccess;
impl NonConstOp for UnionAccess {
fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
// Union accesses are stable in all contexts except `const fn`.
2020-03-14 15:59:10 -07:00
item.const_kind() != ConstKind::ConstFn
|| item.tcx.features().enabled(Self::feature_gate().unwrap())
}
2020-03-14 15:59:10 -07:00
fn feature_gate() -> Option<Symbol> {
Some(sym::const_fn_union)
}
fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
feature_err(
2019-12-22 17:42:04 -05:00
&item.tcx.sess.parse_sess,
sym::const_fn_union,
span,
"unions in const fn are unstable",
)
.emit();
}
}