Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,10 @@ pub struct ParamEnv<'tcx> {
}

impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
fn empty() -> Self {
Self::empty()
}

fn caller_bounds(self) -> impl inherent::SliceLike<Item = ty::Clause<'tcx>> {
self.caller_bounds()
}
Expand Down
113 changes: 113 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: Clone>(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<I, I::Predicate>) -> Goal<I, I::Predicate> {
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<I>| -> 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.
///
Expand Down Expand Up @@ -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<I, I::Predicate>,
) -> Result<(NestedNormalizationGoals<I>, GoalEvaluation<I>), 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);

Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_ty_utils/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ty_utils/src/sig_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand All @@ -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()
}
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/inherent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {

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

Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_type_ir/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,24 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
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 {
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/traits/next-solver/use-const-generic-in-opaque.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ compile-flags: -Znext-solver
//@ check-pass

fn foo<const N: usize>() -> impl Iterator<Item = [u8; N]> {
std::iter::empty()
}

fn main() {}
Loading