diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index 094c1cec31acc..f159857e74467 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -65,6 +65,12 @@ pub enum ProcessResult { Error(E), } +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +struct ObligationTreeId(usize); + +type ObligationTreeIdGenerator = + ::std::iter::Map<::std::ops::RangeFrom, fn(usize) -> ObligationTreeId>; + pub struct ObligationForest { /// The list of obligations. In between calls to /// `process_obligations`, this list only contains nodes in the @@ -79,11 +85,25 @@ pub struct ObligationForest { /// at a higher index than its parent. This is needed by the /// backtrace iterator (which uses `split_at`). nodes: Vec>, + /// A cache of predicates that have been successfully completed. done_cache: FxHashSet, + /// An cache of the nodes in `nodes`, indexed by predicate. waiting_cache: FxHashMap, + scratch: Option>, + + obligation_tree_id_generator: ObligationTreeIdGenerator, + + /// Per tree error cache. This is used to deduplicate errors, + /// which is necessary to avoid trait resolution overflow in + /// some cases. + /// + /// See [this][details] for details. + /// + /// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780 + error_cache: FxHashMap>, } #[derive(Debug)] @@ -99,6 +119,9 @@ struct Node { /// Obligations that depend on this obligation for their /// completion. They must all be in a non-pending state. dependents: Vec, + + /// Identifier of the obligation tree to which this node belongs. + obligation_tree_id: ObligationTreeId, } /// The state of one node in some tree within the forest. This @@ -165,6 +188,8 @@ impl ObligationForest { done_cache: FxHashSet(), waiting_cache: FxHashMap(), scratch: Some(vec![]), + obligation_tree_id_generator: (0..).map(|i| ObligationTreeId(i)), + error_cache: FxHashMap(), } } @@ -214,9 +239,29 @@ impl ObligationForest { Entry::Vacant(v) => { debug!("register_obligation_at({:?}, {:?}) - ok, new index is {}", obligation, parent, self.nodes.len()); - v.insert(NodeIndex::new(self.nodes.len())); - self.nodes.push(Node::new(parent, obligation)); - Ok(()) + + let obligation_tree_id = match parent { + Some(p) => { + let parent_node = &self.nodes[p.get()]; + parent_node.obligation_tree_id + } + None => self.obligation_tree_id_generator.next().unwrap() + }; + + let already_failed = + parent.is_some() + && self.error_cache + .get(&obligation_tree_id) + .map(|errors| errors.contains(obligation.as_predicate())) + .unwrap_or(false); + + if already_failed { + Err(()) + } else { + v.insert(NodeIndex::new(self.nodes.len())); + self.nodes.push(Node::new(parent, obligation, obligation_tree_id)); + Ok(()) + } } } } @@ -251,6 +296,15 @@ impl ObligationForest { .collect() } + fn insert_into_error_cache(&mut self, node_index: usize) { + let node = &self.nodes[node_index]; + + self.error_cache + .entry(node.obligation_tree_id) + .or_insert_with(|| FxHashSet()) + .insert(node.obligation.as_predicate().clone()); + } + /// Perform a pass through the obligation list. This must /// be called in a loop until `outcome.stalled` is false. /// @@ -264,22 +318,15 @@ impl ObligationForest { let mut stalled = true; for index in 0..self.nodes.len() { - debug!("process_obligations: node {} == {:?}", - index, - self.nodes[index]); + debug!("process_obligations: node {} == {:?}", index, self.nodes[index]); let result = match self.nodes[index] { - Node { ref state, ref mut obligation, .. } - if state.get() == NodeState::Pending => - { - processor.process_obligation(obligation) - } + Node { ref state, ref mut obligation, .. } if state.get() == NodeState::Pending => + processor.process_obligation(obligation), _ => continue }; - debug!("process_obligations: node {} got result {:?}", - index, - result); + debug!("process_obligations: node {} got result {:?}", index, result); match result { ProcessResult::Unchanged => { @@ -420,13 +467,13 @@ impl ObligationForest { } while let Some(i) = error_stack.pop() { - let node = &self.nodes[i]; - - match node.state.get() { + match self.nodes[i].state.get() { NodeState::Error => continue, - _ => node.state.set(NodeState::Error) + _ => self.nodes[i].state.set(NodeState::Error), } + let node = &self.nodes[i]; + error_stack.extend( node.parent.iter().chain(node.dependents.iter()).map(|x| x.get()) ); @@ -514,6 +561,7 @@ impl ObligationForest { self.waiting_cache.remove(self.nodes[i].obligation.as_predicate()); node_rewrites[i] = nodes_len; dead_nodes += 1; + self.insert_into_error_cache(i); } NodeState::OnDfsStack | NodeState::Success => unreachable!() } @@ -587,12 +635,17 @@ impl ObligationForest { } impl Node { - fn new(parent: Option, obligation: O) -> Node { + fn new( + parent: Option, + obligation: O, + obligation_tree_id: ObligationTreeId + ) -> Node { Node { obligation, state: Cell::new(NodeState::Pending), parent, dependents: vec![], + obligation_tree_id, } } } diff --git a/src/test/ui/issue-40827.rs b/src/test/ui/issue-40827.rs new file mode 100644 index 0000000000000..4b079ace3ca32 --- /dev/null +++ b/src/test/ui/issue-40827.rs @@ -0,0 +1,27 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rc::Rc; +use std::sync::Arc; + +struct Foo(Arc); + +enum Bar { + A(Rc), + B(Option), +} + +fn f(_: T) {} + +fn main() { + f(Foo(Arc::new(Bar::B(None)))); + //~^ ERROR E0277 + //~| ERROR E0277 +} diff --git a/src/test/ui/issue-40827.stderr b/src/test/ui/issue-40827.stderr new file mode 100644 index 0000000000000..dd0ebf96d19e4 --- /dev/null +++ b/src/test/ui/issue-40827.stderr @@ -0,0 +1,35 @@ +error[E0277]: `std::rc::Rc` cannot be sent between threads safely + --> $DIR/issue-40827.rs:24:5 + | +LL | f(Foo(Arc::new(Bar::B(None)))); + | ^ `std::rc::Rc` cannot be sent between threads safely + | + = help: within `Bar`, the trait `std::marker::Send` is not implemented for `std::rc::Rc` + = note: required because it appears within the type `Bar` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc` + = note: required because it appears within the type `Foo` +note: required by `f` + --> $DIR/issue-40827.rs:21:1 + | +LL | fn f(_: T) {} + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `std::rc::Rc` cannot be shared between threads safely + --> $DIR/issue-40827.rs:24:5 + | +LL | f(Foo(Arc::new(Bar::B(None)))); + | ^ `std::rc::Rc` cannot be shared between threads safely + | + = help: within `Bar`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc` + = note: required because it appears within the type `Bar` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc` + = note: required because it appears within the type `Foo` +note: required by `f` + --> $DIR/issue-40827.rs:21:1 + | +LL | fn f(_: T) {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/recursion/recursive-requirements.rs b/src/test/ui/recursion/recursive-requirements.rs index 2c0f0338b2d15..dd6d2f6a2256a 100644 --- a/src/test/ui/recursion/recursive-requirements.rs +++ b/src/test/ui/recursion/recursive-requirements.rs @@ -23,5 +23,7 @@ pub struct Bar { } fn main() { - let _: AssertSync = unimplemented!(); //~ ERROR E0275 + let _: AssertSync = unimplemented!(); + //~^ ERROR E0277 + //~| ERROR E0277 } diff --git a/src/test/ui/recursion/recursive-requirements.stderr b/src/test/ui/recursion/recursive-requirements.stderr index d9e08102ab6a6..8fe282505e907 100644 --- a/src/test/ui/recursion/recursive-requirements.stderr +++ b/src/test/ui/recursion/recursive-requirements.stderr @@ -1,15 +1,33 @@ -error[E0275]: overflow evaluating the requirement `Foo: std::marker::Sync` +error[E0277]: `*const Bar` cannot be shared between threads safely --> $DIR/recursive-requirements.rs:26:12 | -LL | let _: AssertSync = unimplemented!(); //~ ERROR E0275 - | ^^^^^^^^^^^^^^^ +LL | let _: AssertSync = unimplemented!(); + | ^^^^^^^^^^^^^^^ `*const Bar` cannot be shared between threads safely | - = help: consider adding a `#![recursion_limit="128"]` attribute to your crate - = note: required because it appears within the type `std::marker::PhantomData` + = help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Bar` + = note: required because it appears within the type `Foo` +note: required by `AssertSync` + --> $DIR/recursive-requirements.rs:13:1 + | +LL | struct AssertSync(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `*const Foo` cannot be shared between threads safely + --> $DIR/recursive-requirements.rs:26:12 + | +LL | let _: AssertSync = unimplemented!(); + | ^^^^^^^^^^^^^^^ `*const Foo` cannot be shared between threads safely + | + = help: within `Foo`, the trait `std::marker::Sync` is not implemented for `*const Foo` = note: required because it appears within the type `Bar` = note: required because it appears within the type `std::marker::PhantomData` = note: required because it appears within the type `Foo` +note: required by `AssertSync` + --> $DIR/recursive-requirements.rs:13:1 + | +LL | struct AssertSync(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0277`.