@@ -65,6 +65,12 @@ pub enum ProcessResult<O, E> {
65
65
Error(E),
66
66
}
67
67
68
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
69
+ struct ObligationTreeId(usize);
70
+
71
+ type ObligationTreeIdGenerator =
72
+ ::std::iter::Map<::std::ops::RangeFrom<usize>, fn(usize) -> ObligationTreeId>;
73
+
68
74
pub struct ObligationForest<O: ForestObligation> {
69
75
/// The list of obligations. In between calls to
70
76
/// `process_obligations`, this list only contains nodes in the
@@ -79,11 +85,25 @@ pub struct ObligationForest<O: ForestObligation> {
79
85
/// at a higher index than its parent. This is needed by the
80
86
/// backtrace iterator (which uses `split_at`).
81
87
nodes: Vec<Node<O>>,
88
+
82
89
/// A cache of predicates that have been successfully completed.
83
90
done_cache: FxHashSet<O::Predicate>,
91
+
84
92
/// An cache of the nodes in `nodes`, indexed by predicate.
85
93
waiting_cache: FxHashMap<O::Predicate, NodeIndex>,
94
+
86
95
scratch: Option<Vec<usize>>,
96
+
97
+ obligation_tree_id_generator: ObligationTreeIdGenerator,
98
+
99
+ /// Per tree error cache. This is used to deduplicate errors,
100
+ /// which is necessary to avoid trait resolution overflow in
101
+ /// some cases.
102
+ ///
103
+ /// See [this][details] for details.
104
+ ///
105
+ /// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780
106
+ error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::Predicate>>,
87
107
}
88
108
89
109
#[derive(Debug)]
@@ -99,6 +119,9 @@ struct Node<O> {
99
119
/// Obligations that depend on this obligation for their
100
120
/// completion. They must all be in a non-pending state.
101
121
dependents: Vec<NodeIndex>,
122
+
123
+ /// Identifier of the obligation tree to which this node belongs.
124
+ obligation_tree_id: ObligationTreeId,
102
125
}
103
126
104
127
/// The state of one node in some tree within the forest. This
@@ -165,6 +188,8 @@ impl<O: ForestObligation> ObligationForest<O> {
165
188
done_cache: FxHashSet(),
166
189
waiting_cache: FxHashMap(),
167
190
scratch: Some(vec![]),
191
+ obligation_tree_id_generator: (0..).map(|i| ObligationTreeId(i)),
192
+ error_cache: FxHashMap(),
168
193
}
169
194
}
170
195
@@ -214,9 +239,29 @@ impl<O: ForestObligation> ObligationForest<O> {
214
239
Entry::Vacant(v) => {
215
240
debug!("register_obligation_at({:?}, {:?}) - ok, new index is {}",
216
241
obligation, parent, self.nodes.len());
217
- v.insert(NodeIndex::new(self.nodes.len()));
218
- self.nodes.push(Node::new(parent, obligation));
219
- Ok(())
242
+
243
+ let obligation_tree_id = match parent {
244
+ Some(p) => {
245
+ let parent_node = &self.nodes[p.get()];
246
+ parent_node.obligation_tree_id
247
+ }
248
+ None => self.obligation_tree_id_generator.next().unwrap()
249
+ };
250
+
251
+ let already_failed =
252
+ parent.is_some()
253
+ && self.error_cache
254
+ .get(&obligation_tree_id)
255
+ .map(|errors| errors.contains(obligation.as_predicate()))
256
+ .unwrap_or(false);
257
+
258
+ if already_failed {
259
+ Err(())
260
+ } else {
261
+ v.insert(NodeIndex::new(self.nodes.len()));
262
+ self.nodes.push(Node::new(parent, obligation, obligation_tree_id));
263
+ Ok(())
264
+ }
220
265
}
221
266
}
222
267
}
@@ -251,6 +296,15 @@ impl<O: ForestObligation> ObligationForest<O> {
251
296
.collect()
252
297
}
253
298
299
+ fn insert_into_error_cache(&mut self, node_index: usize) {
300
+ let node = &self.nodes[node_index];
301
+
302
+ self.error_cache
303
+ .entry(node.obligation_tree_id)
304
+ .or_insert_with(|| FxHashSet())
305
+ .insert(node.obligation.as_predicate().clone());
306
+ }
307
+
254
308
/// Perform a pass through the obligation list. This must
255
309
/// be called in a loop until `outcome.stalled` is false.
256
310
///
@@ -264,22 +318,15 @@ impl<O: ForestObligation> ObligationForest<O> {
264
318
let mut stalled = true;
265
319
266
320
for index in 0..self.nodes.len() {
267
- debug!("process_obligations: node {} == {:?}",
268
- index,
269
- self.nodes[index]);
321
+ debug!("process_obligations: node {} == {:?}", index, self.nodes[index]);
270
322
271
323
let result = match self.nodes[index] {
272
- Node { ref state, ref mut obligation, .. }
273
- if state.get() == NodeState::Pending =>
274
- {
275
- processor.process_obligation(obligation)
276
- }
324
+ Node { ref state, ref mut obligation, .. } if state.get() == NodeState::Pending =>
325
+ processor.process_obligation(obligation),
277
326
_ => continue
278
327
};
279
328
280
- debug!("process_obligations: node {} got result {:?}",
281
- index,
282
- result);
329
+ debug!("process_obligations: node {} got result {:?}", index, result);
283
330
284
331
match result {
285
332
ProcessResult::Unchanged => {
@@ -420,13 +467,13 @@ impl<O: ForestObligation> ObligationForest<O> {
420
467
}
421
468
422
469
while let Some(i) = error_stack.pop() {
423
- let node = &self.nodes[i];
424
-
425
- match node.state.get() {
470
+ match self.nodes[i].state.get() {
426
471
NodeState::Error => continue,
427
- _ => node. state.set(NodeState::Error)
472
+ _ => self.nodes[i]. state.set(NodeState::Error),
428
473
}
429
474
475
+ let node = &self.nodes[i];
476
+
430
477
error_stack.extend(
431
478
node.parent.iter().chain(node.dependents.iter()).map(|x| x.get())
432
479
);
@@ -514,6 +561,7 @@ impl<O: ForestObligation> ObligationForest<O> {
514
561
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
515
562
node_rewrites[i] = nodes_len;
516
563
dead_nodes += 1;
564
+ self.insert_into_error_cache(i);
517
565
}
518
566
NodeState::OnDfsStack | NodeState::Success => unreachable!()
519
567
}
@@ -587,12 +635,17 @@ impl<O: ForestObligation> ObligationForest<O> {
587
635
}
588
636
589
637
impl<O> Node<O> {
590
- fn new(parent: Option<NodeIndex>, obligation: O) -> Node<O> {
638
+ fn new(
639
+ parent: Option<NodeIndex>,
640
+ obligation: O,
641
+ obligation_tree_id: ObligationTreeId
642
+ ) -> Node<O> {
591
643
Node {
592
644
obligation,
593
645
state: Cell::new(NodeState::Pending),
594
646
parent,
595
647
dependents: vec![],
648
+ obligation_tree_id,
596
649
}
597
650
}
598
651
}
0 commit comments