Skip to content

Commit 5c39a2a

Browse files
committed
add cycle-reporting logic
Fixes #33344
1 parent 957500b commit 5c39a2a

File tree

6 files changed

+151
-89
lines changed

6 files changed

+151
-89
lines changed

src/librustc/traits/error_reporting.rs

+1
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
513513
/// going to help).
514514
pub fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
515515
let cycle = self.resolve_type_vars_if_possible(&cycle.to_owned());
516+
assert!(cycle.len() > 0);
516517

517518
debug!("report_overflow_error_cycle: cycle={:?}", cycle);
518519

src/librustc/traits/fulfill.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,13 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
314314
}).collect()))
315315
}
316316

317-
fn process_backedge(&mut self, cycle: &[Self::Obligation])
317+
fn process_backedge<'c, I>(&mut self, cycle: I)
318+
where I: Clone + Iterator<Item=*const Self::Obligation>,
318319
{
319-
if coinductive_match(self.selcx, &cycle) {
320+
if coinductive_match(self.selcx, cycle.clone()) {
320321
debug!("process_child_obligations: coinductive match");
321322
} else {
322-
let cycle : Vec<_> = cycle.iter().map(|c| c.obligation.clone()).collect();
323+
let cycle : Vec<_> = cycle.map(|c| unsafe { &*c }.obligation.clone()).collect();
323324
self.selcx.infcx().report_overflow_error_cycle(&cycle);
324325
}
325326
}
@@ -535,21 +536,21 @@ fn process_predicate<'a, 'gcx, 'tcx>(
535536
/// - it also appears in the backtrace at some position `X`; and,
536537
/// - all the predicates at positions `X..` between `X` an the top are
537538
/// also defaulted traits.
538-
fn coinductive_match<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
539-
cycle: &[PendingPredicateObligation<'tcx>])
540-
-> bool
539+
fn coinductive_match<'a,'gcx,'tcx,I>(selcx: &mut SelectionContext<'a,'gcx,'tcx>, cycle: I) -> bool
540+
where I: Iterator<Item=*const PendingPredicateObligation<'tcx>>
541541
{
542+
let mut cycle = cycle;
542543
cycle
543-
.iter()
544544
.all(|bt_obligation| {
545+
let bt_obligation = unsafe { &*bt_obligation };
545546
let result = coinductive_obligation(selcx, &bt_obligation.obligation);
546547
debug!("coinductive_match: bt_obligation={:?} coinductive={}",
547548
bt_obligation, result);
548549
result
549550
})
550551
}
551552

552-
fn coinductive_obligation<'a, 'gcx, 'tcx>(selcx: &SelectionContext<'a, 'gcx, 'tcx>,
553+
fn coinductive_obligation<'a,'gcx,'tcx>(selcx: &SelectionContext<'a,'gcx,'tcx>,
553554
obligation: &PredicateObligation<'tcx>)
554555
-> bool {
555556
match obligation.predicate {

src/librustc_data_structures/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#![feature(nonzero)]
2929
#![feature(rustc_private)]
3030
#![feature(staged_api)]
31+
#![feature(unboxed_closures)]
32+
#![feature(fn_traits)]
3133

3234
#![cfg_attr(test, feature(test))]
3335

src/librustc_data_structures/obligation_forest/mod.rs

+109-54
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
use fnv::{FnvHashMap, FnvHashSet};
1919

20+
use std::cell::Cell;
2021
use std::collections::hash_map::Entry;
2122
use std::fmt::Debug;
2223
use std::hash;
@@ -41,7 +42,9 @@ pub trait ObligationProcessor {
4142
obligation: &mut Self::Obligation)
4243
-> Result<Option<Vec<Self::Obligation>>, Self::Error>;
4344

44-
fn process_backedge(&mut self, cycle: &[Self::Obligation]);
45+
// FIXME: crazy lifetime troubles
46+
fn process_backedge<I>(&mut self, cycle: I)
47+
where I: Clone + Iterator<Item=*const Self::Obligation>;
4548
}
4649

4750
struct SnapshotData {
@@ -77,7 +80,7 @@ pub struct Snapshot {
7780
#[derive(Debug)]
7881
struct Node<O> {
7982
obligation: O,
80-
state: NodeState,
83+
state: Cell<NodeState>,
8184

8285
// these both go *in the same direction*.
8386
parent: Option<NodeIndex>,
@@ -87,14 +90,17 @@ struct Node<O> {
8790
/// The state of one node in some tree within the forest. This
8891
/// represents the current state of processing for the obligation (of
8992
/// type `O`) associated with this node.
90-
#[derive(Debug, PartialEq, Eq)]
93+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
9194
enum NodeState {
9295
/// Obligation not yet resolved to success or error.
9396
Pending,
9497

9598
/// Used before garbage collection
9699
Success,
97100

101+
/// Used in DFS loops
102+
InLoop,
103+
98104
/// Obligation resolved to success; `num_incomplete_children`
99105
/// indicates the number of children still in an "incomplete"
100106
/// state. Incomplete means that either the child is still
@@ -225,7 +231,7 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
225231
let mut errors = vec![];
226232
for index in 0..self.nodes.len() {
227233
debug_assert!(!self.nodes[index].is_popped());
228-
if let NodeState::Pending = self.nodes[index].state {
234+
if let NodeState::Pending = self.nodes[index].state.get() {
229235
let backtrace = self.error_at(index);
230236
errors.push(Error {
231237
error: error.clone(),
@@ -244,7 +250,7 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
244250
{
245251
self.nodes
246252
.iter()
247-
.filter(|n| n.state == NodeState::Pending)
253+
.filter(|n| n.state.get() == NodeState::Pending)
248254
.map(|n| n.obligation.clone())
249255
.collect()
250256
}
@@ -270,7 +276,9 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
270276
self.nodes[index]);
271277

272278
let result = match self.nodes[index] {
273-
Node { state: NodeState::Pending, ref mut obligation, .. } => {
279+
Node { state: ref _state, ref mut obligation, .. }
280+
if _state.get() == NodeState::Pending =>
281+
{
274282
processor.process_obligation(obligation)
275283
}
276284
_ => continue
@@ -292,7 +300,7 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
292300
Some(NodeIndex::new(index)));
293301
}
294302

295-
self.nodes[index].state = NodeState::Success;
303+
self.nodes[index].state.set(NodeState::Success);
296304
}
297305
Err(err) => {
298306
let backtrace = self.error_at(index);
@@ -319,29 +327,69 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
319327
}
320328
}
321329

322-
pub fn process_cycles<P>(&mut self, _processor: &mut P)
330+
pub fn process_cycles<P>(&mut self, processor: &mut P)
323331
where P: ObligationProcessor<Obligation=O>
324332
{
325-
// TODO: implement
326-
for node in &mut self.nodes {
327-
if node.state == NodeState::Success {
328-
node.state = NodeState::Done;
329-
}
333+
let mut stack = self.scratch.take().unwrap();
334+
335+
for node in 0..self.nodes.len() {
336+
self.visit_node(&mut stack, processor, node);
330337
}
338+
339+
self.scratch = Some(stack);
340+
}
341+
342+
fn visit_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
343+
where P: ObligationProcessor<Obligation=O>
344+
{
345+
let node = &self.nodes[index];
346+
let state = node.state.get();
347+
match state {
348+
NodeState::InLoop => {
349+
let index =
350+
stack.iter().rposition(|n| *n == index).unwrap();
351+
// I need a Clone closure
352+
#[derive(Clone)]
353+
struct GetObligation<'a, O: 'a>(&'a [Node<O>]);
354+
impl<'a, 'b, O> FnOnce<(&'b usize,)> for GetObligation<'a, O> {
355+
type Output = *const O;
356+
extern "rust-call" fn call_once(self, args: (&'b usize,)) -> *const O {
357+
&self.0[*args.0].obligation
358+
}
359+
}
360+
impl<'a, 'b, O> FnMut<(&'b usize,)> for GetObligation<'a, O> {
361+
extern "rust-call" fn call_mut(&mut self, args: (&'b usize,)) -> *const O {
362+
&self.0[*args.0].obligation
363+
}
364+
}
365+
366+
processor.process_backedge(stack[index..].iter().map(GetObligation(&self.nodes)));
367+
}
368+
NodeState::Success => {
369+
node.state.set(NodeState::InLoop);
370+
stack.push(index);
371+
if let Some(parent) = node.parent {
372+
self.visit_node(stack, processor, parent.get());
373+
}
374+
for dependant in &node.dependants {
375+
self.visit_node(stack, processor, dependant.get());
376+
}
377+
stack.pop();
378+
node.state.set(NodeState::Done);
379+
},
380+
_ => return
381+
};
331382
}
332383

333384
/// Returns a vector of obligations for `p` and all of its
334385
/// ancestors, putting them into the error state in the process.
335-
/// The fact that the root is now marked as an error is used by
336-
/// `inherit_error` above to propagate the error state to the
337-
/// remainder of the tree.
338386
fn error_at(&mut self, p: usize) -> Vec<O> {
339387
let mut error_stack = self.scratch.take().unwrap();
340388
let mut trace = vec![];
341389

342390
let mut n = p;
343391
loop {
344-
self.nodes[n].state = NodeState::Error;
392+
self.nodes[n].state.set(NodeState::Error);
345393
trace.push(self.nodes[n].obligation.clone());
346394
error_stack.extend(self.nodes[n].dependants.iter().map(|x| x.get()));
347395

@@ -359,12 +407,13 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
359407
None => break
360408
};
361409

362-
match self.nodes[i].state {
410+
let node = &self.nodes[i];
411+
412+
match node.state.get() {
363413
NodeState::Error => continue,
364-
ref mut s => *s = NodeState::Error
414+
_ => node.state.set(NodeState::Error)
365415
}
366416

367-
let node = &self.nodes[i];
368417
error_stack.extend(
369418
node.dependants.iter().cloned().chain(node.parent).map(|x| x.get())
370419
);
@@ -374,41 +423,37 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
374423
trace
375424
}
376425

377-
fn mark_as_waiting(&mut self) {
378-
for node in &mut self.nodes {
379-
if node.state == NodeState::Waiting {
380-
node.state = NodeState::Success;
426+
/// Marks all nodes that depend on a pending node as "waiting".
427+
fn mark_as_waiting(&self) {
428+
for node in &self.nodes {
429+
if node.state.get() == NodeState::Waiting {
430+
node.state.set(NodeState::Success);
381431
}
382432
}
383433

384-
let mut undone_stack = self.scratch.take().unwrap();
385-
undone_stack.extend(
386-
self.nodes.iter().enumerate()
387-
.filter(|&(_i, n)| n.state == NodeState::Pending)
388-
.map(|(i, _n)| i));
389-
390-
loop {
391-
// non-standard `while let` to bypass #6393
392-
let i = match undone_stack.pop() {
393-
Some(i) => i,
394-
None => break
395-
};
434+
for node in &self.nodes {
435+
if node.state.get() == NodeState::Pending {
436+
self.mark_as_waiting_from(node)
437+
}
438+
}
439+
}
396440

397-
match self.nodes[i].state {
398-
NodeState::Pending | NodeState::Done => {},
399-
NodeState::Waiting | NodeState::Error => continue,
400-
ref mut s @ NodeState::Success => {
401-
*s = NodeState::Waiting;
402-
}
441+
fn mark_as_waiting_from(&self, node: &Node<O>) {
442+
match node.state.get() {
443+
NodeState::Pending | NodeState::Done => {},
444+
NodeState::Waiting | NodeState::Error | NodeState::InLoop => return,
445+
NodeState::Success => {
446+
node.state.set(NodeState::Waiting);
403447
}
448+
}
404449

405-
let node = &self.nodes[i];
406-
undone_stack.extend(
407-
node.dependants.iter().cloned().chain(node.parent).map(|x| x.get())
408-
);
450+
if let Some(parent) = node.parent {
451+
self.mark_as_waiting_from(&self.nodes[parent.get()]);
409452
}
410453

411-
self.scratch = Some(undone_stack);
454+
for dependant in &node.dependants {
455+
self.mark_as_waiting_from(&self.nodes[dependant.get()]);
456+
}
412457
}
413458

414459
/// Compresses the vector, removing all popped nodes. This adjusts
@@ -433,12 +478,22 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
433478
// self.nodes[i - dead_nodes..i] are all dead
434479
// self.nodes[i..] are unchanged
435480
for i in 0..self.nodes.len() {
436-
if let NodeState::Done = self.nodes[i].state {
437-
self.done_cache.insert(self.nodes[i].obligation.as_predicate().clone());
481+
match self.nodes[i].state.get() {
482+
NodeState::Done => {
483+
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
484+
// FIXME(HashMap): why can't I get my key back?
485+
self.done_cache.insert(self.nodes[i].obligation.as_predicate().clone());
486+
}
487+
NodeState::Error => {
488+
// We *intentionally* remove the node from the cache at this point. Otherwise
489+
// tests must come up with a different type on every type error they
490+
// check against.
491+
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
492+
}
493+
_ => {}
438494
}
439495

440496
if self.nodes[i].is_popped() {
441-
self.waiting_cache.remove(self.nodes[i].obligation.as_predicate());
442497
node_rewrites[i] = nodes_len;
443498
dead_nodes += 1;
444499
} else {
@@ -461,7 +516,7 @@ impl<O: Debug + ForestObligation> ObligationForest<O> {
461516
let successful = (0..dead_nodes)
462517
.map(|_| self.nodes.pop().unwrap())
463518
.flat_map(|node| {
464-
match node.state {
519+
match node.state.get() {
465520
NodeState::Error => None,
466521
NodeState::Done => Some(node.obligation),
467522
_ => unreachable!()
@@ -521,15 +576,15 @@ impl<O> Node<O> {
521576
Node {
522577
obligation: obligation,
523578
parent: parent,
524-
state: NodeState::Pending,
579+
state: Cell::new(NodeState::Pending),
525580
dependants: vec![],
526581
}
527582
}
528583

529584
fn is_popped(&self) -> bool {
530-
match self.state {
585+
match self.state.get() {
531586
NodeState::Pending | NodeState::Success | NodeState::Waiting => false,
532-
NodeState::Error | NodeState::Done => true,
587+
NodeState::Error | NodeState::Done | NodeState::InLoop => true,
533588
}
534589
}
535590
}

src/librustc_data_structures/obligation_forest/tree_index.rs

-27
This file was deleted.

0 commit comments

Comments
 (0)