Skip to content

Commit 6bfa6aa

Browse files
committed
Deduplicate errors in the obligation forest.
Fixes #40827.
1 parent d2ff5d6 commit 6bfa6aa

File tree

5 files changed

+162
-27
lines changed

5 files changed

+162
-27
lines changed

src/librustc_data_structures/obligation_forest/mod.rs

+72-19
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ pub enum ProcessResult<O, E> {
6565
Error(E),
6666
}
6767

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+
6874
pub struct ObligationForest<O: ForestObligation> {
6975
/// The list of obligations. In between calls to
7076
/// `process_obligations`, this list only contains nodes in the
@@ -79,11 +85,25 @@ pub struct ObligationForest<O: ForestObligation> {
7985
/// at a higher index than its parent. This is needed by the
8086
/// backtrace iterator (which uses `split_at`).
8187
nodes: Vec<Node<O>>,
88+
8289
/// A cache of predicates that have been successfully completed.
8390
done_cache: FxHashSet<O::Predicate>,
91+
8492
/// An cache of the nodes in `nodes`, indexed by predicate.
8593
waiting_cache: FxHashMap<O::Predicate, NodeIndex>,
94+
8695
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>>,
87107
}
88108

89109
#[derive(Debug)]
@@ -99,6 +119,9 @@ struct Node<O> {
99119
/// Obligations that depend on this obligation for their
100120
/// completion. They must all be in a non-pending state.
101121
dependents: Vec<NodeIndex>,
122+
123+
/// Identifier of the obligation tree to which this node belongs.
124+
obligation_tree_id: ObligationTreeId,
102125
}
103126

104127
/// The state of one node in some tree within the forest. This
@@ -165,6 +188,8 @@ impl<O: ForestObligation> ObligationForest<O> {
165188
done_cache: FxHashSet(),
166189
waiting_cache: FxHashMap(),
167190
scratch: Some(vec![]),
191+
obligation_tree_id_generator: (0..).map(|i| ObligationTreeId(i)),
192+
error_cache: FxHashMap(),
168193
}
169194
}
170195

@@ -214,9 +239,29 @@ impl<O: ForestObligation> ObligationForest<O> {
214239
Entry::Vacant(v) => {
215240
debug!("register_obligation_at({:?}, {:?}) - ok, new index is {}",
216241
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+
}
220265
}
221266
}
222267
}
@@ -251,6 +296,15 @@ impl<O: ForestObligation> ObligationForest<O> {
251296
.collect()
252297
}
253298

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+
254308
/// Perform a pass through the obligation list. This must
255309
/// be called in a loop until `outcome.stalled` is false.
256310
///
@@ -264,22 +318,15 @@ impl<O: ForestObligation> ObligationForest<O> {
264318
let mut stalled = true;
265319

266320
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]);
270322

271323
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),
277326
_ => continue
278327
};
279328

280-
debug!("process_obligations: node {} got result {:?}",
281-
index,
282-
result);
329+
debug!("process_obligations: node {} got result {:?}", index, result);
283330

284331
match result {
285332
ProcessResult::Unchanged => {
@@ -420,13 +467,13 @@ impl<O: ForestObligation> ObligationForest<O> {
420467
}
421468

422469
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() {
426471
NodeState::Error => continue,
427-
_ => node.state.set(NodeState::Error)
472+
_ => self.nodes[i].state.set(NodeState::Error),
428473
}
429474

475+
let node = &self.nodes[i];
476+
430477
error_stack.extend(
431478
node.parent.iter().chain(node.dependents.iter()).map(|x| x.get())
432479
);
@@ -514,6 +561,7 @@ impl<O: ForestObligation> ObligationForest<O> {
514561
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
515562
node_rewrites[i] = nodes_len;
516563
dead_nodes += 1;
564+
self.insert_into_error_cache(i);
517565
}
518566
NodeState::OnDfsStack | NodeState::Success => unreachable!()
519567
}
@@ -587,12 +635,17 @@ impl<O: ForestObligation> ObligationForest<O> {
587635
}
588636

589637
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> {
591643
Node {
592644
obligation,
593645
state: Cell::new(NodeState::Pending),
594646
parent,
595647
dependents: vec![],
648+
obligation_tree_id,
596649
}
597650
}
598651
}

src/test/ui/issue-40827.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::rc::Rc;
12+
use std::sync::Arc;
13+
14+
struct Foo(Arc<Bar>);
15+
16+
enum Bar {
17+
A(Rc<Foo>),
18+
B(Option<Foo>),
19+
}
20+
21+
fn f<T: Send>(_: T) {}
22+
23+
fn main() {
24+
f(Foo(Arc::new(Bar::B(None))));
25+
//~^ ERROR E0277
26+
//~| ERROR E0277
27+
}

src/test/ui/issue-40827.stderr

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error[E0277]: `std::rc::Rc<Foo>` cannot be sent between threads safely
2+
--> $DIR/issue-40827.rs:24:5
3+
|
4+
LL | f(Foo(Arc::new(Bar::B(None))));
5+
| ^ `std::rc::Rc<Foo>` cannot be sent between threads safely
6+
|
7+
= help: within `Bar`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<Foo>`
8+
= note: required because it appears within the type `Bar`
9+
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Bar>`
10+
= note: required because it appears within the type `Foo`
11+
note: required by `f`
12+
--> $DIR/issue-40827.rs:21:1
13+
|
14+
LL | fn f<T: Send>(_: T) {}
15+
| ^^^^^^^^^^^^^^^^^^^
16+
17+
error[E0277]: `std::rc::Rc<Foo>` cannot be shared between threads safely
18+
--> $DIR/issue-40827.rs:24:5
19+
|
20+
LL | f(Foo(Arc::new(Bar::B(None))));
21+
| ^ `std::rc::Rc<Foo>` cannot be shared between threads safely
22+
|
23+
= help: within `Bar`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc<Foo>`
24+
= note: required because it appears within the type `Bar`
25+
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<Bar>`
26+
= note: required because it appears within the type `Foo`
27+
note: required by `f`
28+
--> $DIR/issue-40827.rs:21:1
29+
|
30+
LL | fn f<T: Send>(_: T) {}
31+
| ^^^^^^^^^^^^^^^^^^^
32+
33+
error: aborting due to 2 previous errors
34+
35+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/recursion/recursive-requirements.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,7 @@ pub struct Bar {
2323
}
2424

2525
fn main() {
26-
let _: AssertSync<Foo> = unimplemented!(); //~ ERROR E0275
26+
let _: AssertSync<Foo> = unimplemented!();
27+
//~^ ERROR E0277
28+
//~| ERROR E0277
2729
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1-
error[E0275]: overflow evaluating the requirement `Foo: std::marker::Sync`
1+
error[E0277]: `*const Bar` cannot be shared between threads safely
22
--> $DIR/recursive-requirements.rs:26:12
33
|
4-
LL | let _: AssertSync<Foo> = unimplemented!(); //~ ERROR E0275
5-
| ^^^^^^^^^^^^^^^
4+
LL | let _: AssertSync<Foo> = unimplemented!();
5+
| ^^^^^^^^^^^^^^^ `*const Bar` cannot be shared between threads safely
66
|
7-
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
8-
= note: required because it appears within the type `std::marker::PhantomData<Foo>`
7+
= help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Bar`
8+
= note: required because it appears within the type `Foo`
9+
note: required by `AssertSync`
10+
--> $DIR/recursive-requirements.rs:13:1
11+
|
12+
LL | struct AssertSync<T: Sync>(PhantomData<T>);
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error[E0277]: `*const Foo` cannot be shared between threads safely
16+
--> $DIR/recursive-requirements.rs:26:12
17+
|
18+
LL | let _: AssertSync<Foo> = unimplemented!();
19+
| ^^^^^^^^^^^^^^^ `*const Foo` cannot be shared between threads safely
20+
|
21+
= help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Foo`
922
= note: required because it appears within the type `Bar`
1023
= note: required because it appears within the type `std::marker::PhantomData<Bar>`
1124
= note: required because it appears within the type `Foo`
25+
note: required by `AssertSync`
26+
--> $DIR/recursive-requirements.rs:13:1
27+
|
28+
LL | struct AssertSync<T: Sync>(PhantomData<T>);
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1230

13-
error: aborting due to previous error
31+
error: aborting due to 2 previous errors
1432

15-
For more information about this error, try `rustc --explain E0275`.
33+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)