Skip to content

Commit 32741d5

Browse files
committed
Split process_obligation in two.
Because it really has two halves: - A read-only part that checks if further work is needed. - The further work part, which is much less hot. This makes things a bit clearer and nicer.
1 parent 281229a commit 32741d5

File tree

3 files changed

+44
-46
lines changed

3 files changed

+44
-46
lines changed

compiler/rustc_data_structures/src/obligation_forest/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ pub trait ObligationProcessor {
9696
type Obligation: ForestObligation;
9797
type Error: Debug;
9898

99+
fn needs_process_obligation(&self, obligation: &Self::Obligation) -> bool;
100+
99101
fn process_obligation(
100102
&mut self,
101103
obligation: &mut Self::Obligation,
@@ -143,7 +145,7 @@ pub struct ObligationForest<O: ForestObligation> {
143145

144146
/// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
145147
/// its contents are not guaranteed to match those of `nodes`. See the
146-
/// comments in [`Self::process_obligation` for details.
148+
/// comments in `Self::process_obligation` for details.
147149
active_cache: FxHashMap<O::CacheKey, usize>,
148150

149151
/// A vector reused in [Self::compress()] and [Self::find_cycles_from_node()],
@@ -417,15 +419,18 @@ impl<O: ForestObligation> ObligationForest<O> {
417419
// nodes. Therefore we use a `while` loop.
418420
let mut index = 0;
419421
while let Some(node) = self.nodes.get_mut(index) {
422+
if node.state.get() != NodeState::Pending
423+
|| !processor.needs_process_obligation(&node.obligation)
424+
{
425+
index += 1;
426+
continue;
427+
}
428+
420429
// `processor.process_obligation` can modify the predicate within
421430
// `node.obligation`, and that predicate is the key used for
422431
// `self.active_cache`. This means that `self.active_cache` can get
423432
// out of sync with `nodes`. It's not very common, but it does
424433
// happen, and code in `compress` has to allow for it.
425-
if node.state.get() != NodeState::Pending {
426-
index += 1;
427-
continue;
428-
}
429434

430435
match processor.process_obligation(&mut node.obligation) {
431436
ProcessResult::Unchanged => {

compiler/rustc_data_structures/src/obligation_forest/tests.rs

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ where
6565
type Obligation = O;
6666
type Error = E;
6767

68+
fn needs_process_obligation(&self, _obligation: &Self::Obligation) -> bool {
69+
true
70+
}
71+
6872
fn process_obligation(
6973
&mut self,
7074
obligation: &mut Self::Obligation,

compiler/rustc_trait_selection/src/traits/fulfill.rs

+30-41
Original file line numberDiff line numberDiff line change
@@ -253,22 +253,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
253253
type Obligation = PendingPredicateObligation<'tcx>;
254254
type Error = FulfillmentErrorCode<'tcx>;
255255

256-
/// Processes a predicate obligation and returns either:
257-
/// - `Changed(v)` if the predicate is true, presuming that `v` are also true
258-
/// - `Unchanged` if we don't have enough info to be sure
259-
/// - `Error(e)` if the predicate does not hold
256+
/// Identifies whether a predicate obligation needs processing.
260257
///
261258
/// This is always inlined, despite its size, because it has a single
262259
/// callsite and it is called *very* frequently.
263260
#[inline(always)]
264-
fn process_obligation(
265-
&mut self,
266-
pending_obligation: &mut Self::Obligation,
267-
) -> ProcessResult<Self::Obligation, Self::Error> {
261+
fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
268262
// If we were stalled on some unresolved variables, first check whether
269263
// any of them have been resolved; if not, don't bother doing more work
270264
// yet.
271-
let change = match pending_obligation.stalled_on.len() {
265+
match pending_obligation.stalled_on.len() {
272266
// Match arms are in order of frequency, which matters because this
273267
// code is so hot. 1 and 0 dominate; 2+ is fairly rare.
274268
1 => {
@@ -291,42 +285,18 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
291285
false
292286
})()
293287
}
294-
};
295-
296-
if !change {
297-
debug!(
298-
"process_predicate: pending obligation {:?} still stalled on {:?}",
299-
self.selcx.infcx().resolve_vars_if_possible(pending_obligation.obligation.clone()),
300-
pending_obligation.stalled_on
301-
);
302-
return ProcessResult::Unchanged;
303288
}
304-
305-
self.process_changed_obligations(pending_obligation)
306289
}
307290

308-
fn process_backedge<'c, I>(
309-
&mut self,
310-
cycle: I,
311-
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
312-
) where
313-
I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
314-
{
315-
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
316-
debug!("process_child_obligations: coinductive match");
317-
} else {
318-
let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
319-
self.selcx.infcx().report_overflow_error_cycle(&cycle);
320-
}
321-
}
322-
}
323-
324-
impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
325-
// The code calling this method is extremely hot and only rarely
326-
// actually uses this, so move this part of the code
327-
// out of that loop.
291+
/// Processes a predicate obligation and returns either:
292+
/// - `Changed(v)` if the predicate is true, presuming that `v` are also true
293+
/// - `Unchanged` if we don't have enough info to be sure
294+
/// - `Error(e)` if the predicate does not hold
295+
///
296+
/// This is called much less often than `needs_process_obligation`, so we
297+
/// never inline it.
328298
#[inline(never)]
329-
fn process_changed_obligations(
299+
fn process_obligation(
330300
&mut self,
331301
pending_obligation: &mut PendingPredicateObligation<'tcx>,
332302
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
@@ -341,6 +311,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
341311
self.selcx.infcx().resolve_vars_if_possible(obligation.predicate);
342312
}
343313

314+
let obligation = &pending_obligation.obligation;
315+
344316
debug!(?obligation, ?obligation.cause, "process_obligation");
345317

346318
let infcx = self.selcx.infcx();
@@ -655,6 +627,23 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
655627
}
656628
}
657629

630+
fn process_backedge<'c, I>(
631+
&mut self,
632+
cycle: I,
633+
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
634+
) where
635+
I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
636+
{
637+
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
638+
debug!("process_child_obligations: coinductive match");
639+
} else {
640+
let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
641+
self.selcx.infcx().report_overflow_error_cycle(&cycle);
642+
}
643+
}
644+
}
645+
646+
impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
658647
#[instrument(level = "debug", skip(self, obligation, stalled_on))]
659648
fn process_trait_obligation(
660649
&mut self,

0 commit comments

Comments
 (0)