public sealed override object VisitCFG(ControlFlowGraph x)
{
OnVisitCFG(x);
ExploredColor = x.NewColor();
ChangedColor = x.NewColor();
RepairedColor = x.NewColor();
_updatedBlocks = null;
// Traverse the whole graph and possibly obtain new versions of start and exit
var updatedStart = (StartBlock)Accept(x.Start);
var updatedExit = TryGetNewVersion(x.Exit);
// Assume that yields and unreachable blocks stay the same
var yields = x.Yields;
var unreachableBlocks = x.UnreachableBlocks;
// Fix the structure of the graph if any changes were performed
if (_updatedBlocks != null)
{
Debug.Assert(updatedStart != x.Start);
// Rescan and repair nodes and edges if any blocks were modified
var repairer = new GraphRepairer(this);
updatedStart = (StartBlock)updatedStart.Accept(repairer);
updatedExit = TryGetNewVersion(x.Exit);
// Handle newly unreachable blocks
var newlyUnreachableBlocks =
_possiblyUnreachableBlocks?.Where(b => !IsExplored(b)).ToList() // Confirm that they are unexplored
?? Enumerable.Empty <BoundBlock>();
if (newlyUnreachableBlocks.Any())
{
// Scan all the newly unreachable blocks (for yields, declarations,...)
var unreachableProcessor = new UnreachableProcessor(this, ExploredColor);
newlyUnreachableBlocks.ForEach(b => b.Accept(unreachableProcessor));
// Remove the discovered yields from the next CFG version
if (unreachableProcessor.Yields != null)
{
yields = yields.RemoveRange(unreachableProcessor.Yields);
}
}
// Repair all the unreachable blocks so that they reference the updated versions of the blocks
// (enables to properly produce reachability diagnostics)
unreachableBlocks =
unreachableBlocks.Concat(newlyUnreachableBlocks)
.Select(b => (BoundBlock)b.Accept(repairer))
.ToImmutableArray();
}
// Create a new CFG from the new versions of blocks and edges (expressions and statements are reused where unchanged)
return(x.Update(
updatedStart,
updatedExit,
x.Labels, // Keep all the labels, they are here only for the diagnostic purposes
yields,
unreachableBlocks));
}