about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-01-10 03:55:20 -0500
committerGitHub <noreply@github.com>2025-01-10 03:55:20 -0500
commit9e24b6ba8ec976b1eeb3b10aa08af853ecae8ef0 (patch)
treeefc39beecf4a21851457b27b5c03e4aa91734a10
parent34fa27b0bd4d689051ff0dd01bc5374ef6f4b813 (diff)
parent924000d70ebb5f0de849447fd21127e34f8a8ba1 (diff)
downloadrust-9e24b6ba8ec976b1eeb3b10aa08af853ecae8ef0.tar.gz
rust-9e24b6ba8ec976b1eeb3b10aa08af853ecae8ef0.zip
Rollup merge of #134732 - compiler-errors:unify-conditional-const-error-reporting, r=RalfJung
Unify conditional-const error reporting with non-const error reporting

This PR unifies the error reporting between `ConditionallyConstCall` and `FnCallNonConst` so that the former will refer to syntactical sugar like operators by their sugared name, rather than calling all operators "methods". We achieve this by making the "non-const" part of the error message generic over the "non" part so we can plug in "conditionally" instead.

This should ensure that as we constify traits in the standard library, we don't regress error messages for things like `==`.

r? fmease or reassign
-rw-r--r--compiler/rustc_const_eval/messages.ftl59
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs14
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs423
-rw-r--r--compiler/rustc_const_eval/src/errors.rs39
-rw-r--r--tests/ui/issues/issue-25901.rs2
-rw-r--r--tests/ui/issues/issue-25901.stderr9
-rw-r--r--tests/ui/self/arbitrary-self-from-method-substs-ice.stderr1
-rw-r--r--tests/ui/traits/const-traits/cross-crate.stock.stderr5
-rw-r--r--tests/ui/traits/const-traits/cross-crate.stocknc.stderr10
-rw-r--r--tests/ui/traits/const-traits/staged-api-user-crate.stderr1
-rw-r--r--tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr5
-rw-r--r--tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr5
12 files changed, 325 insertions, 248 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 0c2242b810b..4861b7a4430 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -12,8 +12,6 @@ const_eval_already_reported =
 const_eval_assume_false =
     `assume` called with `false`
 
-const_eval_await_non_const =
-    cannot convert `{$ty}` into a future in {const_eval_const_context}s
 const_eval_bounds_check_failed =
     indexing out of bounds: the len is {$len} but the index is {$index}
 const_eval_call_nonzero_intrinsic =
@@ -23,11 +21,6 @@ const_eval_closure_call =
     closures need an RFC before allowed to be called in {const_eval_const_context}s
 const_eval_closure_fndef_not_const =
     function defined here, but it is not `const`
-const_eval_closure_non_const =
-    cannot call non-const closure in {const_eval_const_context}s
-
-const_eval_conditionally_const_call =
-    cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
 
 const_eval_consider_dereferencing =
     consider dereferencing here
@@ -62,10 +55,6 @@ const_eval_dealloc_incorrect_layout =
 const_eval_dealloc_kind_mismatch =
     deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation
 
-const_eval_deref_coercion_non_const =
-    cannot perform deref coercion on `{$ty}` in {const_eval_const_context}s
-    .note = attempting to deref into `{$target_ty}`
-    .target_note = deref defined here
 const_eval_deref_function_pointer =
     accessing {$allocation} which contains a function
 const_eval_deref_vtable_pointer =
@@ -109,9 +98,6 @@ const_eval_extern_type_field = `extern type` field does not have a known offset
 
 const_eval_fn_ptr_call =
     function pointers need an RFC before allowed to be called in {const_eval_const_context}s
-const_eval_for_loop_into_iter_non_const =
-    cannot use `for` loop on `{$ty}` in {const_eval_const_context}s
-
 const_eval_frame_note = {$times ->
     [0] {const_eval_frame_note_inner}
     *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...]
@@ -216,9 +202,6 @@ const_eval_long_running =
     .label = the const evaluator is currently interpreting this expression
     .help = the constant being evaluated
 
-const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s
-    .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
-
 const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
 
 const_eval_memory_access_test = memory access failed
@@ -249,11 +232,26 @@ const_eval_mutable_ref_escaping =
         If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
 
 const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
+
+const_eval_non_const_await =
+    cannot convert `{$ty}` into a future in {const_eval_const_context}s
+
+const_eval_non_const_closure =
+    cannot call {$non_or_conditionally}-const closure in {const_eval_const_context}s
+
+const_eval_non_const_deref_coercion =
+    cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {const_eval_const_context}s
+    .note = attempting to deref into `{$target_ty}`
+    .target_note = deref defined here
+
 const_eval_non_const_fmt_macro_call =
-    cannot call non-const formatting macro in {const_eval_const_context}s
+    cannot call {$non_or_conditionally}-const formatting macro in {const_eval_const_context}s
 
 const_eval_non_const_fn_call =
-    cannot call non-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
+    cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s
+
+const_eval_non_const_for_loop_into_iter =
+    cannot use `for` loop on `{$ty}` in {const_eval_const_context}s
 
 const_eval_non_const_impl =
     impl defined here, but it is not `const`
@@ -261,6 +259,20 @@ const_eval_non_const_impl =
 const_eval_non_const_intrinsic =
     cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s
 
+const_eval_non_const_match_eq = cannot match on `{$ty}` in {const_eval_const_context}s
+    .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
+
+const_eval_non_const_operator =
+    cannot call {$non_or_conditionally}-const operator in {const_eval_const_context}s
+
+const_eval_non_const_question_branch =
+    `?` is not allowed on `{$ty}` in {const_eval_const_context}s
+const_eval_non_const_question_from_residual =
+    `?` is not allowed on `{$ty}` in {const_eval_const_context}s
+
+const_eval_non_const_try_block_from_output =
+    `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
+
 const_eval_not_enough_caller_args =
     calling a function with fewer arguments than it requires
 
@@ -281,8 +293,6 @@ const_eval_offset_from_unsigned_overflow =
         *[false] offset
     } than second: {$a_offset} < {$b_offset}
 
-const_eval_operator_non_const =
-    cannot call non-const operator in {const_eval_const_context}s
 const_eval_overflow_arith =
     arithmetic overflow in `{$intrinsic}`
 const_eval_overflow_shift =
@@ -325,11 +335,6 @@ const_eval_ptr_as_bytes_1 =
 const_eval_ptr_as_bytes_2 =
     the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
-const_eval_question_branch_non_const =
-    `?` is not allowed on `{$ty}` in {const_eval_const_context}s
-const_eval_question_from_residual_non_const =
-    `?` is not allowed on `{$ty}` in {const_eval_const_context}s
-
 const_eval_range = in the range {$lo}..={$hi}
 const_eval_range_lower = greater or equal to {$lo}
 const_eval_range_singular = equal to {$lo}
@@ -379,8 +384,6 @@ const_eval_too_generic =
 const_eval_too_many_caller_args =
     calling a function with more arguments than it expected
 
-const_eval_try_block_from_output_non_const =
-    `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
 const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
 
 const_eval_unallowed_heap_allocations =
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index e895c44199b..015e1671682 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -708,7 +708,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                     if trait_is_const {
                         // Trait calls are always conditionally-const.
-                        self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
+                        self.check_op(ops::ConditionallyConstCall {
+                            callee,
+                            args: fn_args,
+                            span: *fn_span,
+                            call_source,
+                        });
                         // FIXME(const_trait_impl): do a more fine-grained check whether this
                         // particular trait can be const-stably called.
                     } else {
@@ -726,7 +731,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                 // Even if we know the callee, ensure we can use conditionally-const calls.
                 if has_const_conditions {
-                    self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
+                    self.check_op(ops::ConditionallyConstCall {
+                        callee,
+                        args: fn_args,
+                        span: *fn_span,
+                        call_source,
+                    });
                 }
 
                 // At this point, we are calling a function, `callee`, whose `DefId` is known...
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index ebd680ac28a..6707ebe7d1c 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -15,6 +15,7 @@ use rustc_middle::ty::{
     suggest_constraining_type_param,
 };
 use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind};
+use rustc_session::parse::add_feature_diagnostics;
 use rustc_span::{BytePos, Pos, Span, Symbol, sym};
 use rustc_trait_selection::traits::SelectionContext;
 use tracing::debug;
@@ -77,6 +78,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
 pub(crate) struct ConditionallyConstCall<'tcx> {
     pub callee: DefId,
     pub args: GenericArgsRef<'tcx>,
+    pub span: Span,
+    pub call_source: CallSource,
 }
 
 impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
@@ -91,16 +94,22 @@ impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
         }
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
-        ccx.tcx.sess.create_feature_err(
-            errors::ConditionallyConstCall {
-                span,
-                def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args),
-                def_descr: ccx.tcx.def_descr(self.callee),
-                kind: ccx.const_kind(),
-            },
-            sym::const_trait_impl,
-        )
+    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
+        let mut diag = build_error_for_const_call(
+            ccx,
+            self.callee,
+            self.args,
+            self.span,
+            self.call_source,
+            "conditionally",
+            |_, _, _| {},
+        );
+
+        // Override code and mention feature.
+        diag.code(E0658);
+        add_feature_diagnostics(&mut diag, ccx.tcx.sess, sym::const_trait_impl);
+
+        diag
     }
 }
 
@@ -118,210 +127,250 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[allow(rustc::untranslatable_diagnostic)]
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
-        let FnCallNonConst { callee, args, span, call_source } = *self;
-        let ConstCx { tcx, typing_env, .. } = *ccx;
+        let tcx = ccx.tcx;
         let caller = ccx.def_id();
 
-        let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
-            let trait_ref = TraitRef::from_method(tcx, trait_id, args);
-
-            match self_ty.kind() {
-                Param(param_ty) => {
-                    debug!(?param_ty);
-                    if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
-                        let constraint = with_no_trimmed_paths!(format!(
-                            "~const {}",
-                            trait_ref.print_trait_sugared(),
-                        ));
-                        suggest_constraining_type_param(
-                            tcx,
-                            generics,
-                            err,
-                            param_ty.name.as_str(),
-                            &constraint,
-                            Some(trait_ref.def_id),
-                            None,
-                        );
+        let mut err = build_error_for_const_call(
+            ccx,
+            self.callee,
+            self.args,
+            self.span,
+            self.call_source,
+            "non",
+            |err, self_ty, trait_id| {
+                // FIXME(const_trait_impl): Do we need any of this on the non-const codepath?
+
+                let trait_ref = TraitRef::from_method(tcx, trait_id, self.args);
+
+                match self_ty.kind() {
+                    Param(param_ty) => {
+                        debug!(?param_ty);
+                        if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() {
+                            let constraint = with_no_trimmed_paths!(format!(
+                                "~const {}",
+                                trait_ref.print_trait_sugared(),
+                            ));
+                            suggest_constraining_type_param(
+                                tcx,
+                                generics,
+                                err,
+                                param_ty.name.as_str(),
+                                &constraint,
+                                Some(trait_ref.def_id),
+                                None,
+                            );
+                        }
                     }
-                }
-                ty::Adt(..) => {
-                    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
-                    let obligation =
-                        Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
-                    let mut selcx = SelectionContext::new(&infcx);
-                    let implsrc = selcx.select(&obligation);
-                    if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
-                        // FIXME(const_trait_impl) revisit this
-                        if !tcx.is_const_trait_impl(data.impl_def_id) {
-                            let span = tcx.def_span(data.impl_def_id);
-                            err.subdiagnostic(errors::NonConstImplNote { span });
+                    ty::Adt(..) => {
+                        let (infcx, param_env) =
+                            tcx.infer_ctxt().build_with_typing_env(ccx.typing_env);
+                        let obligation =
+                            Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
+                        let mut selcx = SelectionContext::new(&infcx);
+                        let implsrc = selcx.select(&obligation);
+                        if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
+                            // FIXME(const_trait_impl) revisit this
+                            if !tcx.is_const_trait_impl(data.impl_def_id) {
+                                let span = tcx.def_span(data.impl_def_id);
+                                err.subdiagnostic(errors::NonConstImplNote { span });
+                            }
                         }
                     }
+                    _ => {}
                 }
-                _ => {}
+            },
+        );
+
+        if let ConstContext::Static(_) = ccx.const_kind() {
+            err.note(fluent_generated::const_eval_lazy_lock);
+        }
+
+        err
+    }
+}
+
+/// Build an error message reporting that a function call is not const (or only
+/// conditionally const). In case that this call is desugared (like an operator
+/// or sugar from something like a `for` loop), try to build a better error message
+/// that doesn't call it a method.
+fn build_error_for_const_call<'tcx>(
+    ccx: &ConstCx<'_, 'tcx>,
+    callee: DefId,
+    args: ty::GenericArgsRef<'tcx>,
+    span: Span,
+    call_source: CallSource,
+    non_or_conditionally: &'static str,
+    note_trait_if_possible: impl FnOnce(&mut Diag<'tcx>, Ty<'tcx>, DefId),
+) -> Diag<'tcx> {
+    let tcx = ccx.tcx;
+
+    let call_kind =
+        call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None);
+
+    debug!(?call_kind);
+
+    let mut err = match call_kind {
+        CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
+            macro_rules! error {
+                ($err:ident) => {
+                    tcx.dcx().create_err(errors::$err {
+                        span,
+                        ty: self_ty,
+                        kind: ccx.const_kind(),
+                        non_or_conditionally,
+                    })
+                };
             }
-        };
-
-        let call_kind =
-            call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None);
-
-        debug!(?call_kind);
-
-        let mut err = match call_kind {
-            CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
-                macro_rules! error {
-                    ($err:ident) => {
-                        tcx.dcx().create_err(errors::$err {
-                            span,
-                            ty: self_ty,
-                            kind: ccx.const_kind(),
-                        })
-                    };
-                }
 
-                // Don't point at the trait if this is a desugaring...
-                // FIXME(const_trait_impl): we could perhaps do this for `Iterator`.
-                match kind {
-                    CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => {
-                        error!(NonConstForLoopIntoIter)
-                    }
-                    CallDesugaringKind::QuestionBranch => {
-                        error!(NonConstQuestionBranch)
-                    }
-                    CallDesugaringKind::QuestionFromResidual => {
-                        error!(NonConstQuestionFromResidual)
-                    }
-                    CallDesugaringKind::TryBlockFromOutput => {
-                        error!(NonConstTryBlockFromOutput)
-                    }
-                    CallDesugaringKind::Await => {
-                        error!(NonConstAwait)
-                    }
+            // Don't point at the trait if this is a desugaring...
+            // FIXME(const_trait_impl): we could perhaps do this for `Iterator`.
+            match kind {
+                CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => {
+                    error!(NonConstForLoopIntoIter)
+                }
+                CallDesugaringKind::QuestionBranch => {
+                    error!(NonConstQuestionBranch)
+                }
+                CallDesugaringKind::QuestionFromResidual => {
+                    error!(NonConstQuestionFromResidual)
+                }
+                CallDesugaringKind::TryBlockFromOutput => {
+                    error!(NonConstTryBlockFromOutput)
+                }
+                CallDesugaringKind::Await => {
+                    error!(NonConstAwait)
                 }
             }
-            CallKind::FnCall { fn_trait_id, self_ty } => {
-                let note = match self_ty.kind() {
-                    FnDef(def_id, ..) => {
-                        let span = tcx.def_span(*def_id);
-                        if ccx.tcx.is_const_fn(*def_id) {
-                            span_bug!(span, "calling const FnDef errored when it shouldn't");
-                        }
-
-                        Some(errors::NonConstClosureNote::FnDef { span })
+        }
+        CallKind::FnCall { fn_trait_id, self_ty } => {
+            let note = match self_ty.kind() {
+                FnDef(def_id, ..) => {
+                    let span = tcx.def_span(*def_id);
+                    if ccx.tcx.is_const_fn(*def_id) {
+                        span_bug!(span, "calling const FnDef errored when it shouldn't");
                     }
-                    FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
-                    Closure(..) => Some(errors::NonConstClosureNote::Closure),
-                    _ => None,
-                };
 
-                let mut err = tcx.dcx().create_err(errors::NonConstClosure {
+                    Some(errors::NonConstClosureNote::FnDef { span })
+                }
+                FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
+                Closure(..) => Some(errors::NonConstClosureNote::Closure),
+                _ => None,
+            };
+
+            let mut err = tcx.dcx().create_err(errors::NonConstClosure {
+                span,
+                kind: ccx.const_kind(),
+                note,
+                non_or_conditionally,
+            });
+
+            note_trait_if_possible(&mut err, self_ty, fn_trait_id);
+            err
+        }
+        CallKind::Operator { trait_id, self_ty, .. } => {
+            let mut err = if let CallSource::MatchCmp = call_source {
+                tcx.dcx().create_err(errors::NonConstMatchEq {
                     span,
                     kind: ccx.const_kind(),
-                    note,
-                });
-
-                diag_trait(&mut err, self_ty, fn_trait_id);
-                err
-            }
-            CallKind::Operator { trait_id, self_ty, .. } => {
-                let mut err = if let CallSource::MatchCmp = call_source {
-                    tcx.dcx().create_err(errors::NonConstMatchEq {
-                        span,
-                        kind: ccx.const_kind(),
-                        ty: self_ty,
-                    })
-                } else {
-                    let mut sugg = None;
-
-                    if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
-                        match (args[0].unpack(), args[1].unpack()) {
-                            (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
-                                if self_ty == rhs_ty
-                                    && self_ty.is_ref()
-                                    && self_ty.peel_refs().is_primitive() =>
-                            {
-                                let mut num_refs = 0;
-                                let mut tmp_ty = self_ty;
-                                while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
-                                    num_refs += 1;
-                                    tmp_ty = *inner_ty;
-                                }
-                                let deref = "*".repeat(num_refs);
-
-                                if let Ok(call_str) =
-                                    ccx.tcx.sess.source_map().span_to_snippet(span)
-                                {
-                                    if let Some(eq_idx) = call_str.find("==") {
-                                        if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
-                                            .find(|c: char| !c.is_whitespace())
-                                        {
-                                            let rhs_pos = span.lo()
-                                                + BytePos::from_usize(eq_idx + 2 + rhs_idx);
-                                            let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
-                                            sugg = Some(errors::ConsiderDereferencing {
-                                                deref,
-                                                span: span.shrink_to_lo(),
-                                                rhs_span,
-                                            });
-                                        }
+                    ty: self_ty,
+                    non_or_conditionally,
+                })
+            } else {
+                let mut sugg = None;
+
+                if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
+                    match (args[0].unpack(), args[1].unpack()) {
+                        (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+                            if self_ty == rhs_ty
+                                && self_ty.is_ref()
+                                && self_ty.peel_refs().is_primitive() =>
+                        {
+                            let mut num_refs = 0;
+                            let mut tmp_ty = self_ty;
+                            while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+                                num_refs += 1;
+                                tmp_ty = *inner_ty;
+                            }
+                            let deref = "*".repeat(num_refs);
+
+                            if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
+                                if let Some(eq_idx) = call_str.find("==") {
+                                    if let Some(rhs_idx) =
+                                        call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
+                                    {
+                                        let rhs_pos =
+                                            span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+                                        let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+                                        sugg = Some(errors::ConsiderDereferencing {
+                                            deref,
+                                            span: span.shrink_to_lo(),
+                                            rhs_span,
+                                        });
                                     }
                                 }
                             }
-                            _ => {}
                         }
+                        _ => {}
                     }
-                    tcx.dcx().create_err(errors::NonConstOperator {
-                        span,
-                        kind: ccx.const_kind(),
-                        sugg,
-                    })
-                };
-
-                diag_trait(&mut err, self_ty, trait_id);
-                err
-            }
-            CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
-                // Check first whether the source is accessible (issue #87060)
-                let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
-                    Some(deref_target)
-                } else {
-                    None
-                };
-
-                let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion {
+                }
+                tcx.dcx().create_err(errors::NonConstOperator {
                     span,
-                    ty: self_ty,
                     kind: ccx.const_kind(),
-                    target_ty: deref_target_ty,
-                    deref_target: target,
-                });
+                    sugg,
+                    non_or_conditionally,
+                })
+            };
 
-                diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
-                err
-            }
-            _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
-                ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() })
-            }
-            _ => ccx.dcx().create_err(errors::NonConstFnCall {
+            note_trait_if_possible(&mut err, self_ty, trait_id);
+            err
+        }
+        CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
+            // Check first whether the source is accessible (issue #87060)
+            let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
+                Some(deref_target)
+            } else {
+                None
+            };
+
+            let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion {
                 span,
-                def_descr: ccx.tcx.def_descr(callee),
-                def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
+                ty: self_ty,
                 kind: ccx.const_kind(),
-            }),
-        };
+                target_ty: deref_target_ty,
+                deref_target: target,
+                non_or_conditionally,
+            });
+
+            note_trait_if_possible(
+                &mut err,
+                self_ty,
+                tcx.require_lang_item(LangItem::Deref, Some(span)),
+            );
+            err
+        }
+        _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => {
+            ccx.dcx().create_err(errors::NonConstFmtMacroCall {
+                span,
+                kind: ccx.const_kind(),
+                non_or_conditionally,
+            })
+        }
+        _ => ccx.dcx().create_err(errors::NonConstFnCall {
+            span,
+            def_descr: ccx.tcx.def_descr(callee),
+            def_path_str: ccx.tcx.def_path_str_with_args(callee, args),
+            kind: ccx.const_kind(),
+            non_or_conditionally,
+        }),
+    };
 
-        err.note(format!(
-            "calls in {}s are limited to constant functions, \
+    err.note(format!(
+        "calls in {}s are limited to constant functions, \
              tuple structs and tuple variants",
-            ccx.const_kind(),
-        ));
-
-        if let ConstContext::Static(_) = ccx.const_kind() {
-            err.note(fluent_generated::const_eval_lazy_lock);
-        }
+        ccx.const_kind(),
+    ));
 
-        err
-    }
+    err
 }
 
 /// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function.
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 57534540019..3fe78171cd9 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -174,16 +174,7 @@ pub(crate) struct NonConstFmtMacroCall {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
-}
-
-#[derive(Diagnostic)]
-#[diag(const_eval_conditionally_const_call)]
-pub(crate) struct ConditionallyConstCall {
-    #[primary_span]
-    pub span: Span,
-    pub def_path_str: String,
-    pub def_descr: &'static str,
-    pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
@@ -194,6 +185,7 @@ pub(crate) struct NonConstFnCall {
     pub def_path_str: String,
     pub def_descr: &'static str,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
@@ -293,68 +285,75 @@ pub struct RawBytesNote {
 // FIXME(fee1-dead) do not use stringly typed `ConstContext`
 
 #[derive(Diagnostic)]
-#[diag(const_eval_match_eq_non_const, code = E0015)]
+#[diag(const_eval_non_const_match_eq, code = E0015)]
 #[note]
 pub struct NonConstMatchEq<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_for_loop_into_iter_non_const, code = E0015)]
+#[diag(const_eval_non_const_for_loop_into_iter, code = E0015)]
 pub struct NonConstForLoopIntoIter<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_question_branch_non_const, code = E0015)]
+#[diag(const_eval_non_const_question_branch, code = E0015)]
 pub struct NonConstQuestionBranch<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_question_from_residual_non_const, code = E0015)]
+#[diag(const_eval_non_const_question_from_residual, code = E0015)]
 pub struct NonConstQuestionFromResidual<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_try_block_from_output_non_const, code = E0015)]
+#[diag(const_eval_non_const_try_block_from_output, code = E0015)]
 pub struct NonConstTryBlockFromOutput<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_await_non_const, code = E0015)]
+#[diag(const_eval_non_const_await, code = E0015)]
 pub struct NonConstAwait<'tcx> {
     #[primary_span]
     pub span: Span,
     pub ty: Ty<'tcx>,
     pub kind: ConstContext,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_closure_non_const, code = E0015)]
+#[diag(const_eval_non_const_closure, code = E0015)]
 pub struct NonConstClosure {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
     #[subdiagnostic]
     pub note: Option<NonConstClosureNote>,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Subdiagnostic)]
@@ -381,17 +380,18 @@ pub struct ConsiderDereferencing {
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_operator_non_const, code = E0015)]
+#[diag(const_eval_non_const_operator, code = E0015)]
 pub struct NonConstOperator {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
     #[subdiagnostic]
     pub sugg: Option<ConsiderDereferencing>,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
-#[diag(const_eval_deref_coercion_non_const, code = E0015)]
+#[diag(const_eval_non_const_deref_coercion, code = E0015)]
 #[note]
 pub struct NonConstDerefCoercion<'tcx> {
     #[primary_span]
@@ -401,6 +401,7 @@ pub struct NonConstDerefCoercion<'tcx> {
     pub target_ty: Ty<'tcx>,
     #[note(const_eval_target_note)]
     pub deref_target: Option<Span>,
+    pub non_or_conditionally: &'static str,
 }
 
 #[derive(Diagnostic)]
diff --git a/tests/ui/issues/issue-25901.rs b/tests/ui/issues/issue-25901.rs
index eae038c71a0..bfcee1ac503 100644
--- a/tests/ui/issues/issue-25901.rs
+++ b/tests/ui/issues/issue-25901.rs
@@ -2,7 +2,7 @@ struct A;
 struct B;
 
 static S: &'static B = &A;
-//~^ ERROR cannot call conditionally-const method
+//~^ ERROR cannot perform conditionally-const deref coercion
 
 use std::ops::Deref;
 
diff --git a/tests/ui/issues/issue-25901.stderr b/tests/ui/issues/issue-25901.stderr
index 655a8b78c6a..a954f38af83 100644
--- a/tests/ui/issues/issue-25901.stderr
+++ b/tests/ui/issues/issue-25901.stderr
@@ -1,9 +1,16 @@
-error[E0658]: cannot call conditionally-const method `<A as Deref>::deref` in statics
+error[E0658]: cannot perform conditionally-const deref coercion on `A` in statics
   --> $DIR/issue-25901.rs:4:24
    |
 LL | static S: &'static B = &A;
    |                        ^^
    |
+   = note: attempting to deref into `B`
+note: deref defined here
+  --> $DIR/issue-25901.rs:10:5
+   |
+LL |     type Target = B;
+   |     ^^^^^^^^^^^
+   = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
index cf4c219215e..90b63249eca 100644
--- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
+++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
@@ -4,6 +4,7 @@ error[E0658]: cannot call conditionally-const method `<R as Deref>::deref` in co
 LL |         self.0
    |         ^^^^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/cross-crate.stock.stderr b/tests/ui/traits/const-traits/cross-crate.stock.stderr
index 09bf9c023c8..7cdde5a079f 100644
--- a/tests/ui/traits/const-traits/cross-crate.stock.stderr
+++ b/tests/ui/traits/const-traits/cross-crate.stock.stderr
@@ -1,9 +1,10 @@
 error[E0658]: cannot call conditionally-const method `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
-  --> $DIR/cross-crate.rs:22:5
+  --> $DIR/cross-crate.rs:22:11
    |
 LL |     Const.func();
-   |     ^^^^^^^^^^^^
+   |           ^^^^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr
index e52e5609b01..2358731c901 100644
--- a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr
+++ b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr
@@ -1,19 +1,21 @@
 error[E0658]: cannot call conditionally-const method `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
-  --> $DIR/cross-crate.rs:19:5
+  --> $DIR/cross-crate.rs:19:14
    |
 LL |     NonConst.func();
-   |     ^^^^^^^^^^^^^^^
+   |              ^^^^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: cannot call conditionally-const method `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
-  --> $DIR/cross-crate.rs:22:5
+  --> $DIR/cross-crate.rs:22:11
    |
 LL |     Const.func();
-   |     ^^^^^^^^^^^^
+   |           ^^^^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.stderr b/tests/ui/traits/const-traits/staged-api-user-crate.stderr
index bf7466b8e16..400c76fcaf4 100644
--- a/tests/ui/traits/const-traits/staged-api-user-crate.stderr
+++ b/tests/ui/traits/const-traits/staged-api-user-crate.stderr
@@ -4,6 +4,7 @@ error[E0658]: cannot call conditionally-const associated function `<staged_api::
 LL |     Unstable::func();
    |     ^^^^^^^^^^^^^^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr
index 8abda1c8f8a..024db4b6d68 100644
--- a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr
+++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr
@@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)]
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: cannot call conditionally-const method `<T as Foo>::a` in constant functions
-  --> $DIR/super-traits-fail-3.rs:36:5
+  --> $DIR/super-traits-fail-3.rs:36:7
    |
 LL |     x.a();
-   |     ^^^^^
+   |       ^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr
index 8abda1c8f8a..024db4b6d68 100644
--- a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr
+++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr
@@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)]
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: cannot call conditionally-const method `<T as Foo>::a` in constant functions
-  --> $DIR/super-traits-fail-3.rs:36:5
+  --> $DIR/super-traits-fail-3.rs:36:7
    |
 LL |     x.a();
-   |     ^^^^^
+   |       ^^^
    |
+   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date