diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index b713b2285a65f..c079146edbf42 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -208,11 +208,17 @@ impl ObligationForest { /// /// This CAN be done in a snapshot pub fn register_obligation(&mut self, obligation: O) { - self.register_obligation_at(obligation, None) + // Ignore errors here - there is no guarantee of success. + let _ = self.register_obligation_at(obligation, None); } - fn register_obligation_at(&mut self, obligation: O, parent: Option) { - if self.done_cache.contains(obligation.as_predicate()) { return } + // returns Err(()) if we already know this obligation failed. + fn register_obligation_at(&mut self, obligation: O, parent: Option) + -> Result<(), ()> + { + if self.done_cache.contains(obligation.as_predicate()) { + return Ok(()) + } match self.waiting_cache.entry(obligation.as_predicate().clone()) { Entry::Occupied(o) => { @@ -226,6 +232,11 @@ impl ObligationForest { self.nodes[o.get().get()].dependents.push(parent); } } + if let NodeState::Error = self.nodes[o.get().get()].state.get() { + Err(()) + } else { + Ok(()) + } } Entry::Vacant(v) => { debug!("register_obligation_at({:?}, {:?}) - ok", @@ -233,8 +244,9 @@ impl ObligationForest { v.insert(NodeIndex::new(self.nodes.len())); self.cache_list.push(obligation.as_predicate().clone()); self.nodes.push(Node::new(parent, obligation)); + Ok(()) } - }; + } } /// Convert all remaining obligations to the given error. @@ -306,12 +318,19 @@ impl ObligationForest { Ok(Some(children)) => { // if we saw a Some(_) result, we are not (yet) stalled stalled = false; + self.nodes[index].state.set(NodeState::Success); + for child in children { - self.register_obligation_at(child, - Some(NodeIndex::new(index))); + let st = self.register_obligation_at( + child, + Some(NodeIndex::new(index)) + ); + if let Err(()) = st { + // error already reported - propagate it + // to our node. + self.error_at(index); + } } - - self.nodes[index].state.set(NodeState::Success); } Err(err) => { let backtrace = self.error_at(index); diff --git a/src/librustc_data_structures/obligation_forest/test.rs b/src/librustc_data_structures/obligation_forest/test.rs index 8eac8892a3efe..a95b2b84b34c8 100644 --- a/src/librustc_data_structures/obligation_forest/test.rs +++ b/src/librustc_data_structures/obligation_forest/test.rs @@ -418,3 +418,43 @@ fn orphan() { let errors = forest.to_errors(()); assert_eq!(errors.len(), 0); } + +#[test] +fn simultaneous_register_and_error() { + // check that registering a failed obligation works correctly + let mut forest = ObligationForest::new(); + forest.register_obligation("A"); + forest.register_obligation("B"); + + let Outcome { completed: ok, errors: err, .. } = + forest.process_obligations(&mut C(|obligation| { + match *obligation { + "A" => Err("An error"), + "B" => Ok(Some(vec!["A"])), + _ => unreachable!(), + } + }, |_|{})); + assert_eq!(ok.len(), 0); + assert_eq!(err, vec![super::Error { + error: "An error", + backtrace: vec!["A"] + }]); + + let mut forest = ObligationForest::new(); + forest.register_obligation("B"); + forest.register_obligation("A"); + + let Outcome { completed: ok, errors: err, .. } = + forest.process_obligations(&mut C(|obligation| { + match *obligation { + "A" => Err("An error"), + "B" => Ok(Some(vec!["A"])), + _ => unreachable!(), + } + }, |_|{})); + assert_eq!(ok.len(), 0); + assert_eq!(err, vec![super::Error { + error: "An error", + backtrace: vec!["A"] + }]); +} diff --git a/src/test/run-pass/issue-34503.rs b/src/test/run-pass/issue-34503.rs new file mode 100644 index 0000000000000..e6217243eeb4a --- /dev/null +++ b/src/test/run-pass/issue-34503.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +fn main() { + struct X; + trait Foo { + fn foo(&self) where (T, Option): Ord {} + fn bar(&self, x: &Option) -> bool + where Option: Ord { *x < *x } + } + impl Foo for () {} + let _ = &() as &Foo; +}