about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-26 06:44:28 +0000
committerbors <bors@rust-lang.org>2020-09-26 06:44:28 +0000
commitfd15e6180d9c48b4f1157e44cdaff6e901e5f854 (patch)
tree11801a40b3c4b0ccc7c10b589347b28348705871 /compiler
parent9e1c4361780e69ed54444a3b03fef0cbbc26b547 (diff)
parentdaf976f6129e5fb16effc48bf91853548774e235 (diff)
downloadrust-fd15e6180d9c48b4f1157e44cdaff6e901e5f854.tar.gz
rust-fd15e6180d9c48b4f1157e44cdaff6e901e5f854.zip
Auto merge of #70743 - oli-obk:eager_const_to_pat_conversion, r=eddyb
Fully destructure constants into patterns

r? `@varkor`

as discussed in https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/constants.20in.20patterns/near/192789924

we should probably crater it once reviewed
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_mir/src/const_eval/mod.rs41
-rw-r--r--compiler/rustc_mir/src/lib.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs450
-rw-r--r--compiler/rustc_session/src/lint/builtin.rs108
5 files changed, 482 insertions, 129 deletions
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 9c047cbfaef..784d6c3b2ce 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -742,6 +742,14 @@ rustc_queries! {
             desc { "destructure constant" }
         }
 
+        /// Dereference a constant reference or raw pointer and turn the result into a constant
+        /// again.
+        query deref_const(
+            key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
+        ) -> &'tcx ty::Const<'tcx> {
+            desc { "deref constant" }
+        }
+
         query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
             desc { "get a &core::panic::Location referring to a span" }
         }
diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs
index c93feb5096b..978d2fe0004 100644
--- a/compiler/rustc_mir/src/const_eval/mod.rs
+++ b/compiler/rustc_mir/src/const_eval/mod.rs
@@ -2,11 +2,14 @@
 
 use std::convert::TryFrom;
 
+use rustc_hir::Mutability;
 use rustc_middle::mir;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
 
-use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx};
+use crate::interpret::{
+    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
+};
 
 mod error;
 mod eval_queries;
@@ -67,3 +70,39 @@ pub(crate) fn destructure_const<'tcx>(
 
     mir::DestructuredConst { variant, fields }
 }
+
+pub(crate) fn deref_const<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    val: &'tcx ty::Const<'tcx>,
+) -> &'tcx ty::Const<'tcx> {
+    trace!("deref_const: {:?}", val);
+    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+    let op = ecx.const_to_op(val, None).unwrap();
+    let mplace = ecx.deref_operand(op).unwrap();
+    if let Scalar::Ptr(ptr) = mplace.ptr {
+        assert_eq!(
+            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
+            Mutability::Not,
+            "deref_const cannot be used with mutable allocations as \
+            that could allow pattern matching to observe mutable statics",
+        );
+    }
+
+    let ty = match mplace.meta {
+        MemPlaceMeta::None => mplace.layout.ty,
+        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
+        // In case of unsized types, figure out the real type behind.
+        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
+            ty::Str => bug!("there's no sized equivalent of a `str`"),
+            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
+            _ => bug!(
+                "type {} should not have metadata, but had {:?}",
+                mplace.layout.ty,
+                mplace.meta
+            ),
+        },
+    };
+
+    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, mplace.into())), ty })
+}
diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs
index a356ae1709d..c00c6860903 100644
--- a/compiler/rustc_mir/src/lib.rs
+++ b/compiler/rustc_mir/src/lib.rs
@@ -59,4 +59,8 @@ pub fn provide(providers: &mut Providers) {
         let (param_env, value) = param_env_and_value.into_parts();
         const_eval::destructure_const(tcx, param_env, value)
     };
+    providers.deref_const = |tcx, param_env_and_value| {
+        let (param_env, value) = param_env_and_value.into_parts();
+        const_eval::deref_const(tcx, param_env, value)
+    };
 }
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 2816bad7eab..a203b3a1428 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
@@ -3,7 +3,7 @@ use rustc_index::vec::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::Field;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::Span;
 use rustc_trait_selection::traits::predicate_for_trait_def;
@@ -28,10 +28,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
         debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
 
-        self.tcx.infer_ctxt().enter(|infcx| {
+        let pat = self.tcx.infer_ctxt().enter(|infcx| {
             let mut convert = ConstToPat::new(self, id, span, infcx);
             convert.to_pat(cv, mir_structural_match_violation)
-        })
+        });
+
+        debug!("const_to_pat: pat={:?}", pat);
+        pat
     }
 }
 
@@ -40,17 +43,44 @@ struct ConstToPat<'a, 'tcx> {
     span: Span,
     param_env: ty::ParamEnv<'tcx>,
 
-    // This tracks if we signal some hard error for a given const value, so that
+    // This tracks if we emitted some hard error for a given const value, so that
     // we will not subsequently issue an irrelevant lint for the same const
     // value.
     saw_const_match_error: Cell<bool>,
 
+    // This tracks if we emitted some diagnostic for a given const value, so that
+    // we will not subsequently issue an irrelevant lint for the same const
+    // value.
+    saw_const_match_lint: Cell<bool>,
+
+    // For backcompat we need to keep allowing non-structurally-eq types behind references.
+    // See also all the `cant-hide-behind` tests.
+    behind_reference: Cell<bool>,
+
     // inference context used for checking `T: Structural` bounds.
     infcx: InferCtxt<'a, 'tcx>,
 
     include_lint_checks: bool,
 }
 
+mod fallback_to_const_ref {
+    #[derive(Debug)]
+    /// This error type signals that we encountered a non-struct-eq situation behind a reference.
+    /// We bubble this up in order to get back to the reference destructuring and make that emit
+    /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
+    /// on such patterns (since that function takes a reference) and not have to jump through any
+    /// hoops to get a reference to the value.
+    pub(super) struct FallbackToConstRef(());
+
+    pub(super) fn fallback_to_const_ref<'a, 'tcx>(
+        c2p: &super::ConstToPat<'a, 'tcx>,
+    ) -> FallbackToConstRef {
+        assert!(c2p.behind_reference.get());
+        FallbackToConstRef(())
+    }
+}
+use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
+
 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
     fn new(
         pat_ctxt: &PatCtxt<'_, 'tcx>,
@@ -65,6 +95,8 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
             param_env: pat_ctxt.param_env,
             include_lint_checks: pat_ctxt.include_lint_checks,
             saw_const_match_error: Cell::new(false),
+            saw_const_match_lint: Cell::new(false),
+            behind_reference: Cell::new(false),
         }
     }
 
@@ -72,11 +104,44 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         self.infcx.tcx
     }
 
-    fn search_for_structural_match_violation(
-        &self,
-        ty: Ty<'tcx>,
-    ) -> Option<traits::NonStructuralMatchTy<'tcx>> {
-        traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
+    fn adt_derive_msg(&self, adt_def: &AdtDef) -> String {
+        let path = self.tcx().def_path_str(adt_def.did);
+        format!(
+            "to use a constant of type `{}` in a pattern, \
+            `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+            path, path,
+        )
+    }
+
+    fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
+        traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty).map(
+            |non_sm_ty| {
+                with_no_trimmed_paths(|| match non_sm_ty {
+                    traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt),
+                    traits::NonStructuralMatchTy::Dynamic => {
+                        "trait objects cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTy::Opaque => {
+                        "opaque types cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTy::Generator => {
+                        "generators cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTy::Closure => {
+                        "closures cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTy::Param => {
+                        bug!("use of a constant whose type is a parameter inside a pattern")
+                    }
+                    traits::NonStructuralMatchTy::Projection => {
+                        bug!("use of a constant whose type is a projection inside a pattern")
+                    }
+                    traits::NonStructuralMatchTy::Foreign => {
+                        bug!("use of a value of a foreign type inside a pattern")
+                    }
+                })
+            },
+        )
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
@@ -95,7 +160,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         // once indirect_structural_match is a full fledged error, this
         // level of indirection can be eliminated
 
-        let inlined_const_as_pat = self.recur(cv);
+        let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
 
         if self.include_lint_checks && !self.saw_const_match_error.get() {
             // If we were able to successfully convert the const to some pat,
@@ -118,70 +183,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 return inlined_const_as_pat;
             }
 
-            if let Some(non_sm_ty) = structural {
-                let msg = with_no_trimmed_paths(|| match non_sm_ty {
-                    traits::NonStructuralMatchTy::Adt(adt_def) => {
-                        let path = self.tcx().def_path_str(adt_def.did);
-                        format!(
-                            "to use a constant of type `{}` in a pattern, \
-                             `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                            path, path,
-                        )
-                    }
-                    traits::NonStructuralMatchTy::Dynamic => {
-                        "trait objects cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTy::Opaque => {
-                        "opaque types cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTy::Generator => {
-                        "generators cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTy::Closure => {
-                        "closures cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTy::Param => {
-                        bug!("use of a constant whose type is a parameter inside a pattern")
-                    }
-                    traits::NonStructuralMatchTy::Projection => {
-                        bug!("use of a constant whose type is a projection inside a pattern")
-                    }
-                    traits::NonStructuralMatchTy::Foreign => {
-                        bug!("use of a value of a foreign type inside a pattern")
-                    }
-                });
-
-                // 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.)
-                //
-                // Note: To fix rust-lang/rust#65466, one could lift this check
-                // *before* any structural-match checking, and unconditionally error
-                // if `PartialEq` is not implemented. However, that breaks stable
-                // code at the moment, because types like `for <'a> fn(&'a ())` do
-                // not *yet* implement `PartialEq`. So for now we leave this here.
-                let ty_is_partial_eq: bool = {
-                    let partial_eq_trait_id =
-                        self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
-                    let obligation: PredicateObligation<'_> = predicate_for_trait_def(
-                        self.tcx(),
-                        self.param_env,
-                        ObligationCause::misc(self.span, self.id),
-                        partial_eq_trait_id,
-                        0,
-                        cv.ty,
-                        &[],
-                    );
-                    // FIXME: should this call a `predicate_must_hold` variant instead?
-                    self.infcx.predicate_may_hold(&obligation)
-                };
-
-                if !ty_is_partial_eq {
+            if let Some(msg) = structural {
+                if !self.type_may_have_partial_eq_impl(cv.ty) {
                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
                     self.tcx().sess.span_fatal(self.span, &msg);
-                } else if mir_structural_match_violation {
+                } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
                     self.tcx().struct_span_lint_hir(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         self.id,
@@ -200,19 +206,57 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         inlined_const_as_pat
     }
 
+    fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
+        // 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 obligation: PredicateObligation<'_> = predicate_for_trait_def(
+            self.tcx(),
+            self.param_env,
+            ObligationCause::misc(self.span, self.id),
+            partial_eq_trait_id,
+            0,
+            ty,
+            &[],
+        );
+        // FIXME: should this call a `predicate_must_hold` variant instead?
+
+        let has_impl = self.infcx.predicate_may_hold(&obligation);
+
+        // Note: To fix rust-lang/rust#65466, we could just remove this type
+        // walk hack for function pointers, and unconditionally error
+        // if `PartialEq` is not implemented. However, that breaks stable
+        // code at the moment, because types like `for <'a> fn(&'a ())` do
+        // not *yet* implement `PartialEq`. So for now we leave this here.
+        has_impl
+            || ty.walk().any(|t| match t.unpack() {
+                ty::subst::GenericArgKind::Lifetime(_) => false,
+                ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
+                ty::subst::GenericArgKind::Const(_) => false,
+            })
+    }
+
     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
-    fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
+    fn recur(
+        &self,
+        cv: &'tcx ty::Const<'tcx>,
+        mir_structural_match_violation: bool,
+    ) -> Result<Pat<'tcx>, FallbackToConstRef> {
         let id = self.id;
         let span = self.span;
         let tcx = self.tcx();
         let param_env = self.param_env;
 
-        let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| {
+        let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| -> Result<_, _> {
             vals.iter()
                 .enumerate()
                 .map(|(idx, val)| {
                     let field = Field::new(idx);
-                    FieldPat { field, pattern: self.recur(val) }
+                    Ok(FieldPat { field, pattern: self.recur(val, false)? })
                 })
                 .collect()
         };
@@ -230,34 +274,65 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
             ty::Adt(adt_def, _) if adt_def.is_union() => {
                 // Matching on union fields is unsafe, we can't hide it in constants
                 self.saw_const_match_error.set(true);
-                tcx.sess.span_err(span, "cannot use unions in constant patterns");
+                let msg = "cannot use unions in constant patterns";
+                if self.include_lint_checks {
+                    tcx.sess.span_err(span, msg);
+                } else {
+                    tcx.sess.delay_span_bug(span, msg)
+                }
                 PatKind::Wild
             }
-            // keep old code until future-compat upgraded to errors.
-            ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
-                debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
-                let path = tcx.def_path_str(adt_def.did);
-                let msg = format!(
-                    "to use a constant of type `{}` in a pattern, \
-                     `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                    path, path,
-                );
+            ty::Adt(..)
+                if !self.type_may_have_partial_eq_impl(cv.ty)
+                    // FIXME(#73448): Find a way to bring const qualification into parity with
+                    // `search_for_structural_match_violation` and then remove this condition.
+                    && self.search_for_structural_match_violation(cv.ty).is_some() =>
+            {
+                // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
+                // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
+                let msg = self.search_for_structural_match_violation(cv.ty).unwrap();
                 self.saw_const_match_error.set(true);
-                tcx.sess.span_err(span, &msg);
+                if self.include_lint_checks {
+                    tcx.sess.span_err(self.span, &msg);
+                } else {
+                    tcx.sess.delay_span_bug(self.span, &msg)
+                }
                 PatKind::Wild
             }
-            // keep old code until future-compat upgraded to errors.
-            ty::Ref(_, adt_ty, _) if adt_ty.is_adt() && !self.type_marked_structural(adt_ty) => {
-                let adt_def =
-                    if let ty::Adt(adt_def, _) = adt_ty.kind() { adt_def } else { unreachable!() };
-
-                debug!(
-                    "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
-                    adt_def, adt_ty
-                );
-
-                // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
-                // would be wrong. Returnging `PatKind::Wild` is not technically correct.
+            // If the type is not structurally comparable, just emit the constant directly,
+            // causing the pattern match code to treat it opaquely.
+            // FIXME: This code doesn't emit errors itself, the caller emits the errors.
+            // So instead of specific errors, you just get blanket errors about the whole
+            // const type. See
+            // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
+            // details.
+            // Backwards compatibility hack because we can't cause hard errors on these
+            // types, so we compare them via `PartialEq::eq` at runtime.
+            ty::Adt(..) if !self.type_marked_structural(cv.ty) && self.behind_reference.get() => {
+                if self.include_lint_checks
+                    && !self.saw_const_match_error.get()
+                    && !self.saw_const_match_lint.get()
+                {
+                    self.saw_const_match_lint.set(true);
+                    let msg = format!(
+                        "to use a constant of type `{}` in a pattern, \
+                        `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
+                        cv.ty, cv.ty,
+                    );
+                    tcx.struct_span_lint_hir(
+                        lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+                        id,
+                        span,
+                        |lint| lint.build(&msg).emit(),
+                    );
+                }
+                // Since we are behind a reference, we can just bubble the error up so we get a
+                // constant at reference type, making it easy to let the fallback call
+                // `PartialEq::eq` on it.
+                return Err(fallback_to_const_ref(self));
+            }
+            ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
+                debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
                 let path = tcx.def_path_str(adt_def.did);
                 let msg = format!(
                     "to use a constant of type `{}` in a pattern, \
@@ -265,7 +340,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                     path, path,
                 );
                 self.saw_const_match_error.set(true);
-                tcx.sess.span_err(span, &msg);
+                if self.include_lint_checks {
+                    tcx.sess.span_err(span, &msg);
+                } else {
+                    tcx.sess.delay_span_bug(span, &msg)
+                }
                 PatKind::Wild
             }
             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@@ -276,30 +355,181 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                     variant_index: destructured
                         .variant
                         .expect("destructed const of adt without variant id"),
-                    subpatterns: field_pats(destructured.fields),
+                    subpatterns: field_pats(destructured.fields)?,
                 }
             }
-            ty::Adt(_, _) => {
-                let destructured = tcx.destructure_const(param_env.and(cv));
-                PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
-            }
-            ty::Tuple(_) => {
+            ty::Tuple(_) | ty::Adt(_, _) => {
                 let destructured = tcx.destructure_const(param_env.and(cv));
-                PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
+                PatKind::Leaf { subpatterns: field_pats(destructured.fields)? }
             }
             ty::Array(..) => PatKind::Array {
                 prefix: tcx
                     .destructure_const(param_env.and(cv))
                     .fields
                     .iter()
-                    .map(|val| self.recur(val))
-                    .collect(),
+                    .map(|val| self.recur(val, false))
+                    .collect::<Result<_, _>>()?,
                 slice: None,
                 suffix: Vec::new(),
             },
-            _ => PatKind::Constant { value: cv },
+            ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
+                // These are not allowed and will error elsewhere anyway.
+                ty::Dynamic(..) => {
+                    self.saw_const_match_error.set(true);
+                    let msg = format!("`{}` cannot be used in patterns", cv.ty);
+                    if self.include_lint_checks {
+                        tcx.sess.span_err(span, &msg);
+                    } else {
+                        tcx.sess.delay_span_bug(span, &msg)
+                    }
+                    PatKind::Wild
+                }
+                // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
+                // optimization for now.
+                ty::Str => PatKind::Constant { value: cv },
+                ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
+                // `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.
+                // FIXME: clean this up, likely by permitting array patterns when matching on slices
+                ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
+                // Cannot merge this with the catch all branch below, because the `const_deref`
+                // changes the type from slice to array, and slice patterns behave differently from
+                // array patterns.
+                ty::Slice(..) => {
+                    let old = self.behind_reference.replace(true);
+                    let array = tcx.deref_const(self.param_env.and(cv));
+                    let val = PatKind::Deref {
+                        subpattern: Pat {
+                            kind: Box::new(PatKind::Slice {
+                                prefix: tcx
+                                    .destructure_const(param_env.and(array))
+                                    .fields
+                                    .iter()
+                                    .map(|val| self.recur(val, false))
+                                    .collect::<Result<_, _>>()?,
+                                slice: None,
+                                suffix: vec![],
+                            }),
+                            span,
+                            ty: pointee_ty,
+                        },
+                    };
+                    self.behind_reference.set(old);
+                    val
+                }
+                // Backwards compatibility hack: support references to non-structural types.
+                // We'll lower
+                // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
+                // reference. This makes the rest of the matching logic simpler as it doesn't have
+                // to figure out how to get a reference again.
+                ty::Adt(adt_def, _) if !self.type_marked_structural(pointee_ty) => {
+                    if self.behind_reference.get() {
+                        if self.include_lint_checks
+                            && !self.saw_const_match_error.get()
+                            && !self.saw_const_match_lint.get()
+                        {
+                            self.saw_const_match_lint.set(true);
+                            let msg = self.adt_derive_msg(adt_def);
+                            self.tcx().struct_span_lint_hir(
+                                lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+                                self.id,
+                                self.span,
+                                |lint| lint.build(&msg).emit(),
+                            );
+                        }
+                        PatKind::Constant { value: cv }
+                    } else {
+                        if !self.saw_const_match_error.get() {
+                            self.saw_const_match_error.set(true);
+                            let msg = self.adt_derive_msg(adt_def);
+                            if self.include_lint_checks {
+                                tcx.sess.span_err(span, &msg);
+                            } else {
+                                tcx.sess.delay_span_bug(span, &msg)
+                            }
+                        }
+                        PatKind::Wild
+                    }
+                }
+                // All other references are converted into deref patterns and then recursively
+                // convert the dereferenced constant to a pattern that is the sub-pattern of the
+                // deref pattern.
+                _ => {
+                    let old = self.behind_reference.replace(true);
+                    // In case there are structural-match violations somewhere in this subpattern,
+                    // we fall back to a const pattern. If we do not do this, we may end up with
+                    // a !structural-match constant that is not of reference type, which makes it
+                    // very hard to invoke `PartialEq::eq` on it as a fallback.
+                    let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
+                        Ok(subpattern) => PatKind::Deref { subpattern },
+                        Err(_) => PatKind::Constant { value: cv },
+                    };
+                    self.behind_reference.set(old);
+                    val
+                }
+            },
+            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
+                PatKind::Constant { value: cv }
+            }
+            ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
+                PatKind::Constant { value: cv }
+            }
+            // FIXME: these can have very suprising behaviour where optimization levels or other
+            // compilation choices change the runtime behaviour of the match.
+            // See https://github.com/rust-lang/rust/issues/70861 for examples.
+            ty::FnPtr(..) | ty::RawPtr(..) => {
+                if self.include_lint_checks
+                    && !self.saw_const_match_error.get()
+                    && !self.saw_const_match_lint.get()
+                {
+                    self.saw_const_match_lint.set(true);
+                    let msg = "function pointers and unsized pointers in patterns behave \
+                        unpredictably and should not be relied upon. \
+                        See https://github.com/rust-lang/rust/issues/70861 for details.";
+                    tcx.struct_span_lint_hir(
+                        lint::builtin::POINTER_STRUCTURAL_MATCH,
+                        id,
+                        span,
+                        |lint| lint.build(&msg).emit(),
+                    );
+                }
+                PatKind::Constant { value: cv }
+            }
+            _ => {
+                self.saw_const_match_error.set(true);
+                let msg = format!("`{}` cannot be used in patterns", cv.ty);
+                if self.include_lint_checks {
+                    tcx.sess.span_err(span, &msg);
+                } else {
+                    tcx.sess.delay_span_bug(span, &msg)
+                }
+                PatKind::Wild
+            }
         };
 
-        Pat { span, ty: cv.ty, kind: Box::new(kind) }
+        if self.include_lint_checks
+            && !self.saw_const_match_error.get()
+            && !self.saw_const_match_lint.get()
+            && mir_structural_match_violation
+            // FIXME(#73448): Find a way to bring const qualification into parity with
+            // `search_for_structural_match_violation` and then remove this condition.
+            && self.search_for_structural_match_violation(cv.ty).is_some()
+        {
+            self.saw_const_match_lint.set(true);
+            // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
+            // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
+            let msg = self.search_for_structural_match_violation(cv.ty).unwrap().replace(
+                "in a pattern,",
+                "in a pattern, the constant's initializer must be trivial or",
+            );
+            tcx.struct_span_lint_hir(
+                lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
+                id,
+                span,
+                |lint| lint.build(&msg).emit(),
+            );
+        }
+
+        Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) })
     }
 }
diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs
index 562df176b14..13a4057a35b 100644
--- a/compiler/rustc_session/src/lint/builtin.rs
+++ b/compiler/rustc_session/src/lint/builtin.rs
@@ -2138,22 +2138,16 @@ declare_lint! {
     /// ```rust,compile_fail
     /// #![deny(indirect_structural_match)]
     ///
-    /// struct Plus(i32, i32);
-    /// const ONE_PLUS_TWO: &&Plus = &&Plus(1, 2);
-    ///
-    /// impl PartialEq for Plus {
-    ///     fn eq(&self, other: &Self) -> bool {
-    ///         self.0 + self.1 == other.0 + other.1
-    ///     }
-    /// }
-    ///
-    /// impl Eq for Plus {}
-    ///
+    /// struct NoDerive(i32);
+    /// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+    /// impl Eq for NoDerive { }
+    /// #[derive(PartialEq, Eq)]
+    /// struct WrapParam<T>(T);
+    /// const WRAP_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &WrapParam(NoDerive(0));
     /// fn main() {
-    ///     if let ONE_PLUS_TWO = &&Plus(3, 0) {
-    ///         println!("semantic!");
-    ///     } else {
-    ///         println!("structural!");
+    ///     match WRAP_INDIRECT_PARAM {
+    ///         WRAP_INDIRECT_PARAM => { }
+    ///         _ => { }
     ///     }
     /// }
     /// ```
@@ -2170,9 +2164,8 @@ declare_lint! {
     /// [issue #62411]: https://github.com/rust-lang/rust/issues/62411
     /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub INDIRECT_STRUCTURAL_MATCH,
-    // defaulting to allow until rust-lang/rust#62614 is fixed.
-    Allow,
-    "pattern with const indirectly referencing non-structural-match type",
+    Warn,
+    "constant used in pattern contains value of non-structural-match type in a field or a variant",
     @future_incompatible = FutureIncompatibleInfo {
         reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
         edition: None,
@@ -2198,6 +2191,83 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `pointer_structural_match` lint detects pointers used in patterns whose behaviour
+    /// cannot be relied upon across compiler versions and optimization levels.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(pointer_structural_match)]
+    /// fn foo(a: usize, b: usize) -> usize { a + b }
+    /// const FOO: fn(usize, usize) -> usize = foo;
+    /// fn main() {
+    ///     match FOO {
+    ///         FOO => {},
+    ///         _ => {},
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Previous versions of Rust allowed function pointers and wide raw pointers in patterns.
+    /// While these work in many cases as expected by users, it is possible that due to
+    /// optimizations pointers are "not equal to themselves" or pointers to different functions
+    /// compare as equal during runtime. This is because LLVM optimizations can deduplicate
+    /// functions if their bodies are the same, thus also making pointers to these functions point
+    /// to the same location. Additionally functions may get duplicated if they are instantiated
+    /// in different crates and not deduplicated again via LTO.
+    pub POINTER_STRUCTURAL_MATCH,
+    Allow,
+    "pointers are not structural-match",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #62411 <https://github.com/rust-lang/rust/issues/70861>",
+        edition: None,
+    };
+}
+
+declare_lint! {
+    /// The `nontrivial_structural_match` lint detects constants that are used in patterns,
+    /// whose type is not structural-match and whose initializer body actually uses values
+    /// that are not structural-match. So `Option<NotStruturalMatch>` is ok if the constant
+    /// is just `None`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(nontrivial_structural_match)]
+    ///
+    /// #[derive(Copy, Clone, Debug)]
+    /// struct NoDerive(u32);
+    /// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
+    /// impl Eq for NoDerive { }
+    /// fn main() {
+    ///     const INDEX: Option<NoDerive> = [None, Some(NoDerive(10))][0];
+    ///     match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), };
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Previous versions of Rust accepted constants in patterns, even if those constants's types
+    /// did not have `PartialEq` derived. Thus the compiler falls back to runtime execution of
+    /// `PartialEq`, which can report that two constants are not equal even if they are
+    /// bit-equivalent.
+    pub NONTRIVIAL_STRUCTURAL_MATCH,
+    Warn,
+    "constant used in pattern of non-structural-match type and the constant's initializer \
+    expression contains values of non-structural-match types",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #73448 <https://github.com/rust-lang/rust/issues/73448>",
+        edition: None,
+    };
+}
+
+declare_lint! {
     /// The `ambiguous_associated_items` lint detects ambiguity between
     /// [associated items] and [enum variants].
     ///
@@ -2630,6 +2700,8 @@ declare_lint_pass! {
         AMBIGUOUS_ASSOCIATED_ITEMS,
         MUTABLE_BORROW_RESERVATION_CONFLICT,
         INDIRECT_STRUCTURAL_MATCH,
+        POINTER_STRUCTURAL_MATCH,
+        NONTRIVIAL_STRUCTURAL_MATCH,
         SOFT_UNSTABLE,
         INLINE_NO_SANITIZE,
         ASM_SUB_REGISTER,