1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use hir::def_id::LocalDefId;
5 error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
8 use rustc_hir::def_id::DefId;
9 use rustc_infer::infer::TyCtxtInferExt;
10 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
11 use rustc_middle::mir;
12 use rustc_middle::ty::print::with_no_trimmed_paths;
13 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
14 use rustc_middle::ty::{
15 suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, TraitPredicate,
18 use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef};
19 use rustc_session::parse::feature_err;
20 use rustc_span::symbol::sym;
21 use rustc_span::{BytePos, Pos, Span, Symbol};
22 use rustc_trait_selection::traits::SelectionContext;
26 MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr,
27 StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw,
29 use crate::util::{call_kind, CallDesugaringKind, CallKind};
31 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
38 #[derive(Clone, Copy)]
39 pub enum DiagnosticImportance {
40 /// An operation that must be removed for const-checking to pass.
43 /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
47 /// An operation that is not *always* allowed in a const context.
48 pub trait NonConstOp<'tcx>: std::fmt::Debug {
49 /// Returns an enum indicating whether this operation is allowed within the given item.
50 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
54 fn importance(&self) -> DiagnosticImportance {
55 DiagnosticImportance::Primary
60 ccx: &ConstCx<'_, 'tcx>,
62 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
66 pub struct FloatingPointOp;
67 impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
68 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
69 if ccx.const_kind() == hir::ConstContext::ConstFn {
70 Status::Unstable(sym::const_fn_floating_point_arithmetic)
78 ccx: &ConstCx<'_, 'tcx>,
80 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
82 &ccx.tcx.sess.parse_sess,
83 sym::const_fn_floating_point_arithmetic,
85 &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
90 /// A function call where the callee is a pointer.
92 pub struct FnCallIndirect;
93 impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
96 ccx: &ConstCx<'_, 'tcx>,
98 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
99 ccx.tcx.sess.struct_span_err(
101 &format!("function pointer calls are not allowed in {}s", ccx.const_kind()),
106 /// A function call where the callee is not marked as `const`.
107 #[derive(Debug, Clone, Copy)]
108 pub struct FnCallNonConst<'tcx> {
109 pub caller: LocalDefId,
111 pub substs: SubstsRef<'tcx>,
113 pub from_hir_call: bool,
116 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
119 ccx: &ConstCx<'_, 'tcx>,
121 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
122 let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self;
123 let ConstCx { tcx, param_env, .. } = *ccx;
125 let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
126 let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
128 match self_ty.kind() {
131 let caller_hir_id = tcx.hir().local_def_id_to_hir_id(caller);
132 if let Some(generics) = tcx.hir().get(caller_hir_id).generics() {
133 let constraint = with_no_trimmed_paths!(format!(
135 trait_ref.print_only_trait_path()
137 suggest_constraining_type_param(
141 ¶m_ty.name.as_str(),
148 let obligation = Obligation::new(
149 ObligationCause::dummy(),
151 Binder::dummy(TraitPredicate {
153 constness: BoundConstness::NotConst,
154 polarity: ImplPolarity::Positive,
158 let implsrc = tcx.infer_ctxt().enter(|infcx| {
159 let mut selcx = SelectionContext::new(&infcx);
160 selcx.select(&obligation)
163 if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
164 let span = tcx.def_span(data.impl_def_id);
165 err.span_note(span, "impl defined here, but it is not `const`");
172 let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
176 let mut err = match call_kind {
177 CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
180 struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
184 let mut err = match kind {
185 CallDesugaringKind::ForLoopIntoIter => {
186 error!("cannot convert `{}` into an iterator in {}s")
188 CallDesugaringKind::QuestionBranch => {
189 error!("`?` cannot determine the branch of `{}` in {}s")
191 CallDesugaringKind::QuestionFromResidual => {
192 error!("`?` cannot convert from residual of `{}` in {}s")
194 CallDesugaringKind::TryBlockFromOutput => {
195 error!("`try` block cannot convert `{}` to the result in {}s")
199 diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
202 CallKind::FnCall { fn_trait_id, self_ty } => {
203 let mut err = struct_span_err!(
207 "cannot call non-const closure in {}s",
211 match self_ty.kind() {
212 FnDef(def_id, ..) => {
213 let span = tcx.def_span(*def_id);
214 if ccx.tcx.is_const_fn_raw(*def_id) {
215 span_bug!(span, "calling const FnDef errored when it shouldn't");
218 err.span_note(span, "function defined here, but it is not `const`");
222 "function pointers need an RFC before allowed to be called in {}s",
228 "closures need an RFC before allowed to be called in {}s",
235 diag_trait(&mut err, self_ty, fn_trait_id);
238 CallKind::Operator { trait_id, self_ty, .. } => {
239 let mut err = struct_span_err!(
243 "cannot call non-const operator in {}s",
247 if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
248 match (substs[0].unpack(), substs[1].unpack()) {
249 (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
252 && self_ty.peel_refs().is_primitive() =>
254 let mut num_refs = 0;
255 let mut tmp_ty = self_ty;
256 while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
260 let deref = "*".repeat(num_refs);
262 if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
263 if let Some(eq_idx) = call_str.find("==") {
264 if let Some(rhs_idx) =
265 call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
268 span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
269 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
270 err.multipart_suggestion(
271 "consider dereferencing here",
273 (span.shrink_to_lo(), deref.clone()),
276 Applicability::MachineApplicable,
286 diag_trait(&mut err, self_ty, trait_id);
289 CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
290 let mut err = struct_span_err!(
294 "cannot perform deref coercion on `{}` in {}s",
299 err.note(&format!("attempting to deref into `{}`", deref_target_ty));
301 // Check first whether the source is accessible (issue #87060)
302 if tcx.sess.source_map().is_span_accessible(deref_target) {
303 err.span_note(deref_target, "deref defined here");
306 diag_trait(&mut err, self_ty, tcx.lang_items().deref_trait().unwrap());
309 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => {
314 "cannot call non-const formatting macro in {}s",
318 _ => struct_span_err!(
322 "cannot call non-const fn `{}` in {}s",
323 ccx.tcx.def_path_str_with_substs(callee, substs),
329 "calls in {}s are limited to constant functions, \
330 tuple structs and tuple variants",
338 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
340 /// Contains the name of the feature that would allow the use of this function.
342 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
344 impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
347 ccx: &ConstCx<'_, 'tcx>,
349 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
350 let FnCallUnstable(def_id, feature) = *self;
352 let mut err = ccx.tcx.sess.struct_span_err(
354 &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
357 if ccx.is_const_stable_const_fn() {
358 err.help("const-stable functions can only call other const-stable functions");
359 } else if ccx.tcx.sess.is_nightly_build() {
360 if let Some(feature) = feature {
362 "add `#![feature({})]` to the crate attributes to enable",
373 pub struct Generator(pub hir::GeneratorKind);
374 impl<'tcx> NonConstOp<'tcx> for Generator {
375 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
376 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
377 Status::Unstable(sym::const_async_blocks)
385 ccx: &ConstCx<'_, 'tcx>,
387 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
388 let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
389 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
390 feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg)
392 ccx.tcx.sess.struct_span_err(span, &msg)
398 pub struct HeapAllocation;
399 impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
402 ccx: &ConstCx<'_, 'tcx>,
404 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
405 let mut err = struct_span_err!(
409 "allocations are not allowed in {}s",
412 err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
413 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
415 "The value of statics and constants must be known at compile time, \
416 and they live for the entire lifetime of a program. Creating a boxed \
417 value allocates memory on the heap at runtime, and therefore cannot \
418 be done at compile time.",
426 pub struct InlineAsm;
427 impl<'tcx> NonConstOp<'tcx> for InlineAsm {
430 ccx: &ConstCx<'_, 'tcx>,
432 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
437 "inline assembly is not allowed in {}s",
444 pub struct LiveDrop {
445 pub dropped_at: Option<Span>,
447 impl<'tcx> NonConstOp<'tcx> for LiveDrop {
450 ccx: &ConstCx<'_, 'tcx>,
452 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
453 let mut err = struct_span_err!(
457 "destructors cannot be evaluated at compile-time"
459 err.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind()));
460 if let Some(span) = self.dropped_at {
461 err.span_label(span, "value is dropped here");
468 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
469 /// the final value of the constant.
470 pub struct TransientCellBorrow;
471 impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
472 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
473 Status::Unstable(sym::const_refs_to_cell)
475 fn importance(&self) -> DiagnosticImportance {
476 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
477 // not additionally emit a feature gate error if activating the feature gate won't work.
478 DiagnosticImportance::Secondary
482 ccx: &ConstCx<'_, 'tcx>,
484 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
486 &ccx.tcx.sess.parse_sess,
487 sym::const_refs_to_cell,
489 "cannot borrow here, since the borrowed element may contain interior mutability",
495 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
496 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
497 /// it in the future for static items.
498 pub struct CellBorrow;
499 impl<'tcx> NonConstOp<'tcx> for CellBorrow {
502 ccx: &ConstCx<'_, 'tcx>,
504 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
505 let mut err = struct_span_err!(
509 "{}s cannot refer to interior mutable data",
514 "this borrow of an interior mutable value may end up in the final value",
516 if let hir::ConstContext::Static(_) = ccx.const_kind() {
518 "to fix this, the value can be extracted to a separate \
519 `static` item and then referenced",
522 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
524 "A constant containing interior mutable data behind a reference can allow you
525 to modify that data. This would make multiple uses of a constant to be able to
526 see different values and allow circumventing the `Send` and `Sync` requirements
527 for shared mutable data, which is unsound.",
535 /// This op is for `&mut` borrows in the trailing expression of a constant
536 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
537 /// static or const items.
538 pub struct MutBorrow(pub hir::BorrowKind);
540 impl<'tcx> NonConstOp<'tcx> for MutBorrow {
541 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
545 fn importance(&self) -> DiagnosticImportance {
546 // If there were primary errors (like non-const function calls), do not emit further
547 // errors about mutable references.
548 DiagnosticImportance::Secondary
553 ccx: &ConstCx<'_, 'tcx>,
555 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
556 let raw = match self.0 {
557 hir::BorrowKind::Raw => "raw ",
558 hir::BorrowKind::Ref => "",
561 let mut err = struct_span_err!(
565 "{}mutable references are not allowed in the final value of {}s",
570 if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
572 "References in statics and constants may only refer \
573 to immutable values.\n\n\
574 Statics are shared everywhere, and if they refer to \
575 mutable data one might violate memory safety since \
576 holding multiple mutable references to shared data \
578 If you really want global mutable state, try using \
579 static mut or a global UnsafeCell.",
587 pub struct TransientMutBorrow(pub hir::BorrowKind);
589 impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
590 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
591 Status::Unstable(sym::const_mut_refs)
596 ccx: &ConstCx<'_, 'tcx>,
598 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
599 let kind = ccx.const_kind();
601 hir::BorrowKind::Raw => ccx
604 .create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs),
605 hir::BorrowKind::Ref => ccx
608 .create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs),
615 impl<'tcx> NonConstOp<'tcx> for MutDeref {
616 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
617 Status::Unstable(sym::const_mut_refs)
620 fn importance(&self) -> DiagnosticImportance {
621 // Usually a side-effect of a `TransientMutBorrow` somewhere.
622 DiagnosticImportance::Secondary
627 ccx: &ConstCx<'_, 'tcx>,
629 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
632 .create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs)
636 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
638 pub struct PanicNonStr;
639 impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
642 ccx: &ConstCx<'_, 'tcx>,
644 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
645 ccx.tcx.sess.create_err(PanicNonStrErr { span })
649 /// Comparing raw pointers for equality.
650 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
651 /// allocation base addresses that are not known at compile-time.
653 pub struct RawPtrComparison;
654 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
657 ccx: &ConstCx<'_, 'tcx>,
659 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
660 ccx.tcx.sess.create_err(RawPtrComparisonErr { span })
665 pub struct RawMutPtrDeref;
666 impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
667 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
668 Status::Unstable(sym::const_mut_refs)
673 ccx: &ConstCx<'_, 'tcx>,
675 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
677 &ccx.tcx.sess.parse_sess,
680 &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
685 /// Casting raw pointer or function pointer to an integer.
686 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
687 /// allocation base addresses that are not known at compile-time.
689 pub struct RawPtrToIntCast;
690 impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
693 ccx: &ConstCx<'_, 'tcx>,
695 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
696 ccx.tcx.sess.create_err(RawPtrToIntErr { span })
700 /// An access to a (non-thread-local) `static`.
702 pub struct StaticAccess;
703 impl<'tcx> NonConstOp<'tcx> for StaticAccess {
704 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
705 if let hir::ConstContext::Static(_) = ccx.const_kind() {
714 ccx: &ConstCx<'_, 'tcx>,
716 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
717 ccx.tcx.sess.create_err(StaticAccessErr {
719 kind: ccx.const_kind(),
720 teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()),
725 /// An access to a thread-local `static`.
727 pub struct ThreadLocalAccess;
728 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
731 ccx: &ConstCx<'_, 'tcx>,
733 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
734 ccx.tcx.sess.create_err(NonConstOpErr { span })
738 // Types that cannot appear in the signature or locals of a `const fn`.
743 pub struct MutRef(pub mir::LocalKind);
744 impl<'tcx> NonConstOp<'tcx> for MutRef {
745 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
746 Status::Unstable(sym::const_mut_refs)
749 fn importance(&self) -> DiagnosticImportance {
751 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
752 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
753 DiagnosticImportance::Primary
760 ccx: &ConstCx<'_, 'tcx>,
762 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
764 &ccx.tcx.sess.parse_sess,
767 &format!("mutable references are not allowed in {}s", ccx.const_kind()),