1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
5 use rustc_hir::def_id::DefId;
6 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
7 use rustc_middle::{mir, ty::AssocKind};
8 use rustc_session::parse::feature_err;
9 use rustc_span::symbol::sym;
10 use rustc_span::{symbol::Ident, Span, Symbol};
11 use rustc_span::{BytePos, Pos};
15 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
22 #[derive(Clone, Copy)]
23 pub enum DiagnosticImportance {
24 /// An operation that must be removed for const-checking to pass.
27 /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
31 /// An operation that is not *always* allowed in a const context.
32 pub trait NonConstOp: std::fmt::Debug {
33 /// Returns an enum indicating whether this operation is allowed within the given item.
34 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
38 fn importance(&self) -> DiagnosticImportance {
39 DiagnosticImportance::Primary
42 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
46 pub struct FloatingPointOp;
47 impl NonConstOp for FloatingPointOp {
48 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
49 if ccx.const_kind() == hir::ConstContext::ConstFn {
50 Status::Unstable(sym::const_fn_floating_point_arithmetic)
56 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
58 &ccx.tcx.sess.parse_sess,
59 sym::const_fn_floating_point_arithmetic,
61 &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
66 /// A function call where the callee is a pointer.
68 pub struct FnCallIndirect;
69 impl NonConstOp for FnCallIndirect {
70 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
71 ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn")
75 /// A function call where the callee is not marked as `const`.
77 pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>);
78 impl<'a> NonConstOp for FnCallNonConst<'a> {
79 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
80 let mut err = struct_span_err!(
84 "calls in {}s are limited to constant functions, \
85 tuple structs and tuple variants",
89 if let FnCallNonConst(Some((callee, substs))) = *self {
90 if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() {
91 if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind(
93 Ident::with_dummy_span(sym::eq),
97 if callee == eq_item.def_id && substs.len() == 2 {
98 match (substs[0].unpack(), substs[1].unpack()) {
99 (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
102 && self_ty.peel_refs().is_primitive() =>
104 let mut num_refs = 0;
105 let mut tmp_ty = self_ty;
106 while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
110 let deref = "*".repeat(num_refs);
112 if let Ok(call_str) =
113 ccx.tcx.sess.source_map().span_to_snippet(span)
115 if let Some(eq_idx) = call_str.find("==") {
116 if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
117 .find(|c: char| !c.is_whitespace())
119 let rhs_pos = span.lo()
120 + BytePos::from_usize(eq_idx + 2 + rhs_idx);
121 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
122 err.multipart_suggestion(
123 "consider dereferencing here",
125 (span.shrink_to_lo(), deref.clone()),
128 Applicability::MachineApplicable,
145 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
147 /// Contains the name of the feature that would allow the use of this function.
149 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
151 impl NonConstOp for FnCallUnstable {
152 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
153 let FnCallUnstable(def_id, feature) = *self;
155 let mut err = ccx.tcx.sess.struct_span_err(
157 &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
160 if ccx.is_const_stable_const_fn() {
161 err.help("const-stable functions can only call other const-stable functions");
162 } else if ccx.tcx.sess.is_nightly_build() {
163 if let Some(feature) = feature {
165 "add `#![feature({})]` to the crate attributes to enable",
176 pub struct FnPtrCast;
177 impl NonConstOp for FnPtrCast {
178 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
179 if ccx.const_kind() != hir::ConstContext::ConstFn {
182 Status::Unstable(sym::const_fn_fn_ptr_basics)
186 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
188 &ccx.tcx.sess.parse_sess,
189 sym::const_fn_fn_ptr_basics,
191 &format!("function pointer casts are not allowed in {}s", ccx.const_kind()),
197 pub struct Generator(pub hir::GeneratorKind);
198 impl NonConstOp for Generator {
199 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
200 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
201 Status::Unstable(sym::const_async_blocks)
207 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
208 let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
209 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
210 feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg)
212 ccx.tcx.sess.struct_span_err(span, &msg)
218 pub struct HeapAllocation;
219 impl NonConstOp for HeapAllocation {
220 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
221 let mut err = struct_span_err!(
225 "allocations are not allowed in {}s",
228 err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
229 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
231 "The value of statics and constants must be known at compile time, \
232 and they live for the entire lifetime of a program. Creating a boxed \
233 value allocates memory on the heap at runtime, and therefore cannot \
234 be done at compile time.",
242 pub struct InlineAsm;
243 impl NonConstOp for InlineAsm {
244 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
249 "inline assembly is not allowed in {}s",
256 pub struct LiveDrop {
257 pub dropped_at: Option<Span>,
259 impl NonConstOp for LiveDrop {
260 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
261 let mut err = struct_span_err!(
265 "destructors cannot be evaluated at compile-time"
267 err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
268 if let Some(span) = self.dropped_at {
269 err.span_label(span, "value is dropped here");
276 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
277 /// the final value of the constant.
278 pub struct TransientCellBorrow;
279 impl NonConstOp for TransientCellBorrow {
280 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
281 Status::Unstable(sym::const_refs_to_cell)
283 fn importance(&self) -> DiagnosticImportance {
284 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
285 // not additionally emit a feature gate error if activating the feature gate won't work.
286 DiagnosticImportance::Secondary
288 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
290 &ccx.tcx.sess.parse_sess,
291 sym::const_refs_to_cell,
293 "cannot borrow here, since the borrowed element may contain interior mutability",
299 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
300 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
301 /// it in the future for static items.
302 pub struct CellBorrow;
303 impl NonConstOp for CellBorrow {
304 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
305 let mut err = struct_span_err!(
309 "{}s cannot refer to interior mutable data",
314 "this borrow of an interior mutable value may end up in the final value",
316 if let hir::ConstContext::Static(_) = ccx.const_kind() {
318 "to fix this, the value can be extracted to a separate \
319 `static` item and then referenced",
322 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
324 "A constant containing interior mutable data behind a reference can allow you
325 to modify that data. This would make multiple uses of a constant to be able to
326 see different values and allow circumventing the `Send` and `Sync` requirements
327 for shared mutable data, which is unsound.",
335 /// This op is for `&mut` borrows in the trailing expression of a constant
336 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
337 /// static or const items.
338 pub struct MutBorrow(pub hir::BorrowKind);
340 impl NonConstOp for MutBorrow {
341 fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
345 fn importance(&self) -> DiagnosticImportance {
346 // If there were primary errors (like non-const function calls), do not emit further
347 // errors about mutable references.
348 DiagnosticImportance::Secondary
351 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
352 let raw = match self.0 {
353 hir::BorrowKind::Raw => "raw ",
354 hir::BorrowKind::Ref => "",
357 let mut err = struct_span_err!(
361 "{}mutable references are not allowed in the final value of {}s",
366 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
368 "References in statics and constants may only refer \
369 to immutable values.\n\n\
370 Statics are shared everywhere, and if they refer to \
371 mutable data one might violate memory safety since \
372 holding multiple mutable references to shared data \
374 If you really want global mutable state, try using \
375 static mut or a global UnsafeCell.",
383 pub struct TransientMutBorrow(pub hir::BorrowKind);
385 impl NonConstOp for TransientMutBorrow {
386 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
387 Status::Unstable(sym::const_mut_refs)
390 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
391 let raw = match self.0 {
392 hir::BorrowKind::Raw => "raw ",
393 hir::BorrowKind::Ref => "",
397 &ccx.tcx.sess.parse_sess,
400 &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
407 impl NonConstOp for MutDeref {
408 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
409 Status::Unstable(sym::const_mut_refs)
412 fn importance(&self) -> DiagnosticImportance {
413 // Usually a side-effect of a `TransientMutBorrow` somewhere.
414 DiagnosticImportance::Secondary
417 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
419 &ccx.tcx.sess.parse_sess,
422 &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()),
427 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
429 pub struct PanicNonStr;
430 impl NonConstOp for PanicNonStr {
431 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
432 ccx.tcx.sess.struct_span_err(
434 "argument to `panic!()` in a const context must have type `&str`",
439 /// Comparing raw pointers for equality.
440 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
441 /// allocation base addresses that are not known at compile-time.
443 pub struct RawPtrComparison;
444 impl NonConstOp for RawPtrComparison {
445 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
449 .struct_span_err(span, "pointers cannot be reliably compared during const eval");
451 "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \
452 for more information",
459 pub struct RawMutPtrDeref;
460 impl NonConstOp for RawMutPtrDeref {
461 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
462 Status::Unstable(sym::const_mut_refs)
465 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
467 &ccx.tcx.sess.parse_sess,
470 &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
475 /// Casting raw pointer or function pointer to an integer.
476 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
477 /// allocation base addresses that are not known at compile-time.
479 pub struct RawPtrToIntCast;
480 impl NonConstOp for RawPtrToIntCast {
481 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
485 .struct_span_err(span, "pointers cannot be cast to integers during const eval");
486 err.note("at compile-time, pointers do not have an integer value");
488 "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior",
494 /// An access to a (non-thread-local) `static`.
496 pub struct StaticAccess;
497 impl NonConstOp for StaticAccess {
498 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
499 if let hir::ConstContext::Static(_) = ccx.const_kind() {
506 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
507 let mut err = struct_span_err!(
511 "{}s cannot refer to statics",
515 "consider extracting the value of the `static` to a `const`, and referring to that",
517 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
519 "`static` and `const` variables can refer to other `const` variables. \
520 A `const` variable, however, cannot refer to a `static` variable.",
522 err.help("To fix this, the value can be extracted to a `const` and then used.");
528 /// An access to a thread-local `static`.
530 pub struct ThreadLocalAccess;
531 impl NonConstOp for ThreadLocalAccess {
532 fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
537 "thread-local statics cannot be \
538 accessed at compile-time"
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<'tcx>(
565 ccx: &ConstCx<'_, 'tcx>,
567 ) -> DiagnosticBuilder<'tcx> {
569 &ccx.tcx.sess.parse_sess,
572 &format!("mutable references are not allowed in {}s", ccx.const_kind()),
578 pub struct FnPtr(pub mir::LocalKind);
579 impl NonConstOp for FnPtr {
580 fn importance(&self) -> DiagnosticImportance {
582 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
583 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
584 DiagnosticImportance::Primary
589 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
590 if ccx.const_kind() != hir::ConstContext::ConstFn {
593 Status::Unstable(sym::const_fn_fn_ptr_basics)
597 fn build_error<'tcx>(
599 ccx: &ConstCx<'_, 'tcx>,
601 ) -> DiagnosticBuilder<'tcx> {
603 &ccx.tcx.sess.parse_sess,
604 sym::const_fn_fn_ptr_basics,
606 &format!("function pointers cannot appear in {}s", ccx.const_kind()),
612 pub struct ImplTrait;
613 impl NonConstOp for ImplTrait {
614 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
615 Status::Unstable(sym::const_impl_trait)
618 fn build_error<'tcx>(
620 ccx: &ConstCx<'_, 'tcx>,
622 ) -> DiagnosticBuilder<'tcx> {
624 &ccx.tcx.sess.parse_sess,
625 sym::const_impl_trait,
627 &format!("`impl Trait` is not allowed in {}s", ccx.const_kind()),
633 pub struct TraitBound(pub mir::LocalKind);
634 impl NonConstOp for TraitBound {
635 fn importance(&self) -> DiagnosticImportance {
637 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
638 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
639 DiagnosticImportance::Primary
644 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
645 if ccx.const_kind() != hir::ConstContext::ConstFn {
648 Status::Unstable(sym::const_fn_trait_bound)
652 fn build_error<'tcx>(
654 ccx: &ConstCx<'_, 'tcx>,
656 ) -> DiagnosticBuilder<'tcx> {
657 let mut err = feature_err(
658 &ccx.tcx.sess.parse_sess,
659 sym::const_fn_trait_bound,
661 "trait bounds other than `Sized` on const fn parameters are unstable",
665 Some(fn_sig) if !fn_sig.span.contains(span) => {
666 err.span_label(fn_sig.span, "function declared as const here");
676 pub struct DynTrait(pub mir::LocalKind);
677 impl NonConstOp for DynTrait {
678 fn importance(&self) -> DiagnosticImportance {
680 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
681 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
682 DiagnosticImportance::Primary
687 fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
688 if ccx.const_kind() != hir::ConstContext::ConstFn {
691 Status::Unstable(sym::const_fn_trait_bound)
695 fn build_error<'tcx>(
697 ccx: &ConstCx<'_, 'tcx>,
699 ) -> DiagnosticBuilder<'tcx> {
700 let mut err = feature_err(
701 &ccx.tcx.sess.parse_sess,
702 sym::const_fn_trait_bound,
704 "trait objects in const fn are unstable",
708 Some(fn_sig) if !fn_sig.span.contains(span) => {
709 err.span_label(fn_sig.span, "function declared as const here");
718 /// A trait bound with the `?const Trait` opt-out
720 pub struct TraitBoundNotConst;
721 impl NonConstOp for TraitBoundNotConst {
722 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
723 Status::Unstable(sym::const_trait_bound_opt_out)
726 fn build_error<'tcx>(
728 ccx: &ConstCx<'_, 'tcx>,
730 ) -> DiagnosticBuilder<'tcx> {
732 &ccx.tcx.sess.parse_sess,
733 sym::const_trait_bound_opt_out,
735 "`?const Trait` syntax is unstable",