1 //! Concrete error types for all operations which may be invalid in a certain const context.
3 use hir::def_id::LocalDefId;
4 use hir::{ConstContext, LangItem};
6 error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
9 use rustc_hir::def_id::DefId;
10 use rustc_infer::infer::TyCtxtInferExt;
11 use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
12 use rustc_middle::mir;
13 use rustc_middle::ty::print::with_no_trimmed_paths;
14 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
15 use rustc_middle::ty::{
16 suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, Ty,
18 use rustc_middle::ty::{Binder, 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 InteriorMutabilityBorrow, InteriorMutableDataRefer, MutDerefErr, NonConstFmtMacroCall,
27 NonConstFnCall, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
28 TransientMutBorrowErr, TransientMutBorrowErrRaw, UnallowedFnPointerCall,
29 UnallowedHeapAllocations, UnallowedInlineAsm, UnallowedMutableRefs, UnallowedMutableRefsRaw,
30 UnallowedOpInConstContext, UnstableConstFn,
32 use crate::util::{call_kind, CallDesugaringKind, CallKind};
34 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
41 #[derive(Clone, Copy)]
42 pub enum DiagnosticImportance {
43 /// An operation that must be removed for const-checking to pass.
46 /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
50 /// An operation that is not *always* allowed in a const context.
51 pub trait NonConstOp<'tcx>: std::fmt::Debug {
52 /// Returns an enum indicating whether this operation is allowed within the given item.
53 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
57 fn importance(&self) -> DiagnosticImportance {
58 DiagnosticImportance::Primary
63 ccx: &ConstCx<'_, 'tcx>,
65 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
69 pub struct FloatingPointOp;
70 impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
71 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
72 if ccx.const_kind() == hir::ConstContext::ConstFn {
73 Status::Unstable(sym::const_fn_floating_point_arithmetic)
81 ccx: &ConstCx<'_, 'tcx>,
83 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
85 &ccx.tcx.sess.parse_sess,
86 sym::const_fn_floating_point_arithmetic,
88 &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
93 /// A function call where the callee is a pointer.
95 pub struct FnCallIndirect;
96 impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
99 ccx: &ConstCx<'_, 'tcx>,
101 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
102 ccx.tcx.sess.create_err(UnallowedFnPointerCall { span, kind: 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,
114 pub feature: Option<Symbol>,
117 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
120 ccx: &ConstCx<'_, 'tcx>,
122 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
123 let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
124 let ConstCx { tcx, param_env, .. } = *ccx;
126 let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
127 let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
129 match self_ty.kind() {
132 let caller_hir_id = tcx.hir().local_def_id_to_hir_id(caller);
133 if let Some(generics) = tcx.hir().get(caller_hir_id).generics() {
134 let constraint = with_no_trimmed_paths!(format!(
136 trait_ref.print_only_trait_path()
138 suggest_constraining_type_param(
142 ¶m_ty.name.as_str(),
149 let obligation = Obligation::new(
151 ObligationCause::dummy(),
153 Binder::dummy(trait_ref),
156 let infcx = tcx.infer_ctxt().build();
157 let mut selcx = SelectionContext::new(&infcx);
158 let implsrc = selcx.select(&obligation);
160 if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
161 let span = tcx.def_span(data.impl_def_id);
162 err.span_note(span, "impl defined here, but it is not `const`");
169 let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
173 let mut err = match call_kind {
174 CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
177 struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
181 let mut err = match kind {
182 CallDesugaringKind::ForLoopIntoIter => {
183 error!("cannot convert `{}` into an iterator in {}s")
185 CallDesugaringKind::QuestionBranch => {
186 error!("`?` cannot determine the branch of `{}` in {}s")
188 CallDesugaringKind::QuestionFromResidual => {
189 error!("`?` cannot convert from residual of `{}` in {}s")
191 CallDesugaringKind::TryBlockFromOutput => {
192 error!("`try` block cannot convert `{}` to the result in {}s")
196 diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
199 CallKind::FnCall { fn_trait_id, self_ty } => {
200 let mut err = struct_span_err!(
204 "cannot call non-const closure in {}s",
208 match self_ty.kind() {
209 FnDef(def_id, ..) => {
210 let span = tcx.def_span(*def_id);
211 if ccx.tcx.is_const_fn_raw(*def_id) {
212 span_bug!(span, "calling const FnDef errored when it shouldn't");
215 err.span_note(span, "function defined here, but it is not `const`");
219 "function pointers need an RFC before allowed to be called in {}s",
225 "closures need an RFC before allowed to be called in {}s",
232 diag_trait(&mut err, self_ty, fn_trait_id);
235 CallKind::Operator { trait_id, self_ty, .. } => {
236 let mut err = struct_span_err!(
240 "cannot call non-const operator in {}s",
244 if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
245 match (substs[0].unpack(), substs[1].unpack()) {
246 (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
249 && self_ty.peel_refs().is_primitive() =>
251 let mut num_refs = 0;
252 let mut tmp_ty = self_ty;
253 while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
257 let deref = "*".repeat(num_refs);
259 if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
260 if let Some(eq_idx) = call_str.find("==") {
261 if let Some(rhs_idx) =
262 call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
265 span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
266 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
267 err.multipart_suggestion(
268 "consider dereferencing here",
270 (span.shrink_to_lo(), deref.clone()),
273 Applicability::MachineApplicable,
283 diag_trait(&mut err, self_ty, trait_id);
286 CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
287 let mut err = struct_span_err!(
291 "cannot perform deref coercion on `{}` in {}s",
296 err.note(&format!("attempting to deref into `{}`", deref_target_ty));
298 // Check first whether the source is accessible (issue #87060)
299 if tcx.sess.source_map().is_span_accessible(deref_target) {
300 err.span_note(deref_target, "deref defined here");
303 diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
306 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => {
307 ccx.tcx.sess.create_err(NonConstFmtMacroCall { span, kind: ccx.const_kind() })
309 _ => ccx.tcx.sess.create_err(NonConstFnCall {
311 def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
312 kind: ccx.const_kind(),
317 "calls in {}s are limited to constant functions, \
318 tuple structs and tuple variants",
322 if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
324 "add `#![feature({})]` to the crate attributes to enable",
329 if let ConstContext::Static(_) = ccx.const_kind() {
330 err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell");
337 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
339 /// Contains the name of the feature that would allow the use of this function.
341 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
343 impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
346 ccx: &ConstCx<'_, 'tcx>,
348 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
349 let FnCallUnstable(def_id, feature) = *self;
354 .create_err(UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
356 if ccx.is_const_stable_const_fn() {
357 err.help("const-stable functions can only call other const-stable functions");
358 } else if ccx.tcx.sess.is_nightly_build() {
359 if let Some(feature) = feature {
361 "add `#![feature({})]` to the crate attributes to enable",
372 pub struct Generator(pub hir::GeneratorKind);
373 impl<'tcx> NonConstOp<'tcx> for Generator {
374 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
375 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
376 Status::Unstable(sym::const_async_blocks)
384 ccx: &ConstCx<'_, 'tcx>,
386 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
387 let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
388 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
389 ccx.tcx.sess.create_feature_err(
390 UnallowedOpInConstContext { span, msg },
391 sym::const_async_blocks,
394 ccx.tcx.sess.create_err(UnallowedOpInConstContext { span, msg })
400 pub struct HeapAllocation;
401 impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
404 ccx: &ConstCx<'_, 'tcx>,
406 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
407 ccx.tcx.sess.create_err(UnallowedHeapAllocations {
409 kind: ccx.const_kind(),
410 teach: ccx.tcx.sess.teach(&error_code!(E0010)).then_some(()),
416 pub struct InlineAsm;
417 impl<'tcx> NonConstOp<'tcx> for InlineAsm {
420 ccx: &ConstCx<'_, 'tcx>,
422 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
423 ccx.tcx.sess.create_err(UnallowedInlineAsm { span, kind: ccx.const_kind() })
428 pub struct LiveDrop<'tcx> {
429 pub dropped_at: Option<Span>,
430 pub dropped_ty: Ty<'tcx>,
432 impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
435 ccx: &ConstCx<'_, 'tcx>,
437 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
438 let mut err = struct_span_err!(
442 "destructor of `{}` cannot be evaluated at compile-time",
447 format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()),
449 if let Some(span) = self.dropped_at {
450 err.span_label(span, "value is dropped here");
457 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
458 /// the final value of the constant.
459 pub struct TransientCellBorrow;
460 impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
461 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
462 Status::Unstable(sym::const_refs_to_cell)
464 fn importance(&self) -> DiagnosticImportance {
465 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
466 // not additionally emit a feature gate error if activating the feature gate won't work.
467 DiagnosticImportance::Secondary
471 ccx: &ConstCx<'_, 'tcx>,
473 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
474 ccx.tcx.sess.create_feature_err(InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
479 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
480 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
481 /// it in the future for static items.
482 pub struct CellBorrow;
483 impl<'tcx> NonConstOp<'tcx> for CellBorrow {
486 ccx: &ConstCx<'_, 'tcx>,
488 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
489 // FIXME: Maybe a more elegant solution to this if else case
490 if let hir::ConstContext::Static(_) = ccx.const_kind() {
491 ccx.tcx.sess.create_err(InteriorMutableDataRefer {
494 kind: ccx.const_kind(),
495 teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
498 ccx.tcx.sess.create_err(InteriorMutableDataRefer {
501 kind: ccx.const_kind(),
502 teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
509 /// This op is for `&mut` borrows in the trailing expression of a constant
510 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
511 /// static or const items.
512 pub struct MutBorrow(pub hir::BorrowKind);
514 impl<'tcx> NonConstOp<'tcx> for MutBorrow {
515 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
519 fn importance(&self) -> DiagnosticImportance {
520 // If there were primary errors (like non-const function calls), do not emit further
521 // errors about mutable references.
522 DiagnosticImportance::Secondary
527 ccx: &ConstCx<'_, 'tcx>,
529 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
531 hir::BorrowKind::Raw => ccx.tcx.sess.create_err(UnallowedMutableRefsRaw {
533 kind: ccx.const_kind(),
534 teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
536 hir::BorrowKind::Ref => ccx.tcx.sess.create_err(UnallowedMutableRefs {
538 kind: ccx.const_kind(),
539 teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
546 pub struct TransientMutBorrow(pub hir::BorrowKind);
548 impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
549 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
550 Status::Unstable(sym::const_mut_refs)
555 ccx: &ConstCx<'_, 'tcx>,
557 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
558 let kind = ccx.const_kind();
560 hir::BorrowKind::Raw => ccx
563 .create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs),
564 hir::BorrowKind::Ref => ccx
567 .create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs),
574 impl<'tcx> NonConstOp<'tcx> for MutDeref {
575 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
576 Status::Unstable(sym::const_mut_refs)
579 fn importance(&self) -> DiagnosticImportance {
580 // Usually a side-effect of a `TransientMutBorrow` somewhere.
581 DiagnosticImportance::Secondary
586 ccx: &ConstCx<'_, 'tcx>,
588 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
591 .create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs)
595 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
597 pub struct PanicNonStr;
598 impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
601 ccx: &ConstCx<'_, 'tcx>,
603 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
604 ccx.tcx.sess.create_err(PanicNonStrErr { span })
608 /// Comparing raw pointers for equality.
609 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
610 /// allocation base addresses that are not known at compile-time.
612 pub struct RawPtrComparison;
613 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
616 _: &ConstCx<'_, 'tcx>,
618 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
619 span_bug!(span, "raw ptr comparison should already be caught in the trait system");
624 pub struct RawMutPtrDeref;
625 impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
626 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
627 Status::Unstable(sym::const_mut_refs)
632 ccx: &ConstCx<'_, 'tcx>,
634 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
636 &ccx.tcx.sess.parse_sess,
639 &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
644 /// Casting raw pointer or function pointer to an integer.
645 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
646 /// allocation base addresses that are not known at compile-time.
648 pub struct RawPtrToIntCast;
649 impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
652 ccx: &ConstCx<'_, 'tcx>,
654 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
655 ccx.tcx.sess.create_err(RawPtrToIntErr { span })
659 /// An access to a (non-thread-local) `static`.
661 pub struct StaticAccess;
662 impl<'tcx> NonConstOp<'tcx> for StaticAccess {
663 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
664 if let hir::ConstContext::Static(_) = ccx.const_kind() {
673 ccx: &ConstCx<'_, 'tcx>,
675 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
676 ccx.tcx.sess.create_err(StaticAccessErr {
678 kind: ccx.const_kind(),
679 teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()),
684 /// An access to a thread-local `static`.
686 pub struct ThreadLocalAccess;
687 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
690 ccx: &ConstCx<'_, 'tcx>,
692 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
693 ccx.tcx.sess.create_err(NonConstOpErr { span })
697 /// Types that cannot appear in the signature or locals of a `const fn`.
702 pub struct MutRef(pub mir::LocalKind);
703 impl<'tcx> NonConstOp<'tcx> for MutRef {
704 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
705 Status::Unstable(sym::const_mut_refs)
708 fn importance(&self) -> DiagnosticImportance {
710 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
711 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
712 DiagnosticImportance::Primary
719 ccx: &ConstCx<'_, 'tcx>,
721 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
723 &ccx.tcx.sess.parse_sess,
726 &format!("mutable references are not allowed in {}s", ccx.const_kind()),