Skip to content

Commit a5c4370

Browse files
author
Markus Westerlind
committed
test
1 parent dee0521 commit a5c4370

File tree

2 files changed

+180
-41
lines changed
  • compiler
    • rustc_data_structures/src/obligation_forest
    • rustc_trait_selection/src/traits

2 files changed

+180
-41
lines changed

compiler/rustc_data_structures/src/obligation_forest/mod.rs

Lines changed: 137 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ pub trait ObligationProcessor {
104104
type Obligation: ForestObligation;
105105
type Error: Debug;
106106

107+
fn checked_process_obligation(
108+
&mut self,
109+
obligation: &mut Self::Obligation,
110+
) -> ProcessResult<Self::Obligation, Self::Error>;
111+
107112
fn process_obligation(
108113
&mut self,
109114
obligation: &mut Self::Obligation,
@@ -383,49 +388,18 @@ enum NodeState {
383388
Error,
384389
}
385390

386-
/// This trait allows us to have two different Outcome types:
387-
/// - the normal one that does as little as possible
388-
/// - one for tests that does some additional work and checking
389-
pub trait OutcomeTrait {
390-
type Error;
391-
type Obligation;
392-
393-
fn new() -> Self;
394-
fn mark_not_stalled(&mut self);
395-
fn is_stalled(&self) -> bool;
396-
fn record_completed(&mut self, outcome: &Self::Obligation);
397-
fn record_error(&mut self, error: Self::Error);
398-
}
399-
400391
#[derive(Debug)]
401392
pub struct Outcome<O, E> {
402393
/// Backtrace of obligations that were found to be in error.
403394
pub errors: Vec<Error<O, E>>,
404395
}
405396

406-
impl<O, E> OutcomeTrait for Outcome<O, E> {
407-
type Error = Error<O, E>;
408-
type Obligation = O;
409-
410-
fn new() -> Self {
411-
Self { stalled: true, errors: vec![] }
412-
}
413-
414-
fn mark_not_stalled(&mut self) {
415-
self.stalled = false;
416-
}
417-
418-
fn is_stalled(&self) -> bool {
419-
self.stalled
420-
}
421-
422-
fn record_completed(&mut self, _outcome: &Self::Obligation) {
423-
// do nothing
424-
}
425-
426-
fn record_error(&mut self, error: Self::Error) {
427-
self.errors.push(error)
428-
}
397+
/// Should `process_obligations` compute the `Outcome::completed` field of its
398+
/// result?
399+
#[derive(PartialEq, Copy, Clone)]
400+
pub enum DoCompleted {
401+
No,
402+
Yes,
429403
}
430404

431405
#[derive(Debug, PartialEq, Eq)]
@@ -602,14 +576,18 @@ impl<O: ForestObligation> ObligationForest<O> {
602576
/// be called in a loop until `outcome.stalled` is false.
603577
///
604578
/// This _cannot_ be unrolled (presently, at least).
605-
pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
579+
pub fn process_obligations<P>(&mut self, processor: &mut P) -> Outcome<O, P::Error>
606580
where
607581
P: ObligationProcessor<Obligation = O>,
608-
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
609582
{
610583
if self.watcher_offset.is_none() {
611584
assert!(!self.done);
612-
self.watcher_offset = Some(processor.register_variable_watcher());
585+
if self.nodes.len() > 100 {
586+
self.watcher_offset = Some(processor.register_variable_watcher());
587+
}
588+
if let Some(outcome) = self.process_obligations_simple(processor, do_completed) {
589+
return outcome;
590+
}
613591
}
614592
let mut errors = vec![];
615593
let mut stalled = true;
@@ -713,6 +691,7 @@ impl<O: ForestObligation> ObligationForest<O> {
713691
}
714692
}
715693
ProcessResult::Error(err) => {
694+
made_progress_this_iteration = true;
716695
stalled = false;
717696
errors.push(Error { error: err, backtrace: self.error_at(index) });
718697
}
@@ -732,10 +711,127 @@ impl<O: ForestObligation> ObligationForest<O> {
732711
self.mark_successes();
733712
self.process_cycles(processor);
734713
let completed = self.compress(do_completed);
735-
736714
Outcome { completed, errors }
737715
}
738716

717+
fn process_obligations_simple<P>(
718+
&mut self,
719+
processor: &mut P,
720+
do_completed: DoCompleted,
721+
) -> Option<Outcome<O, P::Error>>
722+
where
723+
P: ObligationProcessor<Obligation = O>,
724+
{
725+
let mut errors = vec![];
726+
let mut stalled = true;
727+
728+
// Note that the loop body can append new nodes, and those new nodes
729+
// will then be processed by subsequent iterations of the loop.
730+
//
731+
// We can't use an iterator for the loop because `self.nodes` is
732+
// appended to and the borrow checker would complain. We also can't use
733+
// `for index in 0..self.nodes.len() { ... }` because the range would
734+
// be computed with the initial length, and we would miss the appended
735+
// nodes. Therefore we use a `while` loop.
736+
loop {
737+
let mut i = 0;
738+
let mut made_progress_this_iteration = false;
739+
while let Some(&index) = self.pending_nodes.get(i) {
740+
let node = &mut self.nodes[index];
741+
// `processor.process_obligation` can modify the predicate within
742+
// `node.obligation`, and that predicate is the key used for
743+
// `self.active_cache`. This means that `self.active_cache` can get
744+
// out of sync with `nodes`. It's not very common, but it does
745+
// happen, and code in `compress` has to allow for it.
746+
if node.state.get() != NodeState::Pending {
747+
i += 1;
748+
continue;
749+
}
750+
751+
// `processor.process_obligation` can modify the predicate within
752+
// `node.obligation`, and that predicate is the key used for
753+
// `self.active_cache`. This means that `self.active_cache` can get
754+
// out of sync with `nodes`. It's not very common, but it does
755+
// happen, and code in `compress` has to allow for it.
756+
let before = node.obligation.as_cache_key();
757+
let result = processor.checked_process_obligation(&mut node.obligation);
758+
let after = node.obligation.as_cache_key();
759+
if before != after {
760+
node.alternative_predicates.push(before);
761+
}
762+
763+
match result {
764+
ProcessResult::Unchanged => {
765+
// No change in state.
766+
if self.watcher_offset.is_some() {
767+
let stalled_on = node.obligation.stalled_on();
768+
if stalled_on.is_empty() {
769+
// We stalled but the variables that caused it are unknown so we run
770+
// `index` again at the next opportunity
771+
self.stalled_on_unknown.push(index);
772+
} else {
773+
// Register every variable that we stalled on
774+
for var in stalled_on {
775+
self.stalled_on
776+
.entry(var.clone())
777+
.or_insert_with(|| {
778+
processor.watch_variable(var.clone());
779+
Vec::new()
780+
})
781+
.push(index);
782+
}
783+
}
784+
}
785+
}
786+
ProcessResult::Changed(children) => {
787+
// We are not (yet) stalled.
788+
stalled = false;
789+
node.state.set(NodeState::Success);
790+
made_progress_this_iteration = true;
791+
self.success_or_waiting_nodes.push(index);
792+
793+
for child in children {
794+
let st = self.register_obligation_at(child, Some(index));
795+
if let Err(()) = st {
796+
// Error already reported - propagate it
797+
// to our node.
798+
self.error_at(index);
799+
}
800+
}
801+
}
802+
ProcessResult::Error(err) => {
803+
stalled = false;
804+
errors.push(Error { error: err, backtrace: self.error_at(index) });
805+
}
806+
}
807+
i += 1;
808+
}
809+
810+
if stalled {
811+
// There's no need to perform marking, cycle processing and compression when nothing
812+
// changed.
813+
return Some(Outcome {
814+
completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None },
815+
errors,
816+
});
817+
}
818+
819+
if !made_progress_this_iteration {
820+
break;
821+
}
822+
823+
if self.watcher_offset.is_some() {
824+
return None;
825+
}
826+
827+
self.mark_successes();
828+
self.process_cycles(processor);
829+
self.compress(do_completed);
830+
}
831+
832+
Some(Outcome { completed: None, errors })
833+
}
834+
739835
/// Checks which nodes have been unblocked since the last time this was called. All nodes that
740836
/// were unblocked are added to the `unblocked` queue and all watches associated with the
741837
/// variables blocking those nodes are deregistered (since they are now instantiated, they will

compiler/rustc_trait_selection/src/traits/fulfill.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,49 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
278278
/// This is always inlined, despite its size, because it has a single
279279
/// callsite and it is called *very* frequently.
280280
#[inline(always)]
281+
fn checked_process_obligation(
282+
&mut self,
283+
pending_obligation: &mut Self::Obligation,
284+
) -> ProcessResult<Self::Obligation, Self::Error> {
285+
// If we were stalled on some unresolved variables, first check whether
286+
// any of them have been resolved; if not, don't bother doing more work
287+
// yet.
288+
let change = match pending_obligation.stalled_on.len() {
289+
// Match arms are in order of frequency, which matters because this
290+
// code is so hot. 1 and 0 dominate; 2+ is fairly rare.
291+
1 => {
292+
let infer_var = pending_obligation.stalled_on[0];
293+
self.selcx.infcx().ty_or_const_infer_var_changed(infer_var)
294+
}
295+
0 => {
296+
// In this case we haven't changed, but wish to make a change.
297+
true
298+
}
299+
_ => {
300+
// This `for` loop was once a call to `all()`, but this lower-level
301+
// form was a perf win. See #64545 for details.
302+
(|| {
303+
for &infer_var in &pending_obligation.stalled_on {
304+
if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) {
305+
return true;
306+
}
307+
}
308+
false
309+
})()
310+
}
311+
};
312+
if !change {
313+
debug!(
314+
"process_predicate: pending obligation {:?} still stalled on {:?}",
315+
self.selcx.infcx().resolve_vars_if_possible(&pending_obligation.obligation),
316+
pending_obligation.stalled_on
317+
);
318+
return ProcessResult::Unchanged;
319+
}
320+
321+
self.process_obligation(pending_obligation)
322+
}
323+
281324
fn process_obligation(
282325
&mut self,
283326
pending_obligation: &mut Self::Obligation,

0 commit comments

Comments
 (0)