summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2015-03-12 00:27:49 +0100
committerAraq <rumpf_a@web.de>2015-03-12 01:45:14 +0100
commit5a21892da0d16cc0fb321dadb17140a4808b0c17 (patch)
treea40eae85e4182999eb3f60525ffed1b823b62dd8
parent970e98718dfe0c494369bcb42b12d8cf7ace3f7c (diff)
downloadNim-5a21892da0d16cc0fb321dadb17140a4808b0c17.tar.gz
fixes #2287
-rw-r--r--compiler/guards.nim104
-rw-r--r--compiler/renderer.nim474
-rw-r--r--compiler/semparallel.nim4
-rw-r--r--compiler/transf.nim1
-rw-r--r--tests/parallel/tsimple_array_checks.nim41
-rw-r--r--todo.txt1
6 files changed, 345 insertions, 280 deletions
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 3255364c2..b0420cb75 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -15,11 +15,11 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
 const
   someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
     mEqUntracedRef, mEqStr, mEqSet, mEqCString}
-  
+
   # set excluded here as the semantics are vastly different:
   someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
             mLeCh, mLeB, mLePtr, mLeStr}
-  someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, 
+  someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
             mLtCh, mLtB, mLtPtr, mLtStr}
 
   someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
@@ -44,7 +44,7 @@ proc isLet(n: PNode): bool =
   if n.kind == nkSym:
     if n.sym.kind in {skLet, skTemp, skForVar}:
       result = true
-    elif n.sym.kind == skParam and skipTypes(n.sym.typ, 
+    elif n.sym.kind == skParam and skipTypes(n.sym.typ,
                                              abstractInst).kind != tyVar:
       result = true
 
@@ -117,7 +117,7 @@ proc neg(n: PNode): PNode =
     result.sons[0] = n.sons[0]
     result.sons[2] = n.sons[2]
     if t.kind == tyEnum:
-      var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)    
+      var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
       for e in t.n:
         let eAsNode = newIntNode(nkIntLit, e.sym.position)
         if not inSet(n.sons[1], eAsNode): s.add eAsNode
@@ -189,13 +189,17 @@ proc zero(): PNode = nkIntLit.newIntNode(0)
 proc one(): PNode = nkIntLit.newIntNode(1)
 proc minusOne(): PNode = nkIntLit.newIntNode(-1)
 
-proc lowBound*(x: PNode): PNode = 
+proc lowBound*(x: PNode): PNode =
   result = nkIntLit.newIntNode(firstOrd(x.typ))
   result.info = x.info
 
 proc highBound*(x: PNode): PNode =
-  result = if x.typ.skipTypes(abstractInst).kind == tyArray:
-             nkIntLit.newIntNode(lastOrd(x.typ))
+  let typ = x.typ.skipTypes(abstractInst)
+  result = if typ.kind in {tyArrayConstr, tyArray}:
+             nkIntLit.newIntNode(lastOrd(typ))
+           elif typ.kind == tySequence and x.kind == nkSym and
+               x.sym.kind == skConst:
+             nkIntLit.newIntNode(x.sym.ast.len-1)
            else:
              opAdd.buildCall(opLen.buildCall(x), minusOne())
   result.info = x.info
@@ -205,21 +209,32 @@ proc reassociation(n: PNode): PNode =
   # (foo+5)+5 --> foo+10;  same for '*'
   case result.getMagic
   of someAdd:
-    if result[2].isValue and 
+    if result[2].isValue and
         result[1].getMagic in someAdd and result[1][2].isValue:
       result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
   of someMul:
-    if result[2].isValue and 
+    if result[2].isValue and
         result[1].getMagic in someMul and result[1][2].isValue:
       result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2])
   else: discard
 
+proc pred(n: PNode): PNode =
+  if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
+    result = copyNode(n)
+    dec result.intVal
+  else:
+    result = n
+
 proc canon*(n: PNode): PNode =
   # XXX for now only the new code in 'semparallel' uses this
   if n.safeLen >= 1:
     result = shallowCopy(n)
     for i in 0 .. < n.len:
       result.sons[i] = canon(n.sons[i])
+  elif n.kind == nkSym and n.sym.kind == skLet and
+      n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
+      someMax + someHigh + {mUnaryLt} + someSub + someLen):
+    result = n.sym.ast.copyTree
   else:
     result = n
   case result.getMagic
@@ -231,34 +246,40 @@ proc canon*(n: PNode): PNode =
   of someHigh:
     # high == len+(-1)
     result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
-  of mUnaryMinusI, mUnaryMinusI64:
+  of mUnaryLt:
     result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1))
   of someSub:
     # x - 4  -->  x + (-4)
     result = negate(result[1], result[2], result)
   of someLen:
     result.sons[0] = opLen.newSymNode
+  of someLt:
+    # x < y  same as x <= y-1:
+    let y = n[2].canon
+    let p = pred(y)
+    let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
+    result = opLe.buildCall(n[1].canon, minus)
   else: discard
 
   result = skipConv(result)
   result = reassociation(result)
-  # most important rule: (x-4) < a.len -->  x < a.len+4
+  # most important rule: (x-4) <= a.len -->  x <= a.len+4
   case result.getMagic
-  of someLe, someLt:
+  of someLe:
     let x = result[1]
     let y = result[2]
-    if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and 
+    if x.kind in nkCallKinds and x.len == 3 and x[2].isValue and
         isLetLocation(x[1], true):
       case x.getMagic
       of someSub:
-        result = buildCall(result[0].sym, x[1], 
+        result = buildCall(result[0].sym, x[1],
                            reassociation(opAdd.buildCall(y, x[2])))
       of someAdd:
         # Rule A:
         let plus = negate(y, x[2], nil).reassociation
         if plus != nil: result = buildCall(result[0].sym, x[1], plus)
       else: discard
-    elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and 
+    elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
         isLetLocation(y[1], true):
       # a.len < x-3
       case y.getMagic
@@ -317,7 +338,7 @@ proc usefulFact(n: PNode): PNode =
   of mOr:
     # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
     # with that knowledge...
-    # DeMorgan helps a little though: 
+    # DeMorgan helps a little though:
     #   not a or not b --> not (a and b)
     #  (x == 3) or (y == 2)  ---> not ( not (x==3) and not (y == 2))
     #  not (x != 3 and y != 2)
@@ -348,11 +369,11 @@ proc addFact*(m: var TModel, n: PNode) =
   let n = usefulFact(n)
   if n != nil: m.add n
 
-proc addFactNeg*(m: var TModel, n: PNode) = 
+proc addFactNeg*(m: var TModel, n: PNode) =
   let n = n.neg
   if n != nil: addFact(m, n)
 
-proc sameTree*(a, b: PNode): bool = 
+proc sameTree*(a, b: PNode): bool =
   result = false
   if a == b:
     result = true
@@ -382,8 +403,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
   # 'while p != nil: f(p); p = p.next'
   # This is actually quite easy to do:
   # Re-assignments (incl. pass to a 'var' param) trigger an invalidation
-  # of every fact that contains 'v'. 
-  # 
+  # of every fact that contains 'v'.
+  #
   #   if x < 4:
   #     if y < 5
   #       x = unknown()
@@ -402,16 +423,9 @@ proc valuesUnequal(a, b: PNode): bool =
   if a.isValue and b.isValue:
     result = not sameValue(a, b)
 
-proc pred(n: PNode): PNode =
-  if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(BiggestInt):
-    result = copyNode(n)
-    dec result.intVal
-  else:
-    result = n
-
 proc impliesEq(fact, eq: PNode): TImplication =
   let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
-  
+
   case fact.sons[0].sym.magic
   of someEq:
     if sameTree(fact.sons[1], eq.sons[loc]):
@@ -428,12 +442,12 @@ proc impliesEq(fact, eq: PNode): TImplication =
       else: result = impNo
   of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
   else: discard
-  
+
 proc leImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
     # fact:  x <= 4;  question x in {56}?
     # --> true if every value <= 4 is in the set {56}
-    #   
+    #
     var value = newIntNode(c.kind, firstOrd(x.typ))
     # don't iterate too often:
     if c.intVal - value.intVal < 1000:
@@ -449,7 +463,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
     # fact:  x >= 4;  question x in {56}?
     # --> true iff every value >= 4 is in the set {56}
-    #   
+    #
     var value = newIntNode(c.kind, c.intVal)
     let max = lastOrd(x.typ)
     # don't iterate too often:
@@ -568,19 +582,19 @@ proc impliesLe(fact, x, c: PNode): TImplication =
         # fact:  x < 4;  question x <= 2? --> we don't know
     elif sameTree(fact.sons[2], x):
       # fact: 3 < x; question: x <= 1 ?  --> false iff 1 <= 3
-      if isValue(fact.sons[1]) and isValue(c): 
+      if isValue(fact.sons[1]) and isValue(c):
         if leValue(c, fact.sons[1]): result = impNo
-    
+
   of someLe:
     if sameTree(fact.sons[1], x):
       if isValue(fact.sons[2]) and isValue(c):
         # fact:  x <= 4;  question x <= 56? --> true iff 4 <= 56
         if leValue(fact.sons[2], c): result = impYes
         # fact:  x <= 4;  question x <= 2? --> we don't know
-    
+
     elif sameTree(fact.sons[2], x):
       # fact: 3 <= x; question: x <= 2 ?  --> false iff 2 < 3
-      if isValue(fact.sons[1]) and isValue(c): 
+      if isValue(fact.sons[1]) and isValue(c):
         if leValue(c, fact.sons[1].pred): result = impNo
 
   of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
@@ -614,7 +628,7 @@ proc factImplies(fact, prop: PNode): TImplication =
     # it's provably wrong if every value > 4 is in the set {56}
     # That's because we compute the implication and  'a -> not b' cannot
     # be treated the same as 'not a -> b'
-    
+
     #  (not a) -> b  compute as  not (a -> b) ???
     #  == not a or not b == not (a and b)
     let arg = fact.sons[1]
@@ -635,7 +649,7 @@ proc factImplies(fact, prop: PNode): TImplication =
     if result != impUnknown: return result
     return factImplies(fact.sons[2], prop)
   else: discard
-  
+
   case prop.sons[0].sym.magic
   of mNot: result = ~fact.factImplies(prop.sons[1])
   of mIsNil: result = impliesIsNil(fact, prop)
@@ -671,6 +685,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication
 
 proc ple(m: TModel; a, b: PNode): TImplication =
   template `<=?`(a,b): expr = ple(m,a,b) == impYes
+
   #   0 <= 3
   if a.isValue and b.isValue:
     return if leValue(a, b): impYes else: impNo
@@ -744,16 +759,21 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
       # mark as used:
       m[i] = nil
       if ple(m, a, x) == impYes:
-        if ple(m, y, b) == impYes: return impYes
+        if ple(m, y, b) == impYes:
+          return impYes
         #if pleViaModelRec(m, y, b): return impYes
       # fact:  16 <= i
       #         x    y
       # question: i <= 15? no!
       result = impliesLe(fact, a, b)
-      if result != impUnknown: return result
-      if sameTree(y, a):
-        result = ple(m, b, x)
-        if result != impUnknown: return result
+      if result != impUnknown:
+        return result
+      when false:
+        # given: x <= y;  y==a;  x <= a this means: a <= b  if  x <= b
+        if sameTree(y, a):
+          result = ple(m, b, x)
+          if result != impUnknown:
+            return result
 
 proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
   # compute replacements:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index ccf3837ed..689bf23c8 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -9,20 +9,20 @@
 
 # This module implements the renderer of the standard Nim representation.
 
-import 
+import
   lexer, options, idents, strutils, ast, msgs, lists
 
-type 
-  TRenderFlag* = enum 
-    renderNone, renderNoBody, renderNoComments, renderDocComments, 
+type
+  TRenderFlag* = enum
+    renderNone, renderNoBody, renderNoComments, renderDocComments,
     renderNoPragmas, renderIds, renderNoProcDefs
   TRenderFlags* = set[TRenderFlag]
-  TRenderTok*{.final.} = object 
+  TRenderTok*{.final.} = object
     kind*: TTokType
     length*: int16
 
   TRenderTokSeq* = seq[TRenderTok]
-  TSrcGen*{.final.} = object 
+  TSrcGen*{.final.} = object
     indent*: int
     lineLen*: int
     pos*: int              # current position for iteration over the buffer
@@ -41,6 +41,8 @@ proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {})
 proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
 proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {})
 proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string)
+
+proc `$`*(n: PNode): string = n.renderTree
 # implementation
 # We render the source code in a two phases: The first
 # determines how long the subtree will likely be, the second
@@ -65,13 +67,13 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   else:
     result = '`' & x & '`'
 
-const 
+const
   IndentWidth = 2
   longIndentWid = 4
   MaxLineLen = 80
   LineCommentColumn = 30
 
-proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) = 
+proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
   g.comStack = @[]
   g.tokens = @[]
   g.indent = 0
@@ -83,67 +85,67 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
   g.pendingNL = -1
   g.checkAnon = false
 
-proc addTok(g: var TSrcGen, kind: TTokType, s: string) = 
+proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
   var length = len(g.tokens)
   setLen(g.tokens, length + 1)
   g.tokens[length].kind = kind
   g.tokens[length].length = int16(len(s))
   add(g.buf, s)
 
-proc addPendingNL(g: var TSrcGen) = 
-  if g.pendingNL >= 0: 
+proc addPendingNL(g: var TSrcGen) =
+  if g.pendingNL >= 0:
     addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
     g.lineLen = g.pendingNL
     g.pendingNL = - 1
 
-proc putNL(g: var TSrcGen, indent: int) = 
+proc putNL(g: var TSrcGen, indent: int) =
   if g.pendingNL >= 0: addPendingNL(g)
   else: addTok(g, tkSpaces, "\n")
   g.pendingNL = indent
   g.lineLen = indent
 
-proc putNL(g: var TSrcGen) = 
+proc putNL(g: var TSrcGen) =
   putNL(g, g.indent)
 
-proc optNL(g: var TSrcGen, indent: int) = 
+proc optNL(g: var TSrcGen, indent: int) =
   g.pendingNL = indent
   g.lineLen = indent          # BUGFIX
-  
-proc optNL(g: var TSrcGen) = 
+
+proc optNL(g: var TSrcGen) =
   optNL(g, g.indent)
 
-proc indentNL(g: var TSrcGen) = 
+proc indentNL(g: var TSrcGen) =
   inc(g.indent, IndentWidth)
   g.pendingNL = g.indent
   g.lineLen = g.indent
 
-proc dedent(g: var TSrcGen) = 
+proc dedent(g: var TSrcGen) =
   dec(g.indent, IndentWidth)
   assert(g.indent >= 0)
-  if g.pendingNL > IndentWidth: 
+  if g.pendingNL > IndentWidth:
     dec(g.pendingNL, IndentWidth)
     dec(g.lineLen, IndentWidth)
 
-proc put(g: var TSrcGen, kind: TTokType, s: string) = 
+proc put(g: var TSrcGen, kind: TTokType, s: string) =
   addPendingNL(g)
-  if len(s) > 0: 
+  if len(s) > 0:
     addTok(g, kind, s)
     inc(g.lineLen, len(s))
 
-proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) = 
+proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) =
   # use this for tokens over multiple lines.
   addPendingNL(g)
   addTok(g, kind, s)
   g.lineLen = lineLen
 
-proc toNimChar(c: char): string = 
+proc toNimChar(c: char): string =
   case c
   of '\0': result = "\\0"
   of '\x01'..'\x1F', '\x80'..'\xFF': result = "\\x" & strutils.toHex(ord(c), 2)
   of '\'', '\"', '\\': result = '\\' & c
   else: result = c & ""
-  
-proc makeNimString(s: string): string = 
+
+proc makeNimString(s: string): string =
   result = "\""
   for i in countup(0, len(s)-1): add(result, toNimChar(s[i]))
   add(result, '\"')
@@ -174,7 +176,7 @@ proc putComment(g: var TSrcGen, s: string) =
       add(com, s[i])
       inc(i)
       comIndent = 0
-      while s[i] == ' ': 
+      while s[i] == ' ':
         add(com, s[i])
         inc(i)
         inc(comIndent)
@@ -187,109 +189,109 @@ proc putComment(g: var TSrcGen, s: string) =
       # compute length of the following word:
       var j = i
       while s[j] > ' ': inc(j)
-      if not isCode and (g.lineLen + (j - i) > MaxLineLen): 
+      if not isCode and (g.lineLen + (j - i) > MaxLineLen):
         put(g, tkComment, com)
         optNL(g, ind)
         com = '#' & spaces(comIndent)
-      while s[i] > ' ': 
+      while s[i] > ' ':
         add(com, s[i])
         inc(i)
   put(g, tkComment, com)
   optNL(g)
 
-proc maxLineLength(s: string): int = 
+proc maxLineLength(s: string): int =
   if s.isNil: return 0
   var i = 0
   var lineLen = 0
   while true:
     case s[i]
-    of '\0': 
-      break 
-    of '\x0D': 
+    of '\0':
+      break
+    of '\x0D':
       inc(i)
       if s[i] == '\x0A': inc(i)
       result = max(result, lineLen)
       lineLen = 0
-    of '\x0A': 
+    of '\x0A':
       inc(i)
       result = max(result, lineLen)
       lineLen = 0
-    else: 
+    else:
       inc(lineLen)
       inc(i)
 
-proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = 
+proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
   var i = 0
   var hi = len(s) - 1
   var str = ""
-  while i <= hi: 
+  while i <= hi:
     case s[i]
-    of '\x0D': 
+    of '\x0D':
       put(g, kind, str)
       str = ""
       inc(i)
       if (i <= hi) and (s[i] == '\x0A'): inc(i)
       optNL(g, 0)
-    of '\x0A': 
+    of '\x0A':
       put(g, kind, str)
       str = ""
       inc(i)
       optNL(g, 0)
-    else: 
+    else:
       add(str, s[i])
       inc(i)
   put(g, kind, str)
 
-proc containsNL(s: string): bool = 
-  for i in countup(0, len(s) - 1): 
+proc containsNL(s: string): bool =
+  for i in countup(0, len(s) - 1):
     case s[i]
-    of '\x0D', '\x0A': 
+    of '\x0D', '\x0A':
       return true
-    else: 
+    else:
       discard
   result = false
 
-proc pushCom(g: var TSrcGen, n: PNode) = 
+proc pushCom(g: var TSrcGen, n: PNode) =
   var length = len(g.comStack)
   setLen(g.comStack, length + 1)
   g.comStack[length] = n
 
-proc popAllComs(g: var TSrcGen) = 
+proc popAllComs(g: var TSrcGen) =
   setLen(g.comStack, 0)
 
-proc popCom(g: var TSrcGen) = 
+proc popCom(g: var TSrcGen) =
   setLen(g.comStack, len(g.comStack) - 1)
 
-const 
+const
   Space = " "
 
-proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = 
+proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
   result = false
-  if n.comment != nil: 
+  if n.comment != nil:
     result = (renderNoComments notin g.flags) or
         (renderDocComments in g.flags) and startsWith(n.comment, "##")
-  
-proc gcom(g: var TSrcGen, n: PNode) = 
+
+proc gcom(g: var TSrcGen, n: PNode) =
   assert(n != nil)
-  if shouldRenderComment(g, n): 
+  if shouldRenderComment(g, n):
     if (g.pendingNL < 0) and (len(g.buf) > 0) and (g.buf[len(g.buf)-1] != ' '):
-      put(g, tkSpaces, Space) 
+      put(g, tkSpaces, Space)
       # Before long comments we cannot make sure that a newline is generated,
       # because this might be wrong. But it is no problem in practice.
     if (g.pendingNL < 0) and (len(g.buf) > 0) and
-        (g.lineLen < LineCommentColumn): 
+        (g.lineLen < LineCommentColumn):
       var ml = maxLineLength(n.comment)
-      if ml + LineCommentColumn <= MaxLineLen: 
+      if ml + LineCommentColumn <= MaxLineLen:
         put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
     putComment(g, n.comment)  #assert(g.comStack[high(g.comStack)] = n);
-  
-proc gcoms(g: var TSrcGen) = 
+
+proc gcoms(g: var TSrcGen) =
   for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i])
   popAllComs(g)
 
 proc lsub(n: PNode): int
 proc litAux(n: PNode, x: BiggestInt, size: int): string =
-  proc skip(t: PType): PType = 
+  proc skip(t: PType): PType =
     result = t
     while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
                           tyConst, tyMutable}:
@@ -299,20 +301,20 @@ proc litAux(n: PNode, x: BiggestInt, size: int): string =
     # we need a slow linear search because of enums with holes:
     for e in items(enumfields):
       if e.sym.position == x: return e.sym.name.s
-    
+
   if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
   elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
   elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
   else: result = $x
 
-proc ulitAux(n: PNode, x: BiggestInt, size: int): string = 
+proc ulitAux(n: PNode, x: BiggestInt, size: int): string =
   if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
   elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
   elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
   else: result = $x
   # XXX proper unsigned output!
-  
-proc atom(n: PNode): string = 
+
+proc atom(n: PNode): string =
   var f: float32
   case n.kind
   of nkEmpty: result = ""
@@ -335,49 +337,49 @@ proc atom(n: PNode): string =
   of nkFloatLit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
     else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[] , 8)
-  of nkFloat32Lit: 
-    if n.flags * {nfBase2, nfBase8, nfBase16} == {}: 
+  of nkFloat32Lit:
+    if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f32"
-    else: 
+    else:
       f = n.floatVal.float32
       result = litAux(n, (cast[PInt32](addr(f)))[], 4) & "\'f32"
-  of nkFloat64Lit: 
-    if n.flags * {nfBase2, nfBase8, nfBase16} == {}: 
+  of nkFloat64Lit:
+    if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f64"
-    else: 
+    else:
       result = litAux(n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64"
   of nkNilLit: result = "nil"
-  of nkType: 
+  of nkType:
     if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
     else: result = "[type node]"
-  else: 
+  else:
     internalError("rnimsyn.atom " & $n.kind)
     result = ""
-  
-proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int = 
+
+proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int =
   assert(theEnd < 0)
   result = 0
-  for i in countup(start, sonsLen(n) + theEnd): 
+  for i in countup(start, sonsLen(n) + theEnd):
     inc(result, lsub(n.sons[i]))
     inc(result, 2)            # for ``, ``
-  if result > 0: 
+  if result > 0:
     dec(result, 2)            # last does not get a comma!
-  
-proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int = 
+
+proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int =
   assert(theEnd < 0)
   result = 0
   for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(n.sons[i]))
-  
-proc lsub(n: PNode): int = 
+
+proc lsub(n: PNode): int =
   # computes the length of a tree
   if isNil(n): return 0
   if n.comment != nil: return MaxLineLen + 1
   case n.kind
   of nkEmpty: result = 0
-  of nkTripleStrLit: 
+  of nkTripleStrLit:
     if containsNL(n.strVal): result = MaxLineLen + 1
     else: result = len(atom(n))
-  of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: 
+  of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
     result = len(atom(n))
   of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr:
     result = lsub(n.sons[0]) + lcomma(n, 1) + 2
@@ -392,7 +394,7 @@ proc lsub(n: PNode): int =
   of nkArgList: result = lcomma(n)
   of nkTableConstr:
     result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
-  of nkClosedSymChoice, nkOpenSymChoice: 
+  of nkClosedSymChoice, nkOpenSymChoice:
     result = lsons(n) + len("()") + sonsLen(n) - 1
   of nkTupleTy: result = lcomma(n) + len("tuple[]")
   of nkTupleClassTy: result = len("tuple")
@@ -403,7 +405,7 @@ proc lsub(n: PNode): int =
   of nkCheckedFieldExpr: result = lsub(n.sons[0])
   of nkLambda: result = lsons(n) + len("proc__=_")
   of nkDo: result = lsons(n) + len("do__:_")
-  of nkConstDef, nkIdentDefs: 
+  of nkConstDef, nkIdentDefs:
     result = lcomma(n, 0, - 3)
     var L = sonsLen(n)
     if n.sons[L - 2].kind != nkEmpty: result = result + lsub(n.sons[L - 2]) + 2
@@ -412,7 +414,7 @@ proc lsub(n: PNode): int =
   of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(n)
   of nkChckRange64: result = len("chckRange64") + 2 + lcomma(n)
   of nkChckRange: result = len("chckRange") + 2 + lcomma(n)
-  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: 
+  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
     result = 2
     if sonsLen(n) >= 1: result = result + lsub(n.sons[0])
     result = result + lcomma(n, 1)
@@ -426,7 +428,7 @@ proc lsub(n: PNode): int =
   of nkRange: result = lsons(n) + 2
   of nkDerefExpr: result = lsub(n.sons[0]) + 2
   of nkAccQuoted: result = lsons(n) + 2
-  of nkIfExpr: 
+  of nkIfExpr:
     result = lsub(n.sons[0].sons[0]) + lsub(n.sons[0].sons[1]) + lsons(n, 1) +
         len("if_:_")
   of nkElifExpr: result = lsons(n) + len("_elif_:_")
@@ -447,13 +449,13 @@ proc lsub(n: PNode): int =
   of nkProcTy: result = lsons(n) + len("proc_")
   of nkIteratorTy: result = lsons(n) + len("iterator_")
   of nkSharedTy: result = lsons(n) + len("shared_")
-  of nkEnumTy: 
+  of nkEnumTy:
     if sonsLen(n) > 0:
       result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_")
     else:
       result = len("enum")
   of nkEnumFieldDef: result = lsons(n) + 3
-  of nkVarSection, nkLetSection: 
+  of nkVarSection, nkLetSection:
     if sonsLen(n) > 1: result = MaxLineLen + 1
     else: result = lsons(n) + len("var_")
   of nkReturnStmt: result = lsub(n.sons[0]) + len("return_")
@@ -470,50 +472,50 @@ proc lsub(n: PNode): int =
   of nkElse: result = lsub(n.sons[0]) + len("else:_")
   of nkFinally: result = lsub(n.sons[0]) + len("finally:_")
   of nkGenericParams: result = lcomma(n) + 2
-  of nkFormalParams: 
+  of nkFormalParams:
     result = lcomma(n, 1) + 2
     if n.sons[0].kind != nkEmpty: result = result + lsub(n.sons[0]) + 2
-  of nkExceptBranch: 
+  of nkExceptBranch:
     result = lcomma(n, 0, -2) + lsub(lastSon(n)) + len("except_:_")
   else: result = MaxLineLen + 1
-  
-proc fits(g: TSrcGen, x: int): bool = 
+
+proc fits(g: TSrcGen, x: int): bool =
   result = x + g.lineLen <= MaxLineLen
 
-type 
-  TSubFlag = enum 
+type
+  TSubFlag = enum
     rfLongMode, rfNoIndent, rfInConstExpr
   TSubFlags = set[TSubFlag]
   TContext = tuple[spacing: int, flags: TSubFlags]
 
-const 
+const
   emptyContext: TContext = (spacing: 0, flags: {})
 
-proc initContext(c: var TContext) = 
+proc initContext(c: var TContext) =
   c.spacing = 0
   c.flags = {}
 
 proc gsub(g: var TSrcGen, n: PNode, c: TContext)
-proc gsub(g: var TSrcGen, n: PNode) = 
+proc gsub(g: var TSrcGen, n: PNode) =
   var c: TContext
   initContext(c)
   gsub(g, n, c)
 
-proc hasCom(n: PNode): bool = 
+proc hasCom(n: PNode): bool =
   result = false
   if n.comment != nil: return true
   case n.kind
   of nkEmpty..nkNilLit: discard
-  else: 
-    for i in countup(0, sonsLen(n) - 1): 
+  else:
+    for i in countup(0, sonsLen(n) - 1):
       if hasCom(n.sons[i]): return true
-  
-proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) = 
+
+proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) =
   put(g, kind, s)
   put(g, tkSpaces, Space)
 
-proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, 
-               theEnd: int = - 1, separator = tkComma) = 
+proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
+               theEnd: int = - 1, separator = tkComma) =
   for i in countup(start, sonsLen(n) + theEnd):
     var c = i < sonsLen(n) + theEnd
     var sublen = lsub(n.sons[i]) + ord(c)
@@ -523,54 +525,54 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
     if c:
       if g.tokens.len > oldLen:
         putWithSpace(g, separator, TokTypeToStr[separator])
-      if hasCom(n.sons[i]): 
+      if hasCom(n.sons[i]):
         gcoms(g)
         optNL(g, ind)
 
-proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, 
-            theEnd: int = - 1) = 
+proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
+            theEnd: int = - 1) =
   var ind: int
-  if rfInConstExpr in c.flags: 
+  if rfInConstExpr in c.flags:
     ind = g.indent + IndentWidth
-  else: 
+  else:
     ind = g.lineLen
     if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
   gcommaAux(g, n, ind, start, theEnd)
 
-proc gcomma(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = 
+proc gcomma(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
   var ind = g.lineLen
   if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
   gcommaAux(g, n, ind, start, theEnd)
 
-proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) = 
+proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
   var ind = g.lineLen
   if ind > MaxLineLen div 2: ind = g.indent + longIndentWid
   gcommaAux(g, n, ind, start, theEnd, tkSemiColon)
 
-proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, 
-           theEnd: int = - 1) = 
+proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
+           theEnd: int = - 1) =
   for i in countup(start, sonsLen(n) + theEnd): gsub(g, n.sons[i], c)
 
-proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType, 
-              k: string) = 
+proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
+              k: string) =
   if sonsLen(n) == 0: return # empty var sections are possible
   putWithSpace(g, kind, k)
   gcoms(g)
   indentNL(g)
-  for i in countup(0, sonsLen(n) - 1): 
+  for i in countup(0, sonsLen(n) - 1):
     optNL(g)
     gsub(g, n.sons[i], c)
     gcoms(g)
   dedent(g)
 
-proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = 
+proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool =
   result = n.comment != nil
-  if not result: 
+  if not result:
     # check further
-    for i in countup(start, sonsLen(n) + theEnd): 
-      if (lsub(n.sons[i]) > MaxLineLen): 
+    for i in countup(start, sonsLen(n) + theEnd):
+      if (lsub(n.sons[i]) > MaxLineLen):
         result = true
-        break 
+        break
 
 proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
   if n.kind == nkEmpty: return
@@ -590,33 +592,33 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
     gcoms(g)
     optNL(g)
     if rfLongMode in c.flags: dedent(g)
-  
-proc gif(g: var TSrcGen, n: PNode) = 
+
+proc gif(g: var TSrcGen, n: PNode) =
   var c: TContext
   gsub(g, n.sons[0].sons[0])
   initContext(c)
   putWithSpace(g, tkColon, ":")
-  if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[0].sons[1], c)
   var length = sonsLen(n)
-  for i in countup(1, length - 1): 
+  for i in countup(1, length - 1):
     optNL(g)
     gsub(g, n.sons[i], c)
 
-proc gwhile(g: var TSrcGen, n: PNode) = 
+proc gwhile(g: var TSrcGen, n: PNode) =
   var c: TContext
   putWithSpace(g, tkWhile, "while")
   gsub(g, n.sons[0])
   putWithSpace(g, tkColon, ":")
   initContext(c)
-  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[1], c)
 
-proc gpattern(g: var TSrcGen, n: PNode) = 
+proc gpattern(g: var TSrcGen, n: PNode) =
   var c: TContext
   put(g, tkCurlyLe, "{")
   initContext(c)
@@ -626,7 +628,7 @@ proc gpattern(g: var TSrcGen, n: PNode) =
   gstmts(g, n, c)
   put(g, tkCurlyRi, "}")
 
-proc gpragmaBlock(g: var TSrcGen, n: PNode) = 
+proc gpragmaBlock(g: var TSrcGen, n: PNode) =
   var c: TContext
   gsub(g, n.sons[0])
   putWithSpace(g, tkColon, ":")
@@ -636,25 +638,25 @@ proc gpragmaBlock(g: var TSrcGen, n: PNode) =
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[1], c)
 
-proc gtry(g: var TSrcGen, n: PNode) = 
+proc gtry(g: var TSrcGen, n: PNode) =
   var c: TContext
   put(g, tkTry, "try")
   putWithSpace(g, tkColon, ":")
   initContext(c)
-  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[0], c)
   gsons(g, n, c, 1)
 
-proc gfor(g: var TSrcGen, n: PNode) = 
+proc gfor(g: var TSrcGen, n: PNode) =
   var c: TContext
   var length = sonsLen(n)
   putWithSpace(g, tkFor, "for")
   initContext(c)
   if longMode(n) or
       (lsub(n.sons[length - 1]) + lsub(n.sons[length - 2]) + 6 + g.lineLen >
-      MaxLineLen): 
+      MaxLineLen):
     incl(c.flags, rfLongMode)
   gcomma(g, n, c, 0, - 3)
   put(g, tkSpaces, Space)
@@ -664,17 +666,17 @@ proc gfor(g: var TSrcGen, n: PNode) =
   gcoms(g)
   gstmts(g, n.sons[length - 1], c)
 
-proc gmacro(g: var TSrcGen, n: PNode) = 
+proc gmacro(g: var TSrcGen, n: PNode) =
   var c: TContext
   initContext(c)
   gsub(g, n.sons[0])
   putWithSpace(g, tkColon, ":")
-  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)
   gsons(g, n, c, 1)
 
-proc gcase(g: var TSrcGen, n: PNode) = 
+proc gcase(g: var TSrcGen, n: PNode) =
   var c: TContext
   initContext(c)
   var length = sonsLen(n)
@@ -685,18 +687,18 @@ proc gcase(g: var TSrcGen, n: PNode) =
   gcoms(g)
   optNL(g)
   gsons(g, n, c, 1, last)
-  if last == - 2: 
+  if last == - 2:
     initContext(c)
     if longMode(n.sons[length - 1]): incl(c.flags, rfLongMode)
     gsub(g, n.sons[length - 1], c)
 
-proc gproc(g: var TSrcGen, n: PNode) = 
+proc gproc(g: var TSrcGen, n: PNode) =
   var c: TContext
   if n.sons[namePos].kind == nkSym:
     put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
   else:
     gsub(g, n.sons[namePos])
-  
+
   if n.sons[patternPos].kind != nkEmpty:
     gpattern(g, n.sons[patternPos])
   let oldCheckAnon = g.checkAnon
@@ -733,7 +735,7 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) =
   gstmts(g, n[3], c)
   dedent(g)
 
-proc gblock(g: var TSrcGen, n: PNode) = 
+proc gblock(g: var TSrcGen, n: PNode) =
   var c: TContext
   initContext(c)
   if n.sons[0].kind != nkEmpty:
@@ -742,7 +744,7 @@ proc gblock(g: var TSrcGen, n: PNode) =
   else:
     put(g, tkBlock, "block")
   putWithSpace(g, tkColon, ":")
-  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)
   # XXX I don't get why this is needed here! gstmts should already handle this!
@@ -750,17 +752,17 @@ proc gblock(g: var TSrcGen, n: PNode) =
   gstmts(g, n.sons[1], c)
   dedent(g)
 
-proc gstaticStmt(g: var TSrcGen, n: PNode) = 
+proc gstaticStmt(g: var TSrcGen, n: PNode) =
   var c: TContext
   putWithSpace(g, tkStatic, "static")
   putWithSpace(g, tkColon, ":")
   initContext(c)
-  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): 
+  if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n.sons[0], c)
 
-proc gasm(g: var TSrcGen, n: PNode) = 
+proc gasm(g: var TSrcGen, n: PNode) =
   putWithSpace(g, tkAsm, "asm")
   gsub(g, n.sons[0])
   gcoms(g)
@@ -770,16 +772,16 @@ proc gident(g: var TSrcGen, n: PNode) =
   if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
   var t: TTokType
   var s = atom(n)
-  if (s[0] in lexer.SymChars): 
-    if (n.kind == nkIdent): 
+  if (s[0] in lexer.SymChars):
+    if (n.kind == nkIdent):
       if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
-          (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): 
+          (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
         t = tkSymbol
-      else: 
+      else:
         t = TTokType(n.ident.id + ord(tkSymbol))
-    else: 
+    else:
       t = tkSymbol
-  else: 
+  else:
     t = tkOpr
   put(g, t, s)
   if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
@@ -789,12 +791,12 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
     put(g, tkParLe, "(")
     gsemicolon(g, params, 1)
     put(g, tkParRi, ")")
-  
-  if params.sons[0].kind != nkEmpty: 
+
+  if params.sons[0].kind != nkEmpty:
     putWithSpace(g, tkOpr, "->")
     gsub(g, params.sons[0])
 
-proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 
+proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   if isNil(n): return
   var
     a: TContext
@@ -827,14 +829,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkParLe, "(")
     gcomma(g, n, 1)
     put(g, tkParRi, ")")
-  of nkCallStrLit: 
+  of nkCallStrLit:
     gsub(g, n.sons[0])
-    if n.sons[1].kind == nkRStrLit: 
+    if n.sons[1].kind == nkRStrLit:
       put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
-    else: 
+    else:
       gsub(g, n.sons[1])
   of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: gsub(g, n.sons[1])
-  of nkCast: 
+  of nkCast:
     put(g, tkCast, "cast")
     put(g, tkBracketLe, "[")
     gsub(g, n.sons[0])
@@ -842,7 +844,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkParLe, "(")
     gsub(g, n.sons[1])
     put(g, tkParRi, ")")
-  of nkAddr: 
+  of nkAddr:
     put(g, tkAddr, "addr")
     put(g, tkParLe, "(")
     gsub(g, n.sons[0])
@@ -851,7 +853,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkStatic, "static")
     put(g, tkSpaces, Space)
     gsub(g, n.sons[0])
-  of nkBracketExpr: 
+  of nkBracketExpr:
     gsub(g, n.sons[0])
     put(g, tkBracketLe, "[")
     gcomma(g, n, 1)
@@ -861,41 +863,41 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkCurlyLe, "{")
     gcomma(g, n, 1)
     put(g, tkCurlyRi, "}")
-  of nkPragmaExpr: 
+  of nkPragmaExpr:
     gsub(g, n.sons[0])
     gcomma(g, n, 1)
-  of nkCommand: 
+  of nkCommand:
     gsub(g, n.sons[0])
     put(g, tkSpaces, Space)
     gcomma(g, n, 1)
-  of nkExprEqExpr, nkAsgn, nkFastAsgn: 
+  of nkExprEqExpr, nkAsgn, nkFastAsgn:
     gsub(g, n.sons[0])
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, n.sons[1])
-  of nkChckRangeF: 
+  of nkChckRangeF:
     put(g, tkSymbol, "chckRangeF")
     put(g, tkParLe, "(")
     gcomma(g, n)
     put(g, tkParRi, ")")
-  of nkChckRange64: 
+  of nkChckRange64:
     put(g, tkSymbol, "chckRange64")
     put(g, tkParLe, "(")
     gcomma(g, n)
     put(g, tkParRi, ")")
-  of nkChckRange: 
+  of nkChckRange:
     put(g, tkSymbol, "chckRange")
     put(g, tkParLe, "(")
     gcomma(g, n)
     put(g, tkParRi, ")")
-  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: 
+  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
     if sonsLen(n) >= 1: gsub(g, n.sons[0])
     put(g, tkParLe, "(")
     gcomma(g, n, 1)
     put(g, tkParRi, ")")
   of nkClosedSymChoice, nkOpenSymChoice:
     put(g, tkParLe, "(")
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       if i > 0: put(g, tkOpr, "|")
       if n.sons[i].kind == nkSym:
         let s = n[i].sym
@@ -906,11 +908,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       else:
         gsub(g, n.sons[i], c)
     put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
-  of nkPar, nkClosure: 
+  of nkPar, nkClosure:
     put(g, tkParLe, "(")
     gcomma(g, n, c)
     put(g, tkParRi, ")")
-  of nkCurly: 
+  of nkCurly:
     put(g, tkCurlyLe, "{")
     gcomma(g, n, c)
     put(g, tkCurlyRi, "}")
@@ -925,14 +927,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkBracketLe, "[")
     gcomma(g, n, c)
     put(g, tkBracketRi, "]")
-  of nkDotExpr: 
+  of nkDotExpr:
     gsub(g, n.sons[0])
     put(g, tkDot, ".")
     gsub(g, n.sons[1])
-  of nkBind: 
+  of nkBind:
     putWithSpace(g, tkBind, "bind")
     gsub(g, n.sons[0])
-  of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref: 
+  of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref:
     gsub(g, n.sons[0])
   of nkLambda:
     putWithSpace(g, tkProc, "proc")
@@ -950,34 +952,34 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkConstDef, nkIdentDefs:
     gcomma(g, n, 0, -3)
     var L = sonsLen(n)
-    if L >= 2 and n.sons[L - 2].kind != nkEmpty: 
+    if L >= 2 and n.sons[L - 2].kind != nkEmpty:
       putWithSpace(g, tkColon, ":")
       gsub(g, n.sons[L - 2])
-    if L >= 1 and n.sons[L - 1].kind != nkEmpty: 
+    if L >= 1 and n.sons[L - 1].kind != nkEmpty:
       put(g, tkSpaces, Space)
       putWithSpace(g, tkEquals, "=")
       gsub(g, n.sons[L - 1], c)
-  of nkVarTuple: 
+  of nkVarTuple:
     put(g, tkParLe, "(")
     gcomma(g, n, 0, -3)
     put(g, tkParRi, ")")
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, lastSon(n), c)
-  of nkExprColonExpr: 
+  of nkExprColonExpr:
     gsub(g, n.sons[0])
     putWithSpace(g, tkColon, ":")
     gsub(g, n.sons[1])
-  of nkInfix: 
+  of nkInfix:
     gsub(g, n.sons[1])
     put(g, tkSpaces, Space)
     gsub(g, n.sons[0])        # binary operator
-    if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1): 
+    if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1):
       optNL(g, g.indent + longIndentWid)
-    else: 
+    else:
       put(g, tkSpaces, Space)
     gsub(g, n.sons[2])
-  of nkPrefix: 
+  of nkPrefix:
     gsub(g, n.sons[0])
     if n.len > 1:
       put(g, tkSpaces, Space)
@@ -987,10 +989,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
         put(g, tkParRi, ")")
       else:
         gsub(g, n.sons[1])
-  of nkPostfix: 
+  of nkPostfix:
     gsub(g, n.sons[1])
     gsub(g, n.sons[0])
-  of nkRange: 
+  of nkRange:
     gsub(g, n.sons[0])
     put(g, tkDotDot, "..")
     gsub(g, n.sons[1])
@@ -1004,43 +1006,43 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       put(g, tkSpaces, Space)
       gsub(g, n.sons[i])
     put(g, tkAccent, "`")
-  of nkIfExpr: 
+  of nkIfExpr:
     putWithSpace(g, tkIf, "if")
     gsub(g, n.sons[0].sons[0])
     putWithSpace(g, tkColon, ":")
     gsub(g, n.sons[0].sons[1])
     gsons(g, n, emptyContext, 1)
-  of nkElifExpr: 
+  of nkElifExpr:
     putWithSpace(g, tkElif, " elif")
     gsub(g, n.sons[0])
     putWithSpace(g, tkColon, ":")
     gsub(g, n.sons[1])
-  of nkElseExpr: 
+  of nkElseExpr:
     put(g, tkElse, " else")
     putWithSpace(g, tkColon, ":")
     gsub(g, n.sons[0])
   of nkTypeOfExpr:
     putWithSpace(g, tkType, "type")
     if n.len > 0: gsub(g, n.sons[0])
-  of nkRefTy: 
+  of nkRefTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkRef, "ref")
       gsub(g, n.sons[0])
     else:
       put(g, tkRef, "ref")
-  of nkPtrTy: 
+  of nkPtrTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkPtr, "ptr")
       gsub(g, n.sons[0])
     else:
       put(g, tkPtr, "ptr")
-  of nkVarTy: 
+  of nkVarTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkVar, "var")
       gsub(g, n.sons[0])
     else:
       put(g, tkVar, "var")
-  of nkDistinctTy: 
+  of nkDistinctTy:
     if n.len > 0:
       putWithSpace(g, tkDistinct, "distinct")
       gsub(g, n.sons[0])
@@ -1052,14 +1054,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
         gcomma(g, n[1])
     else:
       put(g, tkDistinct, "distinct")
-  of nkTypeDef: 
+  of nkTypeDef:
     gsub(g, n.sons[0])
     gsub(g, n.sons[1])
     put(g, tkSpaces, Space)
-    if n.sons[2].kind != nkEmpty: 
+    if n.sons[2].kind != nkEmpty:
       putWithSpace(g, tkEquals, "=")
       gsub(g, n.sons[2])
-  of nkObjectTy: 
+  of nkObjectTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkObject, "object")
       gsub(g, n.sons[0])
@@ -1068,18 +1070,18 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       gsub(g, n.sons[2])
     else:
       put(g, tkObject, "object")
-  of nkRecList: 
+  of nkRecList:
     indentNL(g)
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       optNL(g)
       gsub(g, n.sons[i], c)
       gcoms(g)
     dedent(g)
     putNL(g)
-  of nkOfInherit: 
+  of nkOfInherit:
     putWithSpace(g, tkOf, "of")
     gsub(g, n.sons[0])
-  of nkProcTy: 
+  of nkProcTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkProc, "proc")
       gsub(g, n.sons[0])
@@ -1098,7 +1100,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkBracketLe, "[")
     if n.len > 0:
       gsub(g, n.sons[0])
-    put(g, tkBracketRi, "]")    
+    put(g, tkBracketRi, "]")
   of nkEnumTy:
     if sonsLen(n) > 0:
       putWithSpace(g, tkEnum, "enum")
@@ -1110,16 +1112,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       dedent(g)
     else:
       put(g, tkEnum, "enum")
-  of nkEnumFieldDef: 
+  of nkEnumFieldDef:
     gsub(g, n.sons[0])
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, n.sons[1])
   of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext)
-  of nkIfStmt: 
+  of nkIfStmt:
     putWithSpace(g, tkIf, "if")
     gif(g, n)
-  of nkWhen, nkRecWhen: 
+  of nkWhen, nkRecWhen:
     putWithSpace(g, tkWhen, "when")
     gif(g, n)
   of nkWhileStmt: gwhile(g, n)
@@ -1130,27 +1132,27 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkBlockStmt, nkBlockExpr: gblock(g, n)
   of nkStaticStmt: gstaticStmt(g, n)
   of nkAsmStmt: gasm(g, n)
-  of nkProcDef: 
+  of nkProcDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
     gproc(g, n)
   of nkConverterDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
     gproc(g, n)
-  of nkMethodDef: 
+  of nkMethodDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkMethod, "method")
     gproc(g, n)
-  of nkIteratorDef: 
+  of nkIteratorDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkIterator, "iterator")
     gproc(g, n)
-  of nkMacroDef: 
+  of nkMacroDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkMacro, "macro")
     gproc(g, n)
-  of nkTemplateDef: 
+  of nkTemplateDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkTemplate, "template")
     gproc(g, n)
-  of nkTypeSection: 
+  of nkTypeSection:
     gsection(g, n, emptyContext, tkType, "type")
-  of nkConstSection: 
+  of nkConstSection:
     initContext(a)
     incl(a.flags, rfInConstExpr)
     gsection(g, n, a, tkConst, "const")
@@ -1159,32 +1161,32 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     if L == 0: return
     if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
     else: putWithSpace(g, tkLet, "let")
-    if L > 1: 
+    if L > 1:
       gcoms(g)
       indentNL(g)
-      for i in countup(0, L - 1): 
+      for i in countup(0, L - 1):
         optNL(g)
         gsub(g, n.sons[i])
         gcoms(g)
       dedent(g)
-    else: 
+    else:
       gsub(g, n.sons[0])
-  of nkReturnStmt: 
+  of nkReturnStmt:
     putWithSpace(g, tkReturn, "return")
     gsub(g, n.sons[0])
-  of nkRaiseStmt: 
+  of nkRaiseStmt:
     putWithSpace(g, tkRaise, "raise")
     gsub(g, n.sons[0])
-  of nkYieldStmt: 
+  of nkYieldStmt:
     putWithSpace(g, tkYield, "yield")
     gsub(g, n.sons[0])
-  of nkDiscardStmt: 
+  of nkDiscardStmt:
     putWithSpace(g, tkDiscard, "discard")
     gsub(g, n.sons[0])
-  of nkBreakStmt: 
+  of nkBreakStmt:
     putWithSpace(g, tkBreak, "break")
     gsub(g, n.sons[0])
-  of nkContinueStmt: 
+  of nkContinueStmt:
     putWithSpace(g, tkContinue, "continue")
     gsub(g, n.sons[0])
   of nkPragma:
@@ -1219,24 +1221,24 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gcommaAux(g, n, g.indent, 1)
     gcoms(g)
     putNL(g)
-  of nkFromStmt: 
+  of nkFromStmt:
     putWithSpace(g, tkFrom, "from")
     gsub(g, n.sons[0])
     put(g, tkSpaces, Space)
     putWithSpace(g, tkImport, "import")
     gcomma(g, n, emptyContext, 1)
     putNL(g)
-  of nkIncludeStmt: 
+  of nkIncludeStmt:
     putWithSpace(g, tkInclude, "include")
     gcoms(g)
     indentNL(g)
     gcommaAux(g, n, g.indent)
     dedent(g)
     putNL(g)
-  of nkCommentStmt: 
+  of nkCommentStmt:
     gcoms(g)
     optNL(g)
-  of nkOfBranch: 
+  of nkOfBranch:
     optNL(g)
     putWithSpace(g, tkOf, "of")
     gcomma(g, n, c, 0, - 2)
@@ -1248,50 +1250,50 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkSpaces, Space)
     putWithSpace(g, tkAs, "as")
     gsub(g, n.sons[1])
-  of nkBindStmt: 
+  of nkBindStmt:
     putWithSpace(g, tkBind, "bind")
     gcomma(g, n, c)
   of nkMixinStmt:
     putWithSpace(g, tkMixin, "mixin")
     gcomma(g, n, c)
-  of nkElifBranch: 
+  of nkElifBranch:
     optNL(g)
     putWithSpace(g, tkElif, "elif")
     gsub(g, n.sons[0])
     putWithSpace(g, tkColon, ":")
     gcoms(g)
     gstmts(g, n.sons[1], c)
-  of nkElse: 
+  of nkElse:
     optNL(g)
     put(g, tkElse, "else")
     putWithSpace(g, tkColon, ":")
     gcoms(g)
     gstmts(g, n.sons[0], c)
-  of nkFinally: 
+  of nkFinally:
     optNL(g)
     put(g, tkFinally, "finally")
     putWithSpace(g, tkColon, ":")
     gcoms(g)
     gstmts(g, n.sons[0], c)
-  of nkExceptBranch: 
+  of nkExceptBranch:
     optNL(g)
     putWithSpace(g, tkExcept, "except")
     gcomma(g, n, 0, - 2)
     putWithSpace(g, tkColon, ":")
     gcoms(g)
     gstmts(g, lastSon(n), c)
-  of nkGenericParams: 
+  of nkGenericParams:
     put(g, tkBracketLe, "[")
     gcomma(g, n)
     put(g, tkBracketRi, "]")
-  of nkFormalParams: 
+  of nkFormalParams:
     put(g, tkParLe, "(")
     gsemicolon(g, n, 1)
     put(g, tkParRi, ")")
-    if n.sons[0].kind != nkEmpty: 
+    if n.sons[0].kind != nkEmpty:
       putWithSpace(g, tkColon, ":")
       gsub(g, n.sons[0])
-  of nkTupleTy: 
+  of nkTupleTy:
     put(g, tkTuple, "tuple")
     put(g, tkBracketLe, "[")
     gcomma(g, n)
@@ -1309,17 +1311,17 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsons(g, n, c)
   of nkTypeClassTy:
     gTypeClassTy(g, n)
-  else: 
-    #nkNone, nkExplicitTypeListCall: 
+  else:
+    #nkNone, nkExplicitTypeListCall:
     internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
 
-proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = 
+proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
   var g: TSrcGen
   initSrcGen(g, renderFlags)
   gsub(g, n)
   result = g.buf
 
-proc renderModule(n: PNode, filename: string, 
+proc renderModule(n: PNode, filename: string,
                   renderFlags: TRenderFlags = {}) =
   var
     f: File
@@ -1339,18 +1341,18 @@ proc renderModule(n: PNode, filename: string,
     write(f, g.buf)
     close(f)
   else:
-    rawMessage(errCannotOpenFile, filename)    
+    rawMessage(errCannotOpenFile, filename)
 
-proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = 
+proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
   initSrcGen(r, renderFlags)
   gsub(r, n)
 
-proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = 
-  if r.idx < len(r.tokens): 
+proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) =
+  if r.idx < len(r.tokens):
     kind = r.tokens[r.idx].kind
     var length = r.tokens[r.idx].length.int
     literal = substr(r.buf, r.pos, r.pos + length - 1)
     inc(r.pos, length)
     inc(r.idx)
-  else: 
+  else:
     kind = tkEof
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index a914832de..6572a7f49 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -37,7 +37,7 @@ is valid, but
   spawn f(a[i])
   spawn f(a[i])
   inc i
-is not! However, 
+is not! However,
   spawn f(a[i])
   if guard: inc i
   spawn f(a[i])
@@ -460,7 +460,7 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
   # - detect used slices
   # - detect used arguments
   #echo "PAR ", renderTree(n)
-  
+
   var a = initAnalysisCtx()
   let body = n.lastSon
   analyse(a, body)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index e5e3bbe63..7e56f09dc 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -152,6 +152,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
       defs[0] = newSymNode(newVar).PTransNode
       defs[1] = it.sons[1].PTransNode
       defs[2] = transform(c, it.sons[2])
+      newVar.ast = defs[2].PNode
       result[i] = defs
     else:
       if it.kind != nkVarTuple:
diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim
new file mode 100644
index 000000000..9874d3299
--- /dev/null
+++ b/tests/parallel/tsimple_array_checks.nim
@@ -0,0 +1,41 @@
+# bug #2287
+
+import threadPool
+
+# If `nums` is an array instead of seq,
+# NONE of the iteration ways below work (including high / len-1)
+let nums = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+
+proc log(n:int) =
+  echo n
+
+proc main =
+  parallel:
+    for n in nums: # Error: cannot prove: i <= len(nums) + -1
+      spawn log(n)
+    #for i in 0 .. <nums.len: # Error: cannot prove: i <= len(nums) + -1
+    #for i in 0 .. nums.len-1: # WORKS!
+    #for i in 0 .. <nums.len: # WORKS!
+    #  spawn log(nums[i])
+
+# Array needs explicit size to work, probably related to issue #2287
+#const a: array[0..5, int] = [1,2,3,4,5,6]
+
+#const a = [1,2,3,4,5,6] # Doesn't work
+const a = @[1,2,3,4,5,6] # Doesn't work
+proc f(n: int) = echo "Hello ", n
+
+proc maino =
+  parallel:
+    # while loop doesn't work:
+    var i = 0
+    while i < a.high:
+      #for i in countup(0, a.high-1, 2):
+      spawn f(a[i])
+      spawn f(a[i+1])
+      i += 2
+
+maino() # Doesn't work outside a proc
+
+when isMainModule:
+  main()
diff --git a/todo.txt b/todo.txt
index 69674f019..47180aaf1 100644
--- a/todo.txt
+++ b/todo.txt
@@ -54,6 +54,7 @@ Bugs
 - VM: ptr/ref T cannot work in general
 - scopes are still broken for generic instantiation!
 - blocks can "export" an identifier but the CCG generates {} for them ...
+- ConcreteTypes in a 'case' means we don't check for duplicated case branches
 
 
 version 0.9.x