Move predecessor cache outside of Body, use wrapper types to manage Cache and Body (WIP, amend this commit)

This commit is contained in:
Paul Daniel Faria 2019-10-09 23:22:58 -04:00
parent 9b335ce1a6
commit c0592faa67
6 changed files with 302 additions and 163 deletions

View File

@ -1,37 +1,42 @@
use rustc_index::vec::IndexVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use crate::ich::StableHashingContext;
use crate::mir::BasicBlock;
//use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
//use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
//use crate::ich::StableHashingContext;
use crate::mir::{BasicBlock, BasicBlockData, Body, LocalDecls, Location, Successors};
use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use std::iter;
use std::ops::{Index, IndexMut};
use std::vec::IntoIter;
#[derive(Clone, Debug)]
pub(in crate::mir) struct Cache {
pub(in crate::mir) predecessors: Option<IndexVec<BasicBlock, Vec<BasicBlock>>>
pub struct Cache {
predecessors: Option<IndexVec<BasicBlock, Vec<BasicBlock>>>,
}
impl rustc_serialize::Encodable for Cache {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
Encodable::encode(&(), s)
}
}
impl rustc_serialize::Decodable for Cache {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
Decodable::decode(d).map(|_v: ()| Self::new())
}
}
impl<'a> HashStable<StableHashingContext<'a>> for Cache {
fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) {
// Do nothing.
}
}
//impl<'tcx, T> rustc_serialize::Encodable for Cache<'tcx, T> {
// fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
// Encodable::encode(&(), s)
// }
//}
//
//impl<'tcx, T> rustc_serialize::Decodable for Cache<'tcx, T> {
// fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
// Decodable::decode(d).map(|_v: ()| Self::new())
// }
//}
//
//impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for Cache<'tcx, T> {
// fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) {
// // Do nothing.
// }
//}
impl Cache {
pub fn new() -> Self {
Cache {
predecessors: None
Self {
predecessors: None,
}
}
@ -40,8 +45,211 @@ impl Cache {
// FIXME: consider being more fine-grained
self.predecessors = None;
}
#[inline]
/// This will recompute the predecessors cache if it is not available
pub fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
if self.predecessors.is_none() {
let mut result = IndexVec::from_elem(vec![], body.basic_blocks());
for (bb, data) in body.basic_blocks().iter_enumerated() {
if let Some(ref term) = data.terminator {
for &tgt in term.successors() {
result[tgt].push(bb);
}
}
}
self.predecessors = Some(result)
}
self.predecessors.as_ref().unwrap()
}
#[inline]
pub fn predecessors_for(&mut self, bb: BasicBlock, body: &Body<'_>) -> &[BasicBlock] {
&self.predecessors(body)[bb]
}
#[inline]
pub fn predecessor_locations<'a>(&'a mut self, loc: Location, body: &'a Body<'a>) -> impl Iterator<Item = Location> + 'a {
let if_zero_locations = if loc.statement_index == 0 {
let predecessor_blocks = self.predecessors_for(loc.block, body);
let num_predecessor_blocks = predecessor_blocks.len();
Some(
(0..num_predecessor_blocks)
.map(move |i| predecessor_blocks[i])
.map(move |bb| body.terminator_loc(bb)),
)
} else {
None
};
let if_not_zero_locations = if loc.statement_index == 0 {
None
} else {
Some(Location { block: loc.block, statement_index: loc.statement_index - 1 })
};
if_zero_locations.into_iter().flatten().chain(if_not_zero_locations)
}
#[inline]
pub fn basic_blocks_mut<'a, 'tcx>(&mut self, body: &'a mut Body<'tcx>) -> &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
debug!("bbm: Clearing predecessors cache for body at: {:?}", body.span.data());
self.invalidate_predecessors();
&mut body.basic_blocks
}
#[inline]
pub fn basic_blocks_and_local_decls_mut<'a, 'tcx>(
&mut self,
body: &'a mut Body<'tcx>
) -> (&'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &'a mut LocalDecls<'tcx>) {
debug!("bbaldm: Clearing predecessors cache for body at: {:?}", body.span.data());
self.invalidate_predecessors();
(&mut body.basic_blocks, &mut body.local_decls)
}
}
CloneTypeFoldableAndLiftImpls! {
Cache,
pub struct OwningCache<'tcx> {
cache: Cache,
body: Body<'tcx>,
}
impl<'tcx> OwningCache<'tcx> {
pub fn borrow(&mut self) -> BorrowedCache<'_, 'tcx> {
BorrowedCache {
cache: &mut self.cache,
body: &self.body,
}
}
pub fn borrow_mut(&mut self) -> MutCache<'_, 'tcx> {
MutCache {
cache: &mut self.cache,
body: &mut self.body,
}
}
}
pub struct BorrowedCache<'a, 'tcx> {
cache: &'a mut Cache,
body: &'a Body<'tcx>
}
impl<'a, 'tcx> BorrowedCache<'a, 'tcx> {
#[inline]
pub fn predecessors_for(&mut self, bb: BasicBlock) -> &[BasicBlock] {
self.cache.predecessors_for(bb, self.body)
}
#[inline]
pub fn body(&self) -> &Body<'tcx> {
self.body
}
#[inline]
pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&self.body.basic_blocks
}
#[inline]
pub fn dominators(&mut self) -> Dominators<BasicBlock> {
dominators(self)
}
}
impl<'a, 'tcx> Index<BasicBlock> for BorrowedCache<'a, 'tcx> {
type Output = BasicBlockData<'tcx>;
#[inline]
fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
&self.body[index]
}
}
impl<'a, 'tcx> graph::DirectedGraph for BorrowedCache<'a, 'tcx> {
type Node = BasicBlock;
}
impl<'a, 'graph, 'tcx> graph::GraphPredecessors<'graph> for BorrowedCache<'a, 'tcx> {
type Item = BasicBlock;
type Iter = IntoIter<BasicBlock>;
}
impl<'a, 'tcx> graph::WithPredecessors for BorrowedCache<'a, 'tcx> {
fn predecessors(
&mut self,
node: Self::Node,
) -> <Self as GraphPredecessors<'_>>::Iter {
self.predecessors_for(node).to_vec().into_iter()
}
}
impl<'a, 'tcx> graph::WithNumNodes for BorrowedCache<'a, 'tcx> {
fn num_nodes(&self) -> usize {
self.body.num_nodes()
}
}
impl<'a, 'tcx> graph::WithStartNode for BorrowedCache<'a, 'tcx> {
fn start_node(&self) -> Self::Node {
self.body.start_node()
}
}
impl<'a, 'tcx> graph::WithSuccessors for BorrowedCache<'a, 'tcx> {
fn successors(
&self,
node: Self::Node,
) -> <Self as GraphSuccessors<'_>>::Iter {
self.body.successors(node)
}
}
impl<'a, 'b, 'tcx> graph::GraphSuccessors<'b> for BorrowedCache<'a, 'tcx> {
type Item = BasicBlock;
type Iter = iter::Cloned<Successors<'b>>;
}
pub struct MutCache<'a, 'tcx> {
cache: &'a mut Cache,
body: &'a mut Body<'tcx>,
}
impl<'a, 'tcx> MutCache<'a, 'tcx> {
#[inline]
pub fn body(&mut self) -> &mut Body<'tcx> {
self.body
}
#[inline]
pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&self.body.basic_blocks
}
#[inline]
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
self.cache.basic_blocks_mut(&mut self.body)
}
}
impl<'a, 'tcx> Index<BasicBlock> for MutCache<'a, 'tcx> {
type Output = BasicBlockData<'tcx>;
#[inline]
fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
&self.body[index]
}
}
impl<'a, 'tcx> IndexMut<BasicBlock> for MutCache<'a, 'tcx> {
fn index_mut(&mut self, index: BasicBlock) -> &mut Self::Output {
self.cache.invalidate_predecessors();
&mut self.body.basic_blocks[index]
}
}
//CloneTypeFoldableAndLiftImpls! {
// Cache,
//}

View File

@ -21,8 +21,8 @@ use crate::ty::{
use polonius_engine::Atom;
use rustc_index::bit_set::BitMatrix;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::graph::{self, GraphSuccessors};
use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::sync::Lrc;
use rustc_macros::HashStable;
@ -30,9 +30,8 @@ use rustc_serialize::{Encodable, Decodable};
use smallvec::SmallVec;
use std::borrow::Cow;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{Index, IndexMut};
use std::ops::Index;
use std::slice;
use std::vec::IntoIter;
use std::{iter, mem, option, u32};
use syntax::ast::Name;
use syntax::symbol::Symbol;
@ -40,7 +39,7 @@ use syntax_pos::{Span, DUMMY_SP};
pub use crate::mir::interpret::AssertMessage;
mod cache;
pub mod cache;
pub mod interpret;
pub mod mono;
pub mod tcx;
@ -153,9 +152,6 @@ pub struct Body<'tcx> {
/// A span representing this MIR, for error reporting.
pub span: Span,
/// A cache for various calculations.
cache: cache::Cache,
}
impl<'tcx> Body<'tcx> {
@ -192,7 +188,6 @@ impl<'tcx> Body<'tcx> {
spread_arg: None,
var_debug_info,
span,
cache: cache::Cache::new(),
control_flow_destroyed,
}
}
@ -202,88 +197,6 @@ impl<'tcx> Body<'tcx> {
&self.basic_blocks
}
#[inline]
pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
debug!("bbm: Clearing predecessors cache for body at: {:?}", self.span.data());
self.cache.invalidate_predecessors();
&mut self.basic_blocks
}
#[inline]
pub fn basic_blocks_and_local_decls_mut(
&mut self,
) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
debug!("bbaldm: Clearing predecessors cache for body at: {:?}", self.span.data());
self.cache.invalidate_predecessors();
(&mut self.basic_blocks, &mut self.local_decls)
}
#[inline]
pub fn unwrap_predecessors(&self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
assert!(
self.cache.predecessors.is_some(),
"Expected cache.predecessors to be `Some(...)` for block at: {:?}",
self.span.data()
);
self.cache.predecessors.as_ref().unwrap()
}
#[inline]
pub fn ensure_predecessors(&mut self) {
if self.cache.predecessors.is_none() {
let mut result = IndexVec::from_elem(vec![], self.basic_blocks());
for (bb, data) in self.basic_blocks().iter_enumerated() {
if let Some(ref term) = data.terminator {
for &tgt in term.successors() {
result[tgt].push(bb);
}
}
}
self.cache.predecessors = Some(result)
}
}
#[inline]
/// This will recompute the predecessors cache if it is not available
pub fn predecessors(&mut self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
self.ensure_predecessors();
self.cache.predecessors.as_ref().unwrap()
}
#[inline]
pub fn predecessors_for(&self, bb: BasicBlock) -> &[BasicBlock] {
&self.unwrap_predecessors()[bb]
}
#[inline]
pub fn predecessor_locations(&self, loc: Location) -> impl Iterator<Item = Location> + '_ {
let if_zero_locations = if loc.statement_index == 0 {
let predecessor_blocks = self.predecessors_for(loc.block);
let num_predecessor_blocks = predecessor_blocks.len();
Some(
(0..num_predecessor_blocks)
.map(move |i| predecessor_blocks[i])
.map(move |bb| self.terminator_loc(bb)),
)
} else {
None
};
let if_not_zero_locations = if loc.statement_index == 0 {
None
} else {
Some(Location { block: loc.block, statement_index: loc.statement_index - 1 })
};
if_zero_locations.into_iter().flatten().chain(if_not_zero_locations)
}
#[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> {
dominators(self)
}
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
/// `START_BLOCK`.
pub fn is_cfg_cyclic(&self) -> bool {
@ -384,7 +297,7 @@ impl<'tcx> Body<'tcx> {
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
/// invalidating statement indices in `Location`s.
pub fn make_statement_nop(&mut self, location: Location) {
let block = &mut self[location.block];
let block = &mut self.basic_blocks[location.block];
debug_assert!(location.statement_index < block.statements.len());
block.statements[location.statement_index].make_nop()
}
@ -444,13 +357,6 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> {
}
}
impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> {
#[inline]
fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
&mut self.basic_blocks_mut()[index]
}
}
#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)]
pub enum ClearCrossCrate<T> {
Clear,
@ -2647,15 +2553,6 @@ impl<'tcx> graph::WithStartNode for Body<'tcx> {
}
}
impl<'tcx> graph::WithPredecessors for Body<'tcx> {
fn predecessors(
&self,
node: Self::Node,
) -> <Self as GraphPredecessors<'_>>::Iter {
self.predecessors_for(node).to_vec().into_iter()
}
}
impl<'tcx> graph::WithSuccessors for Body<'tcx> {
fn successors(
&self,
@ -2665,11 +2562,6 @@ impl<'tcx> graph::WithSuccessors for Body<'tcx> {
}
}
impl<'a, 'b> graph::GraphPredecessors<'b> for Body<'a> {
type Item = BasicBlock;
type Iter = IntoIter<BasicBlock>;
}
impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
type Item = BasicBlock;
type Iter = iter::Cloned<Successors<'b>>;
@ -2704,7 +2596,7 @@ impl Location {
}
/// Returns `true` if `other` is earlier in the control flow graph than `self`.
pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool {
pub fn is_predecessor_of<'tcx>(&self, other: Location, mut body_cache: cache::BorrowedCache<'_, 'tcx>) -> bool {
// If we are in the same block as the other location and are an earlier statement
// then we are a predecessor of `other`.
if self.block == other.block && self.statement_index < other.statement_index {
@ -2712,13 +2604,13 @@ impl Location {
}
// If we're in another block, then we want to check that block is a predecessor of `other`.
let mut queue: Vec<BasicBlock> = body.predecessors_for(other.block).to_vec();
let mut queue: Vec<BasicBlock> = body_cache.predecessors_for(other.block).to_vec();
let mut visited = FxHashSet::default();
while let Some(block) = queue.pop() {
// If we haven't visited this block before, then make sure we visit it's predecessors.
if visited.insert(block) {
queue.extend(body.predecessors_for(block).iter().cloned());
queue.extend(body_cache.predecessors_for(block).iter().cloned());
} else {
continue;
}

View File

@ -1,6 +1,7 @@
use crate::ty::subst::SubstsRef;
use crate::ty::{CanonicalUserTypeAnnotation, Ty};
use crate::mir::*;
use crate::mir::cache::*;
use syntax_pos::Span;
// # The MIR Visitor
@ -71,8 +72,8 @@ macro_rules! make_mir_visitor {
// Override these, and call `self.super_xxx` to revert back to the
// default behavior.
fn visit_body(&mut self, body: & $($mutability)? Body<'tcx>) {
self.super_body(body);
fn visit_body(&mut self, body_cache: & $($mutability)? cache_type!('tcx $($mutability)?)) {
self.super_body(body_cache);
}
fn visit_basic_block_data(&mut self,
@ -241,10 +242,11 @@ macro_rules! make_mir_visitor {
// not meant to be overridden.
fn super_body(&mut self,
body: & $($mutability)? Body<'tcx>) {
if let Some(yield_ty) = &$($mutability)? body.yield_ty {
body_cache: & $($mutability)? cache_type!('tcx $($mutability)?)) {
let span = body_cache.body().span;
if let Some(yield_ty) = &$($mutability)? body_cache.body().yield_ty {
self.visit_ty(yield_ty, TyContext::YieldTy(SourceInfo {
span: body.span,
span,
scope: OUTERMOST_SOURCE_SCOPE,
}));
}
@ -253,13 +255,14 @@ macro_rules! make_mir_visitor {
// than a for-loop, to avoid calling `body::Body::invalidate` for
// each basic block.
macro_rules! basic_blocks {
(mut) => (body.basic_blocks_mut().iter_enumerated_mut());
() => (body.basic_blocks().iter_enumerated());
(mut) => (body_cache.basic_blocks_mut().iter_enumerated_mut());
() => (body_cache.basic_blocks().iter_enumerated());
};
for (bb, data) in basic_blocks!($($mutability)?) {
self.visit_basic_block_data(bb, data);
}
let body = body_cache.body();
for scope in &$($mutability)? body.source_scopes {
self.visit_source_scope_data(scope);
}
@ -790,8 +793,8 @@ macro_rules! make_mir_visitor {
// Convenience methods
fn visit_location(&mut self, body: & $($mutability)? Body<'tcx>, location: Location) {
let basic_block = & $($mutability)? body[location.block];
fn visit_location(&mut self, body_cache: & $($mutability)? cache_type!('tcx $($mutability)?), location: Location) {
let basic_block = & $($mutability)? body_cache[location.block];
if basic_block.statements.len() == location.statement_index {
if let Some(ref $($mutability)? terminator) = basic_block.terminator {
self.visit_terminator(terminator, location)
@ -806,6 +809,11 @@ macro_rules! make_mir_visitor {
}
}
macro_rules! cache_type {
($tcx:lifetime mut) => {MutCache<'_, $tcx>};
($tcx:lifetime) => {BorrowedCache<'_, $tcx>};
}
macro_rules! visit_place_fns {
(mut) => (
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;

View File

@ -7,32 +7,33 @@
use rustc_index::vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;
use std::borrow::BorrowMut;
#[cfg(test)]
mod tests;
pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
let start_node = graph.start_node();
let rpo = reverse_post_order(graph, start_node);
let rpo = reverse_post_order(&graph, start_node);
dominators_given_rpo(graph, &rpo)
}
fn dominators_given_rpo<G: ControlFlowGraph>(
graph: &G,
fn dominators_given_rpo<G: ControlFlowGraph + BorrowMut<G>>(
mut graph: G,
rpo: &[G::Node],
) -> Dominators<G::Node> {
let start_node = graph.start_node();
let start_node = graph.borrow().start_node();
assert_eq!(rpo[0], start_node);
// compute the post order index (rank) for each node
let mut post_order_rank: IndexVec<G::Node, usize> =
(0..graph.num_nodes()).map(|_| 0).collect();
(0..graph.borrow().num_nodes()).map(|_| 0).collect();
for (index, node) in rpo.iter().rev().cloned().enumerate() {
post_order_rank[node] = index;
}
let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
(0..graph.num_nodes()).map(|_| None).collect();
(0..graph.borrow().num_nodes()).map(|_| None).collect();
immediate_dominators[start_node] = Some(start_node);
let mut changed = true;
@ -41,7 +42,7 @@ fn dominators_given_rpo<G: ControlFlowGraph>(
for &node in &rpo[1..] {
let mut new_idom = None;
for pred in graph.predecessors(node) {
for pred in graph.borrow_mut().predecessors(node) {
if immediate_dominators[pred].is_some() {
// (*) dominators for `pred` have been calculated
new_idom = Some(if let Some(new_idom) = new_idom {

View File

@ -50,7 +50,7 @@ where
Self: for<'graph> GraphPredecessors<'graph, Item = <Self as DirectedGraph>::Node>,
{
fn predecessors(
&self,
&mut self,
node: Self::Node,
) -> <Self as GraphPredecessors<'_>>::Iter;
}

View File

@ -4,11 +4,20 @@ impl<'graph, G: DirectedGraph> DirectedGraph for &'graph G {
type Node = G::Node;
}
impl<'graph, G: DirectedGraph> DirectedGraph for &'graph mut G {
type Node = G::Node;
}
impl<'graph, G: WithNumNodes> WithNumNodes for &'graph G {
fn num_nodes(&self) -> usize {
(**self).num_nodes()
}
}
impl<'graph, G: WithNumNodes> WithNumNodes for &'graph mut G {
fn num_nodes(&self) -> usize {
(**self).num_nodes()
}
}
impl<'graph, G: WithStartNode> WithStartNode for &'graph G {
fn start_node(&self) -> Self::Node {
@ -16,14 +25,25 @@ impl<'graph, G: WithStartNode> WithStartNode for &'graph G {
}
}
impl<'graph, G: WithStartNode> WithStartNode for &'graph mut G {
fn start_node(&self) -> Self::Node {
(**self).start_node()
}
}
impl<'graph, G: WithSuccessors> WithSuccessors for &'graph G {
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
(**self).successors(node)
}
}
impl<'graph, G: WithSuccessors> WithSuccessors for &'graph mut G {
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
(**self).successors(node)
}
}
impl<'graph, G: WithPredecessors> WithPredecessors for &'graph G {
fn predecessors(&self,
impl<'graph, G: WithPredecessors> WithPredecessors for &'graph mut G {
fn predecessors(&mut self,
node: Self::Node)
-> <Self as GraphPredecessors<'_>>::Iter {
(**self).predecessors(node)
@ -35,7 +55,17 @@ impl<'iter, 'graph, G: WithPredecessors> GraphPredecessors<'iter> for &'graph G
type Iter = <G as GraphPredecessors<'iter>>::Iter;
}
impl<'iter, 'graph, G: WithPredecessors> GraphPredecessors<'iter> for &'graph mut G {
type Item = G::Node;
type Iter = <G as GraphPredecessors<'iter>>::Iter;
}
impl<'iter, 'graph, G: WithSuccessors> GraphSuccessors<'iter> for &'graph G {
type Item = G::Node;
type Iter = <G as GraphSuccessors<'iter>>::Iter;
}
impl<'iter, 'graph, G: WithSuccessors> GraphSuccessors<'iter> for &'graph mut G {
type Item = G::Node;
type Iter = <G as GraphSuccessors<'iter>>::Iter;
}