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