Skip to content

Commit 388b5bc

Browse files
committed
strip param envs
1 parent 9639eef commit 388b5bc

4 files changed

Lines changed: 113 additions & 1 deletion

File tree

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,10 @@ pub struct ParamEnv<'tcx> {
10091009
}
10101010

10111011
impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
1012+
fn empty() -> Self {
1013+
Self::empty()
1014+
}
1015+
10121016
fn caller_bounds(self) -> impl inherent::SliceLike<Item = ty::Clause<'tcx>> {
10131017
self.caller_bounds()
10141018
}

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_type_ir::solve::{
1515
RerunNonErased, RerunReason, RerunResultExt, SmallCopyList,
1616
};
1717
use 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 {

compiler/rustc_type_ir/src/inherent.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
629629

630630
#[rust_analyzer::prefer_underscore_import]
631631
pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
632+
fn empty() -> Self;
632633
fn caller_bounds(self) -> impl SliceLike<Item = I::Clause>;
633634
}
634635

compiler/rustc_type_ir/src/visit.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,14 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
317317
self.has_type_flags(TypeFlags::HAS_PARAM)
318318
}
319319

320+
fn has_type_param(&self) -> bool {
321+
self.has_type_flags(TypeFlags::HAS_TY_PARAM)
322+
}
323+
324+
fn has_const_param(&self) -> bool {
325+
self.has_type_flags(TypeFlags::HAS_CT_PARAM)
326+
}
327+
320328
/// "Free" regions in this context means that it has any region
321329
/// that is not (a) erased or (b) late-bound.
322330
fn has_free_regions(&self) -> bool {

0 commit comments

Comments
 (0)