diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index 8c81f5227d2..95a28df99aa 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -80,6 +80,7 @@ pub struct BorrowCheckResult<'tcx> { pub struct ConstQualifs { pub has_mut_interior: bool, pub needs_drop: bool, + pub custom_eq: bool, } /// After we borrow check a closure, we are left with various diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 04bd632553e..fc6860b40e8 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -2,9 +2,11 @@ //! //! See the `Qualif` trait for more info. +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits; use super::ConstCx; @@ -12,6 +14,7 @@ pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), + custom_eq: CustomEq::in_any_value_of_ty(cx, ty), } } @@ -108,6 +111,39 @@ impl Qualif for NeedsDrop { } } +/// A constant that cannot be used as part of a pattern in a `match` expression. +pub struct CustomEq; + +impl Qualif for CustomEq { + const ANALYSIS_NAME: &'static str = "flow_custom_eq"; + + fn in_qualifs(qualifs: &ConstQualifs) -> bool { + qualifs.custom_eq + } + + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { + // If *any* component of a composite data type does not implement `Structural{Partial,}Eq`, + // we know that at least some values of that type are not structural-match. I say "some" + // because that component may be part of an enum variant (e.g., + // `Option::::Some`), in which case some values of this type may be + // structural-match (`Option::None`). + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some() + } + + fn in_adt_inherently( + cx: &ConstCx<'_, 'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool { + let ty = cx.tcx.mk_ty(ty::Adt(adt, substs)); + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + cx.tcx + .infer_ctxt() + .enter(|infcx| !traits::type_marked_structural(id, cx.body.span, &infcx, ty)) + } +} + // FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. /// Returns `true` if this `Rvalue` contains qualif `Q`. diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index b87429199ec..c5938426f61 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; use std::ops::Deref; use super::ops::{self, NonConstOp}; -use super::qualifs::{self, HasMutInterior, NeedsDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{is_lang_panic_fn, ConstCx, ConstKind, Qualif}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; @@ -142,9 +142,35 @@ impl Qualifs<'mir, 'tcx> { let return_loc = ccx.body.terminator_loc(return_block); + let custom_eq = match ccx.const_kind() { + // We don't care whether a `const fn` returns a value that is not structurally + // matchable. Functions calls are opaque and always use type-based qualification, so + // this value should never be used. + ConstKind::ConstFn => true, + + // If we know that all values of the return type are structurally matchable, there's no + // need to run dataflow. + ConstKind::Const | ConstKind::Static | ConstKind::StaticMut + if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => + { + false + } + + ConstKind::Const | ConstKind::Static | ConstKind::StaticMut => { + let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) + .into_engine(ccx.tcx, &ccx.body, ccx.def_id) + .iterate_to_fixpoint() + .into_results_cursor(&ccx.body); + + cursor.seek_after(return_loc); + cursor.contains(RETURN_PLACE) + } + }; + ConstQualifs { needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), + custom_eq, } } }