diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 13cf9a934b72c..7867d7ba3822c 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -719,6 +719,11 @@ rustc_queries! { desc { "computing whether `{}` needs drop", env.value } } + /// Query backing `TyS::is_structural_eq_shallow`. + query is_structural_eq_raw(ty: Ty<'tcx>) -> bool { + desc { "computing whether `{:?}` has a derived implementation of `PartialEq` and `Eq`", ty } + } + /// A list of types where the ADT requires drop if and only if any of /// those types require drop. If the ADT is known to always need drop /// then `Err(AlwaysRequiresDrop)` is returned. diff --git a/src/librustc_middle/ty/util.rs b/src/librustc_middle/ty/util.rs index f9c10488ffbc0..6bd7723023451 100644 --- a/src/librustc_middle/ty/util.rs +++ b/src/librustc_middle/ty/util.rs @@ -778,6 +778,27 @@ impl<'tcx> ty::TyS<'tcx> { } } + /// Returns `true` if this type implements both `PartialStructuralEq` and `StructuralEq`. + /// + /// These marker traits are implemented along with `PartialEq` and `Eq` when implementations + /// for those traits are derived. They indicate that two values of this type are equal if all + /// of their fields are equal. A quirk of the marker traits is that their implementation is + /// never conditional on generic parameter bounds. For that reason, this helper function does + /// not take a `ParamEnv`. + /// + /// This function is "shallow" because it may return `true` for a type whose fields are not + /// `StructuralEq`. You will need to use a type visitor if you want to know whether a call to + /// `PartialEq::eq` will proceed structurally for a given type. + #[inline] + pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { + // Fast path for some builtin types + if self.is_primitive() || self.is_str() { + return true; + } + + tcx.is_structural_eq_raw(self) + } + pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind, &b.kind) { (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index fc6860b40e8d2..3a7d72341cfbf 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -2,7 +2,6 @@ //! //! 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; @@ -137,10 +136,7 @@ impl Qualif for CustomEq { 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)) + !ty.is_structural_eq_shallow(cx.tcx) } } 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 28ec2ca13d5af..132d10d89a5e5 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -80,7 +80,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { - traits::type_marked_structural(self.id, self.span, &self.infcx, ty) + ty.is_structural_eq_shallow(self.infcx.tcx) } fn to_pat( diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 9592f93ce2e76..b0f5a59e40d36 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -564,6 +564,7 @@ fn type_implements_trait<'tcx>( pub fn provide(providers: &mut ty::query::Providers<'_>) { object_safety::provide(providers); + structural_match::provide(providers); *providers = ty::query::Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/src/librustc_trait_selection/traits/structural_match.rs index eb63505b69b41..adac350880d6c 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -1,10 +1,11 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::ObligationCause; -use crate::traits::{self, ConstPatternStructural, TraitEngine}; +use crate::traits::{self, TraitEngine}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::{StructuralPeqTraitLangItem, StructuralTeqTraitLangItem}; +use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::Span; @@ -41,14 +42,14 @@ pub enum NonStructuralMatchTy<'tcx> { /// 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>( - id: hir::HirId, - span: Span, + _id: hir::HirId, + _span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { // FIXME: we should instead pass in an `infcx` from the outside. tcx.infer_ctxt().enter(|infcx| { - let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; + let mut search = Search { infcx, found: None, seen: FxHashSet::default() }; ty.visit_with(&mut search); search.found }) @@ -62,26 +63,25 @@ pub fn search_for_structural_match_violation<'tcx>( /// Note that this does *not* recursively check if the substructure of `adt_ty` /// implements the traits. pub fn type_marked_structural( - id: hir::HirId, - span: Span, infcx: &InferCtxt<'_, 'tcx>, adt_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, ) -> bool { let mut fulfillment_cx = traits::FulfillmentContext::new(); - let cause = ObligationCause::new(span, id, ConstPatternStructural); // require `#[derive(PartialEq)]` - let structural_peq_def_id = infcx.tcx.require_lang_item(StructuralPeqTraitLangItem, Some(span)); + let structural_peq_def_id = + infcx.tcx.require_lang_item(StructuralPeqTraitLangItem, Some(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), adt_ty, structural_peq_def_id, - cause, + cause.clone(), ); // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) - let cause = ObligationCause::new(span, id, ConstPatternStructural); - let structural_teq_def_id = infcx.tcx.require_lang_item(StructuralTeqTraitLangItem, Some(span)); + let structural_teq_def_id = + infcx.tcx.require_lang_item(StructuralTeqTraitLangItem, Some(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -106,9 +106,6 @@ pub fn type_marked_structural( /// find instances of ADTs (specifically structs or enums) that do not implement /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). struct Search<'a, 'tcx> { - id: hir::HirId, - span: Span, - infcx: InferCtxt<'a, 'tcx>, /// Records first ADT that does not implement a structural-match trait. @@ -125,7 +122,7 @@ impl Search<'a, 'tcx> { } fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - type_marked_structural(self.id, self.span, &self.infcx, adt_ty) + adt_ty.is_structural_eq_shallow(self.tcx()) } } @@ -220,3 +217,12 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { false } } + +pub fn provide(providers: &mut Providers<'_>) { + providers.is_structural_eq_raw = |tcx, ty| { + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::dummy(); + type_marked_structural(&infcx, ty, cause) + }) + }; +}