Skip to content

Commit e512813

Browse files
committed
strip param envs
1 parent 7740d12 commit e512813

5 files changed

Lines changed: 134 additions & 0 deletions

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: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,108 @@ 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 goal_may_mention_any_params =
578+
goal.predicate.has_any_type_params() || goal.predicate.has_any_const_params();
579+
580+
let is_clause_relevant_for_goal = |clause: ClauseKind<I>| -> bool {
581+
match clause {
582+
ClauseKind::Trait(trait_predicate) => {
583+
let irrelevant =
584+
// If the self type of this clause mentions a generic parameter
585+
// i.e. T: Clone as opposed to i32: Clone
586+
trait_predicate.self_ty().has_any_type_params() &&
587+
// And the goal can never in any way use this clause because it
588+
// doesn't mention generic args, or variables that could unify with
589+
// the generic arg because of the one impl rule
590+
!goal_may_mention_any_params
591+
// then the clause is irrelevant to the outcome of the goal
592+
;
593+
594+
!irrelevant
595+
}
596+
// FIXME: atm never relevant for goal evaluation, but might be in the future so `true`
597+
// to avoid future performance cliffs
598+
ClauseKind::RegionOutlives(_) => true,
599+
ClauseKind::TypeOutlives(_) => true,
600+
ClauseKind::Projection(projection_predicate) => {
601+
let irrelevant =
602+
// If the self type of this clause mentions a generic parameter,
603+
// i.e. T: Clone as opposed to i32: Clone
604+
projection_predicate.self_ty().has_any_type_params() &&
605+
// And the goal can never in any way use this clause because it
606+
// doesn't mention generic args, or variables that could unify with
607+
// the generic arg because of the one impl rule
608+
!goal_may_mention_any_params
609+
// then the clause is irrelevant to the outcome of the goal
610+
;
611+
612+
!irrelevant
613+
}
614+
ClauseKind::ConstArgHasType(c, _) => {
615+
let irrelevant =
616+
// If the const this clause bounds, mentions a generic parameter,
617+
// i.e. N: usize as opposed to 4: usize
618+
c.has_any_const_params() &&
619+
// and the goal can never in any way use this clause because it
620+
// doesn't mention const generic args, or variables that could unify with
621+
// the generic arg because of the one impl rule
622+
!goal_may_mention_any_params
623+
// then the clause is irrelevant to the outcome of the goal
624+
;
625+
626+
!irrelevant
627+
}
628+
ClauseKind::WellFormed(_) => true,
629+
ClauseKind::ConstEvaluatable(c) => {
630+
let irrelevant =
631+
// If the const this clause bounds, mentions a generic parameter,
632+
// i.e. N is evaluatable as opposed to 3 is evaluatable
633+
c.has_any_const_params() &&
634+
// and the goal can never in any way use this clause because it
635+
// doesn't mention const generic args, or variables that could unify with
636+
// the generic arg because of the one impl rule
637+
!goal_may_mention_any_params
638+
// then the clause is irrelevant to the outcome of the goal
639+
;
640+
641+
!irrelevant
642+
}
643+
ClauseKind::HostEffect(_) => true,
644+
ClauseKind::UnstableFeature(_) => true,
645+
}
646+
};
647+
648+
let any_clause_relevant_for_goal = goal
649+
.param_env
650+
.caller_bounds()
651+
.iter()
652+
.any(|i| is_clause_relevant_for_goal(i.kind().skip_binder()));
653+
654+
if !any_clause_relevant_for_goal {
655+
if !goal.param_env.caller_bounds().is_empty() {
656+
tracing::debug!(
657+
"stripping param env {:?} because it is irrelevant to prove {:?}",
658+
goal.param_env,
659+
goal.predicate
660+
);
661+
}
662+
Goal { param_env: ParamEnv::empty(), predicate: goal.predicate }
663+
} else {
664+
goal
665+
}
666+
}
667+
566668
/// Recursively evaluates `goal`, returning the nested goals in case
567669
/// the nested goal is a `NormalizesTo` goal.
568670
///
@@ -605,6 +707,7 @@ where
605707
// duplicate entries.
606708
let opaque_types = self.delegate.clone_opaque_types_lookup_table();
607709
let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types));
710+
let goal = self.try_strip_param_env(goal);
608711
let typing_mode = self.typing_mode();
609712
let step_kind = self.step_kind_for_source(source);
610713

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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,24 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
317317
self.has_type_flags(TypeFlags::HAS_PARAM)
318318
}
319319

320+
fn has_any_type_params(&self) -> bool {
321+
self.has_type_flags(
322+
TypeFlags::HAS_TY_PARAM
323+
| TypeFlags::HAS_TY_BOUND
324+
| TypeFlags::HAS_TY_PLACEHOLDER
325+
| TypeFlags::HAS_TY_INFER,
326+
)
327+
}
328+
329+
fn has_any_const_params(&self) -> bool {
330+
self.has_type_flags(
331+
TypeFlags::HAS_CT_PARAM
332+
| TypeFlags::HAS_CT_BOUND
333+
| TypeFlags::HAS_CT_PLACEHOLDER
334+
| TypeFlags::HAS_CT_INFER,
335+
)
336+
}
337+
320338
/// "Free" regions in this context means that it has any region
321339
/// that is not (a) erased or (b) late-bound.
322340
fn has_free_regions(&self) -> bool {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ compile-flags: -Znext-solver
2+
//@ check-pass
3+
4+
fn foo<const N: usize>() -> impl Iterator<Item = [u8; N]> {
5+
std::iter::empty()
6+
}
7+
8+
fn main() {}

0 commit comments

Comments
 (0)