From 1c583c0b914eb46164f233f9ba2d0d351e64da1f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Aug 2023 14:22:34 +1000 Subject: [PATCH 1/4] Simplify the boolean logic in a closure. And rename a closure argument. --- compiler/rustc_trait_selection/src/traits/project.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 3d800421b764b..4b13abb879020 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1230,12 +1230,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( }; let mut deduped: SsoHashSet<_> = Default::default(); - result.obligations.retain(|projected_obligation| { - if !deduped.insert(projected_obligation.clone()) { - return false; - } - true - }); + result.obligations.retain(|obligation| deduped.insert(obligation.clone())); if use_cache { infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); From 3c99b3dbd01389be9c6f320f179bd3fdf68913f5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Aug 2023 14:24:29 +1000 Subject: [PATCH 2/4] Size the `deduped` set appropriately. Avoids lots of resizing as the set fills up. --- compiler/rustc_trait_selection/src/traits/project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 4b13abb879020..519b5f80d2609 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1229,7 +1229,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( Normalized { value: projected_term, obligations: projected_obligations } }; - let mut deduped: SsoHashSet<_> = Default::default(); + let mut deduped = SsoHashSet::with_capacity(projected_obligations.len()); result.obligations.retain(|obligation| deduped.insert(obligation.clone())); if use_cache { From 8378487f2796571da288ac54a4696bd67c0933b8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Aug 2023 14:25:03 +1000 Subject: [PATCH 3/4] Only dedup obligation after new ones have been added. --- compiler/rustc_trait_selection/src/traits/project.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 519b5f80d2609..06a1027e5dfdb 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1214,7 +1214,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); - let mut result = if projected_term.has_projections() { + let result = if projected_term.has_projections() { let mut normalizer = AssocTypeNormalizer::new( selcx, param_env, @@ -1224,14 +1224,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( ); let normalized_ty = normalizer.fold(projected_term); + let mut deduped = SsoHashSet::with_capacity(projected_obligations.len()); + projected_obligations.retain(|obligation| deduped.insert(obligation.clone())); + Normalized { value: normalized_ty, obligations: projected_obligations } } else { Normalized { value: projected_term, obligations: projected_obligations } }; - let mut deduped = SsoHashSet::with_capacity(projected_obligations.len()); - result.obligations.retain(|obligation| deduped.insert(obligation.clone())); - if use_cache { infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); } From 698f0e39e161cb0fdabb06ac34401ee87cfc5c9e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Aug 2023 13:41:50 +1000 Subject: [PATCH 4/4] Ignore `cause` and `recursion_depth` in `Obligation::{eq,hash}`. This gives massive (~7x) compile time and memory usage reductions for the trait system stress test in https://github.com/rust-lang/rustc-perf/pull/1680. --- compiler/rustc_infer/src/traits/mod.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index dc41630196b9f..a5b2ccce85e1e 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -9,6 +9,7 @@ mod structural_impls; pub mod util; use std::cmp; +use std::hash::{Hash, Hasher}; use hir::def_id::LocalDefId; use rustc_hir as hir; @@ -36,7 +37,7 @@ pub use rustc_middle::traits::*; /// either identifying an `impl` (e.g., `impl Eq for i32`) that /// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone)] pub struct Obligation<'tcx, T> { /// The reason we have to prove this thing. pub cause: ObligationCause<'tcx>, @@ -55,6 +56,27 @@ pub struct Obligation<'tcx, T> { pub recursion_depth: usize, } +impl<'tcx, T: PartialEq> PartialEq> for Obligation<'tcx, T> { + #[inline] + fn eq(&self, other: &Obligation<'tcx, T>) -> bool { + // Ignore `cause` and `recursion_depth`. This is a small performance + // win for a few crates, and a huge performance win for the crate in + // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly + // stresses the trait system. + self.param_env == other.param_env && self.predicate == other.predicate + } +} + +impl Eq for Obligation<'_, T> {} + +impl Hash for Obligation<'_, T> { + fn hash(&self, state: &mut H) -> () { + // See the comment on `Obligation::eq`. + self.param_env.hash(state); + self.predicate.hash(state); + } +} + impl<'tcx, P> From> for solve::Goal<'tcx, P> { fn from(value: Obligation<'tcx, P>) -> Self { solve::Goal { param_env: value.param_env, predicate: value.predicate }