about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2024-12-05 07:29:54 +0100
committerGitHub <noreply@github.com>2024-12-05 07:29:54 +0100
commit626db0640952ed4cce66ca3f03bfcb8897df9eda (patch)
treecd46b6561e50cde851aaa408296067a0d31416f3 /compiler
parentbc13c82e6e3ddeec1ca3d72940452ffa865837cd (diff)
parent4e6a401b224342287469a1a294bba7ad10e8641c (diff)
downloadrust-626db0640952ed4cce66ca3f03bfcb8897df9eda.tar.gz
rust-626db0640952ed4cce66ca3f03bfcb8897df9eda.zip
Rollup merge of #133233 - estebank:const-errors, r=Nadrieril
Add context to "const in pattern" errors

*Each commit addresses specific diagnostics.*

- Add primary span labels
- Point at `const` item, and `const` generic param definition
- Reword messages and notes
- Point at generic param through which an associated `const` is being referenced
- Silence const in pattern with evaluation errors when they come from `const` items that already emit a diagnostic
- On non-structural type in const used as pattern, point at the type that should derive `PartialEq`
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_mir_build/messages.ftl37
-rw-r--r--compiler/rustc_mir_build/src/errors.rs30
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs309
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs14
4 files changed, 307 insertions, 83 deletions
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index f28cf84fa69..f647486f62a 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
 
 mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
 
-mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns
+mir_build_const_defined_here = constant defined here
 
-mir_build_const_pattern_depends_on_generic_parameter =
-    constant pattern depends on a generic parameter
+mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
+    .label = can't be used in patterns
+mir_build_const_param_in_pattern_def = constant defined here
+
+mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters
+    .label = `const` depends on a generic parameter
 
 mir_build_could_not_eval_const_pattern = could not evaluate constant pattern
+    .label = could not evaluate constant
 
 mir_build_deref_raw_pointer_requires_unsafe =
     dereference of raw pointer is unsafe and requires unsafe block
@@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
 
 mir_build_interpreted_as_const = introduce a variable instead
 
-mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
+mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns
+    .label = {$prefix} can't be used in patterns
 
 mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count ->
         [one] pattern
@@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
     .label = mutation of layout constrained field
 
 mir_build_nan_pattern = cannot use NaN in patterns
+    .label = evaluates to `NaN`, which is not allowed in patterns
     .note = NaNs compare inequal to everything, even themselves, so this pattern would never match
     .help = try using the `is_nan` method instead
 
 mir_build_non_const_path = runtime values cannot be referenced in patterns
+    .label = references a runtime value
 
 mir_build_non_empty_never_pattern =
     mismatched types
@@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type
     .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
     .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
 
-mir_build_non_partial_eq_match =
-    to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq`
+mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern
+    .label = constant of non-structural type
 
 mir_build_pattern_not_covered = refutable pattern in {$origin}
     .pattern_ty = the matched value is of type `{$pattern_ty}`
 
-mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
+mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon
+    .label = can't be used in patterns
+    .note = see https://github.com/rust-lang/rust/issues/70861 for details
 
 mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
 
@@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
     .missing_box = `#[rustc_box]` requires the `owned_box` lang item
 
 mir_build_static_in_pattern = statics cannot be referenced in patterns
+    .label = can't be used in patterns
+mir_build_static_in_pattern_def = `static` defined here
 
 mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
 
@@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count ->
         *[other] them
     } into the body
 
-mir_build_type_not_structural =
-     to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]`
-
+mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern
+    .label = constant of non-structural type
+mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
 mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
-
-mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient
+mir_build_type_not_structural_tip =
+    the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
 
 mir_build_unconditional_recursion = function cannot return without recursing
     .label = cannot return without recursing
@@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
     .label = access to union field
 
 mir_build_union_pattern = cannot use unions in constant patterns
+    .label = can't use a `union` here
 
 mir_build_unreachable_making_this_unreachable = collectively making this unreachable
 
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 58487a48184..3632da943e1 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -631,20 +631,27 @@ pub(crate) struct NonExhaustiveMatchAllArmsGuarded;
 #[diag(mir_build_static_in_pattern, code = E0158)]
 pub(crate) struct StaticInPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
+    #[label(mir_build_static_in_pattern_def)]
+    pub(crate) static_span: Span,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_const_param_in_pattern, code = E0158)]
 pub(crate) struct ConstParamInPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
+    #[label(mir_build_const_param_in_pattern_def)]
+    pub(crate) const_span: Span,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_non_const_path, code = E0080)]
 pub(crate) struct NonConstPath {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
@@ -695,6 +702,7 @@ pub(crate) struct WantedConstant {
 #[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)]
 pub(crate) struct ConstPatternDependsOnGenericParameter {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
@@ -702,6 +710,7 @@ pub(crate) struct ConstPatternDependsOnGenericParameter {
 #[diag(mir_build_could_not_eval_const_pattern)]
 pub(crate) struct CouldNotEvalConstPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
@@ -867,33 +876,43 @@ pub(crate) enum Conflict {
 #[diag(mir_build_union_pattern)]
 pub(crate) struct UnionPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_type_not_structural)]
-#[note(mir_build_type_not_structural_tip)]
-#[note(mir_build_type_not_structural_more_info)]
 pub(crate) struct TypeNotStructural<'tcx> {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
-    pub(crate) non_sm_ty: Ty<'tcx>,
+    #[label(mir_build_type_not_structural_def)]
+    pub(crate) ty_def_span: Span,
+    pub(crate) ty: Ty<'tcx>,
+    #[note(mir_build_type_not_structural_tip)]
+    pub(crate) manual_partialeq_impl_span: Option<Span>,
+    #[note(mir_build_type_not_structural_more_info)]
+    pub(crate) manual_partialeq_impl_note: bool,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_non_partial_eq_match)]
+#[note(mir_build_type_not_structural_more_info)]
 pub(crate) struct TypeNotPartialEq<'tcx> {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
-    pub(crate) non_peq_ty: Ty<'tcx>,
+    pub(crate) ty: Ty<'tcx>,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_invalid_pattern)]
 pub(crate) struct InvalidPattern<'tcx> {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
     pub(crate) non_sm_ty: Ty<'tcx>,
+    pub(crate) prefix: String,
 }
 
 #[derive(Diagnostic)]
@@ -910,13 +929,16 @@ pub(crate) struct UnsizedPattern<'tcx> {
 #[help]
 pub(crate) struct NaNPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
 #[derive(Diagnostic)]
 #[diag(mir_build_pointer_pattern)]
+#[note]
 pub(crate) struct PointerPattern {
     #[primary_span]
+    #[label]
     pub(crate) span: Span,
 }
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 5db08f01fdb..aed00aecefc 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -1,14 +1,17 @@
 use rustc_abi::{FieldIdx, VariantIdx};
 use rustc_apfloat::Float;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Diag;
 use rustc_hir as hir;
 use rustc_index::Idx;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::Obligation;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::thir::{FieldPat, Pat, PatKind};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree};
 use rustc_middle::{mir, span_bug};
-use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_span::{Span, sym};
 use rustc_trait_selection::traits::ObligationCause;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use tracing::{debug, instrument, trace};
@@ -35,7 +38,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         id: hir::HirId,
         span: Span,
     ) -> Box<Pat<'tcx>> {
-        let mut convert = ConstToPat::new(self, id, span);
+        let mut convert = ConstToPat::new(self, id, span, c);
 
         match c.kind() {
             ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
@@ -49,21 +52,26 @@ struct ConstToPat<'tcx> {
     tcx: TyCtxt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
     span: Span,
+    id: hir::HirId,
 
     treat_byte_string_as_slice: bool,
+
+    c: ty::Const<'tcx>,
 }
 
 impl<'tcx> ConstToPat<'tcx> {
-    fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self {
+    fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
         trace!(?pat_ctxt.typeck_results.hir_owner);
         ConstToPat {
             tcx: pat_ctxt.tcx,
             typing_env: pat_ctxt.typing_env,
             span,
+            id,
             treat_byte_string_as_slice: pat_ctxt
                 .typeck_results
                 .treat_byte_string_as_slice
                 .contains(&id.local_id),
+            c,
         }
     }
 
@@ -71,13 +79,32 @@ impl<'tcx> ConstToPat<'tcx> {
         ty.is_structural_eq_shallow(self.tcx)
     }
 
+    /// We errored. Signal that in the pattern, so that follow up errors can be silenced.
+    fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
+        if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
+            let def_kind = self.tcx.def_kind(uv.def);
+            if let hir::def::DefKind::AssocConst = def_kind
+                && let Some(def_id) = uv.def.as_local()
+            {
+                // Include the container item in the output.
+                err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
+            }
+            if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
+                err.span_label(
+                    self.tcx.def_span(uv.def),
+                    crate::fluent_generated::mir_build_const_defined_here,
+                );
+            }
+        }
+        Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
+    }
+
     fn unevaluated_to_pat(
         &mut self,
         uv: ty::UnevaluatedConst<'tcx>,
         ty: Ty<'tcx>,
     ) -> Box<Pat<'tcx>> {
         trace!(self.treat_byte_string_as_slice);
-        let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind });
 
         // It's not *technically* correct to be revealing opaque types here as borrowcheck has
         // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
@@ -96,32 +123,60 @@ impl<'tcx> ConstToPat<'tcx> {
             Ok(Ok(c)) => c,
             Err(ErrorHandled::Reported(_, _)) => {
                 // Let's tell the use where this failing const occurs.
-                let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span });
-                return pat_from_kind(PatKind::Error(e));
+                let mut err =
+                    self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
+                // We've emitted an error on the original const, it would be redundant to complain
+                // on its use as well.
+                if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
+                    && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
+                        self.tcx.def_kind(uv.def)
+                {
+                    err.downgrade_to_delayed_bug();
+                }
+                return self.mk_err(err, ty);
             }
             Err(ErrorHandled::TooGeneric(_)) => {
-                let e = self
+                let mut e = self
                     .tcx
                     .dcx()
-                    .emit_err(ConstPatternDependsOnGenericParameter { span: self.span });
-                return pat_from_kind(PatKind::Error(e));
+                    .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
+                for arg in uv.args {
+                    if let ty::GenericArgKind::Type(ty) = arg.unpack()
+                        && let ty::Param(param_ty) = ty.kind()
+                    {
+                        let def_id = self.tcx.hir().enclosing_body_owner(self.id);
+                        let generics = self.tcx.generics_of(def_id);
+                        let param = generics.type_param(*param_ty, self.tcx);
+                        let span = self.tcx.def_span(param.def_id);
+                        e.span_label(span, "constant depends on this generic parameter");
+                        if let Some(ident) = self.tcx.def_ident_span(def_id)
+                            && self.tcx.sess.source_map().is_multiline(ident.between(span))
+                        {
+                            // Display the `fn` name as well in the diagnostic, as the generic isn't
+                            // in the same line and it could be confusing otherwise.
+                            e.span_label(ident, "");
+                        }
+                    }
+                }
+                return self.mk_err(e, ty);
             }
             Ok(Err(bad_ty)) => {
                 // The pattern cannot be turned into a valtree.
                 let e = match bad_ty.kind() {
                     ty::Adt(def, ..) => {
                         assert!(def.is_union());
-                        self.tcx.dcx().emit_err(UnionPattern { span: self.span })
+                        self.tcx.dcx().create_err(UnionPattern { span: self.span })
                     }
                     ty::FnPtr(..) | ty::RawPtr(..) => {
-                        self.tcx.dcx().emit_err(PointerPattern { span: self.span })
+                        self.tcx.dcx().create_err(PointerPattern { span: self.span })
                     }
-                    _ => self
-                        .tcx
-                        .dcx()
-                        .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
+                    _ => self.tcx.dcx().create_err(InvalidPattern {
+                        span: self.span,
+                        non_sm_ty: bad_ty,
+                        prefix: bad_ty.prefix_string(self.tcx).to_string(),
+                    }),
                 };
-                return pat_from_kind(PatKind::Error(e));
+                return self.mk_err(e, ty);
             }
         };
 
@@ -130,42 +185,16 @@ impl<'tcx> ConstToPat<'tcx> {
 
         if !inlined_const_as_pat.references_error() {
             // Always check for `PartialEq` if we had no other errors yet.
-            if !self.type_has_partial_eq_impl(ty) {
-                let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty };
-                let e = self.tcx.dcx().emit_err(err);
-                return pat_from_kind(PatKind::Error(e));
+            if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 {
+                let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
+                extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
+                return self.mk_err(err, ty);
             }
         }
 
         inlined_const_as_pat
     }
 
-    #[instrument(level = "trace", skip(self), ret)]
-    fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
-        let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
-        // double-check there even *is* a semantic `PartialEq` to dispatch to.
-        //
-        // (If there isn't, then we can safely issue a hard
-        // error, because that's never worked, due to compiler
-        // using `PartialEq::eq` in this scenario in the past.)
-        let partial_eq_trait_id =
-            self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span));
-        let partial_eq_obligation = Obligation::new(
-            self.tcx,
-            ObligationCause::dummy(),
-            param_env,
-            ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]),
-        );
-
-        // This *could* accept a type that isn't actually `PartialEq`, because region bounds get
-        // ignored. However that should be pretty much impossible since consts that do not depend on
-        // generics can only mention the `'static` lifetime, and how would one have a type that's
-        // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
-        // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
-        // can ensure that the type really implements `PartialEq`.
-        infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation)
-    }
-
     fn field_pats(
         &self,
         vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
@@ -190,10 +219,25 @@ impl<'tcx> ConstToPat<'tcx> {
                 // Extremely important check for all ADTs! Make sure they opted-in to be used in
                 // patterns.
                 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
-                let err = TypeNotStructural { span, non_sm_ty: ty };
-                let e = tcx.dcx().emit_err(err);
-                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
-                PatKind::Error(e)
+                let (_impls_partial_eq, derived, structural, impl_def_id) =
+                    type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
+                let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
+                    match (structural, impl_def_id) {
+                        (true, _) => (None, false),
+                        (_, Some(def_id)) if def_id.is_local() && !derived => {
+                            (Some(tcx.def_span(def_id)), false)
+                        }
+                        _ => (None, true),
+                    };
+                let ty_def_span = tcx.def_span(adt_def.did());
+                let err = TypeNotStructural {
+                    span,
+                    ty,
+                    ty_def_span,
+                    manual_partialeq_impl_span,
+                    manual_partialeq_impl_note,
+                };
+                return self.mk_err(tcx.dcx().create_err(err), ty);
             }
             ty::Adt(adt_def, args) if adt_def.is_enum() => {
                 let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
@@ -207,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> {
                             adt_def.variants()[variant_index]
                                 .fields
                                 .iter()
-                                .map(|field| field.ty(self.tcx, args)),
+                                .map(|field| field.ty(tcx, args)),
                         ),
                     ),
                 }
@@ -216,7 +260,7 @@ impl<'tcx> ConstToPat<'tcx> {
                 assert!(!def.is_union()); // Valtree construction would never succeed for unions.
                 PatKind::Leaf {
                     subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
-                        def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)),
+                        def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
                     )),
                 }
             }
@@ -252,10 +296,10 @@ impl<'tcx> ConstToPat<'tcx> {
                 // deref pattern.
                 _ => {
                     if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
-                        let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
-                        let e = tcx.dcx().emit_err(err);
-                        // We errored. Signal that in the pattern, so that follow up errors can be silenced.
-                        PatKind::Error(e)
+                        return self.mk_err(
+                            tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
+                            ty,
+                        );
                     } else {
                         // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
                         // matching against references, you can only use byte string literals.
@@ -286,8 +330,7 @@ impl<'tcx> ConstToPat<'tcx> {
                 if is_nan {
                     // NaNs are not ever equal to anything so they make no sense as patterns.
                     // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
-                    let e = tcx.dcx().emit_err(NaNPattern { span });
-                    PatKind::Error(e)
+                    return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
                 } else {
                     PatKind::Constant {
                         value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
@@ -305,13 +348,153 @@ impl<'tcx> ConstToPat<'tcx> {
                 )
             }
             _ => {
-                let err = InvalidPattern { span, non_sm_ty: ty };
-                let e = tcx.dcx().emit_err(err);
-                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
-                PatKind::Error(e)
+                let err = InvalidPattern {
+                    span,
+                    non_sm_ty: ty,
+                    prefix: ty.prefix_string(tcx).to_string(),
+                };
+                return self.mk_err(tcx.dcx().create_err(err), ty);
             }
         };
 
         Box::new(Pat { span, ty, kind })
     }
 }
+
+/// Given a type with type parameters, visit every ADT looking for types that need to
+/// `#[derive(PartialEq)]` for it to be a structural type.
+fn extend_type_not_partial_eq<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
+    ty: Ty<'tcx>,
+    err: &mut Diag<'_>,
+) {
+    /// Collect all types that need to be `StructuralPartialEq`.
+    struct UsedParamsNeedInstantiationVisitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        typing_env: ty::TypingEnv<'tcx>,
+        /// The user has written `impl PartialEq for Ty` which means it's non-structual.
+        adts_with_manual_partialeq: FxHashSet<Span>,
+        /// The type has no `PartialEq` implementation, neither manual or derived.
+        adts_without_partialeq: FxHashSet<Span>,
+        /// The user has written `impl PartialEq for Ty` which means it's non-structual,
+        /// but we don't have a span to point at, so we'll just add them as a `note`.
+        manual: Vec<Ty<'tcx>>,
+        /// The type has no `PartialEq` implementation, neither manual or derived, but
+        /// we don't have a span to point at, so we'll just add them as a `note`.
+        without: Vec<Ty<'tcx>>,
+    }
+
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+            if let ty::Adt(def, _args) = ty.kind() {
+                let ty_def_id = def.did();
+                let ty_def_span = self.tcx.def_span(ty_def_id);
+                let (impls_partial_eq, derived, structural, impl_def_id) =
+                    type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
+                match (impls_partial_eq, derived, structural, impl_def_id) {
+                    (_, _, true, _) => {}
+                    (true, false, _, Some(def_id)) if def_id.is_local() => {
+                        self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
+                    }
+                    (true, false, _, _) if ty_def_id.is_local() => {
+                        self.adts_with_manual_partialeq.insert(ty_def_span);
+                    }
+                    (false, _, _, _) if ty_def_id.is_local() => {
+                        self.adts_without_partialeq.insert(ty_def_span);
+                    }
+                    (true, false, _, _) => {
+                        self.manual.push(ty);
+                    }
+                    (false, _, _, _) => {
+                        self.without.push(ty);
+                    }
+                    _ => {}
+                };
+            }
+            use rustc_middle::ty::TypeSuperVisitable;
+            ty.super_visit_with(self)
+        }
+    }
+    let mut v = UsedParamsNeedInstantiationVisitor {
+        tcx,
+        typing_env,
+        adts_with_manual_partialeq: FxHashSet::default(),
+        adts_without_partialeq: FxHashSet::default(),
+        manual: vec![],
+        without: vec![],
+    };
+    v.visit_ty(ty);
+    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
+    for span in v.adts_with_manual_partialeq {
+        err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details");
+    }
+    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
+    for span in v.adts_without_partialeq {
+        err.span_label(
+            span,
+            "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
+        );
+    }
+    for ty in v.manual {
+        err.note(format!(
+            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"
+        ));
+    }
+    for ty in v.without {
+        err.note(format!(
+            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
+        ));
+    }
+}
+
+#[instrument(level = "trace", skip(tcx), ret)]
+fn type_has_partial_eq_impl<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
+    ty: Ty<'tcx>,
+) -> (
+    /* has impl */ bool,
+    /* is derived */ bool,
+    /* structural partial eq */ bool,
+    /* non-blanket impl */ Option<DefId>,
+) {
+    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
+    // double-check there even *is* a semantic `PartialEq` to dispatch to.
+    //
+    // (If there isn't, then we can safely issue a hard
+    // error, because that's never worked, due to compiler
+    // using `PartialEq::eq` in this scenario in the past.)
+    let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None);
+    let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None);
+
+    let partial_eq_obligation = Obligation::new(
+        tcx,
+        ObligationCause::dummy(),
+        param_env,
+        ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
+    );
+
+    let mut automatically_derived = false;
+    let mut structural_peq = false;
+    let mut impl_def_id = None;
+    for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
+        automatically_derived = tcx.has_attr(def_id, sym::automatically_derived);
+        impl_def_id = Some(def_id);
+    }
+    for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
+        structural_peq = true;
+    }
+    // This *could* accept a type that isn't actually `PartialEq`, because region bounds get
+    // ignored. However that should be pretty much impossible since consts that do not depend on
+    // generics can only mention the `'static` lifetime, and how would one have a type that's
+    // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
+    // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
+    // can ensure that the type really implements `PartialEq`.
+    (
+        infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
+        automatically_derived,
+        structural_peq,
+        impl_def_id,
+    )
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 08c6b4abd3b..3ac53fa6272 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -528,11 +528,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
             _ => {
                 let e = match res {
-                    Res::Def(DefKind::ConstParam, _) => {
-                        self.tcx.dcx().emit_err(ConstParamInPattern { span })
+                    Res::Def(DefKind::ConstParam, def_id) => {
+                        self.tcx.dcx().emit_err(ConstParamInPattern {
+                            span,
+                            const_span: self.tcx().def_span(def_id),
+                        })
                     }
-                    Res::Def(DefKind::Static { .. }, _) => {
-                        self.tcx.dcx().emit_err(StaticInPattern { span })
+                    Res::Def(DefKind::Static { .. }, def_id) => {
+                        self.tcx.dcx().emit_err(StaticInPattern {
+                            span,
+                            static_span: self.tcx().def_span(def_id),
+                        })
                     }
                     _ => self.tcx.dcx().emit_err(NonConstPath { span }),
                 };