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 a `#[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 {
147 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
148 let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
149 ccx.tcx.sess.struct_span_err(span, &msg)
154 pub struct HeapAllocation;
155 impl NonConstOp for HeapAllocation {
156 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
157 let mut err = struct_span_err!(
161 "allocations are not allowed in {}s",
164 err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
165 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
167 "The value of statics and constants must be known at compile time, \
168 and they live for the entire lifetime of a program. Creating a boxed \
169 value allocates memory on the heap at runtime, and therefore cannot \
170 be done at compile time.",
178 pub struct InlineAsm;
179 impl NonConstOp for InlineAsm {
180 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
185 "inline assembly is not allowed in {}s",
192 pub struct LiveDrop {
193 pub dropped_at: Option<Span>,
195 impl NonConstOp for LiveDrop {
196 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
197 let mut err = struct_span_err!(
201 "destructors cannot be evaluated at compile-time"
203 err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
204 if let Some(span) = self.dropped_at {
205 err.span_label(span, "value is dropped here");
212 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
213 /// the final value of the constant.
214 pub struct TransientCellBorrow;
215 impl NonConstOp for TransientCellBorrow {
216 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
217 Status::Unstable(sym::const_refs_to_cell)
219 fn importance(&self) -> DiagnosticImportance {
220 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
221 // not additionally emit a feature gate error if activating the feature gate won't work.
222 DiagnosticImportance::Secondary
224 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
226 &ccx.tcx.sess.parse_sess,
227 sym::const_refs_to_cell,
229 "cannot borrow here, since the borrowed element may contain interior mutability",
235 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
236 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
237 /// it in the future for static items.
238 pub struct CellBorrow;
239 impl NonConstOp for CellBorrow {
240 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
241 let mut err = struct_span_err!(
245 "{}s cannot refer to interior mutable data",
250 format!("this borrow of an interior mutable value may end up in the final value"),
252 if let hir::ConstContext::Static(_) = ccx.const_kind() {
254 "to fix this, the value can be extracted to a separate \
255 `static` item and then referenced",
258 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
260 "A constant containing interior mutable data behind a reference can allow you
261 to modify that data. This would make multiple uses of a constant to be able to
262 see different values and allow circumventing the `Send` and `Sync` requirements
263 for shared mutable data, which is unsound.",
271 /// This op is for `&mut` borrows in the trailing expression of a constant
272 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
273 /// static or const items.
274 pub struct MutBorrow(pub hir::BorrowKind);
276 impl NonConstOp for MutBorrow {
277 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
281 fn importance(&self) -> DiagnosticImportance {
282 // If there were primary errors (like non-const function calls), do not emit further
283 // errors about mutable references.
284 DiagnosticImportance::Secondary
287 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
288 let raw = match self.0 {
289 hir::BorrowKind::Raw => "raw ",
290 hir::BorrowKind::Ref => "",
293 let mut err = struct_span_err!(
297 "{}mutable references are not allowed in the final value of {}s",
302 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
304 "References in statics and constants may only refer \
305 to immutable values.\n\n\
306 Statics are shared everywhere, and if they refer to \
307 mutable data one might violate memory safety since \
308 holding multiple mutable references to shared data \
310 If you really want global mutable state, try using \
311 static mut or a global UnsafeCell.",
319 pub struct TransientMutBorrow(pub hir::BorrowKind);
321 impl NonConstOp for TransientMutBorrow {
322 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
323 Status::Unstable(sym::const_mut_refs)
326 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
327 let raw = match self.0 {
328 hir::BorrowKind::Raw => "raw ",
329 hir::BorrowKind::Ref => "",
333 &ccx.tcx.sess.parse_sess,
336 &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
343 impl NonConstOp for MutDeref {
344 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
345 Status::Unstable(sym::const_mut_refs)
348 fn importance(&self) -> DiagnosticImportance {
349 // Usually a side-effect of a `TransientMutBorrow` somewhere.
350 DiagnosticImportance::Secondary
353 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
355 &ccx.tcx.sess.parse_sess,
358 &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()),
365 impl NonConstOp for Panic {
366 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
367 Status::Unstable(sym::const_panic)
370 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
372 &ccx.tcx.sess.parse_sess,
375 &format!("panicking in {}s is unstable", ccx.const_kind()),
381 pub struct RawPtrComparison;
382 impl NonConstOp for RawPtrComparison {
383 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
387 .struct_span_err(span, "pointers cannot be reliably compared during const eval.");
389 "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
390 for more information",
397 pub struct RawPtrDeref;
398 impl NonConstOp for RawPtrDeref {
399 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
400 Status::Unstable(sym::const_raw_ptr_deref)
403 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
405 &ccx.tcx.sess.parse_sess,
406 sym::const_raw_ptr_deref,
408 &format!("dereferencing raw pointers in {}s is unstable", ccx.const_kind(),),
414 pub struct RawPtrToIntCast;
415 impl NonConstOp for RawPtrToIntCast {
416 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
417 Status::Unstable(sym::const_raw_ptr_to_usize_cast)
420 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
422 &ccx.tcx.sess.parse_sess,
423 sym::const_raw_ptr_to_usize_cast,
425 &format!("casting pointers to integers in {}s is unstable", ccx.const_kind(),),
430 /// An access to a (non-thread-local) `static`.
432 pub struct StaticAccess;
433 impl NonConstOp for StaticAccess {
434 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
435 if let hir::ConstContext::Static(_) = ccx.const_kind() {
442 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
443 let mut err = struct_span_err!(
447 "{}s cannot refer to statics",
451 "consider extracting the value of the `static` to a `const`, and referring to that",
453 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
455 "`static` and `const` variables can refer to other `const` variables. \
456 A `const` variable, however, cannot refer to a `static` variable.",
458 err.help("To fix this, the value can be extracted to a `const` and then used.");
464 /// An access to a thread-local `static`.
466 pub struct ThreadLocalAccess;
467 impl NonConstOp for ThreadLocalAccess {
468 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
473 "thread-local statics cannot be \
474 accessed at compile-time"
480 pub struct Transmute;
481 impl NonConstOp for Transmute {
482 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
483 if ccx.const_kind() != hir::ConstContext::ConstFn {
486 Status::Unstable(sym::const_fn_transmute)
490 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
491 let mut err = feature_err(
492 &ccx.tcx.sess.parse_sess,
493 sym::const_fn_transmute,
495 &format!("`transmute` is not allowed in {}s", ccx.const_kind()),
497 err.note("`transmute` is only allowed in constants and statics for now");
503 pub struct UnionAccess;
504 impl NonConstOp for UnionAccess {
505 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
506 // Union accesses are stable in all contexts except `const fn`.
507 if ccx.const_kind() != hir::ConstContext::ConstFn {
510 Status::Unstable(sym::const_fn_union)
514 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
516 &ccx.tcx.sess.parse_sess,
519 "unions in const fn are unstable",
526 /// [#64992]: https://github.com/rust-lang/rust/issues/64992
528 pub struct UnsizingCast;
529 impl NonConstOp for UnsizingCast {
530 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
531 mcf_status_in_item(ccx)
534 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
538 "unsizing casts to types besides slices are not allowed in const fn",
543 // Types that cannot appear in the signature or locals of a `const fn`.
548 pub struct MutRef(pub mir::LocalKind);
549 impl NonConstOp for MutRef {
550 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
551 Status::Unstable(sym::const_mut_refs)
554 fn importance(&self) -> DiagnosticImportance {
556 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
557 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
558 DiagnosticImportance::Primary
563 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
565 &ccx.tcx.sess.parse_sess,
568 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
574 pub struct FnPtr(pub mir::LocalKind);
575 impl NonConstOp for FnPtr {
576 fn importance(&self) -> DiagnosticImportance {
578 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
579 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
580 DiagnosticImportance::Primary
585 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
586 if ccx.const_kind() != hir::ConstContext::ConstFn {
589 Status::Unstable(sym::const_fn_fn_ptr_basics)
593 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
595 &ccx.tcx.sess.parse_sess,
596 sym::const_fn_fn_ptr_basics,
598 &format!("function pointers cannot appear in {}s", ccx.const_kind()),
604 pub struct ImplTrait;
605 impl NonConstOp for ImplTrait {
606 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
607 Status::Unstable(sym::const_impl_trait)
610 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
612 &ccx.tcx.sess.parse_sess,
613 sym::const_impl_trait,
615 &format!("`impl Trait` is not allowed in {}s", ccx.const_kind()),
621 pub struct TraitBound(pub mir::LocalKind);
622 impl NonConstOp for TraitBound {
623 fn importance(&self) -> DiagnosticImportance {
625 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
626 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
627 DiagnosticImportance::Primary
632 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
633 mcf_status_in_item(ccx)
636 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
640 "trait bounds other than `Sized` on const fn parameters are unstable",
645 /// A trait bound with the `?const Trait` opt-out
647 pub struct TraitBoundNotConst;
648 impl NonConstOp for TraitBoundNotConst {
649 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
650 Status::Unstable(sym::const_trait_bound_opt_out)
653 fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
655 &ccx.tcx.sess.parse_sess,
656 sym::const_trait_bound_opt_out,
658 "`?const Trait` syntax is unstable",
664 fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
665 if ccx.const_kind() != hir::ConstContext::ConstFn {
668 Status::Unstable(sym::const_fn)
672 fn mcf_build_error(ccx: &ConstCx<'_, 'tcx>, span: Span, msg: &str) -> DiagnosticBuilder<'tcx> {
673 let mut err = struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg);
675 "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
676 for more information",
678 err.help("add `#![feature(const_fn)]` to the crate attributes to enable");