summary refs log tree commit diff stats
path: root/compiler/dfa.nim
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-10-12 19:56:51 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-10-12 19:56:51 +0200
commit2fecf4f36a39049e2d21065929f44dff38cf7d5a (patch)
tree09b0f093aa7e2f2d004c19f87f38e0f4d064a790 /compiler/dfa.nim
parentdcc3ac74f46cb25c1088059a38371a95fcc914c1 (diff)
downloadNim-2fecf4f36a39049e2d21065929f44dff38cf7d5a.tar.gz
compiler: cleanup dfa.nim
Diffstat (limited to 'compiler/dfa.nim')
-rw-r--r--compiler/dfa.nim46
1 files changed, 25 insertions, 21 deletions
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 44c89b881..016b4a5d1 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -7,18 +7,22 @@
 #    distribution, for details about the copyright.
 #
 
-## Data flow analysis for Nim. For now the task is to prove that every
-## usage of a local variable 'v' is covered by an initialization to 'v'
-## first.
+## 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.
+## taken). Exhaustive case statements could be translated
+## so that the last branch is transformed into an 'else' branch, but
+## this is currently not done.
 ## ``return`` and ``break`` are all covered by 'goto'.
-## The case to detect is ``use v`` that is not dominated by
-## a ``def v``.
+##
+## 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``.
+##
 ## The data structures and algorithms used here are inspired by
 ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
 ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
@@ -27,15 +31,11 @@ import ast, astalgo, types, intsets, tables, msgs, options, lineinfos
 
 type
   InstrKind* = enum
-    goto, fork, def, use,
-    useWithinCall # this strange special case is used to get more
-                  # move optimizations out of regular code
-                  # XXX This is still overly pessimistic in
-                  # call(let x = foo; bar(x))
+    goto, fork, def, use
   Instr* = object
     n*: PNode
     case kind*: InstrKind
-    of def, use, useWithinCall: sym*: PSym
+    of def, use: sym*: PSym
     of goto, fork: dest*: int
 
   ControlFlowGraph* = seq[Instr]
@@ -71,7 +71,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
     result.add $c[i].kind
     result.add "\t"
     case c[i].kind
-    of def, use, useWithinCall:
+    of def, use:
       result.add c[i].sym.name.s
     of goto, fork:
       result.add "L"
@@ -199,6 +199,13 @@ proc genCase(c: var Con; n: PNode) =
   #  L2:
   #    elsePart
   #  Lend:
+  when false:
+    # XXX Exhaustiveness is not yet mapped to the control flow graph as
+    # it seems to offer no benefits for the 'last read of' question.
+    let isExhaustive = skipTypes(n.sons[0].typ,
+      abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString} or
+      lastSon(n).kind == nkElse
+
   var endings: seq[TPosition] = @[]
   c.gen(n.sons[0])
   for i in 1 ..< n.len:
@@ -250,10 +257,7 @@ proc genUse(c: var Con; n: PNode) =
                    nkAddr, nkHiddenAddr}:
     n = n[0]
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
-    if c.inCall > 0:
-      c.code.add Instr(n: n, kind: useWithinCall, sym: n.sym)
-    else:
-      c.code.add Instr(n: n, kind: use, sym: n.sym)
+    c.code.add Instr(n: n, kind: use, sym: n.sym)
 
 proc genDef(c: var Con; n: PNode) =
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
@@ -345,7 +349,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
     d[i] = initIntSet()
     c[i] = initIntSet()
     case code[i].kind
-    of use, useWithinCall: u[i].incl(code[i].sym.id)
+    of use: u[i].incl(code[i].sym.id)
     of def: d[i].incl(code[i].sym.id)
     of fork, goto:
       let d = i+code[i].dest
@@ -407,7 +411,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
         #if someChange:
         w.add pc + code[pc].dest
         inc pc
-      of use, useWithinCall:
+      of use:
         #if not d[prevPc].missingOrExcl():
         # someChange = true
         consuming = code[pc].sym.id
@@ -424,7 +428,7 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) =
   # now check the condition we're interested in:
   for i in 0..<code.len:
     case code[i].kind
-    of use, useWithinCall:
+    of use:
       let s = code[i].sym
       if s.id notin d[i]:
         localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s)