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 use crate::util::{call_kind, CallDesugaringKind, CallKind};
28 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
35 #[derive(Clone, Copy)]
36 pub enum DiagnosticImportance {
37 /// An operation that must be removed for const-checking to pass.
40 /// An operation that causes const-checking to fail, but is usually a side-effect of a `Primary` operation elsewhere.
44 /// An operation that is not *always* allowed in a const context.
45 pub trait NonConstOp<'tcx>: std::fmt::Debug {
46 /// Returns an enum indicating whether this operation is allowed within the given item.
47 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
51 fn importance(&self) -> DiagnosticImportance {
52 DiagnosticImportance::Primary
57 ccx: &ConstCx<'_, 'tcx>,
59 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
63 pub struct FloatingPointOp;
64 impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
65 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
66 if ccx.const_kind() == hir::ConstContext::ConstFn {
67 Status::Unstable(sym::const_fn_floating_point_arithmetic)
75 ccx: &ConstCx<'_, 'tcx>,
77 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
79 &ccx.tcx.sess.parse_sess,
80 sym::const_fn_floating_point_arithmetic,
82 &format!("floating point arithmetic is not allowed in {}s", ccx.const_kind()),
87 /// A function call where the callee is a pointer.
89 pub struct FnCallIndirect;
90 impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
93 ccx: &ConstCx<'_, 'tcx>,
95 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
96 ccx.tcx.sess.create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
100 /// A function call where the callee is not marked as `const`.
101 #[derive(Debug, Clone, Copy)]
102 pub struct FnCallNonConst<'tcx> {
103 pub caller: LocalDefId,
105 pub substs: SubstsRef<'tcx>,
107 pub from_hir_call: bool,
108 pub feature: Option<Symbol>,
111 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
114 ccx: &ConstCx<'_, 'tcx>,
116 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
117 let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
118 let ConstCx { tcx, param_env, .. } = *ccx;
120 let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
121 let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
123 match self_ty.kind() {
126 let caller_hir_id = tcx.hir().local_def_id_to_hir_id(caller);
127 if let Some(generics) = tcx.hir().get(caller_hir_id).generics() {
128 let constraint = with_no_trimmed_paths!(format!(
130 trait_ref.print_only_trait_path()
132 suggest_constraining_type_param(
136 ¶m_ty.name.as_str(),
144 let obligation = Obligation::new(
146 ObligationCause::dummy(),
148 Binder::dummy(trait_ref),
151 let infcx = tcx.infer_ctxt().build();
152 let mut selcx = SelectionContext::new(&infcx);
153 let implsrc = selcx.select(&obligation);
155 if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
156 let span = tcx.def_span(data.impl_def_id);
157 err.span_note(span, "impl defined here, but it is not `const`");
164 let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
168 let mut err = match call_kind {
169 CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
172 struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
176 let mut err = match kind {
177 CallDesugaringKind::ForLoopIntoIter => {
178 error!("cannot convert `{}` into an iterator in {}s")
180 CallDesugaringKind::QuestionBranch => {
181 error!("`?` cannot determine the branch of `{}` in {}s")
183 CallDesugaringKind::QuestionFromResidual => {
184 error!("`?` cannot convert from residual of `{}` in {}s")
186 CallDesugaringKind::TryBlockFromOutput => {
187 error!("`try` block cannot convert `{}` to the result in {}s")
191 diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
194 CallKind::FnCall { fn_trait_id, self_ty } => {
195 let mut err = struct_span_err!(
199 "cannot call non-const closure in {}s",
203 match self_ty.kind() {
204 FnDef(def_id, ..) => {
205 let span = tcx.def_span(*def_id);
206 if ccx.tcx.is_const_fn_raw(*def_id) {
207 span_bug!(span, "calling const FnDef errored when it shouldn't");
210 err.span_note(span, "function defined here, but it is not `const`");
214 "function pointers need an RFC before allowed to be called in {}s",
220 "closures need an RFC before allowed to be called in {}s",
227 diag_trait(&mut err, self_ty, fn_trait_id);
230 CallKind::Operator { trait_id, self_ty, .. } => {
231 let mut err = struct_span_err!(
235 "cannot call non-const operator in {}s",
239 if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
240 match (substs[0].unpack(), substs[1].unpack()) {
241 (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
244 && self_ty.peel_refs().is_primitive() =>
246 let mut num_refs = 0;
247 let mut tmp_ty = self_ty;
248 while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
252 let deref = "*".repeat(num_refs);
254 if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
255 if let Some(eq_idx) = call_str.find("==") {
256 if let Some(rhs_idx) =
257 call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
260 span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
261 let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
262 err.multipart_suggestion(
263 "consider dereferencing here",
265 (span.shrink_to_lo(), deref.clone()),
268 Applicability::MachineApplicable,
278 diag_trait(&mut err, self_ty, trait_id);
281 CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
282 let mut err = struct_span_err!(
286 "cannot perform deref coercion on `{}` in {}s",
291 err.note(&format!("attempting to deref into `{}`", deref_target_ty));
293 // Check first whether the source is accessible (issue #87060)
294 if tcx.sess.source_map().is_span_accessible(deref_target) {
295 err.span_note(deref_target, "deref defined here");
298 diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
301 _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => ccx
304 .create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }),
305 _ => ccx.tcx.sess.create_err(errors::NonConstFnCall {
307 def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
308 kind: ccx.const_kind(),
313 "calls in {}s are limited to constant functions, \
314 tuple structs and tuple variants",
318 if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
320 "add `#![feature({})]` to the crate attributes to enable",
325 if let ConstContext::Static(_) = ccx.const_kind() {
326 err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell");
333 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
335 /// Contains the name of the feature that would allow the use of this function.
337 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
339 impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
342 ccx: &ConstCx<'_, 'tcx>,
344 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
345 let FnCallUnstable(def_id, feature) = *self;
350 .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
352 if ccx.is_const_stable_const_fn() {
353 err.help("const-stable functions can only call other const-stable functions");
354 } else if ccx.tcx.sess.is_nightly_build() {
355 if let Some(feature) = feature {
357 "add `#![feature({})]` to the crate attributes to enable",
368 pub struct Generator(pub hir::GeneratorKind);
369 impl<'tcx> NonConstOp<'tcx> for Generator {
370 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
371 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
372 Status::Unstable(sym::const_async_blocks)
380 ccx: &ConstCx<'_, 'tcx>,
382 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
383 let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
384 if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
385 ccx.tcx.sess.create_feature_err(
386 errors::UnallowedOpInConstContext { span, msg },
387 sym::const_async_blocks,
390 ccx.tcx.sess.create_err(errors::UnallowedOpInConstContext { span, msg })
396 pub struct HeapAllocation;
397 impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
400 ccx: &ConstCx<'_, 'tcx>,
402 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
403 ccx.tcx.sess.create_err(errors::UnallowedHeapAllocations {
405 kind: ccx.const_kind(),
406 teach: ccx.tcx.sess.teach(&error_code!(E0010)).then_some(()),
412 pub struct InlineAsm;
413 impl<'tcx> NonConstOp<'tcx> for InlineAsm {
416 ccx: &ConstCx<'_, 'tcx>,
418 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
419 ccx.tcx.sess.create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
424 pub struct LiveDrop<'tcx> {
425 pub dropped_at: Option<Span>,
426 pub dropped_ty: Ty<'tcx>,
428 impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
431 ccx: &ConstCx<'_, 'tcx>,
433 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
434 let mut err = struct_span_err!(
438 "destructor of `{}` cannot be evaluated at compile-time",
443 format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()),
445 if let Some(span) = self.dropped_at {
446 err.span_label(span, "value is dropped here");
453 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
454 /// the final value of the constant.
455 pub struct TransientCellBorrow;
456 impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
457 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
458 Status::Unstable(sym::const_refs_to_cell)
460 fn importance(&self) -> DiagnosticImportance {
461 // The cases that cannot possibly work will already emit a `CellBorrow`, so we should
462 // not additionally emit a feature gate error if activating the feature gate won't work.
463 DiagnosticImportance::Secondary
467 ccx: &ConstCx<'_, 'tcx>,
469 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
472 .create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
477 /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
478 /// the final value of the constant, and thus we cannot allow this (for now). We may allow
479 /// it in the future for static items.
480 pub struct CellBorrow;
481 impl<'tcx> NonConstOp<'tcx> for CellBorrow {
484 ccx: &ConstCx<'_, 'tcx>,
486 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
487 // FIXME: Maybe a more elegant solution to this if else case
488 if let hir::ConstContext::Static(_) = ccx.const_kind() {
489 ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
492 kind: ccx.const_kind(),
493 teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
496 ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
499 kind: ccx.const_kind(),
500 teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
507 /// This op is for `&mut` borrows in the trailing expression of a constant
508 /// which uses the "enclosing scopes rule" to leak its locals into anonymous
509 /// static or const items.
510 pub struct MutBorrow(pub hir::BorrowKind);
512 impl<'tcx> NonConstOp<'tcx> for MutBorrow {
513 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
517 fn importance(&self) -> DiagnosticImportance {
518 // If there were primary errors (like non-const function calls), do not emit further
519 // errors about mutable references.
520 DiagnosticImportance::Secondary
525 ccx: &ConstCx<'_, 'tcx>,
527 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
529 hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRefsRaw {
531 kind: ccx.const_kind(),
532 teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
534 hir::BorrowKind::Ref => ccx.tcx.sess.create_err(errors::UnallowedMutableRefs {
536 kind: ccx.const_kind(),
537 teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
544 pub struct TransientMutBorrow(pub hir::BorrowKind);
546 impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
547 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
548 Status::Unstable(sym::const_mut_refs)
553 ccx: &ConstCx<'_, 'tcx>,
555 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
556 let kind = ccx.const_kind();
558 hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
559 errors::TransientMutBorrowErrRaw { span, kind },
562 hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
563 errors::TransientMutBorrowErr { span, kind },
572 impl<'tcx> NonConstOp<'tcx> for MutDeref {
573 fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
574 Status::Unstable(sym::const_mut_refs)
577 fn importance(&self) -> DiagnosticImportance {
578 // Usually a side-effect of a `TransientMutBorrow` somewhere.
579 DiagnosticImportance::Secondary
584 ccx: &ConstCx<'_, 'tcx>,
586 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
587 ccx.tcx.sess.create_feature_err(
588 errors::MutDerefErr { span, kind: ccx.const_kind() },
594 /// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
596 pub struct PanicNonStr;
597 impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
600 ccx: &ConstCx<'_, 'tcx>,
602 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
603 ccx.tcx.sess.create_err(errors::PanicNonStrErr { span })
607 /// Comparing raw pointers for equality.
608 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
609 /// allocation base addresses that are not known at compile-time.
611 pub struct RawPtrComparison;
612 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
615 _: &ConstCx<'_, 'tcx>,
617 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
618 span_bug!(span, "raw ptr comparison should already be caught in the trait system");
623 pub struct RawMutPtrDeref;
624 impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
625 fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
626 Status::Unstable(sym::const_mut_refs)
631 ccx: &ConstCx<'_, 'tcx>,
633 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
635 &ccx.tcx.sess.parse_sess,
638 &format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
643 /// Casting raw pointer or function pointer to an integer.
644 /// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
645 /// allocation base addresses that are not known at compile-time.
647 pub struct RawPtrToIntCast;
648 impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
651 ccx: &ConstCx<'_, 'tcx>,
653 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
654 ccx.tcx.sess.create_err(errors::RawPtrToIntErr { span })
658 /// An access to a (non-thread-local) `static`.
660 pub struct StaticAccess;
661 impl<'tcx> NonConstOp<'tcx> for StaticAccess {
662 fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
663 if let hir::ConstContext::Static(_) = ccx.const_kind() {
672 ccx: &ConstCx<'_, 'tcx>,
674 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
675 ccx.tcx.sess.create_err(errors::StaticAccessErr {
677 kind: ccx.const_kind(),
678 teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()),
683 /// An access to a thread-local `static`.
685 pub struct ThreadLocalAccess;
686 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
689 ccx: &ConstCx<'_, 'tcx>,
691 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
692 ccx.tcx.sess.create_err(errors::NonConstOpErr { span })
696 /// Types that cannot appear in the signature or locals of a `const fn`.
701 pub struct MutRef(pub mir::LocalKind);
702 impl<'tcx> NonConstOp<'tcx> for MutRef {
703 fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
704 Status::Unstable(sym::const_mut_refs)
707 fn importance(&self) -> DiagnosticImportance {
709 mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
710 mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
711 DiagnosticImportance::Primary
718 ccx: &ConstCx<'_, 'tcx>,
720 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
722 &ccx.tcx.sess.parse_sess,
725 &format!("mutable references are not allowed in {}s", ccx.const_kind()),