Skip to content

Commit b751e7a

Browse files
committed
Auto merge of #160473 - xmakro:perf/wf-nominal-obligations, r=lcnr
perf: Push nominal obligations instead of returning them `WfPredicates::nominal_obligations` built a per-predicate `Vec` of origins and a fully instantiated `InstantiatedPredicates` before collecting the result. Instead, this PR walks the `predicates_of` parent chain by recursion and instantiate each level directly into the result, which is allocated once with the exact size. Most items have no parent, so that case is handled in `nominal_obligations` inline, so this common path stays free of calls.
2 parents cc05892 + 4884812 commit b751e7a

1 file changed

Lines changed: 78 additions & 73 deletions

File tree

  • compiler/rustc_trait_selection/src/traits

compiler/rustc_trait_selection/src/traits/wf.rs

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::iter;
77

88
use rustc_hir as hir;
99
use rustc_hir::attrs::lang_items::LangItem;
10-
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
10+
use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, PredicateObligations};
1111
use rustc_middle::bug;
1212
use rustc_middle::ty::{
1313
self, DelayedSet, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable,
@@ -16,7 +16,7 @@ use rustc_middle::ty::{
1616
use rustc_session::diagnostics::feature_err;
1717
use rustc_span::def_id::{DefId, LocalDefId};
1818
use rustc_span::{Span, sym};
19-
use tracing::{debug, instrument, trace};
19+
use tracing::{debug, instrument};
2020

2121
use crate::infer::InferCtxt;
2222
use crate::traits;
@@ -392,10 +392,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
392392
return;
393393
}
394394

395-
// if the trait predicate is not const, the wf obligations should not be const as well.
396-
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.args);
397-
398-
debug!("compute_trait_pred obligations {:?}", obligations);
399395
let param_env = self.param_env;
400396
let depth = self.recursion_depth;
401397

@@ -412,12 +408,20 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
412408
traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate)
413409
};
414410

411+
// if the trait predicate is not const, the wf obligations should not be const as well.
415412
if let Elaborate::All = elaborate {
413+
let mut obligations = PredicateObligations::new();
414+
self.nominal_obligations(trait_ref.def_id, trait_ref.args, |_, obligation| {
415+
obligations.push(obligation)
416+
});
417+
debug!("compute_trait_pred obligations {:?}", obligations);
416418
let implied_obligations = traits::util::elaborate(tcx, obligations);
417419
let implied_obligations = implied_obligations.map(extend);
418420
self.out.extend(implied_obligations);
419421
} else {
420-
self.out.extend(obligations);
422+
self.nominal_obligations(trait_ref.def_id, trait_ref.args, |this, obligation| {
423+
this.out.push(obligation)
424+
});
421425
}
422426

423427
self.out.extend(
@@ -481,8 +485,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
481485
// `i32: Clone`
482486
// `i32: Copy`
483487
// ]
484-
let obligations = self.nominal_obligations(data.expect_projection_def_id(), data.args);
485-
self.out.extend(obligations);
488+
self.nominal_obligations(data.expect_projection_def_id(), data.args, |this, obligation| {
489+
this.out.push(obligation)
490+
});
486491

487492
self.add_wf_preds_for_projection_args(data.args);
488493
}
@@ -511,8 +516,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
511516
&mut self.out,
512517
);
513518
let def_id = data.expect_inherent_def_id();
514-
let obligations = self.nominal_obligations(def_id, args);
515-
self.out.extend(obligations);
519+
self.nominal_obligations(def_id, args, |this, obligation| this.out.push(obligation));
516520
}
517521

518522
data.args.visit_with(self);
@@ -565,51 +569,51 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
565569
debug!(?self.out);
566570
}
567571

568-
#[instrument(level = "debug", skip(self))]
572+
#[instrument(level = "debug", skip(self, push_obligation))]
569573
fn nominal_obligations(
570574
&mut self,
571575
def_id: DefId,
572576
args: GenericArgsRef<'tcx>,
573-
) -> PredicateObligations<'tcx> {
577+
mut push_obligation: impl FnMut(&mut Self, PredicateObligation<'tcx>),
578+
) {
574579
// PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker
575580
// traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining
576581
// the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make
577582
// the compiler do a bunch of work needlessly.
578583
if self.tcx().is_lang_item(def_id, LangItem::Sized) {
579-
return Default::default();
584+
return;
580585
}
581586
if self.tcx().is_lang_item(def_id, LangItem::ConstParamTy)
582587
&& self.tcx().features().const_param_ty_unchecked()
583588
{
584-
return Default::default();
589+
return;
585590
}
586591

587-
let gen_clauses = self.tcx().clauses_of(def_id);
588-
let mut origins = vec![def_id; gen_clauses.clauses.len()];
589-
let mut head = gen_clauses;
590-
while let Some(parent) = head.parent {
591-
head = self.tcx().clauses_of(parent);
592-
origins.extend(iter::repeat(parent).take(head.clauses.len()));
592+
let tcx = self.tcx();
593+
let mut head = (def_id, tcx.clauses_of(def_id));
594+
let mut inner_levels = Vec::new(); // only allocates if a parent chain exists
595+
while let Some(parent) = head.1.parent {
596+
inner_levels.push(head);
597+
head = (parent, tcx.clauses_of(parent));
593598
}
594599

595-
let gen_clauses = gen_clauses.instantiate(self.tcx(), args);
596-
trace!("{:#?}", gen_clauses);
597-
debug_assert_eq!(gen_clauses.clauses.len(), origins.len());
598-
599-
iter::zip(gen_clauses, origins.into_iter().rev())
600-
.map(|((clause, span), origin_def_id)| {
601-
let code = ObligationCauseCode::WhereClause(origin_def_id, span);
602-
let cause = self.cause(code);
603-
traits::Obligation::with_depth(
604-
self.tcx(),
605-
cause,
606-
self.recursion_depth,
607-
self.param_env,
608-
clause.skip_norm_wip(),
609-
)
610-
})
611-
.filter(|clause| !clause.has_escaping_bound_vars())
612-
.collect()
600+
// Emit outermost first, as diagnostics rely on that order.
601+
for &(origin_def_id, clauses) in iter::once(&head).chain(inner_levels.iter().rev()) {
602+
for (clause, span) in clauses.instantiate_own(tcx, args) {
603+
if !clause.has_escaping_bound_vars() {
604+
let code = ObligationCauseCode::WhereClause(origin_def_id, span);
605+
let cause = self.cause(code);
606+
let obligation = traits::Obligation::with_depth(
607+
tcx,
608+
cause,
609+
self.recursion_depth,
610+
self.param_env,
611+
clause.skip_norm_wip(),
612+
);
613+
push_obligation(self, obligation);
614+
}
615+
}
616+
}
613617
}
614618

615619
fn add_wf_preds_for_dyn_ty(
@@ -818,8 +822,9 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
818822
..
819823
},
820824
) => {
821-
let obligations = self.nominal_obligations(def_id, args);
822-
self.out.extend(obligations);
825+
self.nominal_obligations(def_id, args, |this, obligation| {
826+
this.out.push(obligation)
827+
});
823828
}
824829
ty::Alias(_, data @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => {
825830
self.add_wf_preds_for_inherent_projection(data.into());
@@ -828,8 +833,9 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
828833

829834
ty::Adt(def, args) => {
830835
// WfNominalType
831-
let obligations = self.nominal_obligations(def.did(), args);
832-
self.out.extend(obligations);
836+
self.nominal_obligations(def.did(), args, |this, obligation| {
837+
this.out.push(obligation)
838+
});
833839
}
834840

835841
ty::FnDef(did, args) => {
@@ -842,8 +848,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
842848
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args).skip_norm_wip();
843849
fn_sig.output().skip_binder().visit_with(self);
844850

845-
let obligations = self.nominal_obligations(did, args);
846-
self.out.extend(obligations);
851+
self.nominal_obligations(did, args, |this, obligation| this.out.push(obligation));
847852
}
848853

849854
ty::Ref(r, rty, _) => {
@@ -870,8 +875,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
870875
// about the signature of the closure. We don't
871876
// have the problem of implied bounds here since
872877
// coroutines don't take arguments.
873-
let obligations = self.nominal_obligations(did, args);
874-
self.out.extend(obligations);
878+
self.nominal_obligations(did, args, |this, obligation| this.out.push(obligation));
875879
}
876880

877881
ty::Closure(did, args) => {
@@ -890,8 +894,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
890894
// can cause compiler crashes when the user abuses unsafe
891895
// code to procure such a closure.
892896
// See tests/ui/type-alias-impl-trait/wf_check_closures.rs
893-
let obligations = self.nominal_obligations(did, args);
894-
self.out.extend(obligations);
897+
self.nominal_obligations(did, args, |this, obligation| this.out.push(obligation));
895898
// Only check the upvar types for WF, not the rest
896899
// of the types within. This is needed because we
897900
// capture the signature and it may not be WF
@@ -918,8 +921,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
918921

919922
ty::CoroutineClosure(did, args) => {
920923
// See the above comments. The same apply to coroutine-closures.
921-
let obligations = self.nominal_obligations(did, args);
922-
self.out.extend(obligations);
924+
self.nominal_obligations(did, args, |this, obligation| this.out.push(obligation));
923925
let upvars = args.as_coroutine_closure().tupled_upvars_ty();
924926
return upvars.visit_with(self);
925927
}
@@ -988,27 +990,27 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
988990
//
989991
// See also: https://rustc-dev-guide.rust-lang.org/const-generics.html
990992
let args = principal.skip_binder().with_self_ty(self.tcx(), t).args;
991-
let obligations =
992-
self.nominal_obligations(principal_def_id, args).into_iter().filter(|o| {
993-
let kind = o.predicate.kind().skip_binder();
994-
match kind {
995-
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(
996-
ct,
997-
_,
998-
)) if matches!(ct.kind(), ty::ConstKind::Param(..)) => {
999-
// ConstArgHasType clauses are not higher kinded. Assert as
1000-
// such so we can fix this up if that ever changes.
1001-
assert!(o.predicate.kind().bound_vars().is_empty());
1002-
// In stable rust, variables from the trait object binder
1003-
// cannot be referenced by a ConstArgHasType clause. However,
1004-
// under `generic_const_parameter_types`, it can. Ignore those
1005-
// predicates for now, to not have HKT-ConstArgHasTypes.
1006-
!kind.has_escaping_bound_vars()
1007-
}
1008-
_ => false,
993+
self.nominal_obligations(principal_def_id, args, |this, obligation| {
994+
let kind = obligation.predicate.kind().skip_binder();
995+
let keep = match kind {
996+
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _))
997+
if matches!(ct.kind(), ty::ConstKind::Param(..)) =>
998+
{
999+
// ConstArgHasType clauses are not higher kinded. Assert as
1000+
// such so we can fix this up if that ever changes.
1001+
assert!(obligation.predicate.kind().bound_vars().is_empty());
1002+
// In stable rust, variables from the trait object binder
1003+
// cannot be referenced by a ConstArgHasType clause. However,
1004+
// under `generic_const_parameter_types`, it can. Ignore those
1005+
// predicates for now, to not have HKT-ConstArgHasTypes.
1006+
!kind.has_escaping_bound_vars()
10091007
}
1010-
});
1011-
self.out.extend(obligations);
1008+
_ => false,
1009+
};
1010+
if keep {
1011+
this.out.push(obligation);
1012+
}
1013+
});
10121014
}
10131015

10141016
if !t.has_escaping_bound_vars() {
@@ -1100,8 +1102,11 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
11001102
ty::AliasConstKind::Projection { def_id }
11011103
| ty::AliasConstKind::Free { def_id }
11021104
| ty::AliasConstKind::Anon { def_id } => {
1103-
let obligations = self.nominal_obligations(def_id, alias_const.args);
1104-
self.out.extend(obligations);
1105+
self.nominal_obligations(
1106+
def_id,
1107+
alias_const.args,
1108+
|this, obligation| this.out.push(obligation),
1109+
);
11051110
}
11061111
}
11071112
}

0 commit comments

Comments
 (0)