diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3d8ab41b37a31..59042eb199990 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1009,6 +1009,10 @@ pub struct ParamEnv<'tcx> { } impl<'tcx> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'tcx> { + fn empty() -> Self { + Self::empty() + } + fn caller_bounds(self) -> impl inherent::SliceLike> { self.caller_bounds() } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 97aaf076a9d9a..c9a90d3b66ca9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -563,6 +563,108 @@ where WontMakeProgress(stalled_certainty) } + /// This is a fast path optimization: + /// If *all* the self types of all the where clauses in the goals `ParamEnv` are a + /// generic arg (this is common if for example the `ParamEnv` only contains `T: Clone` + /// for some generic function `fn foo(t: T)`) + /// And the goal does not mention any generic args, then we already know for certain that + /// the evaluation of the goal doesn't depend on the `ParamEnv` in any way. That means that + /// it's equivalent to evaluating the goal with an *empty* `ParamEnv`. + /// + /// This is desirable because the `ParamEnv` is part of the cache key, so more cache keys will + /// match if they all mention the same empty `ParamEnv`. + fn try_strip_param_env(&self, goal: Goal) -> Goal { + let goal_may_mention_any_params = + goal.predicate.has_any_type_params() || goal.predicate.has_any_const_params(); + + let is_clause_relevant_for_goal = |clause: ClauseKind| -> bool { + match clause { + ClauseKind::Trait(trait_predicate) => { + let irrelevant = + // If the self type of this clause mentions a generic parameter + // i.e. T: Clone as opposed to i32: Clone + trait_predicate.self_ty().has_any_type_params() && + // And the goal can never in any way use this clause because it + // doesn't mention generic args, or variables that could unify with + // the generic arg because of the one impl rule + !goal_may_mention_any_params + // then the clause is irrelevant to the outcome of the goal + ; + + !irrelevant + } + // FIXME: atm never relevant for goal evaluation, but might be in the future so `true` + // to avoid future performance cliffs + ClauseKind::RegionOutlives(_) => true, + ClauseKind::TypeOutlives(_) => true, + ClauseKind::Projection(projection_predicate) => { + let irrelevant = + // If the self type of this clause mentions a generic parameter, + // i.e. T: Clone as opposed to i32: Clone + projection_predicate.self_ty().has_any_type_params() && + // And the goal can never in any way use this clause because it + // doesn't mention generic args, or variables that could unify with + // the generic arg because of the one impl rule + !goal_may_mention_any_params + // then the clause is irrelevant to the outcome of the goal + ; + + !irrelevant + } + ClauseKind::ConstArgHasType(c, _) => { + let irrelevant = + // If the const this clause bounds, mentions a generic parameter, + // i.e. N: usize as opposed to 4: usize + c.has_any_const_params() && + // and the goal can never in any way use this clause because it + // doesn't mention const generic args, or variables that could unify with + // the generic arg because of the one impl rule + !goal_may_mention_any_params + // then the clause is irrelevant to the outcome of the goal + ; + + !irrelevant + } + ClauseKind::WellFormed(_) => true, + ClauseKind::ConstEvaluatable(c) => { + let irrelevant = + // If the const this clause bounds, mentions a generic parameter, + // i.e. N is evaluatable as opposed to 3 is evaluatable + c.has_any_const_params() && + // and the goal can never in any way use this clause because it + // doesn't mention const generic args, or variables that could unify with + // the generic arg because of the one impl rule + !goal_may_mention_any_params + // then the clause is irrelevant to the outcome of the goal + ; + + !irrelevant + } + ClauseKind::HostEffect(_) => true, + ClauseKind::UnstableFeature(_) => true, + } + }; + + let any_clause_relevant_for_goal = goal + .param_env + .caller_bounds() + .iter() + .any(|i| is_clause_relevant_for_goal(i.kind().skip_binder())); + + if !any_clause_relevant_for_goal { + if !goal.param_env.caller_bounds().is_empty() { + tracing::debug!( + "stripping param env {:?} because it is irrelevant to prove {:?}", + goal.param_env, + goal.predicate + ); + } + Goal { param_env: ParamEnv::empty(), predicate: goal.predicate } + } else { + goal + } + } + /// Recursively evaluates `goal`, returning the nested goals in case /// the nested goal is a `NormalizesTo` goal. /// @@ -590,11 +692,22 @@ where )); } + self.evaluate_goal_cold(source, goal) + } + + #[cold] + #[inline(never)] + pub(super) fn evaluate_goal_cold( + &mut self, + source: GoalSource, + goal: Goal, + ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { // We only care about one entry per `OpaqueTypeKey` here, // so we only canonicalize the lookup table and ignore // duplicate entries. let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + let goal = self.try_strip_param_env(goal); let typing_mode = self.typing_mode(); let step_kind = self.step_kind_for_source(source); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index d95624b9a24d2..e053abcf87b25 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -338,19 +338,21 @@ fn opaque_types_defined_by<'tcx>( DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => { collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); } - DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} + DefKind::AssocTy + | DefKind::TyAlias + | DefKind::GlobalAsm + | DefKind::Ctor(_, _) + | DefKind::Variant => {} DefKind::OpaqueTy | DefKind::Mod | DefKind::Struct | DefKind::Union | DefKind::Enum - | DefKind::Variant | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::TyParam | DefKind::ConstParam - | DefKind::Ctor(_, _) | DefKind::Macro(_) | DefKind::ExternCrate | DefKind::Use diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..9218a0f2fa788 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -129,7 +129,6 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( DefKind::Variant | DefKind::TyParam | DefKind::ConstParam - | DefKind::Ctor(_, _) | DefKind::Field | DefKind::LifetimeParam => { span_bug!( @@ -144,7 +143,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Macro(_) | DefKind::GlobalAsm | DefKind::Mod - | DefKind::Use => {} + | DefKind::Use + | DefKind::Ctor(_, _) => {} } V::Result::output() } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index bffb6aa8060ed..86a24bcd14280 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -629,6 +629,7 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { #[rust_analyzer::prefer_underscore_import] pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { + fn empty() -> Self; fn caller_bounds(self) -> impl SliceLike; } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 492c37481298b..a0568a0c2251c 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -317,6 +317,24 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_type_flags(TypeFlags::HAS_PARAM) } + fn has_any_type_params(&self) -> bool { + self.has_type_flags( + TypeFlags::HAS_TY_PARAM + | TypeFlags::HAS_TY_BOUND + | TypeFlags::HAS_TY_PLACEHOLDER + | TypeFlags::HAS_TY_INFER, + ) + } + + fn has_any_const_params(&self) -> bool { + self.has_type_flags( + TypeFlags::HAS_CT_PARAM + | TypeFlags::HAS_CT_BOUND + | TypeFlags::HAS_CT_PLACEHOLDER + | TypeFlags::HAS_CT_INFER, + ) + } + /// "Free" regions in this context means that it has any region /// that is not (a) erased or (b) late-bound. fn has_free_regions(&self) -> bool { diff --git a/tests/ui/traits/next-solver/use-const-generic-in-opaque.rs b/tests/ui/traits/next-solver/use-const-generic-in-opaque.rs new file mode 100644 index 0000000000000..592066a363aa4 --- /dev/null +++ b/tests/ui/traits/next-solver/use-const-generic-in-opaque.rs @@ -0,0 +1,8 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +fn foo() -> impl Iterator { + std::iter::empty() +} + +fn main() {}