Source   Edit

Data flow analysis for Nim. We transform the AST into a linear list of instructions first to make this easier to handle: There are only 2 different branching instructions: 'goto X' is an unconditional goto, 'fork X' is a conditional goto (either the next instruction or 'X' can be taken). Exhaustive case statements are translated so that the last branch is transformed into an 'else' branch. return and break are all covered by 'goto'.

Control flow through exception handling: Contrary to popular belief, exception handling doesn't cause many problems for this DFA representation, raise is a statement that goes to the outer finally or except if there is one, otherwise it is the same as return. Every call is treated as a call that can potentially raise. However, without a surrounding try we don't emit these fork ReturnLabel instructions in order to speed up the dataflow analysis passes.

The data structures and algorithms used here are inspired by "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.


AliasKind = enum
  yes, no, maybe
  Source   Edit
Instr = object
  n*: PNode
  case kind*: InstrKind
  of goto, fork:
    dest*: int
  Source   Edit
InstrKind = enum
  goto, fork, def, use
  Source   Edit


proc aliases(obj, field: PNode): AliasKind {....raises: [], tags: [].}
  Source   Edit
proc constructCfg(s: PSym; body: PNode): ControlFlowGraph {....raises: [Exception],
    tags: [RootEffect].}
constructs a control flow graph for body.   Source   Edit
proc echoCfg(c: ControlFlowGraph; start = 0; last = -1) {....deprecated,
    raises: [Exception, KeyError], tags: [RootEffect].}
echos the ControlFlowGraph for debugging purposes.   Source   Edit
proc isAnalysableFieldAccess(orig: PNode; owner: PSym): bool {....raises: [],
    tags: [].}
  Source   Edit
proc skipConvDfa(n: PNode): PNode {....raises: [], tags: [].}
  Source   Edit