From 796c0ca5548de6d4a5220dbae500e00db616243b Mon Sep 17 00:00:00 2001
From: Dylan MacKenzie <ecstaticmorse@gmail.com>
Date: Mon, 16 Mar 2020 20:50:20 -0700
Subject: [PATCH] Incorporate MIR const-checker into `Pat` lowering

---
 .../hair/pattern/const_to_pat.rs              |  21 ++-
 src/librustc_mir_build/hair/pattern/mod.rs    | 131 ++++++++++--------
 2 files changed, 89 insertions(+), 63 deletions(-)

diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs
index 50034fb02ace9..ad87afc3e0301 100644
--- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs
+++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs
@@ -22,13 +22,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         cv: &'tcx ty::Const<'tcx>,
         id: hir::HirId,
         span: Span,
+        mir_structural_match_violation: bool,
     ) -> Pat<'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 mut convert = ConstToPat::new(self, id, span, infcx);
-            convert.to_pat(cv)
+            convert.to_pat(cv, mir_structural_match_violation)
         })
     }
 }
@@ -81,7 +82,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
         traits::type_marked_structural(self.id, self.span, &self.infcx, ty)
     }
 
-    fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
+    fn to_pat(
+        &mut self,
+        cv: &'tcx ty::Const<'tcx>,
+        mir_structural_match_violation: bool,
+    ) -> Pat<'tcx> {
         // 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.
@@ -100,6 +105,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
                 cv.ty, structural
             );
+
+            if structural.is_none() && mir_structural_match_violation {
+                bug!("MIR const-checker found novel structural match violation");
+            }
+
             if let Some(non_sm_ty) = structural {
                 let adt_def = match non_sm_ty {
                     traits::NonStructuralMatchTy::Adt(adt_def) => adt_def,
@@ -146,13 +156,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 if !ty_is_partial_eq {
                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
                     self.tcx().sess.span_fatal(self.span, &make_msg());
-                } else {
+                } else if mir_structural_match_violation {
                     self.tcx().struct_span_lint_hir(
                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
                         self.id,
                         self.span,
                         |lint| lint.build(&make_msg()).emit(),
                     );
+                } else {
+                    debug!(
+                        "`search_for_structural_match_violation` found one, but `CustomEq` was \
+                          not in the qualifs for that `const`"
+                    );
                 }
             }
         }
diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs
index 20ee98b0a9c0f..bd75f11fb8f94 100644
--- a/src/librustc_mir_build/hair/pattern/mod.rs
+++ b/src/librustc_mir_build/hair/pattern/mod.rs
@@ -15,7 +15,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::RangeEnd;
 use rustc_index::vec::Idx;
-use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue, ErrorHandled};
+use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue};
 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::mir::UserTypeProjection;
 use rustc_middle::mir::{BorrowKind, Field, Mutability};
@@ -762,69 +762,80 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> {
         let ty = self.tables.node_type(id);
         let res = self.tables.qpath_res(qpath, id);
-        let is_associated_const = match res {
-            Res::Def(DefKind::AssocConst, _) => true,
-            _ => false,
+
+        let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) };
+
+        let (def_id, is_associated_const) = match res {
+            Res::Def(DefKind::Const, def_id) => (def_id, false),
+            Res::Def(DefKind::AssocConst, def_id) => (def_id, true),
+
+            _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])),
         };
-        let kind = match res {
-            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
-                let substs = self.tables.node_substs(id);
-                // Use `Reveal::All` here because patterns are always monomorphic even if their function isn't.
-                match self.tcx.const_eval_resolve(
-                    self.param_env.with_reveal_all(),
-                    def_id,
-                    substs,
-                    None,
-                    Some(span),
-                ) {
-                    Ok(value) => {
-                        let const_ =
-                            ty::Const::from_value(self.tcx, value, self.tables.node_type(id));
-
-                        let pattern = self.const_to_pat(&const_, id, span);
-                        if !is_associated_const {
-                            return pattern;
-                        }
 
-                        let user_provided_types = self.tables().user_provided_types();
-                        return if let Some(u_ty) = user_provided_types.get(id) {
-                            let user_ty = PatTyProj::from_user_type(*u_ty);
-                            Pat {
-                                span,
-                                kind: Box::new(PatKind::AscribeUserType {
-                                    subpattern: pattern,
-                                    ascription: Ascription {
-                                        /// Note that use `Contravariant` here. See the
-                                        /// `variance` field documentation for details.
-                                        variance: ty::Variance::Contravariant,
-                                        user_ty,
-                                        user_ty_span: span,
-                                    },
-                                }),
-                                ty: const_.ty,
-                            }
-                        } else {
-                            pattern
-                        };
-                    }
-                    Err(ErrorHandled::TooGeneric) => {
-                        self.errors.push(if is_associated_const {
-                            PatternError::AssocConstInPattern(span)
-                        } else {
-                            PatternError::StaticInPattern(span)
-                        });
-                        PatKind::Wild
-                    }
-                    Err(_) => {
-                        self.tcx.sess.span_err(span, "could not evaluate constant pattern");
-                        PatKind::Wild
-                    }
-                }
+        // Use `Reveal::All` here because patterns are always monomorphic even if their function
+        // isn't.
+        let param_env_reveal_all = self.param_env.with_reveal_all();
+        let substs = self.tables.node_substs(id);
+        let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) {
+            Ok(Some(i)) => i,
+            Ok(None) => {
+                self.errors.push(if is_associated_const {
+                    PatternError::AssocConstInPattern(span)
+                } else {
+                    PatternError::StaticInPattern(span)
+                });
+
+                return pat_from_kind(PatKind::Wild);
+            }
+
+            Err(_) => {
+                self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+                return pat_from_kind(PatKind::Wild);
             }
-            _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]),
         };
 
-        Pat { span, ty, kind: Box::new(kind) }
+        // `mir_const_qualif` must be called with the `DefId` of the item where the const is
+        // defined, not where it is declared. The difference is significant for associated
+        // constants.
+        let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq;
+        debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation);
+
+        match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) {
+            Ok(value) => {
+                let const_ = ty::Const::from_value(self.tcx, value, self.tables.node_type(id));
+
+                let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation);
+
+                if !is_associated_const {
+                    return pattern;
+                }
+
+                let user_provided_types = self.tables().user_provided_types();
+                if let Some(u_ty) = user_provided_types.get(id) {
+                    let user_ty = PatTyProj::from_user_type(*u_ty);
+                    Pat {
+                        span,
+                        kind: Box::new(PatKind::AscribeUserType {
+                            subpattern: pattern,
+                            ascription: Ascription {
+                                /// Note that use `Contravariant` here. See the
+                                /// `variance` field documentation for details.
+                                variance: ty::Variance::Contravariant,
+                                user_ty,
+                                user_ty_span: span,
+                            },
+                        }),
+                        ty: const_.ty,
+                    }
+                } else {
+                    pattern
+                }
+            }
+            Err(_) => {
+                self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+                pat_from_kind(PatKind::Wild)
+            }
+        }
     }
 
     /// Converts literals, paths and negation of literals to patterns.
@@ -849,7 +860,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
             let lit_input = LitToConstInput { lit: &lit.node, ty: self.tables.expr_ty(expr), neg };
             match self.tcx.at(expr.span).lit_to_const(lit_input) {
-                Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind,
+                Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span, false).kind,
                 Err(LitToConstError::UnparseableFloat) => {
                     self.errors.push(PatternError::FloatBug);
                     PatKind::Wild