Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2b45cdb

Browse files
committedMar 17, 2025··
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 344cd34 commit 2b45cdb

18 files changed

+229
-80
lines changed
 

‎compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+16-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,28 @@ 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+
return ecx.probe_trait_candidate(source).enter(then);
6978
}
70-
} else {
71-
Err(NoSolution)
7279
}
80+
81+
Err(NoSolution)
7382
}
7483

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

‎compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -132,31 +132,38 @@ 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+
return ecx.probe_trait_candidate(source).enter(then);
156163
}
157-
} else {
158-
Err(NoSolution)
159164
}
165+
166+
Err(NoSolution)
160167
}
161168

162169
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+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
587+
// check for a `Sized` subtrait when looking for `MetaSized` (even for this negated clause).
588+
if clause
589+
.as_trait_clause()
590+
.map(|c| root_infcx.tcx.is_lang_item(c.def_id(), LangItem::MetaSized))
591+
.unwrap_or(false)
592+
&& param_env
593+
.caller_bounds()
594+
.iter()
595+
.filter_map(|p| p.as_trait_clause())
596+
.any(|c| root_infcx.tcx.is_lang_item(c.def_id(), LangItem::Sized))
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

+32
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
197197
}
198198

199199
selcx.infcx.probe(|_| {
200+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve
201+
// perf, so check for a `Sized` subtrait when looking for `MetaSized`.
202+
// `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't
203+
// need this.
204+
if selcx
205+
.tcx()
206+
.is_lang_item(placeholder_trait_predicate.def_id(), LangItem::MetaSized)
207+
&& selcx.tcx().is_lang_item(
208+
bound.map_bound(|pred| pred.trait_ref).def_id(),
209+
LangItem::Sized,
210+
)
211+
{
212+
candidates.vec.push(ProjectionCandidate(idx));
213+
return;
214+
}
215+
200216
// We checked the polarity already
201217
match selcx.match_normalize_trait_ref(
202218
obligation,
@@ -235,6 +251,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
235251
) -> Result<(), SelectionError<'tcx>> {
236252
debug!(?stack.obligation);
237253

254+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
255+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
256+
// are syntactic sugar for a lack of bounds so don't need this.
257+
if self.tcx().is_lang_item(stack.obligation.predicate.def_id(), LangItem::MetaSized)
258+
&& let Some(bound) = stack
259+
.obligation
260+
.param_env
261+
.caller_bounds()
262+
.iter()
263+
.filter_map(|p| p.as_trait_clause())
264+
.find(|c| self.tcx().is_lang_item(c.def_id(), LangItem::Sized))
265+
{
266+
candidates.vec.push(ParamCandidate(bound));
267+
return Ok(());
268+
}
269+
238270
let bounds = stack
239271
.obligation
240272
.param_env

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

+9
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
189189
&mut obligations,
190190
);
191191

192+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
193+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
194+
// are syntactic sugar for a lack of bounds so don't need this.
195+
if tcx.is_lang_item(obligation.predicate.def_id(), LangItem::MetaSized)
196+
&& tcx.is_lang_item(candidate.def_id, LangItem::Sized)
197+
{
198+
return Ok(obligations);
199+
}
200+
192201
obligations.extend(
193202
self.infcx
194203
.at(&obligation.cause, obligation.param_env)

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

+11
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use rustc_middle::ty::{
3333
use rustc_span::{Symbol, sym};
3434
use rustc_type_ir::elaborate;
3535
use rustc_type_ir::solve::SizedTraitKind;
36+
use thin_vec::thin_vec;
3637
use tracing::{debug, instrument, trace};
3738

3839
use self::EvaluationResult::*;
@@ -2641,6 +2642,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
26412642
HigherRankedType,
26422643
poly_trait_ref,
26432644
);
2645+
2646+
// PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so
2647+
// check for a `Sized` subtrait when looking for `MetaSized`. `PointeeSized` bounds
2648+
// are syntactic sugar for a lack of bounds so don't need this.
2649+
if self.tcx().is_lang_item(predicate.def_id(), LangItem::MetaSized)
2650+
&& self.tcx().is_lang_item(trait_ref.def_id, LangItem::Sized)
2651+
{
2652+
return Ok(thin_vec![]);
2653+
}
2654+
26442655
self.infcx
26452656
.at(&obligation.cause, obligation.param_env)
26462657
.eq(DefineOpaqueTypes::No, predicate.trait_ref, trait_ref)

‎compiler/rustc_type_ir/src/elaborate.rs

+47-5
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

@@ -79,20 +80,57 @@ pub fn elaborate<I: Interner, O: Elaboratable<I>>(
7980
) -> Elaborator<I, O> {
8081
let mut elaborator =
8182
Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All };
82-
elaborator.extend_deduped(obligations);
83+
elaborator.extend_deduped(None, obligations);
8384
elaborator
8485
}
8586

8687
impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
87-
fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
88+
/// Adds `obligations` to the stack. `current_clause` is the clause which was elaborated to
89+
/// produce these obligations.
90+
fn extend_deduped(
91+
&mut self,
92+
current_clause: Option<I::Clause>,
93+
obligations: impl IntoIterator<Item = O>,
94+
) {
8895
// Only keep those bounds that we haven't already seen.
8996
// This is necessary to prevent infinite recursion in some
9097
// cases. One common case is when people define
9198
// `trait Sized: Sized { }` rather than `trait Sized { }`.
9299
self.stack.extend(
93-
obligations.into_iter().filter(|o| {
94-
self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind()))
95-
}),
100+
obligations
101+
.into_iter()
102+
.filter(|o| self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind())))
103+
.filter(|o| {
104+
let Some(current_clause) = current_clause else {
105+
return true;
106+
};
107+
let Some(next_clause) = o.predicate().as_clause() else {
108+
return true;
109+
};
110+
111+
let current_did = match current_clause.kind().skip_binder() {
112+
ty::ClauseKind::Trait(data) => data.def_id(),
113+
ty::ClauseKind::HostEffect(data) => data.def_id(),
114+
_ => return true,
115+
};
116+
let next_did = match next_clause.kind().skip_binder() {
117+
ty::ClauseKind::Trait(data) => data.def_id(),
118+
ty::ClauseKind::HostEffect(data) => data.def_id(),
119+
_ => return true,
120+
};
121+
122+
// PERF(sized-hierarchy): To avoid iterating over sizedness supertraits in
123+
// parameter environments, as an optimisation, sizedness supertraits aren't
124+
// elaborated, so check if a `Sized` obligation is being elaborated to a
125+
// `MetaSized` obligation and emit it. Candidate assembly and confirmation
126+
// are modified to check for the `Sized` subtrait when a `MetaSized` obligation
127+
// is present.
128+
if self.cx.is_lang_item(current_did, TraitSolverLangItem::Sized) {
129+
!self.cx.is_lang_item(next_did, TraitSolverLangItem::MetaSized)
130+
} else {
131+
true
132+
}
133+
}),
96134
);
97135
}
98136

@@ -132,12 +170,14 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
132170
// Get predicates implied by the trait, or only super predicates if we only care about self predicates.
133171
match self.mode {
134172
Filter::All => self.extend_deduped(
173+
Some(clause),
135174
cx.explicit_implied_predicates_of(data.def_id())
136175
.iter_identity()
137176
.enumerate()
138177
.map(map_to_child_clause),
139178
),
140179
Filter::OnlySelf => self.extend_deduped(
180+
Some(clause),
141181
cx.explicit_super_predicates_of(data.def_id())
142182
.iter_identity()
143183
.enumerate()
@@ -147,6 +187,7 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
147187
}
148188
// `T: ~const Trait` implies `T: ~const Supertrait`.
149189
ty::ClauseKind::HostEffect(data) => self.extend_deduped(
190+
Some(clause),
150191
cx.explicit_implied_const_bounds(data.def_id()).iter_identity().map(|trait_ref| {
151192
elaboratable.child(
152193
trait_ref
@@ -177,6 +218,7 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
177218
let mut components = smallvec![];
178219
push_outlives_components(cx, ty_max, &mut components);
179220
self.extend_deduped(
221+
Some(clause),
180222
components
181223
.into_iter()
182224
.filter_map(|component| elaborate_component_to_clause(cx, component, r_min))

‎tests/ui/attributes/dump-preds.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ LL | type Assoc<P: Eq>: std::ops::Deref<Target = ()>
4242
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::ops::Deref>, polarity:Positive), bound_vars: [] }
4343
= note: Binder { value: HostEffectPredicate { trait_ref: <<Self as Trait<T>>::Assoc<P> as std::marker::Sized>, constness: Const }, bound_vars: [] }
4444
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::marker::Sized>, polarity:Positive), bound_vars: [] }
45-
= note: Binder { value: TraitPredicate(<<Self as Trait<T>>::Assoc<P> as std::marker::MetaSized>, polarity:Positive), bound_vars: [] }
4645

4746
error: aborting due to 3 previous errors
4847

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

+2
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

+28-2
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

+1-1
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>)
99
where
1010
for<'b> &'b T: A,
1111
<&'static T as A>::X: Sized

0 commit comments

Comments
 (0)
Please sign in to comment.