1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use rustc::hir::def_id::DefId;
4 use rustc::mir::BorrowKind;
5 use rustc::session::config::nightly_options;
7 use syntax::feature_gate::{emit_feature_err, GateIssue};
8 use syntax::symbol::sym;
9 use syntax_pos::{Span, Symbol};
11 use super::{ConstKind, Item};
13 use rustc_error_codes::*;
15 /// An operation that is not *always* allowed in a const context.
16 pub trait NonConstOp: std::fmt::Debug {
17 /// Whether this operation can be evaluated by miri.
18 const IS_SUPPORTED_IN_MIRI: bool = true;
20 /// Returns a boolean indicating whether the feature gate that would allow this operation is
21 /// enabled, or `None` if such a feature gate does not exist.
22 fn feature_gate(_tcx: TyCtxt<'tcx>) -> Option<bool> {
26 /// Returns `true` if this operation is allowed in the given item.
28 /// This check should assume that we are not in a non-const `fn`, where all operations are
30 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
31 Self::feature_gate(item.tcx).unwrap_or(false)
34 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
35 let mut err = struct_span_err!(
39 "{} contains unimplemented expression type",
42 if item.tcx.sess.teach(&err.get_code().unwrap()) {
43 err.note("A function call isn't allowed in the const's initialization expression \
44 because the expression's value must be known at compile-time.");
45 err.note("Remember: you can't use a function call inside a const's initialization \
46 expression! However, you can use it anywhere else.");
52 /// A `Downcast` projection.
55 impl NonConstOp for Downcast {}
57 /// A function call where the callee is a pointer.
59 pub struct FnCallIndirect;
60 impl NonConstOp for FnCallIndirect {
61 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
62 let mut err = item.tcx.sess.struct_span_err(
64 &format!("function pointers are not allowed in const fn"));
69 /// A function call where the callee is not marked as `const`.
71 pub struct FnCallNonConst(pub DefId);
72 impl NonConstOp for FnCallNonConst {
73 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
74 let mut err = struct_span_err!(
78 "calls in {}s are limited to constant functions, \
79 tuple structs and tuple variants",
86 /// A function call where the callee is not a function definition or function pointer, e.g. a
89 /// This can be subdivided in the future to produce a better error message.
91 pub struct FnCallOther;
92 impl NonConstOp for FnCallOther {
93 const IS_SUPPORTED_IN_MIRI: bool = false;
96 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
98 /// Contains the name of the feature that would allow the use of this function.
100 pub struct FnCallUnstable(pub DefId, pub Symbol);
101 impl NonConstOp for FnCallUnstable {
102 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
103 let FnCallUnstable(def_id, feature) = *self;
105 let mut err = item.tcx.sess.struct_span_err(span,
106 &format!("`{}` is not yet stable as a const fn",
107 item.tcx.def_path_str(def_id)));
108 if nightly_options::is_nightly_build() {
110 "add `#![feature({})]` to the \
111 crate attributes to enable",
119 pub struct HeapAllocation;
120 impl NonConstOp for HeapAllocation {
121 const IS_SUPPORTED_IN_MIRI: bool = false;
123 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
124 let mut err = struct_span_err!(item.tcx.sess, span, E0010,
125 "allocations are not allowed in {}s", item.const_kind());
126 err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
127 if item.tcx.sess.teach(&err.get_code().unwrap()) {
129 "The value of statics and constants must be known at compile time, \
130 and they live for the entire lifetime of a program. Creating a boxed \
131 value allocates memory on the heap at runtime, and therefore cannot \
132 be done at compile time."
140 pub struct IfOrMatch;
141 impl NonConstOp for IfOrMatch {
142 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
143 // This should be caught by the HIR const-checker.
144 item.tcx.sess.delay_span_bug(
146 "complex control flow is forbidden in a const context",
153 impl NonConstOp for LiveDrop {
154 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
155 struct_span_err!(item.tcx.sess, span, E0493,
156 "destructors cannot be evaluated at compile-time")
157 .span_label(span, format!("{}s cannot evaluate destructors",
165 impl NonConstOp for Loop {
166 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
167 // This should be caught by the HIR const-checker.
168 item.tcx.sess.delay_span_bug(
170 "complex control flow is forbidden in a const context",
176 pub struct MutBorrow(pub BorrowKind);
177 impl NonConstOp for MutBorrow {
178 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
180 if let BorrowKind::Mut { .. } = kind {
181 let mut err = struct_span_err!(item.tcx.sess, span, E0017,
182 "references in {}s may only refer \
183 to immutable values", item.const_kind());
184 err.span_label(span, format!("{}s require immutable values",
186 if item.tcx.sess.teach(&err.get_code().unwrap()) {
187 err.note("References in statics and constants may only refer \
188 to immutable values.\n\n\
189 Statics are shared everywhere, and if they refer to \
190 mutable data one might violate memory safety since \
191 holding multiple mutable references to shared data \
193 If you really want global mutable state, try using \
194 static mut or a global UnsafeCell.");
198 span_err!(item.tcx.sess, span, E0492,
199 "cannot borrow a constant which may contain \
200 interior mutability, create a static instead");
207 impl NonConstOp for MutDeref {}
211 impl NonConstOp for Panic {
212 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
213 Some(tcx.features().const_panic)
216 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
218 &item.tcx.sess.parse_sess,
222 &format!("panicking in {}s is unstable", item.const_kind()),
228 pub struct RawPtrComparison;
229 impl NonConstOp for RawPtrComparison {
230 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
231 Some(tcx.features().const_compare_raw_pointers)
234 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
236 &item.tcx.sess.parse_sess,
237 sym::const_compare_raw_pointers,
240 &format!("comparing raw pointers inside {}", item.const_kind()),
246 pub struct RawPtrDeref;
247 impl NonConstOp for RawPtrDeref {
248 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
249 Some(tcx.features().const_raw_ptr_deref)
252 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
254 &item.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
255 span, GateIssue::Language,
257 "dereferencing raw pointers in {}s is unstable",
265 pub struct RawPtrToIntCast;
266 impl NonConstOp for RawPtrToIntCast {
267 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
268 Some(tcx.features().const_raw_ptr_to_usize_cast)
271 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
273 &item.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
274 span, GateIssue::Language,
276 "casting pointers to integers in {}s is unstable",
283 /// An access to a (non-thread-local) `static`.
285 pub struct StaticAccess;
286 impl NonConstOp for StaticAccess {
287 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
288 item.const_kind().is_static()
291 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
292 let mut err = struct_span_err!(item.tcx.sess, span, E0013,
293 "{}s cannot refer to statics, use \
294 a constant instead", item.const_kind());
295 if item.tcx.sess.teach(&err.get_code().unwrap()) {
297 "Static and const variables can refer to other const variables. \
298 But a const variable cannot refer to a static variable."
301 "To fix this, the value can be extracted as a const and then used."
308 /// An access to a thread-local `static`.
310 pub struct ThreadLocalAccess;
311 impl NonConstOp for ThreadLocalAccess {
312 const IS_SUPPORTED_IN_MIRI: bool = false;
314 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
315 span_err!(item.tcx.sess, span, E0625,
316 "thread-local statics cannot be \
317 accessed at compile-time");
322 pub struct Transmute;
323 impl NonConstOp for Transmute {
324 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
325 Some(tcx.features().const_transmute)
328 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
330 &item.tcx.sess.parse_sess, sym::const_transmute,
331 span, GateIssue::Language,
332 &format!("The use of std::mem::transmute() \
333 is gated in {}s", item.const_kind()));
338 pub struct UnionAccess;
339 impl NonConstOp for UnionAccess {
340 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
341 // Union accesses are stable in all contexts except `const fn`.
342 item.const_kind() != ConstKind::ConstFn || Self::feature_gate(item.tcx).unwrap()
345 fn feature_gate(tcx: TyCtxt<'_>) -> Option<bool> {
346 Some(tcx.features().const_fn_union)
349 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
351 &item.tcx.sess.parse_sess, sym::const_fn_union,
352 span, GateIssue::Language,
353 "unions in const fn are unstable",