about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-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--compiler/rustc_feature/src/builtin_attrs.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs10
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs5
-rw-r--r--compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs2
9 files changed, 311 insertions, 245 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/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5421517046d..3c3bbc8c31d 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -572,7 +572,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // `#[coroutine]` attribute to be applied to closures to make them coroutines instead
     gated!(
         coroutine, Normal, template!(Word), ErrorFollowing,
-        EncodeCrossCrate::No, coroutines, experimental!(coroutines)
+        EncodeCrossCrate::No, coroutines, experimental!(coroutine)
     ),
 
     // RFC 3543
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 0c19e2e4c51..1569c0963c8 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -1036,7 +1036,7 @@ pub(super) fn const_conditions<'tcx>(
 
         icx.lowerer().lower_bounds(
             tcx.types.self_param,
-            supertraits.into_iter(),
+            supertraits,
             &mut bounds,
             ty::List::empty(),
             PredicateFilter::ConstIfConst,
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 72a1e4af9bf..b3d87ef4ad2 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1096,7 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     )
                 ) {
                     continue;
-                };
+                }
 
                 match self.tcx.hir().get_if_local(item_def_id) {
                     // Unmet obligation comes from a `derive` macro, point at it once to
@@ -1210,8 +1210,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         entry.1.insert((cause_span, "unsatisfied trait bound introduced here"));
                         entry.2.push(p);
                     }
-                    Some(node) => unreachable!("encountered `{node:?}` due to `{cause:#?}`"),
-                    None => (),
+                    _ => {
+                        // It's possible to use well-formedness clauses to get obligations
+                        // which point arbitrary items like ADTs, so there's no use in ICEing
+                        // here if we find that the obligation originates from some other
+                        // node that we don't handle.
+                    }
                 }
             }
             let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 4185b3f4d4d..5bd20e00eb6 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -367,9 +367,8 @@ fn calc_test_vectors_index(conditions: &mut Vec<MCDCBranch>) -> usize {
         })
         .collect::<FxIndexMap<_, _>>();
 
-    let mut queue = std::collections::VecDeque::from_iter(
-        next_conditions.swap_remove(&ConditionId::START).into_iter(),
-    );
+    let mut queue =
+        std::collections::VecDeque::from_iter(next_conditions.swap_remove(&ConditionId::START));
     num_paths_stats[ConditionId::START] = 1;
     let mut decision_end_nodes = Vec::new();
     while let Some(branch) = queue.pop_front() {
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index 6590702118c..50d10883d2c 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -691,7 +691,7 @@ impl Subdiagnostic for LocalLabel<'_> {
         for dtor in self.destructors {
             dtor.add_to_diag_with(diag, f);
         }
-        let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue.into());
+        let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue);
         diag.span_label(self.span, msg);
     }
 }