about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-07-13 17:24:50 +0200
committerRalf Jung <post@ralfj.de>2024-07-18 11:58:16 +0200
commite613bc92a12c176d0e206b2f83b1bae8011e90c1 (patch)
treef03b3121c792b941e6382851daf9a032a1ff54b9
parentfa74a9e6aa525a285cfd530cdea2ddeb9fca013c (diff)
downloadrust-e613bc92a12c176d0e206b2f83b1bae8011e90c1.tar.gz
rust-e613bc92a12c176d0e206b2f83b1bae8011e90c1.zip
const_to_pat: cleanup leftovers from when we had to deal with non-structural constants
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs52
-rw-r--r--compiler/rustc_mir_build/Cargo.toml1
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs211
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs122
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs173
-rw-r--r--tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs11
-rw-r--r--tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs2
-rw-r--r--tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr5
-rw-r--r--tests/ui/consts/invalid-inline-const-in-match-arm.rs1
-rw-r--r--tests/ui/consts/invalid-inline-const-in-match-arm.stderr8
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs2
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr5
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs4
-rw-r--r--tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr34
16 files changed, 174 insertions, 460 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2d3bc59dddb..1c1607e4c1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4411,6 +4411,7 @@ dependencies = [
 name = "rustc_mir_build"
 version = "0.0.0"
 dependencies = [
+ "either",
  "itertools",
  "rustc_apfloat",
  "rustc_arena",
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index d4c39a25a1f..4d213d14af1 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,6 +1,7 @@
 use crate::middle::resolve_bound_vars as rbv;
 use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar};
 use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use either::Either;
 use rustc_data_structures::intern::Interned;
 use rustc_error_messages::MultiSpan;
 use rustc_hir as hir;
@@ -312,14 +313,16 @@ impl<'tcx> Const<'tcx> {
         Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize))
     }
 
-    /// Returns the evaluated constant
+    /// Returns the evaluated constant as a valtree;
+    /// if that fails due to a valtree-incompatible type, indicate which type that is
+    /// by returning `Err(Left(bad_type))`.
     #[inline]
-    pub fn eval(
+    pub fn eval_valtree(
         self,
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
         span: Span,
-    ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> {
+    ) -> Result<(Ty<'tcx>, ValTree<'tcx>), Either<Ty<'tcx>, ErrorHandled>> {
         assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
         match self.kind() {
             ConstKind::Unevaluated(unevaluated) => {
@@ -328,26 +331,47 @@ impl<'tcx> Const<'tcx> {
                 let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
                 // try to resolve e.g. associated constants to their definition on an impl, and then
                 // evaluate the const.
-                let Ok(c) = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)? else {
-                    // This can happen when we run on ill-typed code.
-                    let e = tcx.dcx().span_delayed_bug(
-                        span,
-                        "`ty::Const::eval` called on a non-valtree-compatible type",
-                    );
-                    return Err(e.into());
-                };
-                Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c))
+                match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span) {
+                    Ok(Ok(c)) => {
+                        Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c))
+                    }
+                    Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)),
+                    Err(err) => Err(Either::Right(err.into())),
+                }
             }
             ConstKind::Value(ty, val) => Ok((ty, val)),
-            ConstKind::Error(g) => Err(g.into()),
+            ConstKind::Error(g) => Err(Either::Right(g.into())),
             ConstKind::Param(_)
             | ConstKind::Infer(_)
             | ConstKind::Bound(_, _)
             | ConstKind::Placeholder(_)
-            | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)),
+            | ConstKind::Expr(_) => Err(Either::Right(ErrorHandled::TooGeneric(span))),
         }
     }
 
+    /// Returns the evaluated constant
+    #[inline]
+    pub fn eval(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        span: Span,
+    ) -> Result<(Ty<'tcx>, ValTree<'tcx>), ErrorHandled> {
+        self.eval_valtree(tcx, param_env, span).map_err(|err| {
+            match err {
+                Either::Right(err) => err,
+                Either::Left(_bad_ty) => {
+                    // This can happen when we run on ill-typed code.
+                    let e = tcx.dcx().span_delayed_bug(
+                        span,
+                        "`ty::Const::eval` called on a non-valtree-compatible type",
+                    );
+                    e.into()
+                }
+            }
+        })
+    }
+
     /// Normalizes the constant to a value or an error if possible.
     #[inline]
     pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index 5d828d0093f..529e9cc2711 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
+either = "1.5.0"
 itertools = "0.12"
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
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 5745dc0969c..76efb6574e1 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,42 +1,46 @@
+use either::Either;
 use rustc_apfloat::Float;
 use rustc_hir as hir;
 use rustc_index::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::Obligation;
 use rustc_middle::mir;
-use rustc_middle::span_bug;
+use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::thir::{FieldPat, Pat, PatKind};
 use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_target::abi::{FieldIdx, VariantIdx};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_trait_selection::traits::{self, ObligationCause};
+use rustc_trait_selection::traits::ObligationCause;
 use tracing::{debug, instrument, trace};
 
 use std::cell::Cell;
 
 use super::PatCtxt;
 use crate::errors::{
-    InvalidPattern, NaNPattern, PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern,
-    UnsizedPattern,
+    ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
+    PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
 };
 
 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
-    /// Converts an evaluated constant to a pattern (if possible).
+    /// Converts a constant to a pattern (if possible).
     /// This means aggregate values (like structs and enums) are converted
     /// to a pattern that matches the value (as if you'd compared via structural equality).
     ///
-    /// `cv` must be a valtree or a `mir::ConstValue`.
+    /// Only type system constants are supported, as we are using valtrees
+    /// as an intermediate step. Unfortunately those don't carry a type
+    /// so we have to carry one ourselves.
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn const_to_pat(
         &self,
-        cv: mir::Const<'tcx>,
+        c: ty::Const<'tcx>,
+        ty: Ty<'tcx>,
         id: hir::HirId,
         span: Span,
     ) -> Box<Pat<'tcx>> {
         let infcx = self.tcx.infer_ctxt().build();
         let mut convert = ConstToPat::new(self, id, span, infcx);
-        convert.to_pat(cv)
+        convert.to_pat(c, ty)
     }
 }
 
@@ -56,12 +60,6 @@ struct ConstToPat<'tcx> {
     treat_byte_string_as_slice: bool,
 }
 
-/// This error type signals that we encountered a non-struct-eq situation.
-/// We will fall back to calling `PartialEq::eq` on such patterns,
-/// and exhaustiveness checking will consider them as matching nothing.
-#[derive(Debug)]
-struct FallbackToOpaqueConst;
-
 impl<'tcx> ConstToPat<'tcx> {
     fn new(
         pat_ctxt: &PatCtxt<'_, 'tcx>,
@@ -91,116 +89,55 @@ impl<'tcx> ConstToPat<'tcx> {
         ty.is_structural_eq_shallow(self.infcx.tcx)
     }
 
-    fn to_pat(&mut self, cv: mir::Const<'tcx>) -> Box<Pat<'tcx>> {
+    fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
         trace!(self.treat_byte_string_as_slice);
-        // This method is just a wrapper handling a validity check; the heavy lifting is
-        // performed by the recursive `recur` method, which is not meant to be
-        // invoked except by this method.
-        //
-        // once indirect_structural_match is a full fledged error, this
-        // level of indirection can be eliminated
+        let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind });
 
-        let have_valtree =
-            matches!(cv, mir::Const::Ty(_, c) if matches!(c.kind(), ty::ConstKind::Value(_, _)));
-        let inlined_const_as_pat = match cv {
-            mir::Const::Ty(_, c) => match c.kind() {
-                ty::ConstKind::Param(_)
-                | ty::ConstKind::Infer(_)
-                | ty::ConstKind::Bound(_, _)
-                | ty::ConstKind::Placeholder(_)
-                | ty::ConstKind::Unevaluated(_)
-                | ty::ConstKind::Error(_)
-                | ty::ConstKind::Expr(_) => {
-                    span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind())
-                }
-                ty::ConstKind::Value(ty, valtree) => {
-                    self.recur(valtree, ty).unwrap_or_else(|_: FallbackToOpaqueConst| {
-                        Box::new(Pat {
-                            span: self.span,
-                            ty: cv.ty(),
-                            kind: PatKind::Constant { value: cv },
-                        })
-                    })
-                }
-            },
-            mir::Const::Unevaluated(_, _) => {
-                span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}")
+        // Get a valtree. If that fails, this const is definitely not valid for use as a pattern.
+        let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) {
+            Ok((_, valtree)) => valtree,
+            Err(Either::Right(e)) => {
+                let err = match e {
+                    ErrorHandled::Reported(..) => {
+                        // Let's tell the use where this failing const occurs.
+                        self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span })
+                    }
+                    ErrorHandled::TooGeneric(_) => self
+                        .tcx()
+                        .dcx()
+                        .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }),
+                };
+                return pat_from_kind(PatKind::Error(err));
+            }
+            Err(Either::Left(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 })
+                    }
+                    ty::FnPtr(..) | ty::RawPtr(..) => {
+                        self.tcx().dcx().emit_err(PointerPattern { span: self.span })
+                    }
+                    _ => self
+                        .tcx()
+                        .dcx()
+                        .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }),
+                };
+                return pat_from_kind(PatKind::Error(e));
             }
-            mir::Const::Val(_, _) => Box::new(Pat {
-                span: self.span,
-                ty: cv.ty(),
-                kind: PatKind::Constant { value: cv },
-            }),
         };
 
-        if self.saw_const_match_error.get().is_none() {
-            // If we were able to successfully convert the const to some pat (possibly with some
-            // lints, but no errors), double-check that all types in the const implement
-            // `PartialEq`. Even if we have a valtree, we may have found something
-            // in there with non-structural-equality, meaning we match using `PartialEq`
-            // and we hence have to check if that impl exists.
-            // This is all messy but not worth cleaning up: at some point we'll emit
-            // a hard error when we don't have a valtree or when we find something in
-            // the valtree that is not structural; then this can all be made a lot simpler.
-
-            let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty());
-            debug!(
-                "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
-                cv.ty(),
-                structural
-            );
-
-            if let Some(non_sm_ty) = structural {
-                if !self.type_has_partial_eq_impl(cv.ty()) {
-                    // This is reachable and important even if we have a valtree: there might be
-                    // non-structural things in a valtree, in which case we fall back to `PartialEq`
-                    // comparison, in which case we better make sure the trait is implemented for
-                    // each inner type (and not just for the surrounding type).
-                    let e = if let ty::Adt(def, ..) = non_sm_ty.kind() {
-                        if def.is_union() {
-                            let err = UnionPattern { span: self.span };
-                            self.tcx().dcx().emit_err(err)
-                        } else {
-                            // fatal avoids ICE from resolution of nonexistent method (rare case).
-                            self.tcx()
-                                .dcx()
-                                .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty })
-                        }
-                    } else {
-                        let err = InvalidPattern { span: self.span, non_sm_ty };
-                        self.tcx().dcx().emit_err(err)
-                    };
-                    // All branches above emitted an error. Don't print any more lints.
-                    // We errored. Signal that in the pattern, so that follow up errors can be silenced.
-                    let kind = PatKind::Error(e);
-                    return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
-                } else if !have_valtree {
-                    // Not being structural prevented us from constructing a valtree,
-                    // so this is definitely a case we want to reject.
-                    let err = TypeNotStructural { span: self.span, non_sm_ty };
-                    let e = self.tcx().dcx().emit_err(err);
-                    let kind = PatKind::Error(e);
-                    return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
-                } else {
-                    // This could be a violation in an inactive enum variant.
-                    // Since we have a valtree, we trust that we have traversed the full valtree and
-                    // complained about structural match violations there, so we don't
-                    // have to check anything any more.
-                }
-            } else if !have_valtree {
-                // The only way valtree construction can fail without the structural match
-                // checker finding a violation is if there is a pointer somewhere.
-                let e = self.tcx().dcx().emit_err(PointerPattern { span: self.span });
-                let kind = PatKind::Error(e);
-                return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
-            }
+        // Convert the valtree to a const.
+        let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
 
+        if self.saw_const_match_error.get().is_none() {
             // Always check for `PartialEq` if we had no other errors yet.
-            if !self.type_has_partial_eq_impl(cv.ty()) {
-                let err = TypeNotPartialEq { span: self.span, non_peq_ty: cv.ty() };
+            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);
                 let kind = PatKind::Error(e);
-                return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
+                return Box::new(Pat { span: self.span, ty: ty, kind });
             }
         }
 
@@ -243,36 +180,28 @@ impl<'tcx> ConstToPat<'tcx> {
     fn field_pats(
         &self,
         vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
-    ) -> Result<Vec<FieldPat<'tcx>>, FallbackToOpaqueConst> {
+    ) -> Vec<FieldPat<'tcx>> {
         vals.enumerate()
             .map(|(idx, (val, ty))| {
                 let field = FieldIdx::new(idx);
                 // Patterns can only use monomorphic types.
                 let ty = self.tcx().normalize_erasing_regions(self.param_env, ty);
-                Ok(FieldPat { field, pattern: self.recur(val, ty)? })
+                FieldPat { field, pattern: self.valtree_to_pat(val, ty) }
             })
             .collect()
     }
 
     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
     #[instrument(skip(self), level = "debug")]
-    fn recur(
-        &self,
-        cv: ValTree<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Result<Box<Pat<'tcx>>, FallbackToOpaqueConst> {
+    fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
         let span = self.span;
         let tcx = self.tcx();
         let param_env = self.param_env;
 
         let kind = match ty.kind() {
-            ty::FnDef(..) => {
-                let e = tcx.dcx().emit_err(InvalidPattern { span, non_sm_ty: ty });
-                self.saw_const_match_error.set(Some(e));
-                // We errored. Signal that in the pattern, so that follow up errors can be silenced.
-                PatKind::Error(e)
-            }
             ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
+                // 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);
@@ -294,13 +223,9 @@ impl<'tcx> ConstToPat<'tcx> {
                                 .iter()
                                 .map(|field| field.ty(self.tcx(), args)),
                         ),
-                    )?,
+                    ),
                 }
             }
-            ty::Tuple(fields) => PatKind::Leaf {
-                subpatterns: self
-                    .field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?,
-            },
             ty::Adt(def, args) => {
                 assert!(!def.is_union()); // Valtree construction would never succeed for unions.
                 PatKind::Leaf {
@@ -311,15 +236,18 @@ impl<'tcx> ConstToPat<'tcx> {
                                 .iter()
                                 .map(|field| field.ty(self.tcx(), args)),
                         ),
-                    )?,
+                    ),
                 }
             }
+            ty::Tuple(fields) => PatKind::Leaf {
+                subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())),
+            },
             ty::Slice(elem_ty) => PatKind::Slice {
                 prefix: cv
                     .unwrap_branch()
                     .iter()
-                    .map(|val| self.recur(*val, *elem_ty))
-                    .collect::<Result<_, _>>()?,
+                    .map(|val| self.valtree_to_pat(*val, *elem_ty))
+                    .collect(),
                 slice: None,
                 suffix: Box::new([]),
             },
@@ -327,8 +255,8 @@ impl<'tcx> ConstToPat<'tcx> {
                 prefix: cv
                     .unwrap_branch()
                     .iter()
-                    .map(|val| self.recur(*val, *elem_ty))
-                    .collect::<Result<_, _>>()?,
+                    .map(|val| self.valtree_to_pat(*val, *elem_ty))
+                    .collect(),
                 slice: None,
                 suffix: Box::new([]),
             },
@@ -345,6 +273,7 @@ impl<'tcx> ConstToPat<'tcx> {
                     if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
                         let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
                         let e = tcx.dcx().emit_err(err);
+                        self.saw_const_match_error.set(Some(e));
                         // We errored. Signal that in the pattern, so that follow up errors can be silenced.
                         PatKind::Error(e)
                     } else {
@@ -361,7 +290,7 @@ impl<'tcx> ConstToPat<'tcx> {
                             _ => *pointee_ty,
                         };
                         // References have the same valtree representation as their pointee.
-                        let subpattern = self.recur(cv, pointee_ty)?;
+                        let subpattern = self.valtree_to_pat(cv, pointee_ty);
                         PatKind::Deref { subpattern }
                     }
                 }
@@ -379,7 +308,7 @@ impl<'tcx> ConstToPat<'tcx> {
                     // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
                     let e = tcx.dcx().emit_err(NaNPattern { span });
                     self.saw_const_match_error.set(Some(e));
-                    return Err(FallbackToOpaqueConst);
+                    PatKind::Error(e)
                 } else {
                     PatKind::Constant {
                         value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
@@ -405,6 +334,6 @@ impl<'tcx> ConstToPat<'tcx> {
             }
         };
 
-        Ok(Box::new(Pat { span, ty, kind }))
+        Box::new(Pat { span, ty, kind })
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 25b7d24de39..553c5e17512 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -14,8 +14,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
 use rustc_index::Idx;
 use rustc_lint as lint;
-use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput};
-use rustc_middle::mir::{self, Const};
+use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::thir::{
     Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
 };
@@ -575,63 +574,39 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
         };
 
-        let cid = GlobalId { instance, promoted: None };
-        // Prefer valtrees over opaque constants.
-        let const_value = self
-            .tcx
-            .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span)
-            .map(|val| match val {
-                Ok(valtree) => mir::Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
-                Err(_) => mir::Const::Val(
-                    self.tcx
-                        .const_eval_global_id(param_env_reveal_all, cid, span)
-                        .expect("const_eval_global_id_for_typeck should have already failed"),
-                    ty,
-                ),
-            });
-
-        match const_value {
-            Ok(const_) => {
-                let pattern = self.const_to_pat(const_, id, span);
-
-                if !is_associated_const {
-                    return pattern;
-                }
+        let c = ty::Const::new_unevaluated(
+            self.tcx,
+            ty::UnevaluatedConst { def: instance.def_id(), args: instance.args },
+        );
 
-                let user_provided_types = self.typeck_results().user_provided_types();
-                if let Some(&user_ty) = user_provided_types.get(id) {
-                    let annotation = CanonicalUserTypeAnnotation {
-                        user_ty: Box::new(user_ty),
-                        span,
-                        inferred_ty: self.typeck_results().node_type(id),
-                    };
-                    Box::new(Pat {
-                        span,
-                        kind: PatKind::AscribeUserType {
-                            subpattern: pattern,
-                            ascription: Ascription {
-                                annotation,
-                                // Note that use `Contravariant` here. See the
-                                // `variance` field documentation for details.
-                                variance: ty::Contravariant,
-                            },
-                        },
-                        ty: const_.ty(),
-                    })
-                } else {
-                    pattern
-                }
-            }
-            Err(ErrorHandled::TooGeneric(_)) => {
-                // While `Reported | Linted` cases will have diagnostics emitted already
-                // it is not true for TooGeneric case, so we need to give user more information.
-                let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span });
-                pat_from_kind(PatKind::Error(e))
-            }
-            Err(_) => {
-                let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span });
-                pat_from_kind(PatKind::Error(e))
-            }
+        let pattern = self.const_to_pat(c, ty, id, span);
+
+        if !is_associated_const {
+            return pattern;
+        }
+
+        let user_provided_types = self.typeck_results().user_provided_types();
+        if let Some(&user_ty) = user_provided_types.get(id) {
+            let annotation = CanonicalUserTypeAnnotation {
+                user_ty: Box::new(user_ty),
+                span,
+                inferred_ty: self.typeck_results().node_type(id),
+            };
+            Box::new(Pat {
+                span,
+                kind: PatKind::AscribeUserType {
+                    subpattern: pattern,
+                    ascription: Ascription {
+                        annotation,
+                        // Note that use `Contravariant` here. See the
+                        // `variance` field documentation for details.
+                        variance: ty::Contravariant,
+                    },
+                },
+                ty,
+            })
+        } else {
+            pattern
         }
     }
 
@@ -662,7 +637,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         };
         if let Some(lit_input) = lit_input {
             match tcx.at(expr.span).lit_to_const(lit_input) {
-                Ok(c) => return self.const_to_pat(Const::Ty(ty, c), id, span).kind,
+                Ok(c) => return self.const_to_pat(c, ty, id, span).kind,
                 // If an error occurred, ignore that it's a literal
                 // and leave reporting the error up to const eval of
                 // the unevaluated constant below.
@@ -675,32 +650,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             tcx.erase_regions(ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id));
         let args = ty::InlineConstArgs::new(tcx, ty::InlineConstArgsParts { parent_args, ty }).args;
 
-        let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), args, promoted: None };
         debug_assert!(!args.has_free_regions());
 
         let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args };
-        // First try using a valtree in order to destructure the constant into a pattern.
-        // FIXME: replace "try to do a thing, then fall back to another thing"
-        // but something more principled, like a trait query checking whether this can be turned into a valtree.
-        if let Ok(Ok(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) {
-            let subpattern = self.const_to_pat(
-                Const::Ty(ty, ty::Const::new_value(self.tcx, valtree, ty)),
-                id,
-                span,
-            );
-            PatKind::InlineConstant { subpattern, def: def_id }
-        } else {
-            // If that fails, convert it to an opaque constant pattern.
-            match tcx.const_eval_resolve(self.param_env, uneval, span) {
-                Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind,
-                Err(ErrorHandled::TooGeneric(_)) => {
-                    // If we land here it means the const can't be evaluated because it's `TooGeneric`.
-                    let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span });
-                    PatKind::Error(e)
-                }
-                Err(ErrorHandled::Reported(err, ..)) => PatKind::Error(err.into()),
-            }
-        }
+        let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span);
+        PatKind::InlineConstant { subpattern, def: def_id }
     }
 
     /// Converts literals, paths and negation of literals to patterns.
@@ -728,9 +682,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let ct_ty = self.typeck_results.expr_ty(expr);
         let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg };
         match self.tcx.at(expr.span).lit_to_const(lit_input) {
-            Ok(constant) => {
-                self.const_to_pat(Const::Ty(ct_ty, constant), expr.hir_id, lit.span).kind
-            }
+            Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind,
             Err(LitToConstError::Reported(e)) => PatKind::Error(e),
             Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index d28982ed849..f7eb1730582 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -16,7 +16,6 @@ pub mod query;
 #[allow(hidden_glob_reexports)]
 mod select;
 mod specialize;
-mod structural_match;
 mod structural_normalize;
 #[allow(hidden_glob_reexports)]
 mod util;
@@ -60,7 +59,6 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
 pub use self::specialize::{
     specialization_graph, translate_args, translate_args_with_cause, OverlapError,
 };
-pub use self::structural_match::search_for_structural_match_violation;
 pub use self::structural_normalize::StructurallyNormalizeExt;
 pub use self::util::elaborate;
 pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
deleted file mode 100644
index d4535db951e..00000000000
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use std::ops::ControlFlow;
-
-/// This method traverses the structure of `ty`, trying to find an
-/// instance of an ADT (i.e. struct or enum) that doesn't implement
-/// the structural-match traits, or a generic type parameter
-/// (which cannot be determined to be structural-match).
-///
-/// The "structure of a type" includes all components that would be
-/// considered when doing a pattern match on a constant of that
-/// type.
-///
-///  * This means this method descends into fields of structs/enums,
-///    and also descends into the inner type `T` of `&T` and `&mut T`
-///
-///  * The traversal doesn't dereference unsafe pointers (`*const T`,
-///    `*mut T`), and it does not visit the type arguments of an
-///    instantiated generic like `PhantomData<T>`.
-///
-/// The reason we do this search is Rust currently require all ADTs
-/// reachable from a constant's type to implement the
-/// structural-match traits, which essentially say that
-/// the implementation of `PartialEq::eq` behaves *equivalently* to a
-/// comparison against the unfolded structure.
-///
-/// For more background on why Rust has this requirement, and issues
-/// that arose when the requirement was not enforced completely, see
-/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
-pub fn search_for_structural_match_violation<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-) -> Option<Ty<'tcx>> {
-    ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value()
-}
-
-/// This implements the traversal over the structure of a given type to try to
-/// find instances of ADTs (specifically structs or enums) that do not implement
-/// `StructuralPartialEq`.
-struct Search<'tcx> {
-    tcx: TyCtxt<'tcx>,
-
-    /// Tracks ADTs previously encountered during search, so that
-    /// we will not recur on them again.
-    seen: FxHashSet<hir::def_id::DefId>,
-}
-
-impl<'tcx> Search<'tcx> {
-    fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
-        adt_ty.is_structural_eq_shallow(self.tcx)
-    }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
-    type Result = ControlFlow<Ty<'tcx>>;
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-        debug!("Search visiting ty: {:?}", ty);
-
-        let (adt_def, args) = match *ty.kind() {
-            ty::Adt(adt_def, args) => (adt_def, args),
-            ty::Param(_) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Dynamic(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Foreign(_) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Alias(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Closure(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::CoroutineClosure(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Coroutine(..) | ty::CoroutineWitness(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::FnDef(..) => {
-                // Types of formals and return in `fn(_) -> _` are also irrelevant;
-                // so we do not recur into them via `super_visit_with`
-                return ControlFlow::Continue(());
-            }
-            ty::Array(_, n)
-                if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
-            {
-                // rust-lang/rust#62336: ignore type of contents
-                // for empty array.
-                return ControlFlow::Continue(());
-            }
-            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
-                // These primitive types are always structural match.
-                //
-                // `Never` is kind of special here, but as it is not inhabitable, this should be fine.
-                return ControlFlow::Continue(());
-            }
-
-            ty::FnPtr(..) => {
-                return ControlFlow::Continue(());
-            }
-
-            ty::RawPtr(..) => {
-                // structural-match ignores substructure of
-                // `*const _`/`*mut _`, so skip `super_visit_with`.
-                //
-                // For example, if you have:
-                // ```
-                // struct NonStructural;
-                // #[derive(PartialEq, Eq)]
-                // struct T(*const NonStructural);
-                // const C: T = T(std::ptr::null());
-                // ```
-                //
-                // Even though `NonStructural` does not implement `PartialEq`,
-                // structural equality on `T` does not recur into the raw
-                // pointer. Therefore, one can still use `C` in a pattern.
-                return ControlFlow::Continue(());
-            }
-
-            ty::Float(_) => {
-                return ControlFlow::Continue(());
-            }
-
-            ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
-                // First check all contained types and then tell the caller to continue searching.
-                return ty.super_visit_with(self);
-            }
-            ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
-                bug!("unexpected type during structural-match checking: {:?}", ty);
-            }
-            ty::Error(_) => {
-                // We still want to check other types after encountering an error,
-                // as this may still emit relevant errors.
-                return ControlFlow::Continue(());
-            }
-        };
-
-        if !self.seen.insert(adt_def.did()) {
-            debug!("Search already seen adt_def: {:?}", adt_def);
-            return ControlFlow::Continue(());
-        }
-
-        if !self.type_marked_structural(ty) {
-            debug!("Search found ty: {:?}", ty);
-            return ControlFlow::Break(ty);
-        }
-
-        // structural-match does not care about the
-        // instantiation of the generics in an ADT (it
-        // instead looks directly at its fields outside
-        // this match), so we skip super_visit_with.
-        //
-        // (Must not recur on args for `PhantomData<T>` cf
-        // rust-lang/rust#55028 and rust-lang/rust#55837; but also
-        // want to skip args when only uses of generic are
-        // behind unsafe pointers `*const T`/`*mut T`.)
-
-        // even though we skip super_visit_with, we must recur on
-        // fields of ADT.
-        let tcx = self.tcx;
-        adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| {
-            let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
-            debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
-            ty.visit_with(self)
-        })
-    }
-}
diff --git a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs
index 2e7061e7c4b..ab8eec876bc 100644
--- a/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs
+++ b/tests/ui/consts/const_in_pattern/custom-eq-branch-pass.rs
@@ -23,9 +23,20 @@ const BAR_BAZ: Foo = if 42 == 42 {
     Foo::Qux(CustomEq) // dead arm
 };
 
+const EMPTY: &[CustomEq] = &[];
+
 fn main() {
+    // BAR_BAZ itself is fine but the enum has other variants
+    // that are non-structural. Still, this should be accepted.
     match Foo::Qux(CustomEq) {
         BAR_BAZ => panic!(),
         _ => {}
     }
+
+    // Similarly, an empty slice of a type that is non-structural
+    // is accepted.
+    match &[CustomEq] as &[CustomEq] {
+        EMPTY => panic!(),
+        _ => {},
+    }
 }
diff --git a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs
index 86d971044fe..645e1418912 100644
--- a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs
+++ b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.rs
@@ -26,7 +26,7 @@ fn main() {
 
     match None {
         NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"),
-        //~^ ERROR must be annotated with `#[derive(PartialEq)]`
+        //~^ ERROR must implement `PartialEq`
         _ => panic!("whoops"),
     }
 }
diff --git a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr
index 88b82d5004b..ed531a1fead 100644
--- a/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr
+++ b/tests/ui/consts/const_in_pattern/reject_non_partial_eq.stderr
@@ -1,11 +1,8 @@
-error: to use a constant of type `NoPartialEq` in a pattern, `NoPartialEq` must be annotated with `#[derive(PartialEq)]`
+error: to use a constant of type `Option<NoPartialEq>` in a pattern, the type must implement `PartialEq`
   --> $DIR/reject_non_partial_eq.rs:28:9
    |
 LL |         NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"),
    |         ^^^^^^^^^^^^^^^^^^
-   |
-   = note: the traits must be derived, manual `impl`s are not sufficient
-   = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.rs b/tests/ui/consts/invalid-inline-const-in-match-arm.rs
index 0654fd82fbc..4fe4b0d33c8 100644
--- a/tests/ui/consts/invalid-inline-const-in-match-arm.rs
+++ b/tests/ui/consts/invalid-inline-const-in-match-arm.rs
@@ -4,5 +4,6 @@ fn main() {
     match () {
         const { (|| {})() } => {}
         //~^ ERROR cannot call non-const closure in constants
+        //~| ERROR could not evaluate constant pattern
     }
 }
diff --git a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
index 7579f7f9692..0e41053a29d 100644
--- a/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
+++ b/tests/ui/consts/invalid-inline-const-in-match-arm.stderr
@@ -11,6 +11,12 @@ help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 LL + #![feature(const_trait_impl)]
    |
 
-error: aborting due to 1 previous error
+error: could not evaluate constant pattern
+  --> $DIR/invalid-inline-const-in-match-arm.rs:5:9
+   |
+LL |         const { (|| {})() } => {}
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs
index c69fe145f77..c01f8934c75 100644
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs
@@ -13,7 +13,7 @@ const A: &[B] = &[];
 pub fn main() {
     match &[][..] {
         A => (),
-        //~^ ERROR must be annotated with `#[derive(PartialEq)]`
+        //~^ ERROR must implement `PartialEq`
         _ => (),
     }
 }
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr
index 02e2bab4d0a..736e4c30c8a 100644
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr
@@ -1,11 +1,8 @@
-error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]`
+error: to use a constant of type `&[B]` in a pattern, the type must implement `PartialEq`
   --> $DIR/issue-61188-match-slice-forbidden-without-eq.rs:15:9
    |
 LL |         A => (),
    |         ^
-   |
-   = note: the traits must be derived, manual `impl`s are not sufficient
-   = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs
index 6d6a336e688..f343013f2b0 100644
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs
@@ -28,13 +28,9 @@ fn main() {
     // Also cover range patterns
     match x {
         NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns
-        //~^ ERROR lower range bound must be less than or equal to upper
         -1.0..=NAN => {}, //~ ERROR cannot use NaN in patterns
-        //~^ ERROR lower range bound must be less than or equal to upper
         NAN.. => {}, //~ ERROR cannot use NaN in patterns
-        //~^ ERROR lower range bound must be less than or equal to upper
         ..NAN => {}, //~ ERROR cannot use NaN in patterns
-        //~^ ERROR lower range bound must be less than upper
         _ => {},
     };
 }
diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr
index baca1d75048..44b05ea31e9 100644
--- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr
+++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr
@@ -34,14 +34,8 @@ LL |         NAN..=1.0 => {},
    = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
    = help: try using the `is_nan` method instead
 
-error[E0030]: lower range bound must be less than or equal to upper
-  --> $DIR/issue-6804-nan-match.rs:30:9
-   |
-LL |         NAN..=1.0 => {},
-   |         ^^^^^^^^^ lower bound larger than upper bound
-
 error: cannot use NaN in patterns
-  --> $DIR/issue-6804-nan-match.rs:32:16
+  --> $DIR/issue-6804-nan-match.rs:31:16
    |
 LL |         -1.0..=NAN => {},
    |                ^^^
@@ -49,14 +43,8 @@ LL |         -1.0..=NAN => {},
    = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
    = help: try using the `is_nan` method instead
 
-error[E0030]: lower range bound must be less than or equal to upper
-  --> $DIR/issue-6804-nan-match.rs:32:9
-   |
-LL |         -1.0..=NAN => {},
-   |         ^^^^^^^^^^ lower bound larger than upper bound
-
 error: cannot use NaN in patterns
-  --> $DIR/issue-6804-nan-match.rs:34:9
+  --> $DIR/issue-6804-nan-match.rs:32:9
    |
 LL |         NAN.. => {},
    |         ^^^
@@ -64,14 +52,8 @@ LL |         NAN.. => {},
    = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
    = help: try using the `is_nan` method instead
 
-error[E0030]: lower range bound must be less than or equal to upper
-  --> $DIR/issue-6804-nan-match.rs:34:9
-   |
-LL |         NAN.. => {},
-   |         ^^^^^ lower bound larger than upper bound
-
 error: cannot use NaN in patterns
-  --> $DIR/issue-6804-nan-match.rs:36:11
+  --> $DIR/issue-6804-nan-match.rs:33:11
    |
 LL |         ..NAN => {},
    |           ^^^
@@ -79,13 +61,5 @@ LL |         ..NAN => {},
    = note: NaNs compare inequal to everything, even themselves, so this pattern would never match
    = help: try using the `is_nan` method instead
 
-error[E0579]: lower range bound must be less than upper
-  --> $DIR/issue-6804-nan-match.rs:36:9
-   |
-LL |         ..NAN => {},
-   |         ^^^^^
-
-error: aborting due to 11 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0030, E0579.
-For more information about an error, try `rustc --explain E0030`.