about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/mir_build.ftl19
-rw-r--r--compiler/rustc_mir_build/src/errors.rs51
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs190
3 files changed, 122 insertions, 138 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
index 60d3d3e69ab..4faaffce7bb 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
@@ -299,3 +299,22 @@ mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once a
     .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
     .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
     .moved = also moved into `{$name_moved}` here
+
+mir_build_union_pattern = cannot use unions in constant patterns
+
+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, Eq)]`
+
+mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns
+
+mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
+
+mir_build_float_pattern = floating-point types cannot be used in patterns
+
+mir_build_pointer_pattern = 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.
+
+mir_build_indirect_structural_match = 
+    to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
+
+mir_build_nontrivial_structural_match = 
+    to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
\ No newline at end of file
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 68179001b91..840a1faf95a 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -614,3 +614,54 @@ pub enum MultipleMutBorrowOccurence {
         name_moved: Ident,
     },
 }
+
+#[derive(Diagnostic)]
+#[diag(mir_build_union_pattern)]
+pub struct UnionPattern {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_type_not_structural)]
+pub struct TypeNotStructural<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_invalid_pattern)]
+pub struct InvalidPattern<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_unsized_pattern)]
+pub struct UnsizedPattern<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_float_pattern)]
+pub struct FloatPattern;
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_pointer_pattern)]
+pub struct PointerPattern;
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_indirect_structural_match)]
+pub struct IndirectStructuralMatch<'tcx> {
+    pub non_sm_ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_nontrivial_structural_match)]
+pub struct NontrivialStructuralMatch<'tcx> {
+    pub non_sm_ty: Ty<'tcx>,
+}
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 6470efab2e9..8e7ca32feb3 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,11 +1,9 @@
-use rustc_errors::DelayDm;
 use rustc_hir as hir;
 use rustc_index::vec::Idx;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::{self, Field};
 use rustc_middle::thir::{FieldPat, Pat, PatKind};
-use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::Span;
 use rustc_trait_selection::traits::predicate_for_trait_def;
@@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
 use std::cell::Cell;
 
 use super::PatCtxt;
+use crate::errors::{
+    FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
+    PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
+};
 
 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// Converts an evaluated constant to a pattern (if possible).
@@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> {
         self.infcx.tcx
     }
 
-    fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> 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.span, self.tcx(), ty).map(|non_sm_ty| {
-            with_no_trimmed_paths!(match non_sm_ty.kind() {
-                ty::Adt(adt, _) => self.adt_derive_msg(*adt),
-                ty::Dynamic(..) => {
-                    "trait objects cannot be used in patterns".to_string()
-                }
-                ty::Alias(ty::Opaque, ..) => {
-                    "opaque types cannot be used in patterns".to_string()
-                }
-                ty::Closure(..) => {
-                    "closures cannot be used in patterns".to_string()
-                }
-                ty::Generator(..) | ty::GeneratorWitness(..) => {
-                    "generators cannot be used in patterns".to_string()
-                }
-                ty::Float(..) => {
-                    "floating-point numbers cannot be used in patterns".to_string()
-                }
-                ty::FnPtr(..) => {
-                    "function pointers cannot be used in patterns".to_string()
-                }
-                ty::RawPtr(..) => {
-                    "raw pointers cannot be used in patterns".to_string()
-                }
-                _ => {
-                    bug!("use of a value of `{non_sm_ty}` inside a pattern")
-                }
-            })
-        })
-    }
-
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
         ty.is_structural_eq_shallow(self.infcx.tcx)
     }
@@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> {
             // If we were able to successfully convert the const to some pat,
             // double-check that all types in the const implement `Structural`.
 
-            let structural = self.search_for_structural_match_violation(cv.ty());
+            let structural =
+                traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
             debug!(
                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
                 cv.ty(),
@@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> {
                 return inlined_const_as_pat;
             }
 
-            if let Some(msg) = structural {
+            if let Some(non_sm_ty) = 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);
+                    // fatal avoids ICE from resolution of non-existent method (rare case).
+                    self.tcx()
+                        .sess
+                        .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
                 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
-                    self.tcx().struct_span_lint_hir(
+                    self.tcx().emit_spanned_lint(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         self.id,
                         self.span,
-                        msg,
-                        |lint| lint,
+                        IndirectStructuralMatch { non_sm_ty },
                     );
                 } else {
                     debug!(
@@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> {
         let kind = match cv.ty().kind() {
             ty::Float(_) => {
                 if self.include_lint_checks {
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
                         id,
                         span,
-                        "floating-point types cannot be used in patterns",
-                        |lint| lint,
+                        FloatPattern,
                     );
                 }
                 PatKind::Constant { value: cv }
@@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'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);
-                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);
-                }
+                let err = UnionPattern { span };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             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 Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
             {
-                // 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);
-                if self.include_lint_checks {
-                    tcx.sess.span_err(self.span, &msg);
-                } else {
-                    tcx.sess.delay_span_bug(self.span, &msg);
-                }
+                let err = TypeNotStructural { span, non_sm_ty };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             // If the type is not structurally comparable, just emit the constant directly,
@@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> {
                     && !self.saw_const_match_lint.get()
                 {
                     self.saw_const_match_lint.set(true);
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         id,
                         span,
-                        DelayDm(|| {
-                            format!(
-                                "to use a constant of type `{}` in a pattern, \
-                                 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
-                                cv.ty(),
-                                cv.ty(),
-                            )
-                        }),
-                        |lint| lint,
+                        IndirectStructuralMatch { non_sm_ty: cv.ty() },
                     );
                 }
                 // Since we are behind a reference, we can just bubble the error up so we get a
@@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> {
                     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,
-                );
                 self.saw_const_match_error.set(true);
-                if self.include_lint_checks {
-                    tcx.sess.span_err(span, &msg);
-                } else {
-                    tcx.sess.delay_span_bug(span, &msg);
-                }
+                let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
+                tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> {
                 // 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);
-                    }
+                    let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+                    tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                     PatKind::Wild
                 }
                 // `&str` is represented as `ConstValue::Slice`, let's keep using this
@@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> {
                 // 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) => {
+                ty::Adt(_, _) 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(
+                           self.saw_const_match_lint.set(true);
+                           tcx.emit_spanned_lint(
                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                                 self.id,
-                                self.span,
-                                msg,
-                                |lint| lint,
+                                span,
+                                IndirectStructuralMatch { non_sm_ty: *pointee_ty },
                             );
                         }
                         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);
-                            }
+                            let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
+                            tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                         }
                         PatKind::Wild
                     }
@@ -507,13 +435,11 @@ impl<'tcx> ConstToPat<'tcx> {
                 _ => {
                     if !pointee_ty.is_sized(tcx, param_env) {
                         // `tcx.deref_mir_constant()` below will ICE with an unsized type
-                        // (except slices, which are handled in a separate arm above).
-                        let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
-                        if self.include_lint_checks {
-                            tcx.sess.span_err(span, &msg);
-                        } else {
-                            tcx.sess.delay_span_bug(span, &msg);
-                        }
+                        // (except slices, which are handled in a separate arm above).                        
+
+                        let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
+                        tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
+
                         PatKind::Wild
                     } else {
                         let old = self.behind_reference.replace(true);
@@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> {
                     && !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(
+                    tcx.emit_spanned_lint(
                         lint::builtin::POINTER_STRUCTURAL_MATCH,
                         id,
                         span,
-                        msg,
-                        |lint| lint,
+                        PointerPattern
                     );
                 }
                 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);
-                }
+                let err = InvalidPattern { span, non_sm_ty: cv.ty() };
+                    tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
                 PatKind::Wild
             }
         };
@@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> {
             && 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(
+            && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
+        {
+            self.saw_const_match_lint.set(true);
+            tcx.emit_spanned_lint(
                 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
                 id,
                 span,
-                msg,
-                |lint| lint,
+                NontrivialStructuralMatch {non_sm_ty}
             );
         }