Skip to content

Commit 576655a

Browse files
committed
wip: trait_sel: skip elaboration of sizedness supertrait
As a performance optimization, skip elaborating the supertraits of `Sized`, and if a `MetaSized` obligation is being checked, then look for a `Sized` predicate in the parameter environment. This makes the `ParamEnv` smaller which should improve compiler performance as it avoids all the iteration over the larger `ParamEnv`.
1 parent 86f4f6c commit 576655a

19 files changed

+310
-85
lines changed

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ where
4545
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
4646
) -> Result<Candidate<I>, NoSolution> {
4747
if let Some(host_clause) = assumption.as_host_effect_clause() {
48-
if host_clause.def_id() == goal.predicate.def_id()
48+
let goal_did = goal.predicate.def_id();
49+
let host_clause_did = host_clause.def_id();
50+
if host_clause_did == goal_did
4951
&& host_clause.constness().satisfies(goal.predicate.constness)
5052
{
5153
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
@@ -55,21 +57,34 @@ where
5557
return Err(NoSolution);
5658
}
5759

58-
ecx.probe_trait_candidate(source).enter(|ecx| {
60+
return ecx.probe_trait_candidate(source).enter(|ecx| {
5961
let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
6062
ecx.eq(
6163
goal.param_env,
6264
goal.predicate.trait_ref,
6365
assumption_trait_pred.trait_ref,
6466
)?;
6567
then(ecx)
66-
})
67-
} else {
68-
Err(NoSolution)
68+
});
69+
}
70+
71+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
72+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
73+
// are syntactic sugar for a lack of bounds so don't need this.
74+
if ecx.cx().is_lang_item(goal_did, TraitSolverLangItem::MetaSized)
75+
&& ecx.cx().is_lang_item(host_clause_did, TraitSolverLangItem::Sized)
76+
&& {
77+
let expected_self_ty = ecx.resolve_vars_if_possible(goal.predicate.self_ty());
78+
let found_self_ty =
79+
ecx.resolve_vars_if_possible(host_clause.self_ty().skip_binder());
80+
expected_self_ty == found_self_ty
81+
}
82+
{
83+
return ecx.probe_trait_candidate(source).enter(then);
6984
}
70-
} else {
71-
Err(NoSolution)
7285
}
86+
87+
Err(NoSolution)
7388
}
7489

7590
/// Register additional assumptions for aliases corresponding to `~const` item bounds.

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+22-9
Original file line numberDiff line numberDiff line change
@@ -132,31 +132,44 @@ where
132132
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
133133
) -> Result<Candidate<I>, NoSolution> {
134134
if let Some(trait_clause) = assumption.as_trait_clause() {
135-
if trait_clause.def_id() == goal.predicate.def_id()
136-
&& trait_clause.polarity() == goal.predicate.polarity
137-
{
135+
let goal_did = goal.predicate.def_id();
136+
let trait_clause_did = trait_clause.def_id();
137+
if trait_clause_did == goal_did && trait_clause.polarity() == goal.predicate.polarity {
138138
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
139139
goal.predicate.trait_ref.args,
140140
trait_clause.skip_binder().trait_ref.args,
141141
) {
142142
return Err(NoSolution);
143143
}
144144

145-
ecx.probe_trait_candidate(source).enter(|ecx| {
145+
return ecx.probe_trait_candidate(source).enter(|ecx| {
146146
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
147147
ecx.eq(
148148
goal.param_env,
149149
goal.predicate.trait_ref,
150150
assumption_trait_pred.trait_ref,
151151
)?;
152152
then(ecx)
153-
})
154-
} else {
155-
Err(NoSolution)
153+
});
154+
}
155+
156+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
157+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
158+
// are syntactic sugar for a lack of bounds so don't need this.
159+
if ecx.cx().is_lang_item(goal_did, TraitSolverLangItem::MetaSized)
160+
&& ecx.cx().is_lang_item(trait_clause_did, TraitSolverLangItem::Sized)
161+
&& {
162+
let expected_self_ty = ecx.resolve_vars_if_possible(goal.predicate.self_ty());
163+
let found_self_ty =
164+
ecx.resolve_vars_if_possible(trait_clause.self_ty().skip_binder());
165+
expected_self_ty == found_self_ty
166+
}
167+
{
168+
return ecx.probe_trait_candidate(source).enter(then);
156169
}
157-
} else {
158-
Err(NoSolution)
159170
}
171+
172+
Err(NoSolution)
160173
}
161174

162175
fn consider_auto_trait_candidate(

compiler/rustc_trait_selection/src/traits/coherence.rs

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::fmt::Debug;
88

99
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1010
use rustc_errors::{Diag, EmissionGuarantee};
11+
use rustc_hir::LangItem;
1112
use rustc_hir::def::DefKind;
1213
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
1314
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
@@ -582,6 +583,21 @@ fn try_prove_negated_where_clause<'tcx>(
582583
return false;
583584
};
584585

586+
let maybe_meta_sized_self_ty = clause
587+
.as_trait_clause()
588+
.filter(|c| root_infcx.tcx.is_lang_item(c.def_id(), LangItem::MetaSized))
589+
.map(|c| c.self_ty());
590+
if let Some(meta_sized_self_ty) = maybe_meta_sized_self_ty
591+
&& util::sizedness_elab_opt_fast_path_from_paramenv(
592+
root_infcx,
593+
meta_sized_self_ty,
594+
param_env,
595+
)
596+
.is_some()
597+
{
598+
return false;
599+
}
600+
585601
// N.B. We don't need to use intercrate mode here because we're trying to prove
586602
// the *existence* of a negative goal, not the non-existence of a positive goal.
587603
// Without this, we over-eagerly register coherence ambiguity candidates when

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
1616
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
1717
use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode};
1818
use rustc_middle::{bug, span_bug};
19-
use rustc_type_ir::{Interner, elaborate};
19+
use rustc_type_ir::{Binder, Interner, elaborate};
2020
use tracing::{debug, instrument, trace};
2121

2222
use super::SelectionCandidate::*;
@@ -188,15 +188,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
188188
let mut distinct_normalized_bounds = FxHashSet::default();
189189
let _ = self.for_each_item_bound::<!>(
190190
placeholder_trait_predicate.self_ty(),
191-
|selcx, bound, idx| {
192-
let Some(bound) = bound.as_trait_clause() else {
191+
|selcx, clause, idx| {
192+
let Some(bound) = clause.as_trait_clause() else {
193193
return ControlFlow::Continue(());
194194
};
195195
if bound.polarity() != placeholder_trait_predicate.polarity {
196196
return ControlFlow::Continue(());
197197
}
198198

199199
selcx.infcx.probe(|_| {
200+
if selcx
201+
.tcx()
202+
.is_lang_item(placeholder_trait_predicate.def_id(), LangItem::MetaSized)
203+
&& util::sizedness_elab_opt_fast_path_from_clauses(
204+
selcx.infcx,
205+
Binder::dummy(placeholder_trait_predicate.self_ty()),
206+
[clause],
207+
)
208+
.is_some()
209+
{
210+
candidates.vec.push(ProjectionCandidate(idx));
211+
return;
212+
}
213+
200214
// We checked the polarity already
201215
match selcx.match_normalize_trait_ref(
202216
obligation,
@@ -235,6 +249,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
235249
) -> Result<(), SelectionError<'tcx>> {
236250
debug!(?stack.obligation);
237251

252+
if self.tcx().is_lang_item(stack.obligation.predicate.def_id(), LangItem::MetaSized)
253+
&& let Some(bound) = util::sizedness_elab_opt_fast_path_from_paramenv(
254+
self.infcx,
255+
stack.obligation.self_ty(),
256+
stack.obligation.param_env,
257+
)
258+
{
259+
candidates.vec.push(ParamCandidate(bound));
260+
return Ok(());
261+
}
262+
238263
let bounds = stack
239264
.obligation
240265
.param_env

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
1818
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast};
1919
use rustc_middle::{bug, span_bug};
2020
use rustc_span::def_id::DefId;
21-
use rustc_type_ir::elaborate;
21+
use rustc_type_ir::{Binder, elaborate};
2222
use thin_vec::thin_vec;
2323
use tracing::{debug, instrument};
2424

@@ -189,6 +189,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
189189
&mut obligations,
190190
);
191191

192+
if tcx.is_lang_item(obligation.predicate.def_id(), LangItem::MetaSized)
193+
&& util::sizedness_elab_opt_fast_path_from_traitrefs(
194+
self.infcx,
195+
obligation.predicate.self_ty(),
196+
[Binder::dummy(candidate)],
197+
)
198+
.is_some()
199+
{
200+
return Ok(obligations);
201+
}
202+
192203
obligations.extend(
193204
self.infcx
194205
.at(&obligation.cause, obligation.param_env)

compiler/rustc_trait_selection/src/traits/select/mod.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ use rustc_middle::ty::{
3131
TypingMode, Upcast,
3232
};
3333
use rustc_span::{Symbol, sym};
34-
use rustc_type_ir::elaborate;
3534
use rustc_type_ir::solve::SizedTraitKind;
35+
use rustc_type_ir::{Binder, elaborate};
36+
use thin_vec::thin_vec;
3637
use tracing::{debug, instrument, trace};
3738

3839
use self::EvaluationResult::*;
@@ -2641,6 +2642,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26412642
HigherRankedType,
26422643
poly_trait_ref,
26432644
);
2645+
2646+
if self.tcx().is_lang_item(predicate.def_id(), LangItem::MetaSized)
2647+
&& util::sizedness_elab_opt_fast_path_from_traitrefs(
2648+
self.infcx,
2649+
Binder::dummy(predicate.self_ty()),
2650+
[Binder::dummy(trait_ref)],
2651+
)
2652+
.is_some()
2653+
{
2654+
return Ok(thin_vec![]);
2655+
}
2656+
26442657
self.infcx
26452658
.at(&obligation.cause, obligation.param_env)
26462659
.eq(DefineOpaqueTypes::No, predicate.trait_ref, trait_ref)

compiler/rustc_trait_selection/src/traits/util.rs

+67
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use rustc_middle::ty::{
99
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
1010
};
1111
use rustc_span::Span;
12+
use rustc_type_ir::lang_items::TraitSolverLangItem;
13+
use rustc_type_ir::{InferCtxtLike, Interner, Upcast};
1214
use smallvec::{SmallVec, smallvec};
1315
use tracing::debug;
1416

@@ -504,3 +506,68 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
504506
}
505507
}
506508
}
509+
510+
/// To improve performance, Sizedness traits are not elaborated and so special-casing is required
511+
/// in the trait solver to find a `Sized` candidate for a `MetaSized` predicate. This is a helper
512+
/// function for that special-casing, intended to be used when the obligation to be proven has been
513+
/// confirmed to be `MetaSized`, and checking whether the `param_env` contains `Sized` for the same
514+
/// `self_ty` is all that remains to be done.
515+
// Ideally this would be re-used by the next solver, but there is no `InferCtxtLike` implementor.
516+
pub(crate) fn sizedness_elab_opt_fast_path_from_paramenv<Infcx, I>(
517+
infcx: &Infcx,
518+
self_ty: rustc_type_ir::Binder<I, I::Ty>,
519+
param_env: I::ParamEnv,
520+
) -> Option<rustc_type_ir::Binder<I, rustc_type_ir::TraitPredicate<I>>>
521+
where
522+
I: Interner,
523+
Infcx: InferCtxtLike<Interner = I>,
524+
{
525+
#[allow(rustc::usage_of_type_ir_inherent)]
526+
use rustc_type_ir::inherent::*;
527+
sizedness_elab_opt_fast_path_from_clauses(infcx, self_ty, param_env.caller_bounds().iter())
528+
}
529+
530+
/// Same as `sizedness_elab_opt_fast_path_from_paramenv` but takes an iterator of clauses instead
531+
/// of a parameter environment.
532+
pub(crate) fn sizedness_elab_opt_fast_path_from_clauses<Infcx, I, Trs>(
533+
infcx: &Infcx,
534+
self_ty: rustc_type_ir::Binder<I, I::Ty>,
535+
clauses: Trs,
536+
) -> Option<rustc_type_ir::Binder<I, rustc_type_ir::TraitPredicate<I>>>
537+
where
538+
I: Interner,
539+
Infcx: InferCtxtLike<Interner = I>,
540+
Trs: IntoIterator<Item = I::Clause>,
541+
{
542+
#[allow(rustc::usage_of_type_ir_inherent)]
543+
use rustc_type_ir::inherent::*;
544+
sizedness_elab_opt_fast_path_from_traitrefs(
545+
infcx,
546+
self_ty,
547+
clauses
548+
.into_iter()
549+
.filter_map(|p| p.as_trait_clause())
550+
.map(|c| c.map_bound(|c| c.trait_ref)),
551+
)
552+
.map(|f| f.map_bound(|f| f.upcast(infcx.cx())))
553+
}
554+
555+
/// Same as `sizedness_elab_opt_fast_path_from_paramenv` but takes an iterator of trait refs instead
556+
/// of a parameter environment.
557+
pub(crate) fn sizedness_elab_opt_fast_path_from_traitrefs<Infcx, I, TraitRefs>(
558+
infcx: &Infcx,
559+
self_ty: rustc_type_ir::Binder<I, I::Ty>,
560+
trait_refs: TraitRefs,
561+
) -> Option<rustc_type_ir::Binder<I, rustc_type_ir::TraitRef<I>>>
562+
where
563+
I: Interner,
564+
Infcx: InferCtxtLike<Interner = I>,
565+
TraitRefs: IntoIterator<Item = rustc_type_ir::Binder<I, rustc_type_ir::TraitRef<I>>>,
566+
{
567+
trait_refs.into_iter().find(|c| {
568+
let expected_self_ty = infcx.resolve_vars_if_possible(self_ty);
569+
let found_self_ty = infcx.resolve_vars_if_possible(c.self_ty());
570+
expected_self_ty == found_self_ty
571+
&& infcx.cx().is_lang_item(c.def_id(), TraitSolverLangItem::Sized)
572+
})
573+
}

0 commit comments

Comments
 (0)