Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ebd0dfd

Browse files
committed
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 a74155b commit ebd0dfd

File tree

16 files changed

+251
-40
lines changed

16 files changed

+251
-40
lines changed

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_type_ir::inherent::*;
66
use rustc_type_ir::lang_items::TraitSolverLangItem;
77
use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
88
use rustc_type_ir::{
9-
self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode,
9+
self as ty, Interner, Movability, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode,
1010
Upcast as _, elaborate,
1111
};
1212
use tracing::{instrument, trace};
@@ -131,16 +131,37 @@ where
131131
assumption: I::Clause,
132132
) -> Result<(), NoSolution> {
133133
if let Some(trait_clause) = assumption.as_trait_clause() {
134-
if trait_clause.def_id() == goal.predicate.def_id()
135-
&& trait_clause.polarity() == goal.predicate.polarity
136-
{
134+
if trait_clause.polarity() != goal.predicate.polarity {
135+
return Err(NoSolution);
136+
}
137+
138+
if trait_clause.def_id() == goal.predicate.def_id() {
137139
if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
138140
goal.predicate.trait_ref.args,
139141
trait_clause.skip_binder().trait_ref.args,
140142
) {
141143
return Ok(());
142144
}
143145
}
146+
147+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
148+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
149+
// are syntactic sugar for a lack of bounds so don't need this.
150+
if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
151+
&& ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
152+
{
153+
let meta_sized_clause = trait_clause
154+
.map_bound(|c| TraitPredicate {
155+
trait_ref: TraitRef::new_from_args(
156+
ecx.cx(),
157+
goal.predicate.def_id(),
158+
c.trait_ref.args,
159+
),
160+
polarity: c.polarity,
161+
})
162+
.upcast(ecx.cx());
163+
return Self::fast_reject_assumption(ecx, goal, meta_sized_clause);
164+
}
144165
}
145166

146167
Err(NoSolution)
@@ -153,6 +174,25 @@ where
153174
) -> Result<(), NoSolution> {
154175
let trait_clause = assumption.as_trait_clause().unwrap();
155176

177+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
178+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
179+
// are syntactic sugar for a lack of bounds so don't need this.
180+
if ecx.cx().is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::MetaSized)
181+
&& ecx.cx().is_lang_item(trait_clause.def_id(), TraitSolverLangItem::Sized)
182+
{
183+
let meta_sized_clause = trait_clause
184+
.map_bound(|c| TraitPredicate {
185+
trait_ref: TraitRef::new_from_args(
186+
ecx.cx(),
187+
goal.predicate.def_id(),
188+
c.trait_ref.args,
189+
),
190+
polarity: c.polarity,
191+
})
192+
.upcast(ecx.cx());
193+
return Self::match_assumption(ecx, goal, meta_sized_clause);
194+
}
195+
156196
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
157197
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
158198

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
208208
let mut distinct_normalized_bounds = FxHashSet::default();
209209
let _ = self.for_each_item_bound::<!>(
210210
placeholder_trait_predicate.self_ty(),
211-
|selcx, bound, idx| {
212-
let Some(bound) = bound.as_trait_clause() else {
211+
|selcx, clause, idx| {
212+
let Some(bound) = clause.as_trait_clause() else {
213213
return ControlFlow::Continue(());
214214
};
215215
if bound.polarity() != placeholder_trait_predicate.polarity {
216216
return ControlFlow::Continue(());
217217
}
218218

219219
selcx.infcx.probe(|_| {
220+
let bound =
221+
util::unelaborated_sizedness_candidate(selcx.infcx, obligation, bound);
222+
220223
// We checked the polarity already
221224
match selcx.match_normalize_trait_ref(
222225
obligation,
@@ -261,14 +264,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
261264
.caller_bounds()
262265
.iter()
263266
.filter_map(|p| p.as_trait_clause())
264-
// Micro-optimization: filter out predicates relating to different traits.
265-
.filter(|p| p.def_id() == stack.obligation.predicate.def_id())
267+
// Micro-optimization: filter out predicates with different polarities.
266268
.filter(|p| p.polarity() == stack.obligation.predicate.polarity());
267269

268270
let drcx = DeepRejectCtxt::relate_rigid_rigid(self.tcx());
269271
let obligation_args = stack.obligation.predicate.skip_binder().trait_ref.args;
270272
// Keep only those bounds which may apply, and propagate overflow if it occurs.
271273
for bound in bounds {
274+
let bound = util::unelaborated_sizedness_candidate(self.infcx, stack.obligation, bound);
275+
276+
// Micro-optimization: filter out predicates relating to different traits.
277+
if bound.def_id() != stack.obligation.predicate.def_id() {
278+
continue;
279+
}
280+
272281
let bound_trait_ref = bound.map_bound(|t| t.trait_ref);
273282
if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) {
274283
continue;

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
165165
)
166166
.break_value()
167167
.expect("expected to index into clause that exists");
168-
let candidate = candidate_predicate
168+
let candidate_predicate = candidate_predicate
169169
.as_trait_clause()
170-
.expect("projection candidate is not a trait predicate")
171-
.map_bound(|t| t.trait_ref);
170+
.expect("projection candidate is not a trait predicate");
171+
let candidate_predicate =
172+
util::unelaborated_sizedness_candidate(self.infcx, obligation, candidate_predicate);
173+
174+
let candidate = candidate_predicate.map_bound(|t| t.trait_ref);
172175

173176
let candidate = self.infcx.instantiate_binder_with_fresh_vars(
174177
obligation.cause.span,
@@ -225,6 +228,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
225228
) -> PredicateObligations<'tcx> {
226229
debug!(?obligation, ?param, "confirm_param_candidate");
227230

231+
let param = util::unelaborated_sizedness_candidate(
232+
self.infcx,
233+
obligation,
234+
param.upcast(self.infcx.tcx),
235+
)
236+
.map_bound(|p| p.trait_ref);
237+
228238
// During evaluation, we already checked that this
229239
// where-clause trait-ref could be unified with the obligation
230240
// trait-ref. Repeat that unification now without any

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,6 +2706,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
27062706
HigherRankedType,
27072707
poly_trait_ref,
27082708
);
2709+
27092710
self.infcx
27102711
.at(&obligation.cause, obligation.param_env)
27112712
.eq(DefineOpaqueTypes::No, predicate.trait_ref, trait_ref)

compiler/rustc_trait_selection/src/traits/util.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
44
use rustc_hir::LangItem;
55
use rustc_hir::def_id::DefId;
66
use rustc_infer::infer::InferCtxt;
7+
use rustc_infer::traits::PolyTraitObligation;
78
pub use rustc_infer::traits::util::*;
89
use rustc_middle::bug;
10+
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
911
use rustc_middle::ty::{
10-
self, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
12+
self, PolyTraitPredicate, SizedTraitKind, TraitPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
13+
TypeFolder, TypeSuperFoldable, TypeVisitableExt,
1114
};
1215
use rustc_span::Span;
1316
use smallvec::{SmallVec, smallvec};
@@ -528,3 +531,48 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
528531

529532
false
530533
}
534+
535+
/// To improve performance, sizedness traits are not elaborated and so special-casing is required
536+
/// in the trait solver to find a `Sized` candidate for a `MetaSized` obligation. Returns the
537+
/// predicate to used in the candidate for such a `obligation`, given a `candidate`.
538+
pub(crate) fn unelaborated_sizedness_candidate<'tcx>(
539+
infcx: &InferCtxt<'tcx>,
540+
obligation: &PolyTraitObligation<'tcx>,
541+
candidate: PolyTraitPredicate<'tcx>,
542+
) -> PolyTraitPredicate<'tcx> {
543+
use crate::infer::InferCtxtExt;
544+
if !infcx.tcx.is_lang_item(obligation.predicate.def_id(), LangItem::MetaSized)
545+
|| !infcx.tcx.is_lang_item(candidate.def_id(), LangItem::Sized)
546+
{
547+
return candidate;
548+
}
549+
550+
let drcx = DeepRejectCtxt::relate_rigid_rigid(infcx.tcx);
551+
let expected_self_ty = obligation.self_ty();
552+
let found_self_ty = candidate.self_ty();
553+
let matches = obligation.predicate.polarity() == candidate.polarity()
554+
&& drcx.args_may_unify(
555+
obligation.predicate.skip_binder().trait_ref.args,
556+
candidate.skip_binder().trait_ref.args,
557+
)
558+
&& expected_self_ty.bound_vars() == found_self_ty.bound_vars()
559+
&& {
560+
let expected_self_ty =
561+
infcx.tcx.instantiate_bound_regions_with_erased(expected_self_ty);
562+
let found_self_ty = infcx.tcx.instantiate_bound_regions_with_erased(found_self_ty);
563+
infcx.can_eq(obligation.param_env, expected_self_ty, found_self_ty)
564+
};
565+
566+
if matches {
567+
candidate.map_bound(|c| TraitPredicate {
568+
trait_ref: TraitRef::new_from_args(
569+
infcx.tcx,
570+
obligation.predicate.def_id(),
571+
c.trait_ref.args,
572+
),
573+
polarity: c.polarity,
574+
})
575+
} else {
576+
candidate
577+
}
578+
}

compiler/rustc_type_ir/src/elaborate.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use smallvec::smallvec;
44

55
use crate::data_structures::HashSet;
66
use crate::inherent::*;
7+
use crate::lang_items::TraitSolverLangItem;
78
use crate::outlives::{Component, push_outlives_components};
89
use crate::{self as ty, Interner, Upcast as _};
910

@@ -84,6 +85,8 @@ pub fn elaborate<I: Interner, O: Elaboratable<I>>(
8485
}
8586

8687
impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
88+
/// Adds `obligations` to the stack. `current_clause` is the clause which was elaborated to
89+
/// produce these obligations.
8790
fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
8891
// Only keep those bounds that we haven't already seen.
8992
// This is necessary to prevent infinite recursion in some
@@ -111,6 +114,18 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
111114
return;
112115
};
113116

117+
// PERF(sized-hierarchy): To avoid iterating over sizedness supertraits in
118+
// parameter environments, as an optimisation, sizedness supertraits aren't
119+
// elaborated, so check if a `Sized` obligation is being elaborated to a
120+
// `MetaSized` obligation and emit it. Candidate assembly and confirmation
121+
// are modified to check for the `Sized` subtrait when a `MetaSized` obligation
122+
// is present.
123+
if let Some(did) = clause.as_trait_clause().map(|c| c.def_id()) {
124+
if self.cx.is_lang_item(did, TraitSolverLangItem::Sized) {
125+
return;
126+
}
127+
}
128+
114129
let bound_clause = clause.kind();
115130
match bound_clause.skip_binder() {
116131
ty::ClauseKind::Trait(data) => {

tests/ui/attributes/dump-preds.stderr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ LL | type Assoc<P: Eq>: std::ops::Deref<Target = ()>
3636
= note: Binder { value: ProjectionPredicate(AliasTerm { args: [Alias(Projection, AliasTy { args: [Self/#0, T/#1, P/#2], def_id: DefId(..), .. })], def_id: DefId(..), .. }, Term::Ty(())), bound_vars: [] }
3737
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::ops::Deref>, polarity:Positive), bound_vars: [] }
3838
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::marker::Sized>, polarity:Positive), bound_vars: [] }
39-
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::marker::MetaSized>, polarity:Positive), bound_vars: [] }
4039

4140
error: aborting due to 3 previous errors
4241

tests/ui/extern/extern-types-unsized.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ fn main() {
2727

2828
assert_sized::<Bar<A>>();
2929
//~^ ERROR the size for values of type
30+
//~| ERROR the size for values of type
3031

3132
assert_sized::<Bar<Bar<A>>>();
3233
//~^ ERROR the size for values of type
34+
//~| ERROR the size for values of type
3335
}

tests/ui/extern/extern-types-unsized.stderr

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,21 @@ help: consider relaxing the implicit `Sized` restriction
5959
LL | fn assert_sized<T: ?Sized>() {}
6060
| ++++++++
6161

62+
error[E0277]: the size for values of type `A` cannot be known
63+
--> $DIR/extern-types-unsized.rs:28:20
64+
|
65+
LL | assert_sized::<Bar<A>>();
66+
| ^^^^^^ doesn't have a known size
67+
|
68+
= help: the trait `MetaSized` is not implemented for `A`
69+
note: required by a bound in `Bar`
70+
--> $DIR/extern-types-unsized.rs:14:12
71+
|
72+
LL | struct Bar<T: ?Sized> {
73+
| ^ required by this bound in `Bar`
74+
6275
error[E0277]: the size for values of type `A` cannot be known at compilation time
63-
--> $DIR/extern-types-unsized.rs:31:20
76+
--> $DIR/extern-types-unsized.rs:32:20
6477
|
6578
LL | assert_sized::<Bar<Bar<A>>>();
6679
| ^^^^^^^^^^^ doesn't have a size known at compile-time
@@ -81,6 +94,19 @@ help: consider relaxing the implicit `Sized` restriction
8194
LL | fn assert_sized<T: ?Sized>() {}
8295
| ++++++++
8396

84-
error: aborting due to 4 previous errors
97+
error[E0277]: the size for values of type `A` cannot be known
98+
--> $DIR/extern-types-unsized.rs:32:20
99+
|
100+
LL | assert_sized::<Bar<Bar<A>>>();
101+
| ^^^^^^^^^^^ doesn't have a known size
102+
|
103+
= help: the trait `MetaSized` is not implemented for `A`
104+
note: required by a bound in `Bar`
105+
--> $DIR/extern-types-unsized.rs:14:12
106+
|
107+
LL | struct Bar<T: ?Sized> {
108+
| ^ required by this bound in `Bar`
109+
110+
error: aborting due to 6 previous errors
85111

86112
For more information about this error, try `rustc --explain E0277`.

tests/ui/nll/issue-50716.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait A {
55
type X: ?Sized;
66
}
77

8-
fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) //~ ERROR mismatched types
8+
fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>) //~ ERROR
99
where
1010
for<'b> &'b T: A,
1111
<&'static T as A>::X: Sized

0 commit comments

Comments
 (0)