1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use rustc_errors::struct_span_err;
4 use rustc_hir::def_id::DefId;
5 use rustc_session::config::nightly_options;
6 use rustc_session::parse::feature_err;
7 use rustc_span::symbol::sym;
8 use rustc_span::{Span, Symbol};
10 use super::{ConstKind, Item};
12 /// An operation that is not *always* allowed in a const context.
13 pub trait NonConstOp: std::fmt::Debug {
14 /// Whether this operation can be evaluated by miri.
15 const IS_SUPPORTED_IN_MIRI: bool = true;
17 /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
18 /// or `None` if such a feature gate does not exist.
19 fn feature_gate() -> Option<Symbol> {
23 /// Returns `true` if this operation is allowed in the given item.
25 /// This check should assume that we are not in a non-const `fn`, where all operations are
28 /// By default, it returns `true` if and only if this operation has a corresponding feature
29 /// gate and that gate is enabled.
30 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
31 Self::feature_gate().map_or(false, |gate| item.tcx.features().enabled(gate))
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()) {
44 "A function call isn't allowed in the const's initialization expression \
45 because the expression's value must be known at compile-time.",
48 "Remember: you can't use a function call inside a const's initialization \
49 expression! However, you can use it anywhere else.",
56 /// A `Downcast` projection.
59 impl NonConstOp for Downcast {
60 fn feature_gate() -> Option<Symbol> {
61 Some(sym::const_if_match)
65 /// A function call where the callee is a pointer.
67 pub struct FnCallIndirect;
68 impl NonConstOp for FnCallIndirect {
69 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
71 item.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn");
76 /// A function call where the callee is not marked as `const`.
78 pub struct FnCallNonConst(pub DefId);
79 impl NonConstOp for FnCallNonConst {
80 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
81 let mut err = struct_span_err!(
85 "calls in {}s are limited to constant functions, \
86 tuple structs and tuple variants",
93 /// A function call where the callee is not a function definition or function pointer, e.g. a
96 /// This can be subdivided in the future to produce a better error message.
98 pub struct FnCallOther;
99 impl NonConstOp for FnCallOther {
100 const IS_SUPPORTED_IN_MIRI: bool = false;
103 /// A call to a `#[unstable]` const fn or `#[rustc_const_unstable]` function.
105 /// Contains the name of the feature that would allow the use of this function.
107 pub struct FnCallUnstable(pub DefId, pub Symbol);
108 impl NonConstOp for FnCallUnstable {
109 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
110 let FnCallUnstable(def_id, feature) = *self;
112 let mut err = item.tcx.sess.struct_span_err(
114 &format!("`{}` is not yet stable as a const fn", item.tcx.def_path_str(def_id)),
116 if nightly_options::is_nightly_build() {
117 err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
124 pub struct HeapAllocation;
125 impl NonConstOp for HeapAllocation {
126 const IS_SUPPORTED_IN_MIRI: bool = false;
128 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
129 let mut err = struct_span_err!(
133 "allocations are not allowed in {}s",
136 err.span_label(span, format!("allocation not allowed in {}s", item.const_kind()));
137 if item.tcx.sess.teach(&err.get_code().unwrap()) {
139 "The value of statics and constants must be known at compile time, \
140 and they live for the entire lifetime of a program. Creating a boxed \
141 value allocates memory on the heap at runtime, and therefore cannot \
142 be done at compile time.",
150 pub struct IfOrMatch;
151 impl NonConstOp for IfOrMatch {
152 fn feature_gate() -> Option<Symbol> {
153 Some(sym::const_if_match)
156 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
157 // This should be caught by the HIR const-checker.
158 item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
164 impl NonConstOp for LiveDrop {
165 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
170 "destructors cannot be evaluated at compile-time"
172 .span_label(span, format!("{}s cannot evaluate destructors", item.const_kind()))
179 impl NonConstOp for Loop {
180 fn feature_gate() -> Option<Symbol> {
181 Some(sym::const_loop)
184 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
185 // This should be caught by the HIR const-checker.
186 item.tcx.sess.delay_span_bug(span, "complex control flow is forbidden in a const context");
191 pub struct CellBorrow;
192 impl NonConstOp for CellBorrow {
193 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
198 "cannot borrow a constant which may contain \
199 interior mutability, create a static instead"
206 pub struct MutBorrow;
207 impl NonConstOp for MutBorrow {
208 fn feature_gate() -> Option<Symbol> {
209 Some(sym::const_mut_refs)
212 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
213 let mut err = feature_err(
214 &item.tcx.sess.parse_sess,
218 "references in {}s may only refer \
219 to immutable values",
223 err.span_label(span, format!("{}s require immutable values", item.const_kind()));
224 if item.tcx.sess.teach(&err.get_code().unwrap()) {
226 "References in statics and constants may only refer \
227 to immutable values.\n\n\
228 Statics are shared everywhere, and if they refer to \
229 mutable data one might violate memory safety since \
230 holding multiple mutable references to shared data \
232 If you really want global mutable state, try using \
233 static mut or a global UnsafeCell.",
241 pub struct MutAddressOf;
242 impl NonConstOp for MutAddressOf {
243 fn feature_gate() -> Option<Symbol> {
244 Some(sym::const_mut_refs)
247 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
249 &item.tcx.sess.parse_sess,
252 &format!("`&raw mut` is not allowed in {}s", item.const_kind()),
260 impl NonConstOp for MutDeref {
261 fn feature_gate() -> Option<Symbol> {
262 Some(sym::const_mut_refs)
268 impl NonConstOp for Panic {
269 fn feature_gate() -> Option<Symbol> {
270 Some(sym::const_panic)
273 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
275 &item.tcx.sess.parse_sess,
278 &format!("panicking in {}s is unstable", item.const_kind()),
285 pub struct RawPtrComparison;
286 impl NonConstOp for RawPtrComparison {
287 fn feature_gate() -> Option<Symbol> {
288 Some(sym::const_compare_raw_pointers)
291 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
293 &item.tcx.sess.parse_sess,
294 sym::const_compare_raw_pointers,
296 &format!("comparing raw pointers inside {}", item.const_kind()),
303 pub struct RawPtrDeref;
304 impl NonConstOp for RawPtrDeref {
305 fn feature_gate() -> Option<Symbol> {
306 Some(sym::const_raw_ptr_deref)
309 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
311 &item.tcx.sess.parse_sess,
312 sym::const_raw_ptr_deref,
314 &format!("dereferencing raw pointers in {}s is unstable", item.const_kind(),),
321 pub struct RawPtrToIntCast;
322 impl NonConstOp for RawPtrToIntCast {
323 fn feature_gate() -> Option<Symbol> {
324 Some(sym::const_raw_ptr_to_usize_cast)
327 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
329 &item.tcx.sess.parse_sess,
330 sym::const_raw_ptr_to_usize_cast,
332 &format!("casting pointers to integers in {}s is unstable", item.const_kind(),),
338 /// An access to a (non-thread-local) `static`.
340 pub struct StaticAccess;
341 impl NonConstOp for StaticAccess {
342 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
343 item.const_kind().is_static()
346 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
347 let mut err = struct_span_err!(
351 "{}s cannot refer to statics",
355 "consider extracting the value of the `static` to a `const`, and referring to that",
357 if item.tcx.sess.teach(&err.get_code().unwrap()) {
359 "`static` and `const` variables can refer to other `const` variables. \
360 A `const` variable, however, cannot refer to a `static` variable.",
362 err.help("To fix this, the value can be extracted to a `const` and then used.");
368 /// An access to a thread-local `static`.
370 pub struct ThreadLocalAccess;
371 impl NonConstOp for ThreadLocalAccess {
372 const IS_SUPPORTED_IN_MIRI: bool = false;
374 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
379 "thread-local statics cannot be \
380 accessed at compile-time"
387 pub struct UnionAccess;
388 impl NonConstOp for UnionAccess {
389 fn is_allowed_in_item(&self, item: &Item<'_, '_>) -> bool {
390 // Union accesses are stable in all contexts except `const fn`.
391 item.const_kind() != ConstKind::ConstFn
392 || item.tcx.features().enabled(Self::feature_gate().unwrap())
395 fn feature_gate() -> Option<Symbol> {
396 Some(sym::const_fn_union)
399 fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
401 &item.tcx.sess.parse_sess,
404 "unions in const fn are unstable",