@@ -15,7 +15,7 @@ use rustc_type_ir::solve::{
1515 RerunNonErased , RerunReason , RerunResultExt , SmallCopyList ,
1616} ;
1717use rustc_type_ir:: {
18- self as ty, CanonicalVarValues , ClauseKind , InferCtxtLike , Interner , MayBeErased ,
18+ self as ty, CanonicalVarValues , ClauseKind , ConstKind , InferCtxtLike , Interner , MayBeErased ,
1919 OpaqueTypeKey , PredicateKind , TypeFoldable , TypeFolder , TypeSuperFoldable , TypeSuperVisitable ,
2020 TypeVisitable , TypeVisitableExt , TypeVisitor , TypingMode ,
2121} ;
@@ -563,6 +563,102 @@ where
563563 WontMakeProgress ( stalled_certainty)
564564 }
565565
566+ /// This is a fast path optimization:
567+ /// If *all* the self types of all the where clauses in the goals `ParamEnv` are a
568+ /// generic arg (this is common if for example the `ParamEnv` only contains `T: Clone`
569+ /// for some generic function `fn foo<T: Clone>(t: T)`)
570+ /// And the goal does not mention any generic args, then we already know for certain that
571+ /// the evaluation of the goal doesn't depend on the `ParamEnv` in any way. That means that
572+ /// it's equivalent to evaluating the goal with an *empty* `ParamEnv`.
573+ ///
574+ /// This is desirable because the `ParamEnv` is part of the cache key, so more cache keys will
575+ /// match if they all mention the same empty `ParamEnv`.
576+ fn try_strip_param_env ( & self , goal : Goal < I , I :: Predicate > ) -> Goal < I , I :: Predicate > {
577+ let clause_relevant_for_goal_evaluation = |clause : ClauseKind < I > | -> bool {
578+ match clause {
579+ ClauseKind :: Trait ( trait_predicate) => {
580+ let irrelevant =
581+ // If the self type of this clause is a generic parameter
582+ // i.e. T: Clone as opposed to i32: Clone or Vec<T>: Clone
583+ matches ! ( trait_predicate. self_ty( ) . kind( ) , ty:: Param ( _) )
584+ // And the goal can never in any way use this clause because it
585+ // doesn't mention any type params
586+ && !goal. predicate . has_param ( )
587+ // then the clause is irrelevant to the outcome of the goal.
588+ ;
589+
590+ !irrelevant
591+ }
592+ ClauseKind :: RegionOutlives ( _) => true ,
593+ // FIXME: atm never relevant for goal evaluation, but might be in the future so `true`
594+ // to avoid future performance cliffs
595+ ClauseKind :: TypeOutlives ( _) => true ,
596+ ClauseKind :: Projection ( projection_predicate) => {
597+ let irrelevant =
598+ // If the self type of this projection clause is a generic parameter
599+ // i.e. <T as Bar>::Foo as opposed to <i32 as Bar>::Foo or <Vec<T> as Bar>::Foo
600+ matches ! ( projection_predicate. self_ty( ) . kind( ) , ty:: Param ( _) )
601+ // And the goal can never in any way use this clause because it
602+ // doesn't mention any type params
603+ && !goal. predicate . has_param ( )
604+ // then the clause is irrelevant to the outcome of the goal.
605+ ;
606+
607+ !irrelevant
608+ }
609+ ClauseKind :: ConstArgHasType ( c, _) => {
610+ let irrelevant =
611+ // If the const this clause bounds, is a generic parameter,
612+ // i.e. N: usize as opposed to 4: usize
613+ matches ! ( c. kind( ) , ConstKind :: Param ( _) )
614+ // and the goal can never in any way use this clause because it
615+ // doesn't mention any const params
616+ && !goal. predicate . has_const_param ( )
617+ // then the clause is irrelevant to the outcome of the goal.
618+ ;
619+
620+ !irrelevant
621+ }
622+ ClauseKind :: WellFormed ( _) => true ,
623+ ClauseKind :: ConstEvaluatable ( c) => {
624+ let irrelevant =
625+ // If the const this clause bounds, is a generic parameter,
626+ // i.e. N is evaluatable as opposed to 3 is evaluatable
627+ matches ! ( c. kind( ) , ConstKind :: Param ( _) )
628+ // and the goal can never in any way use this clause because it
629+ // doesn't mention any const params
630+ && !goal. predicate . has_const_param ( )
631+ // then the clause is irrelevant to the outcome of the goal.
632+ ;
633+
634+ !irrelevant
635+ }
636+ ClauseKind :: HostEffect ( _) => true ,
637+ ClauseKind :: UnstableFeature ( _) => true ,
638+ }
639+ } ;
640+
641+ let any_clause_relevant_for_goal = goal
642+ . param_env
643+ . caller_bounds ( )
644+ . iter ( )
645+ . any ( |i| clause_relevant_for_goal_evaluation ( i. kind ( ) . skip_binder ( ) ) ) ;
646+
647+ // If no clause is relevant to the outcome of the goal, we can strip the `ParamEnv`.
648+ if !any_clause_relevant_for_goal {
649+ if !goal. param_env . caller_bounds ( ) . is_empty ( ) {
650+ tracing:: debug!(
651+ "stripping param env {:?} because it is irrelevant to prove {:?}" ,
652+ goal. param_env,
653+ goal. predicate
654+ ) ;
655+ }
656+ Goal { param_env : ParamEnv :: empty ( ) , predicate : goal. predicate }
657+ } else {
658+ goal
659+ }
660+ }
661+
566662 /// Recursively evaluates `goal`, returning the nested goals in case
567663 /// the nested goal is a `NormalizesTo` goal.
568664 ///
@@ -590,6 +686,7 @@ where
590686 ) ) ;
591687 }
592688
689+ let opaques = self . delegate . clone_opaque_types_lookup_table ( ) ;
593690 self . evaluate_goal_cold ( source, goal)
594691 }
595692
@@ -618,6 +715,8 @@ where
618715 . entered ( ) ;
619716
620717 let ( result, orig_values, canonical_goal, succeeded_in_erased) = ' retry_canonicalize: {
718+ let goal = self . try_strip_param_env ( goal) ;
719+
621720 let skip_erased_attempt = if typing_mode. is_coherence ( ) {
622721 true
623722 } else {
0 commit comments