1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use rustc_errors::{struct_span_err, DiagnosticBuilder};
5 use rustc_hir::def_id::DefId;
7 use rustc_session::parse::feature_err;
8 use rustc_span::symbol::sym;
9 use rustc_span::{Span, Symbol};
13 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
20 #[derive(Clone, Copy)]
21 pub enum DiagnosticImportance {
22 /// An operation that must be removed for const-checking to pass.
25 /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
29 /// An operation that is not *always* allowed in a const context.
30 pub trait NonConstOp: std::fmt::Debug {
31 /// Returns an enum indicating whether this operation is allowed within the given item.
32 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
36 fn importance(&self) -> DiagnosticImportance {
37 DiagnosticImportance::Primary
40 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
44 pub struct FloatingPointOp;
45 impl NonConstOp for FloatingPointOp {
46 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
47 if ccx.const_kind() == hir::ConstContext::ConstFn {
48 Status::Unstable(sym::const_fn_floating_point_arithmetic)
54 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
56 &ccx.tcx.sess.parse_sess,
57 sym::const_fn_floating_point_arithmetic,
59 &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
64 /// A function call where the callee is a pointer.
66 pub struct FnCallIndirect;
67 impl NonConstOp for FnCallIndirect {
68 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
69 ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn")
73 /// A function call where the callee is not marked as `const`.
75 pub struct FnCallNonConst;
76 impl NonConstOp for FnCallNonConst {
77 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
82 "calls in {}s are limited to constant functions, \
83 tuple structs and tuple variants",
89 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
91 /// Contains the name of the feature that would allow the use of this function.
93 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
95 impl NonConstOp for FnCallUnstable {
96 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
97 let FnCallUnstable(def_id, feature) = *self;
99 let mut err = ccx.tcx.sess.struct_span_err(
101 &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
104 if ccx.is_const_stable_const_fn() {
105 err.help("const-stable functions can only call other const-stable functions");
106 } else if ccx.tcx.sess.is_nightly_build() {
107 if let Some(feature) = feature {
109 "add `#![feature({})]` to the crate attributes to enable",
120 pub struct FnPtrCast;
121 impl NonConstOp for FnPtrCast {
122 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
123 if ccx.const_kind() != hir::ConstContext::ConstFn {
126 Status::Unstable(sym::const_fn_fn_ptr_basics)
130 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
132 &ccx.tcx.sess.parse_sess,
133 sym::const_fn_fn_ptr_basics,
135 &format!("function pointer casts are not allowed in {}s", ccx.const_kind()),
141 pub struct Generator(pub hir::GeneratorKind);
142 impl NonConstOp for Generator {
143 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
144 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
145 Status::Unstable(sym::const_async_blocks)
151 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
152 let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
153 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
154 feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg)
156 ccx.tcx.sess.struct_span_err(span, &msg)
162 pub struct HeapAllocation;
163 impl NonConstOp for HeapAllocation {
164 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
165 let mut err = struct_span_err!(
169 "allocations are not allowed in {}s",
172 err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
173 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
175 "The value of statics and constants must be known at compile time, \
176 and they live for the entire lifetime of a program. Creating a boxed \
177 value allocates memory on the heap at runtime, and therefore cannot \
178 be done at compile time.",
186 pub struct InlineAsm;
187 impl NonConstOp for InlineAsm {
188 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
193 "inline assembly is not allowed in {}s",
200 pub struct LiveDrop {
201 pub dropped_at: Option<Span>,
203 impl NonConstOp for LiveDrop {
204 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
205 let mut err = struct_span_err!(
209 "destructors cannot be evaluated at compile-time"
211 err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
212 if let Some(span) = self.dropped_at {
213 err.span_label(span, "value is dropped here");
220 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
221 /// the final value of the constant.
222 pub struct TransientCellBorrow;
223 impl NonConstOp for TransientCellBorrow {
224 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
225 Status::Unstable(sym::const_refs_to_cell)
227 fn importance(&self) -> DiagnosticImportance {
228 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
229 // not additionally emit a feature gate error if activating the feature gate won't work.
230 DiagnosticImportance::Secondary
232 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
234 &ccx.tcx.sess.parse_sess,
235 sym::const_refs_to_cell,
237 "cannot borrow here, since the borrowed element may contain interior mutability",
243 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
244 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
245 /// it in the future for static items.
246 pub struct CellBorrow;
247 impl NonConstOp for CellBorrow {
248 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
249 let mut err = struct_span_err!(
253 "{}s cannot refer to interior mutable data",
258 "this borrow of an interior mutable value may end up in the final value",
260 if let hir::ConstContext::Static(_) = ccx.const_kind() {
262 "to fix this, the value can be extracted to a separate \
263 `static` item and then referenced",
266 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
268 "A constant containing interior mutable data behind a reference can allow you
269 to modify that data. This would make multiple uses of a constant to be able to
270 see different values and allow circumventing the `Send` and `Sync` requirements
271 for shared mutable data, which is unsound.",
279 /// This op is for `&mut` borrows in the trailing expression of a constant
280 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
281 /// static or const items.
282 pub struct MutBorrow(pub hir::BorrowKind);
284 impl NonConstOp for MutBorrow {
285 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
289 fn importance(&self) -> DiagnosticImportance {
290 // If there were primary errors (like non-const function calls), do not emit further
291 // errors about mutable references.
292 DiagnosticImportance::Secondary
295 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
296 let raw = match self.0 {
297 hir::BorrowKind::Raw => "raw ",
298 hir::BorrowKind::Ref => "",
301 let mut err = struct_span_err!(
305 "{}mutable references are not allowed in the final value of {}s",
310 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
312 "References in statics and constants may only refer \
313 to immutable values.\n\n\
314 Statics are shared everywhere, and if they refer to \
315 mutable data one might violate memory safety since \
316 holding multiple mutable references to shared data \
318 If you really want global mutable state, try using \
319 static mut or a global UnsafeCell.",
327 pub struct TransientMutBorrow(pub hir::BorrowKind);
329 impl NonConstOp for TransientMutBorrow {
330 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
331 Status::Unstable(sym::const_mut_refs)
334 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
335 let raw = match self.0 {
336 hir::BorrowKind::Raw => "raw ",
337 hir::BorrowKind::Ref => "",
341 &ccx.tcx.sess.parse_sess,
344 &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
351 impl NonConstOp for MutDeref {
352 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
353 Status::Unstable(sym::const_mut_refs)
356 fn importance(&self) -> DiagnosticImportance {
357 // Usually a side-effect of a `TransientMutBorrow` somewhere.
358 DiagnosticImportance::Secondary
361 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
363 &ccx.tcx.sess.parse_sess,
366 &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()),
371 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
373 pub struct PanicNonStr;
374 impl NonConstOp for PanicNonStr {
375 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
376 ccx.tcx.sess.struct_span_err(
378 "argument to `panic!()` in a const context must have type `&str`",
383 /// Comparing raw pointers for equality.
384 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
385 /// allocation base addresses that are not known at compile-time.
387 pub struct RawPtrComparison;
388 impl NonConstOp for RawPtrComparison {
389 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
393 .struct_span_err(span, "pointers cannot be reliably compared during const eval");
395 "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
396 for more information",
403 pub struct RawPtrDeref;
404 impl NonConstOp for RawPtrDeref {
405 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
406 Status::Unstable(sym::const_raw_ptr_deref)
409 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
411 &ccx.tcx.sess.parse_sess,
412 sym::const_raw_ptr_deref,
414 &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
419 /// Casting raw pointer or function pointer to an integer.
420 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
421 /// allocation base addresses that are not known at compile-time.
423 pub struct RawPtrToIntCast;
424 impl NonConstOp for RawPtrToIntCast {
425 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
429 .struct_span_err(span, "pointers cannot be cast to integers during const eval");
430 err.note("at compile-time, pointers do not have an integer value");
432 "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior",
438 /// An access to a (non-thread-local) `static`.
440 pub struct StaticAccess;
441 impl NonConstOp for StaticAccess {
442 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
443 if let hir::ConstContext::Static(_) = ccx.const_kind() {
450 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
451 let mut err = struct_span_err!(
455 "{}s cannot refer to statics",
459 "consider extracting the value of the `static` to a `const`, and referring to that",
461 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
463 "`static` and `const` variables can refer to other `const` variables. \
464 A `const` variable, however, cannot refer to a `static` variable.",
466 err.help("To fix this, the value can be extracted to a `const` and then used.");
472 /// An access to a thread-local `static`.
474 pub struct ThreadLocalAccess;
475 impl NonConstOp for ThreadLocalAccess {
476 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
481 "thread-local statics cannot be \
482 accessed at compile-time"
487 // Types that cannot appear in the signature or locals of a `const fn`.
492 pub struct MutRef(pub mir::LocalKind);
493 impl NonConstOp for MutRef {
494 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
495 Status::Unstable(sym::const_mut_refs)
498 fn importance(&self) -> DiagnosticImportance {
500 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
501 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
502 DiagnosticImportance::Primary
507 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
509 &ccx.tcx.sess.parse_sess,
512 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
518 pub struct FnPtr(pub mir::LocalKind);
519 impl NonConstOp for FnPtr {
520 fn importance(&self) -> DiagnosticImportance {
522 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
523 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
524 DiagnosticImportance::Primary
529 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
530 if ccx.const_kind() != hir::ConstContext::ConstFn {
533 Status::Unstable(sym::const_fn_fn_ptr_basics)
537 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
539 &ccx.tcx.sess.parse_sess,
540 sym::const_fn_fn_ptr_basics,
542 &format!("function pointers cannot appear in {}s", ccx.const_kind()),
548 pub struct ImplTrait;
549 impl NonConstOp for ImplTrait {
550 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
551 Status::Unstable(sym::const_impl_trait)
554 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
556 &ccx.tcx.sess.parse_sess,
557 sym::const_impl_trait,
559 &format!("`impl Trait` is not allowed in {}s", ccx.const_kind()),
565 pub struct TraitBound(pub mir::LocalKind);
566 impl NonConstOp for TraitBound {
567 fn importance(&self) -> DiagnosticImportance {
569 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
570 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
571 DiagnosticImportance::Primary
576 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
577 if ccx.const_kind() != hir::ConstContext::ConstFn {
580 Status::Unstable(sym::const_fn_trait_bound)
584 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
585 let mut err = feature_err(
586 &ccx.tcx.sess.parse_sess,
587 sym::const_fn_trait_bound,
589 "trait bounds other than `Sized` on const fn parameters are unstable",
593 Some(fn_sig) if !fn_sig.span.contains(span) => {
594 err.span_label(fn_sig.span, "function declared as const here");
604 pub struct DynTrait(pub mir::LocalKind);
605 impl NonConstOp for DynTrait {
606 fn importance(&self) -> DiagnosticImportance {
608 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
609 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
610 DiagnosticImportance::Primary
615 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
616 if ccx.const_kind() != hir::ConstContext::ConstFn {
619 Status::Unstable(sym::const_fn_trait_bound)
623 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
624 let mut err = feature_err(
625 &ccx.tcx.sess.parse_sess,
626 sym::const_fn_trait_bound,
628 "trait objects in const fn are unstable",
632 Some(fn_sig) if !fn_sig.span.contains(span) => {
633 err.span_label(fn_sig.span, "function declared as const here");
642 /// A trait bound with the `?const Trait` opt-out
644 pub struct TraitBoundNotConst;
645 impl NonConstOp for TraitBoundNotConst {
646 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
647 Status::Unstable(sym::const_trait_bound_opt_out)
650 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
652 &ccx.tcx.sess.parse_sess,
653 sym::const_trait_bound_opt_out,
655 "`?const Trait` syntax is unstable",