summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim18
-rw-r--r--compiler/c2nim/cparse.nim226
-rw-r--r--compiler/c2nim/tests/enum.h40
-rw-r--r--compiler/c2nim/tests/struct_anonym.h27
-rw-r--r--compiler/ccgcalls.nim44
-rw-r--r--compiler/ccgexprs.nim44
-rw-r--r--compiler/ccgstmts.nim1
-rw-r--r--compiler/ccgtypes.nim52
-rw-r--r--compiler/ccgutils.nim24
-rw-r--r--compiler/cgen.nim8
-rw-r--r--compiler/cgendata.nim1
-rw-r--r--compiler/commands.nim32
-rw-r--r--compiler/docgen.nim10
-rw-r--r--compiler/evals.nim1502
-rw-r--r--compiler/guards.nim316
-rw-r--r--compiler/importer.nim4
-rw-r--r--compiler/jsgen.nim12
-rw-r--r--compiler/lexer.nim35
-rw-r--r--compiler/llstream.nim3
-rw-r--r--compiler/lookups.nim21
-rw-r--r--compiler/lowerings.nim354
-rw-r--r--compiler/main.nim9
-rw-r--r--compiler/modules.nim2
-rw-r--r--compiler/msgs.nim6
-rw-r--r--compiler/nimrod.ini3
-rw-r--r--compiler/nversion.nim3
-rw-r--r--compiler/options.nim2
-rw-r--r--compiler/parampatterns.nim5
-rw-r--r--compiler/parser.nim124
-rw-r--r--compiler/patterns.nim2
-rw-r--r--compiler/pragmas.nim13
-rw-r--r--compiler/pretty.nim62
-rw-r--r--compiler/renderer.nim25
-rw-r--r--compiler/rodutils.nim2
-rw-r--r--compiler/sem.nim22
-rw-r--r--compiler/semcall.nim6
-rw-r--r--compiler/semdata.nim5
-rw-r--r--compiler/semexprs.nim106
-rw-r--r--compiler/semfold.nim18
-rw-r--r--compiler/semgnrc.nim42
-rw-r--r--compiler/seminst.nim8
-rw-r--r--compiler/semmagic.nim3
-rw-r--r--compiler/semparallel.nim465
-rw-r--r--compiler/sempass2.nim12
-rw-r--r--compiler/semstmts.nim25
-rw-r--r--compiler/semtempl.nim6
-rw-r--r--compiler/semtypes.nim36
-rw-r--r--compiler/sigmatch.nim12
-rw-r--r--compiler/suggest.nim43
-rw-r--r--compiler/transf.nim5
-rw-r--r--compiler/types.nim14
-rw-r--r--compiler/typesrenderer.nim4
-rw-r--r--compiler/vm.nim41
-rw-r--r--compiler/vmdef.nim2
-rw-r--r--compiler/vmdeps.nim2
-rw-r--r--compiler/vmgen.nim161
56 files changed, 2021 insertions, 2049 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 97f48b253..516954b88 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -510,6 +510,7 @@ const
   tfUncheckedArray* = tfVarargs
   tfUnion* = tfNoSideEffect
   tfGcSafe* = tfThread
+  tfObjHasKids* = tfEnumHasHoles
   skError* = skUnknown
   
   # type flags that are essential for type equality:
@@ -549,7 +550,7 @@ type
     mFields, mFieldPairs, mOmpParFor,
     mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
-    mIsPartOf, mAstToStr, mRand,
+    mIsPartOf, mAstToStr, mParallel,
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap,
     mReset,
@@ -560,7 +561,7 @@ type
     mFloat, mFloat32, mFloat64, mFloat128,
     mBool, mChar, mString, mCstring,
     mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
-    mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn,
+    mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
     mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
     mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType,
     mNaN, mInf, mNegInf,
@@ -597,18 +598,17 @@ const
     mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, 
     mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, 
     mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, 
-    mConTArr, mConTT, mSlice, 
+    mConTArr, mConTT,
     mAppendStrCh, mAppendStrStr, mAppendSeqElem, 
     mInRange, mInSet, mRepr,
-    mRand, 
     mCopyStr, mCopyStrLast}
   # magics that require special semantic checking and
   # thus cannot be overloaded (also documented in the spec!):
   SpecialSemMagics* = {
     mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, 
-    mEcho, mShallowCopy, mExpandToAst}
+    mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn}
 
-type 
+type
   PNode* = ref TNode
   TNodeSeq* = seq[PNode]
   PType* = ref TType
@@ -873,7 +873,7 @@ const
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
                                       nfDotSetter, nfDotField,
-                                      nfAllConst,nfIsRef}
+                                      nfIsRef}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -886,6 +886,8 @@ const
 
   nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
                   nkCommand, nkCallStrLit, nkHiddenCallConv}
+  nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
+                   nkClosedSymChoice}
 
   nkLiterals* = {nkCharLit..nkTripleStrLit}
   nkLambdaKinds* = {nkLambda, nkDo}
@@ -1046,7 +1048,7 @@ proc discardSons(father: PNode) =
   father.sons = nil
 
 when defined(useNodeIds):
-  const nodeIdToDebug* = 482228 # 612794
+  const nodeIdToDebug* = 310841 # 612794
   #612840 # 612905 # 614635 # 614637 # 614641
   # 423408
   #429107 # 430443 # 441048 # 441090 # 441153
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim
index 52d50ca39..2e31af528 100644
--- a/compiler/c2nim/cparse.nim
+++ b/compiler/c2nim/cparse.nim
@@ -19,7 +19,7 @@
 
 import 
   os, llstream, renderer, clex, idents, strutils, pegs, ast, astalgo, msgs,
-  options, strtabs
+  options, strtabs, hashes, algorithm
 
 type 
   TParserFlag = enum
@@ -63,6 +63,15 @@ type
 
   ERetryParsing = object of ESynch
 
+
+
+proc addTypeDef(section, name, t: PNode)
+proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode
+proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool,
+                     kind: TNodeKind = nkRecList): PNode
+
+
+
 proc newParserOptions*(): PParserOptions = 
   new(result)
   result.prefixes = @[]
@@ -682,12 +691,75 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode =
     else: result = mangledIdent(p.tok.s, p)
     getTok(p, result)
 
-proc parseStructBody(p: var TParser, isUnion: bool,
+proc structPragmas(p: TParser, name: PNode, origName: string): PNode = 
+  assert name.kind == nkIdent
+  result = newNodeP(nkPragmaExpr, p)
+  addSon(result, exportSym(p, name, origName))
+  var pragmas = newNodeP(nkPragma, p)
+  #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
+  if p.options.header.len > 0:
+    addSon(pragmas, newIdentStrLitPair("importc", origName, p),
+                    newIdentStrLitPair("header", p.options.header, p))
+  if pragmas.len > 0: addSon(result, pragmas)
+  else: addSon(result, ast.emptyNode)
+
+proc hashPosition(p: TParser): string =
+  let lineInfo = parLineInfo(p)
+  let fileInfo = fileInfos[lineInfo.fileIndex]
+  result = $hash(fileInfo.shortName & "_" & $lineInfo.line & "_" & $lineInfo.col).uint
+
+proc parseInnerStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode =
+  getTok(p, nil)
+  if p.tok.xkind != pxCurlyLe:
+    parMessage(p, errUser, "Expected '{' but found '" & $(p.tok[]) & "'")
+  
+  let structName =  if isUnion: "INNER_C_UNION_" & p.hashPosition
+                    else: "INNER_C_STRUCT_" & p.hashPosition
+  let typeSection = newNodeP(nkTypeSection, p)
+  let newStruct = newNodeP(nkObjectTy, p)
+  var pragmas = ast.emptyNode
+  if isUnion:
+    pragmas = newNodeP(nkPragma, p)
+    addSon(pragmas, newIdentNodeP("union", p))
+  addSon(newStruct, pragmas, ast.emptyNode) # no inheritance 
+  result = newNodeP(nkIdent, p)
+  result.ident = getIdent(structName)
+  let struct = parseStructBody(p, stmtList, isUnion)
+  let defName = newNodeP(nkIdent, p)
+  defName.ident = getIdent(structName)
+  addSon(newStruct, struct)
+  addTypeDef(typeSection, structPragmas(p, defName, "no_name"), newStruct)
+  addSon(stmtList, typeSection)
+
+proc parseStructBody(p: var TParser, stmtList: PNode, isUnion: bool,
                      kind: TNodeKind = nkRecList): PNode =
   result = newNodeP(kind, p)
   eat(p, pxCurlyLe, result)
   while p.tok.xkind notin {pxEof, pxCurlyRi}:
-    var baseTyp = typeAtom(p)
+    skipConst(p)
+    var baseTyp: PNode
+    if p.tok.xkind == pxSymbol and (p.tok.s == "struct" or p.tok.s == "union"):
+      let gotUnion = if p.tok.s == "union": true   else: false
+      saveContext(p)
+      getTok(p, nil)
+      if p.tok.xkind == pxSymbol:
+        backtrackContext(p)
+        baseTyp = typeAtom(p)
+      else:
+        backtrackContext(p)
+        baseTyp = parseInnerStruct(p, stmtList, gotUnion)
+        if p.tok.xkind == pxSemiColon:
+          let def = newNodeP(nkIdentDefs, p)
+          var t = pointer(p, baseTyp)
+          let i = fieldIdent("ano_" & p.hashPosition, p)
+          t = parseTypeSuffix(p, t)
+          addSon(def, i, t, ast.emptyNode)
+          addSon(result, def)
+          getTok(p, nil)
+          continue
+    else:
+      baseTyp = typeAtom(p)
+    
     while true:
       var def = newNodeP(nkIdentDefs, p)
       var t = pointer(p, baseTyp)
@@ -700,18 +772,6 @@ proc parseStructBody(p: var TParser, isUnion: bool,
     eat(p, pxSemicolon, lastSon(result))
   eat(p, pxCurlyRi, result)
 
-proc structPragmas(p: TParser, name: PNode, origName: string): PNode = 
-  assert name.kind == nkIdent
-  result = newNodeP(nkPragmaExpr, p)
-  addSon(result, exportSym(p, name, origName))
-  var pragmas = newNodeP(nkPragma, p)
-  #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
-  if p.options.header.len > 0:
-    addSon(pragmas, newIdentStrLitPair("importc", origName, p),
-                    newIdentStrLitPair("header", p.options.header, p))
-  if pragmas.len > 0: addSon(result, pragmas)
-  else: addSon(result, ast.emptyNode)
-
 proc enumPragmas(p: TParser, name: PNode): PNode =
   result = newNodeP(nkPragmaExpr, p)
   addSon(result, name)
@@ -722,7 +782,7 @@ proc enumPragmas(p: TParser, name: PNode): PNode =
   addSon(pragmas, e)
   addSon(result, pragmas)
 
-proc parseStruct(p: var TParser, isUnion: bool): PNode =
+proc parseStruct(p: var TParser, stmtList: PNode, isUnion: bool): PNode =
   result = newNodeP(nkObjectTy, p)
   var pragmas = ast.emptyNode
   if isUnion:
@@ -730,7 +790,7 @@ proc parseStruct(p: var TParser, isUnion: bool): PNode =
     addSon(pragmas, newIdentNodeP("union", p))
   addSon(result, pragmas, ast.emptyNode) # no inheritance 
   if p.tok.xkind == pxCurlyLe:
-    addSon(result, parseStructBody(p, isUnion))
+    addSon(result, parseStructBody(p, stmtList, isUnion))
   else: 
     addSon(result, newNodeP(nkRecList, p))
 
@@ -855,9 +915,28 @@ proc parseTrailingDefinedTypes(p: var TParser, section, typ: PNode) =
     newTyp = parseTypeSuffix(p, newTyp)
     addTypeDef(section, newName, newTyp)
 
-proc enumFields(p: var TParser): PNode = 
+proc createConst(name, typ, val: PNode, p: TParser): PNode =
+  result = newNodeP(nkConstDef, p)
+  addSon(result, name, typ, val)
+
+proc exprToNumber(n: PNode not nil): tuple[succ: bool, val: BiggestInt] =
+  result = (false, 0.BiggestInt)
+  case n.kind:
+  of nkPrefix:
+    # Check for negative/positive numbers  -3  or  +6
+    if n.sons.len == 2 and n.sons[0].kind == nkIdent and n.sons[1].kind == nkIntLit:
+      let pre = n.sons[0]
+      let num = n.sons[1]
+      if pre.ident.s == "-": result = (true, - num.intVal)
+      elif pre.ident.s == "+": result = (true, num.intVal)
+  else: discard
+
+proc enumFields(p: var TParser, constList: PNode): PNode = 
   result = newNodeP(nkEnumTy, p)
   addSon(result, ast.emptyNode) # enum does not inherit from anything
+  var i: BiggestInt = 0
+  var field: tuple[id: BiggestInt, isNumber: bool, node: PNode]
+  var fields = newSeq[type(field)]()
   while true:
     var e = skipIdent(p)
     if p.tok.xkind == pxAsgn: 
@@ -867,17 +946,59 @@ proc enumFields(p: var TParser): PNode =
       e = newNodeP(nkEnumFieldDef, p)
       addSon(e, a, c)
       skipCom(p, e)
-    
-    addSon(result, e)
+      if c.kind == nkIntLit:
+        i = c.intVal
+        field.isNumber = true
+      else:
+        var (success, number) = exprToNumber(c)
+        if success:
+          i = number
+          field.isNumber = true
+        else:
+          field.isNumber = false
+    else:
+      inc(i)
+      field.isNumber = true
+    field.id = i
+    field.node = e
+    fields.add(field)
     if p.tok.xkind != pxComma: break
     getTok(p, e)
     # allow trailing comma:
     if p.tok.xkind == pxCurlyRi: break
-
-proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) = 
+  fields.sort do (x, y: type(field)) -> int:
+    cmp(x.id, y.id)
+  var lastId: BiggestInt
+  var lastIdent: PNode
+  for count, f in fields:
+    if not f.isNumber:
+      addSon(result, f.node)
+    elif f.id == lastId and count > 0:
+      var currentIdent: PNode
+      case f.node.kind:
+      of nkEnumFieldDef:
+        if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent:
+          currentIdent = f.node.sons[0]
+        else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!")
+      of nkIdent: currentIdent = f.node
+      else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!")
+      var constant = createConst( currentIdent, ast.emptyNode, lastIdent, p)
+      constList.addSon(constant)
+    else:
+      addSon(result, f.node)
+      lastId = f.id
+      case f.node.kind:
+      of nkEnumFieldDef:
+        if f.node.sons.len > 0 and f.node.sons[0].kind == nkIdent:
+          lastIdent = f.node.sons[0]
+        else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!")
+      of nkIdent: lastIdent = f.node
+      else: parMessage(p, errGenerated, "Warning: When sorting enum fields an expected nkIdent was not found. Check the fields!")
+
+proc parseTypedefStruct(p: var TParser, result: PNode, stmtList: PNode, isUnion: bool) = 
   getTok(p, result)
   if p.tok.xkind == pxCurlyLe:
-    var t = parseStruct(p, isUnion)
+    var t = parseStruct(p, stmtList, isUnion)
     var origName = p.tok.s
     markTypeIdent(p, nil)
     var name = skipIdent(p)
@@ -890,7 +1011,7 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) =
     var nameOrType = skipIdent(p)
     case p.tok.xkind 
     of pxCurlyLe:
-      var t = parseStruct(p, isUnion)
+      var t = parseStruct(p, stmtList, isUnion)
       if p.tok.xkind == pxSymbol: 
         # typedef struct tagABC {} abc, *pabc;
         # --> abc is a better type name than tagABC!
@@ -914,11 +1035,11 @@ proc parseTypedefStruct(p: var TParser, result: PNode, isUnion: bool) =
   else:
     expectIdent(p)
 
-proc parseTypedefEnum(p: var TParser, result: PNode) = 
+proc parseTypedefEnum(p: var TParser, result, constSection: PNode) = 
   getTok(p, result)
   if p.tok.xkind == pxCurlyLe:
     getTok(p, result)
-    var t = enumFields(p)
+    var t = enumFields(p, constSection)
     eat(p, pxCurlyRi, t)
     var origName = p.tok.s
     markTypeIdent(p, nil)
@@ -933,7 +1054,7 @@ proc parseTypedefEnum(p: var TParser, result: PNode) =
     case p.tok.xkind 
     of pxCurlyLe:
       getTok(p, result)
-      var t = enumFields(p)
+      var t = enumFields(p, constSection)
       eat(p, pxCurlyRi, t)
       if p.tok.xkind == pxSymbol: 
         # typedef enum tagABC {} abc, *pabc;
@@ -960,27 +1081,36 @@ proc parseTypedefEnum(p: var TParser, result: PNode) =
     expectIdent(p)
 
 proc parseTypeDef(p: var TParser): PNode =  
-  result = newNodeP(nkTypeSection, p)
+  result = newNodeP(nkStmtList, p)
+  var typeSection = newNodeP(nkTypeSection, p)
+  var afterStatements = newNodeP(nkStmtList, p)
   while p.tok.xkind == pxSymbol and p.tok.s == "typedef":
-    getTok(p, result)
+    getTok(p, typeSection)
     inc(p.inTypeDef)
     expectIdent(p)
     case p.tok.s
-    of "struct": parseTypedefStruct(p, result, isUnion=false)
-    of "union": parseTypedefStruct(p, result, isUnion=true)
-    of "enum": parseTypedefEnum(p, result)
+    of "struct": parseTypedefStruct(p, typeSection, result, isUnion=false)
+    of "union": parseTypedefStruct(p, typeSection, result, isUnion=true)
+    of "enum":
+      var constSection = newNodeP(nkConstSection, p)
+      parseTypedefEnum(p, typeSection, constSection)
+      addSon(afterStatements, constSection)
     of "class":
       if pfCpp in p.options.flags:
-        parseTypedefStruct(p, result, isUnion=false)
+        parseTypedefStruct(p, typeSection, result, isUnion=false)
       else:
         var t = typeAtom(p)
-        otherTypeDef(p, result, t)
+        otherTypeDef(p, typeSection, t)
     else: 
       var t = typeAtom(p)
-      otherTypeDef(p, result, t)
+      otherTypeDef(p, typeSection, t)
     eat(p, pxSemicolon)
     dec(p.inTypeDef)
-    
+  
+  addSon(result, typeSection)
+  for s in afterStatements:
+    addSon(result, s)
+  
 proc skipDeclarationSpecifiers(p: var TParser) =
   while p.tok.xkind == pxSymbol:
     case p.tok.s
@@ -1092,10 +1222,6 @@ proc declaration(p: var TParser): PNode =
     result = parseVarDecl(p, baseTyp, rettyp, origName)
   assert result != nil
 
-proc createConst(name, typ, val: PNode, p: TParser): PNode =
-  result = newNodeP(nkConstDef, p)
-  addSon(result, name, typ, val)
-
 proc enumSpecifier(p: var TParser): PNode =  
   saveContext(p)
   getTok(p, nil) # skip "enum"
@@ -1141,12 +1267,16 @@ proc enumSpecifier(p: var TParser): PNode =
       closeContext(p)
       var name = result
       # create a type section containing the enum
-      result = newNodeP(nkTypeSection, p)
+      result = newNodeP(nkStmtList, p)
+      var tSection = newNodeP(nkTypeSection, p)
       var t = newNodeP(nkTypeDef, p)
       getTok(p, t)
-      var e = enumFields(p)
+      var constSection = newNodeP(nkConstSection, p)
+      var e = enumFields(p, constSection)
       addSon(t, exportSym(p, name, origName), ast.emptyNode, e)
-      addSon(result, t)
+      addSon(tSection, t)
+      addSon(result, tSection)
+      addSon(result, constSection)
       eat(p, pxCurlyRi, result)
       eat(p, pxSemicolon)
     of pxSemicolon:
@@ -1608,8 +1738,8 @@ proc declarationOrStatement(p: var TParser): PNode =
       result = expressionStatement(p)
   assert result != nil
 
-proc parseTuple(p: var TParser, isUnion: bool): PNode = 
-  result = parseStructBody(p, isUnion, nkTupleTy)
+proc parseTuple(p: var TParser, statements: PNode, isUnion: bool): PNode = 
+  parseStructBody(p, statements, isUnion, nkTupleTy)
 
 proc parseTrailingDefinedIdents(p: var TParser, result, baseTyp: PNode) =
   var varSection = newNodeP(nkVarSection, p)
@@ -1640,13 +1770,13 @@ proc parseStandaloneStruct(p: var TParser, isUnion: bool): PNode =
   if p.tok.xkind in {pxCurlyLe, pxSemiColon}:
     if origName.len > 0: 
       var name = mangledIdent(origName, p)
-      var t = parseStruct(p, isUnion)
+      var t = parseStruct(p, result, isUnion)
       var typeSection = newNodeP(nkTypeSection, p)
       addTypeDef(typeSection, structPragmas(p, name, origName), t)
       addSon(result, typeSection)
       parseTrailingDefinedIdents(p, result, name)
     else:
-      var t = parseTuple(p, isUnion)
+      var t = parseTuple(p, result, isUnion)
       parseTrailingDefinedIdents(p, result, t)
   else:
     backtrackContext(p)
@@ -2034,7 +2164,7 @@ proc parseStandaloneClass(p: var TParser, isStruct: bool): PNode =
       addTypeDef(typeSection, structPragmas(p, name, origName), t)
       parseTrailingDefinedIdents(p, result, name)
     else:
-      var t = parseTuple(p, isUnion=false)
+      var t = parseTuple(p, result, isUnion=false)
       parseTrailingDefinedIdents(p, result, t)
   else:
     backtrackContext(p)
diff --git a/compiler/c2nim/tests/enum.h b/compiler/c2nim/tests/enum.h
new file mode 100644
index 000000000..16bc59058
--- /dev/null
+++ b/compiler/c2nim/tests/enum.h
@@ -0,0 +1,40 @@
+
+enum vehicles
+{
+	car = 0x10,
+	truck,
+	boat = 0x01,
+	ship = 1,
+	speedboat = 1,
+	bicycle = 4,
+	bobycar
+};
+
+enum
+{
+	red = 4,
+	green = 2,
+	blue
+};
+
+typedef enum food
+{
+	bread = 4,
+	toast = 4,
+	bun = 0x04,
+	cucumber = 2,
+	chocolate = 6
+};
+
+typedef enum numbers
+{
+	one = 1,
+	two,
+	nten = - 10,
+	nnine,
+	four = 4,
+	three = + 3,
+	positivenine = + 9,
+	nfour = - 4,
+	negativeten = -10
+};
\ No newline at end of file
diff --git a/compiler/c2nim/tests/struct_anonym.h b/compiler/c2nim/tests/struct_anonym.h
new file mode 100644
index 000000000..859bfc206
--- /dev/null
+++ b/compiler/c2nim/tests/struct_anonym.h
@@ -0,0 +1,27 @@
+
+struct normal{
+	int a;
+	int b;
+};
+
+typedef struct outerStruct {
+	struct normal a_nomal_one;
+	
+	int a;
+	
+	struct {
+		union {
+			int b;
+		} a_union_in_the_struct;
+		
+		int c;
+	};
+	
+	union {
+		int d;
+		
+		struct {
+			int e;
+		} a_struct_in_the_union;
+	} a_union;
+};
\ No newline at end of file
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 84c5bf419..71e23aa1d 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -77,18 +77,38 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
 
 proc openArrayLoc(p: BProc, n: PNode): PRope =
   var a: TLoc
-  initLocExpr(p, n, a)
-  case skipTypes(a.t, abstractVar).kind
-  of tyOpenArray, tyVarargs:
-    result = ropef("$1, $1Len0", [rdLoc(a)])
-  of tyString, tySequence:
-    if skipTypes(n.typ, abstractInst).kind == tyVar:
-      result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()])
-    else:
-      result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()])
-  of tyArray, tyArrayConstr:
-    result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
-  else: internalError("openArrayLoc: " & typeToString(a.t))
+
+  let q = skipConv(n)
+  if getMagic(q) == mSlice:
+    # magic: pass slice to openArray:
+    var b, c: TLoc
+    initLocExpr(p, q[1], a)
+    initLocExpr(p, q[2], b)
+    initLocExpr(p, q[3], c)
+    let fmt =
+      case skipTypes(a.t, abstractVar+{tyPtr}).kind
+      of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
+        "($1)+($2), ($3)-($2)+1"
+      of tyString, tySequence:
+        if skipTypes(n.typ, abstractInst).kind == tyVar:
+          "(*$1)->data+($2), ($3)-($2)+1"
+        else:
+          "$1->data+($2), ($3)-($2)+1"
+      else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
+    result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)])
+  else:
+    initLocExpr(p, n, a)
+    case skipTypes(a.t, abstractVar).kind
+    of tyOpenArray, tyVarargs:
+      result = ropef("$1, $1Len0", [rdLoc(a)])
+    of tyString, tySequence:
+      if skipTypes(n.typ, abstractInst).kind == tyVar:
+        result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()])
+      else:
+        result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()])
+    of tyArray, tyArrayConstr:
+      result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
+    else: internalError("openArrayLoc: " & typeToString(a.t))
 
 proc genArgStringToCString(p: BProc, 
                            n: PNode): PRope {.inline.} =
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 49350fa9c..4698082f1 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -484,7 +484,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     opr: array[mUnaryMinusI..mAbsI64, string] = [
       mUnaryMinusI: "((NI$2)-($1))",
       mUnaryMinusI64: "-($1)",
-      mAbsI: "(NI$2)abs($1)",
+      mAbsI: "($1 > 0? ($1) : -($1))",
       mAbsI64: "($1 > 0? ($1) : -($1))"]
   var
     a: TLoc
@@ -714,11 +714,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: PRope, field: PSym) =
     assert(it.sons[0].kind == nkSym)
     let op = it.sons[0].sym
     if op.magic == mNot: it = it.sons[1]
-    assert(it.sons[2].kind == nkSym)
+    let disc = it.sons[2].skipConv
+    assert(disc.kind == nkSym)
     initLoc(test, locNone, it.typ, OnStack)
     initLocExpr(p, it.sons[1], u)
-    initLoc(v, locExpr, it.sons[2].typ, OnUnknown)
-    v.r = ropef("$1.$2", [obj, it.sons[2].sym.loc.r])
+    initLoc(v, locExpr, disc.typ, OnUnknown)
+    v.r = ropef("$1.$2", [obj, disc.sym.loc.r])
     genInExprAux(p, it, u, v, test)
     let id = nodeTableTestOrSet(p.module.dataCache,
                                newStrNode(nkStrLit, field.name.s), gBackendId)
@@ -1144,6 +1145,24 @@ proc genNewFinalize(p: BProc, e: PNode) =
   genObjectInit(p, cpsStmts, bt, a, false)
   gcUsage(e)
 
+proc genOfHelper(p: BProc; dest: PType; a: PRope): PRope =
+  # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
+  # have to call it here first:
+  let ti = genTypeInfo(p.module, dest)
+  if tfFinal in dest.flags or (p.module.objHasKidsValid and
+                               tfObjHasKids notin dest.flags):
+    result = ropef("$1.m_type == $2", a, ti)
+  else:
+    discard cgsym(p.module, "TNimType")
+    inc p.module.labels
+    let cache = con("Nim_OfCheck_CACHE", p.module.labels.toRope)
+    appf(p.module.s[cfsVars], "static TNimType* $#[2];$n", cache)
+    result = rfmt(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
+  when false:
+    # former version:
+    result = rfmt(p.module, "#isObj($1.m_type, $2)",
+                  a, genTypeInfo(p.module, dest))
+
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, x, a)
@@ -1163,11 +1182,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
     globalError(x.info, errGenerated, 
       "no 'of' operator available for pure objects")
   if nilCheck != nil:
-    r = rfmt(p.module, "(($1) && #isObj($2.m_type, $3))",
-             nilCheck, r, genTypeInfo(p.module, dest))
+    r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
   else:
-    r = rfmt(p.module, "#isObj($1.m_type, $2)",
-             r, genTypeInfo(p.module, dest))
+    r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
   putIntoDest(p, d, getSysType(tyBool), r)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
@@ -1382,10 +1399,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     of mIncl:
       var ts = "NI" & $(size * 8)
       binaryStmtInExcl(p, e, d,
-          "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n")
+          "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
     of mExcl:
       var ts = "NI" & $(size * 8)
-      binaryStmtInExcl(p, e, d, "$1 &= ~((" & ts & ")(1) << (($2) % (sizeof(" &
+      binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
           ts & ")*8)));$n")
     of mCard:
       if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
@@ -1623,7 +1640,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
      mInSet:
     genSetOp(p, e, d, op)
-  of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mRand:
+  of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit:
     var opr = e.sons[0].sym
     if lfNoDecl notin opr.loc.flags:
       discard cgsym(p.module, opr.loc.r.ropeToStr)
@@ -1636,7 +1653,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mSlurp..mQuoteAst:
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   of mSpawn:
-    let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1])
+    let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
+    expr(p, n, d)
+  of mParallel:
+    let n = semparallel.liftParallel(p.module.module, e)
     expr(p, n, d)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 311149cb3..a8cfa57e4 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -593,6 +593,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
       else: 
         # else statement: nothing to do yet
         # but we reserved a label, which we use later
+        discard
     linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", 
             rdLoc(a), toRope(bitMask))
     for j in countup(0, high(branches)):
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index f51e66897..8e762ce27 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -11,49 +11,10 @@
 
 # ------------------------- Name Mangling --------------------------------
 
-proc mangleField(name: string): string = 
-  case name[0]
-  of 'a'..'z': 
-    result = ""
-    add(result, chr(ord(name[0]) - ord('a') + ord('A')))
-  of '0'..'9', 'A'..'Z': 
-    result = ""
-    add(result, name[0])
-  else: result = "HEX" & toHex(ord(name[0]), 2)
-  for i in countup(1, len(name) - 1): 
-    case name[i]
-    of 'A'..'Z': 
-      add(result, chr(ord(name[i]) - ord('A') + ord('a')))
-    of '_': 
-      discard
-    of 'a'..'z', '0'..'9': 
-      add(result, name[i])
-    else: 
-      add(result, "HEX")
-      add(result, toHex(ord(name[i]), 2))
-
-proc mangle(name: string): string = 
-  when false:
-    case name[0]
-    of 'a'..'z': 
-      result = ""
-      add(result, chr(ord(name[0]) - ord('a') + ord('A')))
-    of '0'..'9', 'A'..'Z': 
-      result = ""
-      add(result, name[0])
-    else: result = "HEX" & toHex(ord(name[0]), 2)
-  result = ""
-  for i in countup(0, len(name) - 1): 
-    case name[i]
-    of 'A'..'Z': 
-      add(result, chr(ord(name[i]) - ord('A') + ord('a')))
-    of '_': 
-      discard
-    of 'a'..'z', '0'..'9': 
-      add(result, name[i])
-    else: 
-      add(result, "HEX")
-      add(result, toHex(ord(name[i]), 2))
+proc mangleField(name: string): string =
+  result = mangle(name)
+  result[0] = result[0].toUpper # Mangling makes everything lowercase,
+                                # but some identifiers are C keywords
 
 proc isKeyword(w: PIdent): bool =
   # nimrod and C++ share some keywords
@@ -835,6 +796,11 @@ proc genObjectInfo(m: BModule, typ: PType, name: PRope) =
   var tmp = getNimNode(m)
   genObjectFields(m, typ, typ.n, tmp)
   appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
+  var t = typ.sons[0]
+  while t != nil:
+    t = t.skipTypes(abstractInst)
+    t.flags.incl tfObjHasKids
+    t = t.sons[0]
 
 proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
   genTypeInfoAuxBase(m, typ, name, toRope("0"))
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 1d8f0158b..04983d6a4 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -161,6 +161,30 @@ proc makeSingleLineCString*(s: string): string =
     result.add(c.toCChar)
   result.add('\"')
 
+proc mangle*(name: string): string =
+  ## Lowercases the given name and manges any non-alphanumeric characters
+  ## so they are represented as `HEX____`. If the name starts with a number,
+  ## `N` is prepended
+  result = ""
+  case name[0]
+  of Letters:
+    result.add(name[0].toLower)
+  of Digits:
+    result.add("N" & name[0])
+  else:
+    result = "HEX" & toHex(ord(name[0]), 2)
+  for i in 1..(name.len-1):
+    let c = name[i]
+    case c
+    of 'A'..'Z':
+      add(result, c.toLower)
+    of '_':
+      discard
+    of 'a'..'z', '0'..'9':
+      add(result, c)
+    else:
+      add(result, "HEX" & toHex(ord(c), 2))
+
 proc makeLLVMString*(s: string): PRope = 
   const MaxLineLength = 64
   result = nil
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 8d66d7a3b..e2f3b5ab0 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -14,7 +14,8 @@ import
   options, intsets,
   nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
   times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
-  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings
+  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
+  semparallel
 
 when options.hasTinyCBackend:
   import tccgen
@@ -503,7 +504,8 @@ proc assignLocalVar(p: BProc, s: PSym) =
     if sfRegister in s.flags: app(decl, " register")
     #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
     #  app(decl, " GC_GUARD")
-    if sfVolatile in s.flags or p.nestedTryStmts.len > 0: 
+    if sfVolatile in s.flags or (p.nestedTryStmts.len > 0 and
+                                 gCmd != cmdCompileToCpp):
       app(decl, " volatile")
     appf(decl, " $1;$n", [s.loc.r])
   else:
@@ -1048,6 +1050,7 @@ proc getSomeInitName(m: PSym, suffix: string): PRope =
   assert m.owner.kind == skPackage
   if {sfSystemModule, sfMainModule} * m.flags == {}:
     result = m.owner.name.s.mangle.toRope
+    result.app "_"
   result.app m.name.s
   result.app suffix
   
@@ -1382,6 +1385,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   registerModuleToMain(m.module)
 
   if sfMainModule in m.module.flags: 
+    m.objHasKidsValid = true
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index e7d818556..12041c55b 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -96,6 +96,7 @@ type
                               # a frame var twice in an init proc
     isHeaderFile*: bool       # C source file is the header file
     includesStringh*: bool    # C source file already includes ``<string.h>``
+    objHasKidsValid*: bool    # whether we can rely on tfObjHasKids
     cfilename*: string        # filename of the module (including path,
                               # without extension)
     typeCache*: TIdTable      # cache the generated types
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 8339219ed..38c8dd294 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -9,10 +9,34 @@
 
 # This module handles the parsing of command line arguments.
 
+
+# We do this here before the 'import' statement so 'defined' does not get
+# confused with 'TGCMode.gcGenerational' etc.
+template bootSwitch(name, expr, userString: expr): expr =
+  # Helper to build boot constants, for debugging you can 'echo' the else part.
+  const name = if expr: " " & userString else: ""
+
+bootSwitch(usedRelease, defined(release), "-d:release")
+bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline")
+bootSwitch(usedNoCaas, defined(noCaas), "-d:noCaas")
+bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
+bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
+bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
+bootSwitch(usedNoGC, defined(nogc), "--gc:none")
+
 import 
   os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, 
   wordrecg, parseutils, babelcmd, idents
 
+# but some have deps to imported modules. Yay.
+bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
+bootSwitch(usedAvoidTimeMachine, noTimeMachine, "-d:avoidTimeMachine")
+bootSwitch(usedNativeStacktrace,
+  defined(nativeStackTrace) and nativeStackTraceSupported,
+  "-d:nativeStackTrace")
+bootSwitch(usedFFI, hasFFI, "-d:useFFI")
+
+
 proc writeCommandLineUsage*()
 
 type 
@@ -55,6 +79,14 @@ proc writeVersionInfo(pass: TCmdLinePass) =
     msgWriteln(`%`(HelpMessage, [VersionAsString, 
                                  platform.OS[platform.hostOS].name, 
                                  CPU[platform.hostCPU].name]))
+
+    const gitHash = gorge("git log -n 1 --format=%H")
+    if gitHash.strip.len == 40:
+      msgWriteln("git hash: " & gitHash)
+
+    msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
+      usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
+      usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
     quit(0)
 
 var
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 6948c4979..4c9803401 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -541,8 +541,14 @@ proc genOutFile(d: PDoc): PRope =
   if toc != nil:
     toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
   for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
-  if d.meta[metaTitle].len != 0: title = d.meta[metaTitle]
-  else: title = "Module " & extractFilename(changeFileExt(d.filename, ""))
+
+  # Extract the title. Non API modules generate an entry in the index table.
+  if d.meta[metaTitle].len != 0:
+    title = d.meta[metaTitle]
+    setIndexTerm(d[], "", title)
+  else:
+    # Modules get an automatic title for the HTML, but no entry in the index.
+    title = "Module " & extractFilename(changeFileExt(d.filename, ""))
 
   let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
   content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
diff --git a/compiler/evals.nim b/compiler/evals.nim
deleted file mode 100644
index 151adf690..000000000
--- a/compiler/evals.nim
+++ /dev/null
@@ -1,1502 +0,0 @@
-#
-#
-#           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This file implements the evaluator for Nimrod code.
-# The evaluator is very slow, but simple. Since this
-# is used mainly for evaluating macros and some other
-# stuff at compile time, performance is not that
-# important.
-
-import 
-  strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
-  msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
-  parser, ropes, rodread, idgen, osproc, streams, evaltempl
-
-when hasFFI:
-  import evalffi
-
-type 
-  PStackFrame* = ref TStackFrame
-  TStackFrame* = object
-    prc: PSym                 # current prc; proc that is evaluated
-    slots: TNodeSeq           # parameters passed to the proc + locals;
-                              # parameters come first
-    call: PNode
-    next: PStackFrame         # for stacking
-  
-  TEvalMode* = enum           ## reason for evaluation
-    emRepl,                   ## evaluate because in REPL mode
-    emConst,                  ## evaluate for 'const' according to spec
-    emOptimize,               ## evaluate for optimization purposes (same as
-                              ## emConst?)
-    emStatic                  ## evaluate for enforced compile time eval
-                              ## ('static' context)
-
-  TSandboxFlag* = enum        ## what the evaluation engine should allow
-    allowCast,                ## allow unsafe language feature: 'cast'
-    allowFFI,                 ## allow the FFI
-    allowInfiniteLoops        ## allow endless loops
-  TSandboxFlags* = set[TSandboxFlag]
-
-  TEvalContext* = object of passes.TPassContext
-    module*: PSym
-    tos*: PStackFrame         # top of stack
-    lastException*: PNode
-    callsite: PNode           # for 'callsite' magic
-    mode*: TEvalMode
-    features: TSandboxFlags
-    globals*: TIdNodeTable    # state of global vars
-    getType*: proc(n: PNode): PNode {.closure.}
-    handleIsOperator*: proc(n: PNode): PNode {.closure.}
-
-  PEvalContext* = ref TEvalContext
-
-  TEvalFlag = enum 
-    efNone, efLValue
-  TEvalFlags = set[TEvalFlag]
-
-const
-  evalMaxIterations = 500_000 # max iterations of all loops
-  evalMaxRecDepth = 10_000    # max recursion depth for evaluation
-
-# other idea: use a timeout! -> Wether code compiles depends on the machine
-# the compiler runs on then! Bad idea!
-
-proc newStackFrame*(): PStackFrame =
-  new(result)
-  result.slots = @[]
-
-proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext =
-  new(result)
-  result.module = module
-  result.mode = mode
-  result.features = {allowFFI}
-  initIdNodeTable(result.globals)
-
-proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = 
-  t.next = c.tos
-  c.tos = t
-
-proc popStackFrame*(c: PEvalContext) {.inline.} =
-  if c.tos != nil: c.tos = c.tos.next
-  else: InternalError("popStackFrame")
-
-proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode
-proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode
-
-proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode =
-  if defined(debug) and gVerbosity >= 3: writeStackTrace()
-  result = newNodeI(nkExceptBranch, info)
-  # creating a nkExceptBranch without sons 
-  # means that it could not be evaluated
-
-proc stackTraceAux(x: PStackFrame) =
-  if x != nil:
-    stackTraceAux(x.next)
-    var info = if x.call != nil: x.call.info else: UnknownLineInfo()
-    # we now use the same format as in system/except.nim
-    var s = toFilename(info)
-    var line = toLineNumber(info)
-    if line > 0:
-      add(s, '(')
-      add(s, $line)
-      add(s, ')')
-    if x.prc != nil:
-      for k in 1..max(1, 25-s.len): add(s, ' ')
-      add(s, x.prc.name.s)
-    MsgWriteln(s)
-
-proc stackTrace(c: PEvalContext, info: TLineInfo, msg: TMsgKind, arg = "") = 
-  MsgWriteln("stack trace: (most recent call last)")
-  stackTraceAux(c.tos)
-  LocalError(info, msg, arg)
-
-template isSpecial(n: PNode): bool = n.kind == nkExceptBranch
-template bailout() {.dirty.} =
-  if isSpecial(result): return
-
-template evalX(n, flags) {.dirty.} =
-  result = evalAux(c, n, flags)
-  bailout()
-
-proc myreset(n: PNode) =
-  when defined(system.reset): 
-    var oldInfo = n.info
-    reset(n[])
-    n.info = oldInfo
-
-proc evalIf(c: PEvalContext, n: PNode): PNode = 
-  var i = 0
-  var length = sonsLen(n)
-  while (i < length) and (sonsLen(n.sons[i]) >= 2): 
-    evalX(n.sons[i].sons[0], {})
-    if result.kind == nkIntLit and result.intVal != 0:
-      return evalAux(c, n.sons[i].sons[1], {})
-    inc(i)
-  if (i < length) and (sonsLen(n.sons[i]) < 2):
-    result = evalAux(c, n.sons[i].sons[0], {})
-  else:
-    result = emptyNode
-  
-proc evalCase(c: PEvalContext, n: PNode): PNode = 
-  evalX(n.sons[0], {})
-  var res = result
-  result = emptyNode
-  for i in countup(1, sonsLen(n) - 1): 
-    if n.sons[i].kind == nkOfBranch: 
-      for j in countup(0, sonsLen(n.sons[i]) - 2): 
-        if overlap(res, n.sons[i].sons[j]): 
-          return evalAux(c, lastSon(n.sons[i]), {})
-    else: 
-      result = evalAux(c, lastSon(n.sons[i]), {})
-
-var 
-  gWhileCounter: int # Use a counter to prevent endless loops!
-                     # We make this counter global, because otherwise
-                     # nested loops could make the compiler extremely slow.
-  gNestedEvals: int  # count the recursive calls to ``evalAux`` to prevent
-                     # endless recursion
-
-proc evalWhile(c: PEvalContext, n: PNode): PNode =
-  while true:
-    evalX(n.sons[0], {})
-    if getOrdValue(result) == 0:
-      result = emptyNode; break
-    result = evalAux(c, n.sons[1], {})
-    case result.kind
-    of nkBreakStmt: 
-      if result.sons[0].kind == nkEmpty: 
-        result = emptyNode    # consume ``break`` token
-      # Bugfix (see tmacro2): but break in any case!
-      break 
-    of nkExceptBranch, nkReturnToken: break 
-    else: nil
-    dec(gWhileCounter)
-    if gWhileCounter <= 0:
-      if allowInfiniteLoops in c.features:
-        gWhileCounter = 0
-      else:
-        stackTrace(c, n.info, errTooManyIterations)
-        break
-
-proc evalBlock(c: PEvalContext, n: PNode): PNode =
-  result = evalAux(c, n.sons[1], {})
-  if result.kind == nkBreakStmt:
-    if result.sons[0] != nil: 
-      assert(result.sons[0].kind == nkSym)
-      if n.sons[0].kind != nkEmpty: 
-        assert(n.sons[0].kind == nkSym)
-        if result.sons[0].sym.id == n.sons[0].sym.id: result = emptyNode
-    # blocks can only be left with an explicit label now!
-    #else: 
-    #  result = emptyNode      # consume ``break`` token
-  
-proc evalFinally(c: PEvalContext, n, exc: PNode): PNode = 
-  var finallyNode = lastSon(n)
-  if finallyNode.kind == nkFinally: 
-    result = evalAux(c, finallyNode, {})
-    if result.kind != nkExceptBranch: result = exc
-  else: 
-    result = exc
-  
-proc evalTry(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[0], {})
-  case result.kind
-  of nkBreakStmt, nkReturnToken: 
-    nil
-  of nkExceptBranch: 
-    if sonsLen(result) >= 1: 
-      # creating a nkExceptBranch without sons means that it could not be
-      # evaluated
-      var exc = result
-      var i = 1
-      var length = sonsLen(n)
-      while (i < length) and (n.sons[i].kind == nkExceptBranch): 
-        var blen = sonsLen(n.sons[i])
-        if blen == 1: 
-          # general except section:
-          result = evalAux(c, n.sons[i].sons[0], {})
-          exc = result
-          break 
-        else: 
-          for j in countup(0, blen - 2): 
-            assert(n.sons[i].sons[j].kind == nkType)
-            let a = exc.typ.skipTypes(abstractPtrs)
-            let b = n.sons[i].sons[j].typ.skipTypes(abstractPtrs)
-            if a == b: 
-              result = evalAux(c, n.sons[i].sons[blen - 1], {})
-              exc = result
-              break 
-        inc(i)
-      result = evalFinally(c, n, exc)
-  else: result = evalFinally(c, n, emptyNode)
-  
-proc getNullValue(typ: PType, info: TLineInfo): PNode
-proc getNullValueAux(obj: PNode, result: PNode) = 
-  case obj.kind
-  of nkRecList:
-    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result)
-  of nkRecCase:
-    getNullValueAux(obj.sons[0], result)
-    for i in countup(1, sonsLen(obj) - 1): 
-      getNullValueAux(lastSon(obj.sons[i]), result)
-  of nkSym:
-    var s = obj.sym
-    var p = newNodeIT(nkExprColonExpr, result.info, s.typ)
-    addSon(p, newSymNode(s, result.info))
-    addSon(p, getNullValue(s.typ, result.info))
-    addSon(result, p)
-  else: InternalError(result.info, "getNullValueAux")
-  
-proc getNullValue(typ: PType, info: TLineInfo): PNode = 
-  var t = skipTypes(typ, abstractRange-{tyTypeDesc})
-  result = emptyNode
-  case t.kind
-  of tyBool, tyEnum, tyChar, tyInt..tyInt64: 
-    result = newNodeIT(nkIntLit, info, t)
-  of tyUInt..tyUInt64:
-    result = newNodeIT(nkUIntLit, info, t)
-  of tyFloat..tyFloat128: 
-    result = newNodeIt(nkFloatLit, info, t)
-  of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
-     tyStmt, tyTypeDesc, tyStatic, tyProc:
-    result = newNodeIT(nkNilLit, info, t)
-  of tyObject: 
-    result = newNodeIT(nkPar, info, t)
-    getNullValueAux(t.n, result)
-    # initialize inherited fields:
-    var base = t.sons[0]
-    while base != nil:
-      getNullValueAux(skipTypes(base, skipPtrs).n, result)
-      base = base.sons[0]
-  of tyArray, tyArrayConstr: 
-    result = newNodeIT(nkBracket, info, t)
-    for i in countup(0, int(lengthOrd(t)) - 1): 
-      addSon(result, getNullValue(elemType(t), info))
-  of tyTuple:
-    # XXX nkExprColonExpr is out of fashion ...
-    result = newNodeIT(nkPar, info, t)
-    for i in countup(0, sonsLen(t) - 1):
-      var p = newNodeIT(nkExprColonExpr, info, t.sons[i])
-      var field = if t.n != nil: t.n.sons[i].sym else: newSym(
-        skField, getIdent(":tmp" & $i), t.owner, info)
-      addSon(p, newSymNode(field, info))
-      addSon(p, getNullValue(t.sons[i], info))
-      addSon(result, p)
-  of tySet:
-    result = newNodeIT(nkCurly, info, t)    
-  else: InternalError("getNullValue: " & $t.kind)
-  
-proc evalVarValue(c: PEvalContext, n: PNode): PNode =
-  result = evalAux(c, n, {})
-  if result.kind in {nkType..nkNilLit}: result = result.copyNode
-
-proc allocSlot(c: PStackFrame; sym: PSym): int =
-  result = sym.position + ord(sym.kind == skParam)
-  if result == 0 and sym.kind != skResult:
-    result = c.slots.len
-    if result == 0: result = 1
-    sym.position = result
-  setLen(c.slots, max(result+1, c.slots.len))
-
-proc setSlot(c: PStackFrame, sym: PSym, val: PNode) =
-  assert sym.owner == c.prc or sfFromGeneric in sym.flags
-  let idx = allocSlot(c, sym)
-  c.slots[idx] = val
-
-proc setVar(c: PEvalContext, v: PSym, n: PNode) =
-  if sfGlobal notin v.flags: setSlot(c.tos, v, n)
-  else: IdNodeTablePut(c.globals, v, n)
-
-proc evalVar(c: PEvalContext, n: PNode): PNode =
-  for i in countup(0, sonsLen(n) - 1):
-    let a = n.sons[i]
-    if a.kind == nkCommentStmt: continue
-    #assert(a.sons[0].kind == nkSym) can happen for transformed vars
-    if a.kind == nkVarTuple:
-      result = evalVarValue(c, a.lastSon)
-      if result.kind in {nkType..nkNilLit}:
-        result = result.copyNode
-      bailout()
-      if result.kind != nkPar:
-        return raiseCannotEval(c, n.info)
-      for i in 0 .. a.len-3:
-        var v = a.sons[i].sym
-        setVar(c, v, result.sons[i])
-    else:
-      if a.sons[2].kind != nkEmpty:
-        result = evalVarValue(c, a.sons[2])
-        bailout()
-      else:
-        result = getNullValue(a.sons[0].typ, a.sons[0].info)
-      if a.sons[0].kind == nkSym:
-        var v = a.sons[0].sym
-        setVar(c, v, result)
-      else:
-        # assign to a.sons[0]:
-        var x = result
-        evalX(a.sons[0], {})
-        myreset(x)
-        x.kind = result.kind
-        x.typ = result.typ
-        case x.kind
-        of nkCharLit..nkInt64Lit: x.intVal = result.intVal
-        of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal
-        of nkStrLit..nkTripleStrLit: x.strVal = result.strVal
-        of nkIdent: x.ident = result.ident
-        of nkSym: x.sym = result.sym
-        else:
-          if x.kind notin {nkEmpty..nkNilLit}:
-            discardSons(x)
-            for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j])
-  result = emptyNode
-
-proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = 
-  result = efLValue in flags or n.typ == nil or 
-    n.typ.kind in {tyExpr, tyStatic, tyStmt, tyTypeDesc}
-
-proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
-  # We need to return a node to the actual value,
-  # which can be modified.
-  assert sym.position != 0 or skResult == sym.kind
-  var x = c
-  while x != nil:
-    if sym.owner == x.prc:
-      result = x.slots[sym.position]
-      assert result != nil
-      if not aliasNeeded(result, flags):
-        result = copyTree(result)
-      return
-    x = x.next
-  #internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position)
-  result = raiseCannotEval(nil, sym.info)
-  #result = emptyNode
-
-proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode =
-  if sfCompileTime in s.flags or c.mode == emRepl or s.kind == skForVar:
-    result = IdNodeTableGet(c.globals, s)
-    if result != nil: 
-      if not aliasNeeded(result, flags): 
-        result = copyTree(result)
-    else:
-      when hasFFI:
-        if sfImportc in s.flags and allowFFI in c.features:
-          result = importcSymbol(s)
-          IdNodeTablePut(c.globals, s, result)
-          return result
-      
-      result = s.ast
-      if result == nil or result.kind == nkEmpty:
-        result = getNullValue(s.typ, s.info)
-      else:
-        result = evalAux(c, result, {})
-        if isSpecial(result): return
-      IdNodeTablePut(c.globals, s, result)
-  else:
-    result = raiseCannotEval(nil, s.info)
-
-proc optBody(c: PEvalContext, s: PSym): PNode =
-  result = s.getBody
-
-proc evalCall(c: PEvalContext, n: PNode): PNode = 
-  var d = newStackFrame()
-  d.call = n
-  var prc = n.sons[0]
-  let isClosure = prc.kind == nkClosure
-  setlen(d.slots, sonsLen(n) + ord(isClosure))
-  if isClosure:
-    #debug prc
-    evalX(prc.sons[1], {efLValue})
-    d.slots[sonsLen(n)] = result
-    result = evalAux(c, prc.sons[0], {})
-  else:
-    result = evalAux(c, prc, {})
-
-  if isSpecial(result): return 
-  prc = result
-  # bind the actual params to the local parameter of a new binding
-  if prc.kind != nkSym: 
-    InternalError(n.info, "evalCall " & n.renderTree)
-    return
-  d.prc = prc.sym
-  if prc.sym.kind notin {skProc, skConverter, skMacro}:
-    InternalError(n.info, "evalCall")
-    return
-  for i in countup(1, sonsLen(n) - 1): 
-    evalX(n.sons[i], {})
-    d.slots[i] = result
-  if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info)
-  
-  when hasFFI:
-    if sfImportc in prc.sym.flags and allowFFI in c.features:
-      var newCall = newNodeI(nkCall, n.info, n.len)
-      newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
-      for i in 1 .. <n.len:
-        newCall.sons[i] = d.slots[i]
-      return callForeignFunction(newCall)
-  
-  pushStackFrame(c, d)
-  evalX(optBody(c, prc.sym), {})
-  if n.typ != nil: result = d.slots[0]
-  popStackFrame(c)
-
-proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = 
-  evalX(n.sons[0], flags)
-  var x = result
-  evalX(n.sons[1], {})
-  var idx = getOrdValue(result)
-  result = emptyNode
-  case x.kind
-  of nkPar:
-    if (idx >= 0) and (idx < sonsLen(x)): 
-      result = x.sons[int(idx)]
-      if result.kind == nkExprColonExpr: result = result.sons[1]
-      if not aliasNeeded(result, flags): result = copyTree(result)
-    else: 
-      stackTrace(c, n.info, errIndexOutOfBounds)
-  of nkBracket, nkMetaNode: 
-    if (idx >= 0) and (idx < sonsLen(x)): 
-      result = x.sons[int(idx)]
-      if not aliasNeeded(result, flags): result = copyTree(result)
-    else: 
-      stackTrace(c, n.info, errIndexOutOfBounds)
-  of nkStrLit..nkTripleStrLit:
-    if efLValue in flags: return raiseCannotEval(c, n.info)
-    result = newNodeIT(nkCharLit, x.info, getSysType(tyChar))
-    if (idx >= 0) and (idx < len(x.strVal)): 
-      result.intVal = ord(x.strVal[int(idx) + 0])
-    elif idx == len(x.strVal): 
-      nil
-    else: 
-      stackTrace(c, n.info, errIndexOutOfBounds)
-  else: stackTrace(c, n.info, errNilAccess)
-  
-proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
-  # a real field access; proc calls have already been transformed
-  # XXX: field checks!
-  evalX(n.sons[0], flags)
-  var x = result
-  if x.kind != nkPar: return raiseCannotEval(c, n.info)
-  # this is performance critical:
-  var field = n.sons[1].sym
-  result = x.sons[field.position]
-  if result.kind == nkExprColonExpr: result = result.sons[1]
-  if not aliasNeeded(result, flags): result = copyTree(result)
-
-proc evalAsgn(c: PEvalContext, n: PNode): PNode =
-  var a = n.sons[0]
-  if a.kind == nkBracketExpr and a.sons[0].typ.kind in {tyString, tyCString}: 
-    evalX(a.sons[0], {efLValue})
-    var x = result
-    evalX(a.sons[1], {})
-    var idx = getOrdValue(result)
-
-    evalX(n.sons[1], {})
-    if result.kind notin {nkIntLit, nkCharLit}: return c.raiseCannotEval(n.info)
-
-    if idx >= 0 and idx < len(x.strVal):
-      x.strVal[int(idx)] = chr(int(result.intVal))
-    else:
-      stackTrace(c, n.info, errIndexOutOfBounds)
-  else:
-    evalX(n.sons[0], {efLValue})
-    var x = result
-    evalX(n.sons[1], {})
-    myreset(x)
-    x.kind = result.kind
-    x.typ = result.typ
-    case x.kind
-    of nkCharLit..nkInt64Lit: x.intVal = result.intVal
-    of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal
-    of nkStrLit..nkTripleStrLit: x.strVal = result.strVal
-    of nkIdent: x.ident = result.ident
-    of nkSym: x.sym = result.sym
-    else:
-      if x.kind notin {nkEmpty..nkNilLit}:
-        discardSons(x)
-        for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i])
-  result = emptyNode
-  assert result.kind == nkEmpty
-
-proc evalSwap(c: PEvalContext, n: PNode): PNode = 
-  evalX(n.sons[0], {efLValue})
-  var x = result
-  evalX(n.sons[1], {efLValue})
-  if x.kind != result.kind: 
-    stackTrace(c, n.info, errCannotInterpretNodeX, $n.kind)
-  else:
-    case x.kind
-    of nkCharLit..nkInt64Lit: swap(x.intVal, result.intVal)
-    of nkFloatLit..nkFloat64Lit: swap(x.floatVal, result.floatVal)
-    of nkStrLit..nkTripleStrLit: swap(x.strVal, result.strVal)
-    of nkIdent: swap(x.ident, result.ident)
-    of nkSym: swap(x.sym, result.sym)    
-    else: 
-      var tmpn = copyTree(x)
-      discardSons(x)
-      for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i])
-      discardSons(result)
-      for i in countup(0, sonsLen(tmpn) - 1): addSon(result, tmpn.sons[i])
-  result = emptyNode
-  
-proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = 
-  var s = n.sym
-  case s.kind
-  of skProc, skConverter, skMacro, skType:
-    result = n
-    #result = s.getBody
-  of skVar, skLet, skForVar, skTemp, skResult:
-    if sfGlobal notin s.flags:
-      result = evalVariable(c.tos, s, flags)
-    else:
-      result = evalGlobalVar(c, s, flags)
-  of skParam:
-    # XXX what about LValue?
-    if s.position + 1 <% c.tos.slots.len:
-      result = c.tos.slots[s.position + 1]
-  of skConst: result = s.ast
-  of skEnumField: result = newIntNodeT(s.position, n)
-  else: result = nil
-  let mask = if hasFFI and allowFFI in c.features: {sfForward}
-             else: {sfImportc, sfForward}
-  if result == nil or mask * s.flags != {}:
-    result = raiseCannotEval(c, n.info)
-
-proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = 
-  evalX(n.sons[1], {efLValue})
-  var a = result
-  evalX(n.sons[2], {})
-  var b = result
-  case a.kind
-  of nkCharLit..nkInt64Lit: a.intval = a.intVal + sign * getOrdValue(b)
-  else: return raiseCannotEval(c, n.info)
-  result = emptyNode
-
-proc getStrValue(n: PNode): string = 
-  case n.kind
-  of nkStrLit..nkTripleStrLit: result = n.strVal
-  else: 
-    InternalError(n.info, "getStrValue")
-    result = ""
-
-proc evalEcho(c: PEvalContext, n: PNode): PNode = 
-  for i in countup(1, sonsLen(n) - 1): 
-    evalX(n.sons[i], {})
-    Write(stdout, getStrValue(result))
-  writeln(stdout, "")
-  result = emptyNode
-
-proc evalExit(c: PEvalContext, n: PNode): PNode = 
-  if c.mode in {emRepl, emStatic}:
-    evalX(n.sons[1], {})
-    Message(n.info, hintQuitCalled)
-    quit(int(getOrdValue(result)))
-  else:
-    result = raiseCannotEval(c, n.info)
-
-proc evalOr(c: PEvalContext, n: PNode): PNode = 
-  evalX(n.sons[1], {})
-  if result.intVal == 0: result = evalAux(c, n.sons[2], {})
-  
-proc evalAnd(c: PEvalContext, n: PNode): PNode = 
-  evalX(n.sons[1], {})
-  if result.intVal != 0: result = evalAux(c, n.sons[2], {})
-  
-proc evalNew(c: PEvalContext, n: PNode): PNode = 
-  #if c.mode == emOptimize: return raiseCannotEval(c, n.info)
-  
-  # we ignore the finalizer for now and most likely forever :-)
-  evalX(n.sons[1], {efLValue})
-  var a = result
-  var t = skipTypes(n.sons[1].typ, abstractVar)
-  if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty")
-  myreset(a)
-  let u = getNullValue(t.sons[0], n.info)
-  a.kind = u.kind
-  a.typ = t
-  shallowCopy(a.sons, u.sons)
-  result = emptyNode
-  when false:
-    a.kind = nkRefTy
-    a.info = n.info
-    a.typ = t
-    a.sons = nil
-    addSon(a, getNullValue(t.sons[0], n.info))
-    result = emptyNode
-
-proc evalDeref(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = 
-  evalX(n.sons[0], {efLValue})
-  case result.kind
-  of nkNilLit: stackTrace(c, n.info, errNilAccess)
-  of nkRefTy: 
-    # XXX efLValue?
-    result = result.sons[0]
-  else:
-    if skipTypes(n.sons[0].typ, abstractInst).kind != tyRef:
-      result = raiseCannotEval(c, n.info)
-  
-proc evalAddr(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = 
-  evalX(n.sons[0], {efLValue})
-  var a = result
-  var t = newType(tyPtr, c.module)
-  addSonSkipIntLit(t, a.typ)
-  result = newNodeIT(nkRefTy, n.info, t)
-  addSon(result, a)
-
-proc evalConv(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return
-  if result.typ != nil:
-    var a = result
-    result = foldConv(n, a)
-    if result == nil: 
-      # foldConv() cannot deal with everything that we want to do here:
-      result = a
-
-proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
-  if allowCast in c.features:
-    when hasFFI:
-      result = evalAux(c, n.sons[1], {efLValue})
-      if isSpecial(result): return
-      InternalAssert result.typ != nil
-      result = fficast(result, n.typ)
-    else:
-      result = evalConv(c, n)
-  else:
-    result = raiseCannotEval(c, n.info)
-
-proc evalCheckedFieldAccess(c: PEvalContext, n: PNode, 
-                            flags: TEvalFlags): PNode = 
-  result = evalAux(c, n.sons[0], flags)
-
-proc evalUpConv(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = 
-  result = evalAux(c, n.sons[0], flags)
-  if isSpecial(result): return 
-  var dest = skipTypes(n.typ, abstractPtrs)
-  var src = skipTypes(result.typ, abstractPtrs)
-  if inheritanceDiff(src, dest) > 0: 
-    stackTrace(c, n.info, errInvalidConversionFromTypeX, typeToString(src))
-  
-proc evalRangeChck(c: PEvalContext, n: PNode): PNode = 
-  evalX(n.sons[0], {})
-  var x = result
-  evalX(n.sons[1], {})
-  var a = result
-  evalX(n.sons[2], {})
-  var b = result
-  if leValueConv(a, x) and leValueConv(x, b): 
-    result = x                # a <= x and x <= b
-    result.typ = n.typ
-  else: 
-    stackTrace(c, n.info, errGenerated, 
-      msgKindToString(errIllegalConvFromXtoY) % [
-      typeToString(n.sons[0].typ), typeToString(n.typ)])
-  
-proc evalConvStrToCStr(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[0], {})
-  if isSpecial(result): return 
-  result.typ = n.typ
-
-proc evalConvCStrToStr(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[0], {})
-  if isSpecial(result): return 
-  result.typ = n.typ
-
-proc evalRaise(c: PEvalContext, n: PNode): PNode = 
-  if c.mode in {emRepl, emStatic}:
-    if n.sons[0].kind != nkEmpty: 
-      result = evalAux(c, n.sons[0], {})
-      if isSpecial(result): return 
-      var a = result
-      result = newNodeIT(nkExceptBranch, n.info, a.typ)
-      addSon(result, a)
-      c.lastException = result
-    elif c.lastException != nil: 
-      result = c.lastException
-    else: 
-      stackTrace(c, n.info, errExceptionAlreadyHandled)
-      result = newNodeIT(nkExceptBranch, n.info, nil)
-      addSon(result, ast.emptyNode)
-  else:
-    result = raiseCannotEval(c, n.info)
-
-proc evalReturn(c: PEvalContext, n: PNode): PNode = 
-  if n.sons[0].kind != nkEmpty: 
-    result = evalAsgn(c, n.sons[0])
-    if isSpecial(result): return 
-  result = newNodeIT(nkReturnToken, n.info, nil)
-
-proc evalProc(c: PEvalContext, n: PNode): PNode = 
-  if n.sons[genericParamsPos].kind == nkEmpty: 
-    var s = n.sons[namePos].sym
-    if (resultPos < sonsLen(n)) and (n.sons[resultPos].kind != nkEmpty): 
-      var v = n.sons[resultPos].sym
-      result = getNullValue(v.typ, n.info)
-      if c.tos.slots.len == 0: setLen(c.tos.slots, 1)
-      c.tos.slots[0] = result
-      #IdNodeTablePut(c.tos.mapping, v, result)
-      result = evalAux(c, s.getBody, {})
-      if result.kind == nkReturnToken:
-        result = c.tos.slots[0]
-    else:
-      result = evalAux(c, s.getBody, {})
-      if result.kind == nkReturnToken: 
-        result = emptyNode
-  else: 
-    result = emptyNode
-  
-proc evalHigh(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {})
-  if isSpecial(result): return 
-  case skipTypes(n.sons[1].typ, abstractVar).kind
-  of tyOpenArray, tySequence, tyVarargs: 
-    result = newIntNodeT(sonsLen(result)-1, n)
-  of tyString: result = newIntNodeT(len(result.strVal) - 1, n)
-  else: InternalError(n.info, "evalHigh")
-
-proc evalOf(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {})
-  if isSpecial(result): return 
-  result = newIntNodeT(ord(inheritanceDiff(result.typ, n.sons[2].typ) >= 0), n)
-
-proc evalSetLengthStr(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  case a.kind
-  of nkStrLit..nkTripleStrLit: 
-    var newLen = int(getOrdValue(b))
-    setlen(a.strVal, newLen)
-  else: InternalError(n.info, "evalSetLengthStr")
-  result = emptyNode
-
-proc evalSetLengthSeq(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  if a.kind != nkBracket: 
-    InternalError(n.info, "evalSetLengthSeq")
-    return
-  var newLen = int(getOrdValue(b))
-  var oldLen = sonsLen(a)
-  setlen(a.sons, newLen)
-  for i in countup(oldLen, newLen - 1): 
-    a.sons[i] = getNullValue(skipTypes(n.sons[1].typ, abstractVar), n.info)
-  result = emptyNode
-
-proc evalNewSeq(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  var t = skipTypes(n.sons[1].typ, abstractVar)
-  if a.kind == nkEmpty: InternalError(n.info, "first parameter is empty")
-  myreset(a)
-  a.kind = nkBracket
-  a.info = n.info
-  a.typ = t
-  a.sons = nil
-  var L = int(getOrdValue(b))
-  newSeq(a.sons, L)
-  for i in countup(0, L-1): 
-    a.sons[i] = getNullValue(t.sons[0], n.info)
-  result = emptyNode
- 
-proc evalIncl(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  if not inSet(a, b): addSon(a, copyTree(b))
-  result = emptyNode
-
-proc evalExcl(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = newNodeIT(nkCurly, n.info, n.sons[1].typ)
-  addSon(b, result)
-  var r = diffSets(a, b)
-  discardSons(a)
-  for i in countup(0, sonsLen(r) - 1): addSon(a, r.sons[i])
-  result = emptyNode
-
-proc evalAppendStrCh(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  case a.kind
-  of nkStrLit..nkTripleStrLit: add(a.strVal, chr(int(getOrdValue(b))))
-  else: return raiseCannotEval(c, n.info)
-  result = emptyNode
-
-proc evalConStrStr(c: PEvalContext, n: PNode): PNode = 
-  # we cannot use ``evalOp`` for this as we can here have more than 2 arguments
-  var a = newNodeIT(nkStrLit, n.info, n.typ)
-  a.strVal = ""
-  for i in countup(1, sonsLen(n) - 1): 
-    result = evalAux(c, n.sons[i], {})
-    if isSpecial(result): return 
-    a.strVal.add(getStrOrChar(result))
-  result = a
-
-proc evalAppendStrStr(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  case a.kind
-  of nkStrLit..nkTripleStrLit: a.strVal = a.strVal & getStrOrChar(b)
-  else: return raiseCannotEval(c, n.info)
-  result = emptyNode
-
-proc evalAppendSeqElem(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {efLValue})
-  if isSpecial(result): return 
-  var a = result
-  result = evalAux(c, n.sons[2], {})
-  if isSpecial(result): return 
-  var b = result
-  if a.kind == nkBracket: addSon(a, copyTree(b))
-  else: return raiseCannotEval(c, n.info)
-  result = emptyNode
-
-proc evalRepr(c: PEvalContext, n: PNode): PNode = 
-  result = evalAux(c, n.sons[1], {})
-  if isSpecial(result): return 
-  result = newStrNodeT(renderTree(result, {renderNoComments}), n)
-
-proc isEmpty(n: PNode): bool =
-  result = n != nil and n.kind == nkEmpty
-
-proc evalParseExpr(c: PEvalContext, n: PNode): PNode =
-  var code = evalAux(c, n.sons[1], {})
-  var ast = parseString(code.getStrValue, code.info.toFilename,
-                        code.info.line.int)
-  if sonsLen(ast) != 1:
-    GlobalError(code.info, errExprExpected, "multiple statements")
-  result = ast.sons[0]
-  #result.typ = newType(tyExpr, c.module)
-
-proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
-  var code = evalAux(c, n.sons[1], {})
-  result = parseString(code.getStrValue, code.info.toFilename,
-                       code.info.line.int)
-  #result.typ = newType(tyStmt, c.module)
-
-proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
-  let typ = operand.typ.skipTypes({tyTypeDesc})
-  case trait.sym.name.s.normalize
-  of "name":
-    result = newStrNode(nkStrLit, typ.typeToString(preferName))
-    result.typ = newType(tyString, context)
-    result.info = trait.info
-  of "arity":
-    result = newIntNode(nkIntLit, typ.n.len-1)
-    result.typ = newType(tyInt, context)
-    result.info = trait.info
-  else:
-    internalAssert false
-
-proc expectString(n: PNode) =
-  if n.kind notin nkStrKinds:
-    GlobalError(n.info, errStringLiteralExpected)
-
-proc evalSlurp*(e: PNode, module: PSym): PNode =
-  expectString(e)
-  result = newNodeIT(nkStrLit, e.info, getSysType(tyString))
-  try:
-    var filename = e.strVal.FindFile
-    result.strVal = readFile(filename)
-    # we produce a fake include statement for every slurped filename, so that
-    # the module dependencies are accurate:    
-    appendToModule(module, newNode(nkIncludeStmt, e.info, @[
-      newStrNode(nkStrLit, filename)]))
-  except EIO:
-    result.strVal = ""
-    LocalError(e.info, errCannotOpenFile, e.strVal)
-
-proc readOutput(p: PProcess): string =
-  result = ""
-  var output = p.outputStream
-  discard p.waitForExit
-  while not output.atEnd:
-    result.add(output.readLine)
-
-proc evalStaticExec*(cmd, input: PNode): PNode =
-  expectString(cmd)
-  var p = startCmd(cmd.strVal)
-  if input != nil:
-    expectString(input)
-    p.inputStream.write(input.strVal)
-    p.inputStream.close()
-  result = newStrNode(nkStrLit, p.readOutput)
-  result.typ = getSysType(tyString)
-  result.info = cmd.info
-
-proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
-  var
-    n = original.copyTree
-    macroCall = n.sons[1]
-    expandedSym = macroCall.sons[0].sym
-
-  for i in countup(1, macroCall.sonsLen - 1):
-    macroCall.sons[i] = evalAux(c, macroCall.sons[i], {})
-
-  case expandedSym.kind
-  of skTemplate:
-    let genSymOwner = if c.tos != nil and c.tos.prc != nil:
-                        c.tos.prc 
-                      else:
-                        c.module
-    result = evalTemplate(macroCall, expandedSym, genSymOwner)
-  of skMacro:
-    # At this point macroCall.sons[0] is nkSym node.
-    # To be completely compatible with normal macro invocation,
-    # we want to replace it with nkIdent node featuring
-    # the original unmangled macro name.
-    macroCall.sons[0] = newIdentNode(expandedSym.name, expandedSym.info)
-    result = evalMacroCall(c, macroCall, original, expandedSym)
-  else:
-    InternalError(macroCall.info,
-      "ExpandToAst: expanded symbol is no macro or template")
-    result = emptyNode
-
-proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = 
-  var m = getMagic(n)
-  case m
-  of mNone: result = evalCall(c, n)
-  of mOf: result = evalOf(c, n)
-  of mSizeOf: result = raiseCannotEval(c, n.info)
-  of mHigh: result = evalHigh(c, n)
-  of mExit: result = evalExit(c, n)
-  of mNew, mNewFinalize: result = evalNew(c, n)
-  of mNewSeq: result = evalNewSeq(c, n)
-  of mSwap: result = evalSwap(c, n)
-  of mInc: result = evalIncDec(c, n, 1)
-  of ast.mDec: result = evalIncDec(c, n, - 1)
-  of mEcho: result = evalEcho(c, n)
-  of mSetLengthStr: result = evalSetLengthStr(c, n)
-  of mSetLengthSeq: result = evalSetLengthSeq(c, n)
-  of mIncl: result = evalIncl(c, n)
-  of mExcl: result = evalExcl(c, n)
-  of mAnd: result = evalAnd(c, n)
-  of mOr: result = evalOr(c, n)
-  of mAppendStrCh: result = evalAppendStrCh(c, n)
-  of mAppendStrStr: result = evalAppendStrStr(c, n)
-  of mAppendSeqElem: result = evalAppendSeqElem(c, n)
-  of mParseExprToAst: result = evalParseExpr(c, n)
-  of mParseStmtToAst: result = evalParseStmt(c, n)
-  of mExpandToAst: result = evalExpandToAst(c, n)
-  of mTypeTrait:
-    let operand = evalAux(c, n.sons[1], {})
-    result = evalTypeTrait(n[0], operand, c.module)
-  of mIs:
-    n.sons[1] = evalAux(c, n.sons[1], {})
-    result = c.handleIsOperator(n)
-  of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
-  of mStaticExec:
-    let cmd = evalAux(c, n.sons[1], {})
-    let input = if n.sonsLen == 3: evalAux(c, n.sons[2], {}) else: nil
-    result = evalStaticExec(cmd, input)
-  of mNLen:
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkIntLit, n.info, n.typ)
-    case a.kind
-    of nkEmpty..nkNilLit: nil
-    else: result.intVal = sonsLen(a)
-  of mNChild:
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    var k = getOrdValue(result)
-    if not (a.kind in {nkEmpty..nkNilLit}) and (k >= 0) and (k < sonsLen(a)): 
-      result = a.sons[int(k)]
-      if result == nil: result = newNode(nkEmpty)
-    else: 
-      stackTrace(c, n.info, errIndexOutOfBounds)
-      result = emptyNode
-  of mNSetChild: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    var b = result
-    result = evalAux(c, n.sons[3], {efLValue})
-    if isSpecial(result): return 
-    var k = getOrdValue(b)
-    if (k >= 0) and (k < sonsLen(a)) and not (a.kind in {nkEmpty..nkNilLit}): 
-      a.sons[int(k)] = result
-    else: 
-      stackTrace(c, n.info, errIndexOutOfBounds)
-    result = emptyNode
-  of mNAdd: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    addSon(a, result)
-    result = a
-  of mNAddMultiple: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    for i in countup(0, sonsLen(result) - 1): addSon(a, result.sons[i])
-    result = a
-  of mNDel: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    var b = result
-    result = evalAux(c, n.sons[3], {efLValue})
-    if isSpecial(result): return 
-    for i in countup(0, int(getOrdValue(result)) - 1): 
-      delSon(a, int(getOrdValue(b)))
-    result = emptyNode
-  of mNKind: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkIntLit, n.info, n.typ)
-    result.intVal = ord(a.kind)
-  of mNIntVal: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkIntLit, n.info, n.typ)
-    case a.kind
-    of nkCharLit..nkInt64Lit: result.intVal = a.intVal
-    else: stackTrace(c, n.info, errFieldXNotFound, "intVal")
-  of mNFloatVal: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkFloatLit, n.info, n.typ)
-    case a.kind
-    of nkFloatLit..nkFloat64Lit: result.floatVal = a.floatVal
-    else: stackTrace(c, n.info, errFieldXNotFound, "floatVal")
-  of mNSymbol: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    if result.kind != nkSym: stackTrace(c, n.info, errFieldXNotFound, "symbol")
-  of mNIdent: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    if result.kind != nkIdent: stackTrace(c, n.info, errFieldXNotFound, "ident")
-  of mNGetType:
-    var ast = evalAux(c, n.sons[1], {})
-    InternalAssert c.getType != nil
-    result = c.getType(ast)
-  of mNStrVal: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkStrLit, n.info, n.typ)
-    case a.kind
-    of nkStrLit..nkTripleStrLit: result.strVal = a.strVal
-    else: stackTrace(c, n.info, errFieldXNotFound, "strVal")
-  of mNSetIntVal: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {})
-    if isSpecial(result): return
-    if a.kind in {nkCharLit..nkInt64Lit} and 
-        result.kind in {nkCharLit..nkInt64Lit}:
-      a.intVal = result.intVal
-    else: 
-      stackTrace(c, n.info, errFieldXNotFound, "intVal")
-    result = emptyNode
-  of mNSetFloatVal: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {})
-    if isSpecial(result): return 
-    if a.kind in {nkFloatLit..nkFloat64Lit} and
-        result.kind in {nkFloatLit..nkFloat64Lit}:
-      a.floatVal = result.floatVal
-    else:
-      stackTrace(c, n.info, errFieldXNotFound, "floatVal")
-    result = emptyNode
-  of mNSetSymbol: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    if a.kind == nkSym and result.kind == nkSym:
-      a.sym = result.sym
-    else:
-      stackTrace(c, n.info, errFieldXNotFound, "symbol")
-    result = emptyNode
-  of mNSetIdent: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    if a.kind == nkIdent and result.kind == nkIdent:
-      a.ident = result.ident
-    else:
-      stackTrace(c, n.info, errFieldXNotFound, "ident")
-    result = emptyNode
-  of mNSetType: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    InternalAssert result.kind == nkSym and result.sym.kind == skType
-    a.typ = result.sym.typ
-    result = emptyNode
-  of mNSetStrVal:
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {})
-    if isSpecial(result): return
-    
-    if a.kind in {nkStrLit..nkTripleStrLit} and
-        result.kind in {nkStrLit..nkTripleStrLit}:
-      a.strVal = result.strVal
-    else: stackTrace(c, n.info, errFieldXNotFound, "strVal")
-    result = emptyNode
-  of mNNewNimNode: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var k = getOrdValue(result)
-    result = evalAux(c, n.sons[2], {efLValue})
-    if result.kind == nkExceptBranch: return 
-    var a = result
-    if k < 0 or k > ord(high(TNodeKind)): 
-      internalError(n.info, "request to create a NimNode with invalid kind")
-    result = newNodeI(TNodeKind(int(k)), 
-      if a.kind == nkNilLit: n.info else: a.info)
-  of mNCopyNimNode:
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    result = copyNode(result)
-  of mNCopyNimTree: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    result = copyTree(result)
-  of mNBindSym:
-    # trivial implementation:
-    result = n.sons[1]
-  of mNGenSym:
-    evalX(n.sons[1], {efLValue})
-    let k = getOrdValue(result)
-    evalX(n.sons[2], {efLValue})
-    let b = result
-    let name = if b.strVal.len == 0: ":tmp" else: b.strVal
-    if k < 0 or k > ord(high(TSymKind)):
-      internalError(n.info, "request to create a symbol with invalid kind")
-    result = newSymNode(newSym(k.TSymKind, name.getIdent, c.module, n.info))
-    incl(result.sym.flags, sfGenSym)
-  of mStrToIdent: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    if not (result.kind in {nkStrLit..nkTripleStrLit}): 
-      stackTrace(c, n.info, errFieldXNotFound, "strVal")
-      return
-    var a = result
-    result = newNodeIT(nkIdent, n.info, n.typ)
-    result.ident = getIdent(a.strVal)
-  of mIdentToStr: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkStrLit, n.info, n.typ)
-    if a.kind == nkSym:
-      result.strVal = a.sym.name.s
-    else:
-      if a.kind != nkIdent: InternalError(n.info, "no ident node")
-      result.strVal = a.ident.s
-  of mEqIdent: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {})
-    if isSpecial(result): return 
-    var b = result
-    result = newNodeIT(nkIntLit, n.info, n.typ)
-    if (a.kind == nkIdent) and (b.kind == nkIdent): 
-      if a.ident.id == b.ident.id: result.intVal = 1
-  of mEqNimrodNode: 
-    result = evalAux(c, n.sons[1], {efLValue})
-    if isSpecial(result): return 
-    var a = result
-    result = evalAux(c, n.sons[2], {efLValue})
-    if isSpecial(result): return 
-    var b = result
-    result = newNodeIT(nkIntLit, n.info, n.typ)
-    if (a == b) or
-        (b.kind in {nkNilLit, nkEmpty}) and (a.kind in {nkNilLit, nkEmpty}): 
-      result.intVal = 1
-  of mNLineInfo:
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return
-    result = newStrNodeT(result.info.toFileLineCol, n)
-  of mNHint: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    Message(n.info, hintUser, getStrValue(result))
-    result = emptyNode
-  of mNWarning: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    Message(n.info, warnUser, getStrValue(result))
-    result = emptyNode
-  of mNError: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    stackTrace(c, n.info, errUser, getStrValue(result))
-    result = emptyNode
-  of mConStrStr: 
-    result = evalConStrStr(c, n)
-  of mRepr: 
-    result = evalRepr(c, n)
-  of mNewString: 
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkStrLit, n.info, n.typ)
-    result.strVal = newString(int(getOrdValue(a)))
-  of mNewStringOfCap:
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    result = newNodeIT(nkStrLit, n.info, n.typ)
-    result.strVal = newString(0)
-  of mNCallSite:
-    if c.callsite != nil: result = c.callsite
-    else: stackTrace(c, n.info, errFieldXNotFound, "callsite")
-  else:
-    result = evalAux(c, n.sons[1], {})
-    if isSpecial(result): return 
-    var a = result
-    var b: PNode = nil
-    var cc: PNode = nil
-    if sonsLen(n) > 2: 
-      result = evalAux(c, n.sons[2], {})
-      if isSpecial(result): return 
-      b = result
-      if sonsLen(n) > 3: 
-        result = evalAux(c, n.sons[3], {})
-        if isSpecial(result): return 
-        cc = result
-    if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode
-    else: result = evalOp(m, n, a, b, cc)
-
-proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =   
-  result = emptyNode
-  dec(gNestedEvals)
-  if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations)
-  case n.kind
-  of nkSym: result = evalSym(c, n, flags)
-  of nkType..nkNilLit, nkTypeOfExpr:
-    # nkStrLit is VERY common in the traces, so we should avoid
-    # the 'copyNode' here.
-    result = n #.copyNode
-  of nkAsgn, nkFastAsgn: result = evalAsgn(c, n)
-  of nkCommand..nkHiddenCallConv:
-    result = evalMagicOrCall(c, n)
-  of nkDotExpr: result = evalFieldAccess(c, n, flags)
-  of nkBracketExpr:
-    result = evalArrayAccess(c, n, flags)
-  of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags)
-  of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags)
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n)
-  of nkCurly, nkBracket, nkRange:
-    # flags need to be passed here for mNAddMultiple :-(
-    # XXX this is not correct in every case!
-    var a = copyNode(n)
-    for i in countup(0, sonsLen(n) - 1): 
-      result = evalAux(c, n.sons[i], flags)
-      if isSpecial(result): return 
-      addSon(a, result)
-    result = a
-  of nkPar, nkClosure: 
-    var a = copyTree(n)
-    for i in countup(0, sonsLen(n) - 1): 
-      var it = n.sons[i]
-      if it.kind == nkExprColonExpr:
-        result = evalAux(c, it.sons[1], flags)
-        if isSpecial(result): return 
-        a.sons[i].sons[1] = result
-      else:
-        result = evalAux(c, it, flags)
-        if isSpecial(result): return 
-        a.sons[i] = result
-    result = a
-  of nkObjConstr:
-    let t = skipTypes(n.typ, abstractInst)
-    var a: PNode
-    if t.kind == tyRef:
-      result = newNodeIT(nkRefTy, n.info, t)
-      a = getNullValue(t.sons[0], n.info)
-      addSon(result, a)
-    else:
-      a = getNullValue(t, n.info)
-      result = a
-    for i in countup(1, sonsLen(n) - 1):
-      let it = n.sons[i]
-      if it.kind == nkExprColonExpr:
-        let value = evalAux(c, it.sons[1], flags)
-        if isSpecial(value): return value
-        a.sons[it.sons[0].sym.position] = value
-      else: return raiseCannotEval(c, n.info)
-  of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n)
-  of nkWhileStmt: result = evalWhile(c, n)
-  of nkCaseStmt: result = evalCase(c, n)
-  of nkVarSection, nkLetSection: result = evalVar(c, n)
-  of nkTryStmt: result = evalTry(c, n)
-  of nkRaiseStmt: result = evalRaise(c, n)
-  of nkReturnStmt: result = evalReturn(c, n)
-  of nkBreakStmt, nkReturnToken: result = n
-  of nkBlockExpr, nkBlockStmt: result = evalBlock(c, n)
-  of nkDiscardStmt: result = evalAux(c, n.sons[0], {})
-  of nkCheckedFieldExpr: result = evalCheckedFieldAccess(c, n, flags)
-  of nkObjDownConv: result = evalAux(c, n.sons[0], flags)
-  of nkObjUpConv: result = evalUpConv(c, n, flags)
-  of nkChckRangeF, nkChckRange64, nkChckRange: result = evalRangeChck(c, n)
-  of nkStringToCString: result = evalConvStrToCStr(c, n)
-  of nkCStringToString: result = evalConvCStrToStr(c, n)
-  of nkStmtListExpr, nkStmtList: 
-    for i in countup(0, sonsLen(n) - 1): 
-      result = evalAux(c, n.sons[i], flags)
-      case result.kind
-      of nkExceptBranch, nkReturnToken, nkBreakStmt: break 
-      else: nil
-  of nkProcDef, nkMethodDef, nkMacroDef, nkCommentStmt, nkPragma,
-     nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef,
-     nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt: 
-    nil
-  of nkMetaNode:
-    result = copyTree(n.sons[0])
-    result.typ = n.typ
-  of nkPragmaBlock:
-    result = evalAux(c, n.sons[1], flags)
-  of nkCast:
-    result = evalCast(c, n, flags)
-  of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, 
-     nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt,
-     nkClosedSymChoice, nkOpenSymChoice:
-    result = raiseCannotEval(c, n.info)
-  of nkRefTy:
-    result = evalAux(c, n.sons[0], flags)
-  of nkEmpty: 
-    # nkEmpty occurs once in each trace that I looked at
-    result = n
-  else: InternalError(n.info, "evalAux: " & $n.kind)
-  if result == nil:
-    InternalError(n.info, "evalAux: returned nil " & $n.kind)
-  inc(gNestedEvals)
-
-proc tryEval(c: PEvalContext, n: PNode): PNode =
-  #internalAssert nfTransf in n.flags
-  var n = transformExpr(c.module, n)
-  gWhileCounter = evalMaxIterations
-  gNestedEvals = evalMaxRecDepth
-  result = evalAux(c, n, {})
-  
-proc eval*(c: PEvalContext, n: PNode): PNode = 
-  ## eval never returns nil! This simplifies the code a lot and
-  ## makes it faster too.
-  result = tryEval(c, n)
-  if result.kind == nkExceptBranch:
-    if sonsLen(result) >= 1: 
-      stackTrace(c, n.info, errUnhandledExceptionX, typeToString(result.typ))
-    else:
-      stackTrace(c, result.info, errCannotInterpretNodeX, renderTree(n))
-
-proc evalConstExprAux*(p: PEvalContext, module, prc: PSym, e: PNode): PNode =
-  var s = newStackFrame()
-  s.call = e
-  s.prc = prc
-  pushStackFrame(p, s)
-  result = tryEval(p, e)
-  if result != nil and result.kind == nkExceptBranch: result = nil
-  popStackFrame(p)
-
-proc setupMacroParam(x: PNode): PNode =
-  result = x
-  if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
-
-proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode =
-  # XXX GlobalError() is ugly here, but I don't know a better solution for now
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > 100:
-    GlobalError(n.info, errTemplateInstantiationTooNested)
-
-  c.callsite = nOrig
-  var s = newStackFrame()
-  s.call = n
-  s.prc = sym
-  var L = n.safeLen
-  if L == 0: L = 1
-  setlen(s.slots, L)
-  # return value:
-  s.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
-  # setup parameters:
-  for i in 1 .. < L: s.slots[i] = setupMacroParam(n.sons[i])
-  pushStackFrame(c, s)
-  discard eval(c, optBody(c, sym))
-  result = s.slots[0]
-  popStackFrame(c)
-  if cyclicTree(result): GlobalError(n.info, errCyclicTree)
-  dec(evalTemplateCounter)
-  c.callsite = nil
-
-proc myOpen(module: PSym): PPassContext =
-  var c = newEvalContext(module, emRepl)
-  c.features = {allowCast, allowFFI, allowInfiniteLoops}
-  pushStackFrame(c, newStackFrame())
-  result = c
-
-var oldErrorCount: int
-
-proc myProcess(c: PPassContext, n: PNode): PNode =
-  # don't eval errornous code:
-  if oldErrorCount == msgs.gErrorCounter:
-    result = eval(PEvalContext(c), n)
-  else:
-    result = n
-  oldErrorCount = msgs.gErrorCounter
-
-const evalPass* = makePass(myOpen, nil, myProcess, myProcess)
-
diff --git a/compiler/guards.nim b/compiler/guards.nim
index f475f5068..4cf06fe02 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,7 +9,8 @@
 
 ## This module implements the 'implies' relation for guards.
 
-import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents
+import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
+  saturate
 
 const
   someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@@ -25,6 +26,17 @@ const
 
   someIn = {mInRange, mInSet}
 
+  someHigh = {mHigh}
+  # we don't list unsigned here because wrap around semantics suck for
+  # proving anything:
+  someAdd = {mAddI, mAddI64, mAddF64, mSucc}
+  someSub = {mSubI, mSubI64, mSubF64, mPred}
+  someMul = {mMulI, mMulI64, mMulF64}
+  someDiv = {mDivI, mDivI64, mDivF64}
+  someMod = {mModI, mModI64}
+  someMax = {mMaxI, mMaxI64, mMaxF64}
+  someMin = {mMinI, mMinI64, mMinF64}
+
 proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
 proc isLocation(n: PNode): bool = not n.isValue
 
@@ -69,19 +81,24 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
 
 proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
 
-proc getMagicOp(name: string, m: TMagic): PSym =
+proc createMagic*(name: string, m: TMagic): PSym =
   result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
   result.magic = m
 
 let
-  opLe = getMagicOp("<=", mLeI)
-  opLt = getMagicOp("<", mLtI)
-  opAnd = getMagicOp("and", mAnd)
-  opOr = getMagicOp("or", mOr)
-  opNot = getMagicOp("not", mNot)
-  opIsNil = getMagicOp("isnil", mIsNil)
-  opContains = getMagicOp("contains", mInSet)
-  opEq = getMagicOp("==", mEqI)
+  opLe = createMagic("<=", mLeI)
+  opLt = createMagic("<", mLtI)
+  opAnd = createMagic("and", mAnd)
+  opOr = createMagic("or", mOr)
+  opNot = createMagic("not", mNot)
+  opIsNil = createMagic("isnil", mIsNil)
+  opContains = createMagic("contains", mInSet)
+  opEq = createMagic("==", mEqI)
+  opAdd = createMagic("+", mAddI)
+  opSub = createMagic("-", mSubI)
+  opMul = createMagic("*", mMulI)
+  opDiv = createMagic("div", mDivI)
+  opLen = createMagic("len", mLengthSeq)
 
 proc swapArgs(fact: PNode, newOp: PSym): PNode =
   result = newNodeI(nkCall, fact.info, 3)
@@ -137,17 +154,141 @@ proc neg(n: PNode): PNode =
     result.sons[0] = newSymNode(opNot)
     result.sons[1] = n
 
-proc buildIsNil(arg: PNode): PNode =
-  result = newNodeI(nkCall, arg.info, 2)
-  result.sons[0] = newSymNode(opIsNil)
-  result.sons[1] = arg
+proc buildCall(op: PSym; a: PNode): PNode =
+  result = newNodeI(nkCall, a.info, 2)
+  result.sons[0] = newSymNode(op)
+  result.sons[1] = a
+
+proc buildCall(op: PSym; a, b: PNode): PNode =
+  result = newNodeI(nkInfix, a.info, 3)
+  result.sons[0] = newSymNode(op)
+  result.sons[1] = a
+  result.sons[2] = b
+
+proc `|+|`(a, b: PNode): PNode =
+  result = copyNode(a)
+  if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |+| b.intVal
+  else: result.floatVal = a.floatVal + b.floatVal
+
+proc `|*|`(a, b: PNode): PNode =
+  result = copyNode(a)
+  if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal |*| b.intVal
+  else: result.floatVal = a.floatVal * b.floatVal
+
+proc negate(a, b, res: PNode): PNode =
+  if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
+    var b = copyNode(b)
+    b.intVal = -b.intVal
+    if a.kind in {nkCharLit..nkUInt64Lit}:
+      b.intVal = b.intVal |+| a.intVal
+      result = b
+    else:
+      result = buildCall(opAdd, a, b)
+  elif b.kind in {nkFloatLit..nkFloat64Lit}:
+    var b = copyNode(b)
+    b.floatVal = -b.floatVal
+    result = buildCall(opAdd, a, b)
+  else:
+    result = res
+
+proc zero(): PNode = nkIntLit.newIntNode(0)
+proc one(): PNode = nkIntLit.newIntNode(1)
+proc minusOne(): PNode = nkIntLit.newIntNode(-1)
+
+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))
+           else:
+             opAdd.buildCall(opLen.buildCall(x), minusOne())
+  result.info = x.info
+
+proc reassociation(n: PNode): PNode =
+  result = n
+  # (foo+5)+5 --> foo+10;  same for '*'
+  case result.getMagic
+  of someAdd:
+    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 
+        result[1].getMagic in someMul and result[1][2].isValue:
+      result = opAdd.buildCall(result[1][1], result[1][2] |*| result[2])
+  else: discard
+
+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])
+  else:
+    result = n
+  case result.getMagic
+  of someEq, someAdd, someMul, someMin, someMax:
+    # these are symmetric; put value as last:
+    if result.sons[1].isValue and not result.sons[2].isValue:
+      result = swapArgs(result, result.sons[0].sym)
+      # (4 + foo) + 2 --> (foo + 4) + 2
+  of someHigh:
+    # high == len+(-1)
+    result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
+  of mUnaryMinusI, mUnaryMinusI64:
+    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
+  else: discard
+
+  result = skipConv(result)
+  result = reassociation(result)
+  # most important rule: (x-4) < a.len -->  x < a.len+4
+  case result.getMagic
+  of someLe, someLt:
+    let x = result[1]
+    let y = result[2]
+    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], 
+                           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 
+        isLetLocation(y[1], true):
+      # a.len < x-3
+      case y.getMagic
+      of someSub:
+        result = buildCall(result[0].sym, y[1],
+                           reassociation(opAdd.buildCall(x, y[2])))
+      of someAdd:
+        let plus = negate(x, y[2], nil).reassociation
+        # ensure that Rule A will not trigger afterwards with the
+        # additional 'not isLetLocation' constraint:
+        if plus != nil and not isLetLocation(x, true):
+          result = buildCall(result[0].sym, plus, y[1])
+      else: discard
+  else: discard
+
+proc `+@`*(a: PNode; b: BiggestInt): PNode =
+  canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
 
 proc usefulFact(n: PNode): PNode =
   case n.getMagic
   of someEq:
     if skipConv(n.sons[2]).kind == nkNilLit and (
         isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
-      result = buildIsNil(n.sons[1])
+      result = opIsNil.buildCall(n.sons[1])
     else:
       if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
         # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
@@ -217,7 +358,7 @@ 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
@@ -484,7 +625,7 @@ proc factImplies(fact, prop: PNode): TImplication =
     #  == not a or not b == not (a and b)
     let arg = fact.sons[1]
     case arg.getMagic
-    of mIsNil:
+    of mIsNil, mEqRef:
       return ~factImplies(arg, prop)
     of mAnd:
       # not (a and b)  means  not a or not b:
@@ -519,7 +660,144 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
       if result != impUnknown: return
 
 proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
-  result = doesImply(facts, buildIsNil(arg).neg)
+  result = doesImply(facts, opIsNil.buildCall(arg).neg)
+
+proc simpleSlice*(a, b: PNode): BiggestInt =
+  # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
+  # as if it is (i+0)..(i+0).
+  if guards.sameTree(a, b):
+    if a.getMagic in someAdd and a[2].kind in {nkCharLit..nkUInt64Lit}:
+      result = a[2].intVal
+    else:
+      result = 0
+  else:
+    result = -1
+
+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
+
+  # use type information too:  x <= 4  iff  high(x) <= 4
+  if b.isValue and a.typ != nil and a.typ.isOrdinalType:
+    if lastOrd(a.typ) <= b.intVal: return impYes
+  # 3 <= x   iff  low(x) <= 3
+  if a.isValue and b.typ != nil and b.typ.isOrdinalType:
+    if firstOrd(b.typ) <= a.intVal: return impYes
+
+  # x <= x
+  if sameTree(a, b): return impYes
+
+  # 0 <= x.len
+  if b.getMagic in someLen and a.isValue:
+    if a.intVal <= 0: return impYes
+
+  #   x <= y+c  if 0 <= c and x <= y
+  if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
+
+  #   x+c <= y  if c <= 0 and x <= y
+  if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
+
+  #   x <= y*c  if  1 <= c and x <= y  and 0 <= y
+  if b.getMagic in someMul:
+    if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes
+
+  #   x div c <= y   if   1 <= c  and  0 <= y  and x <= y:
+  if a.getMagic in someDiv:
+    if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes
+
+  # slightly subtle:
+  # x <= max(y, z)  iff x <= y or x <= z
+  # note that 'x <= max(x, z)' is a special case of the above rule
+  if b.getMagic in someMax:
+    if a <=? b[1] or a <=? b[2]: return impYes
+
+  # min(x, y) <= z  iff x <= z or y <= z
+  if a.getMagic in someMin:
+    if a[1] <=? b or a[2] <=? b: return impYes
+
+  # use the knowledge base:
+  return pleViaModel(m, a, b)
+  #return doesImply(m, opLe.buildCall(a, b))
+
+type TReplacements = seq[tuple[a,b: PNode]]
+
+proc replaceSubTree(n, x, by: PNode): PNode =
+  if sameTree(n, x):
+    result = by
+  elif hasSubTree(n, x):
+    result = shallowCopy(n)
+    for i in 0 .. safeLen(n)-1:
+      result.sons[i] = replaceSubTree(n.sons[i], x, by)
+  else:
+    result = n
+
+proc applyReplacements(n: PNode; rep: TReplacements): PNode =
+  result = n
+  for x in rep: result = result.replaceSubTree(x.a, x.b)
+
+proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
+  # now check for inferrable facts: a <= b and b <= c  implies a <= c
+  for i in 0..m.high:
+    let fact = m[i]
+    if fact != nil and fact.getMagic in someLe:
+      # x <= y implies a <= b  if  a <= x and y <= b
+      let x = fact[1]
+      let y = fact[2]
+      # mark as used:
+      m[i] = nil
+      if ple(m, a, x) == 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, x, b)
+        if result != impUnknown: return result
+
+proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
+  # compute replacements:
+  var replacements: TReplacements = @[]
+  for fact in model:
+    if fact != nil and fact.getMagic in someEq:
+      let a = fact[1]
+      let b = fact[2]
+      if a.kind == nkSym: replacements.add((a,b))
+      else: replacements.add((b,a))
+  var m: TModel
+  var a = aa
+  var b = bb
+  if replacements.len > 0:
+    m = @[]
+    # make the other facts consistent:
+    for fact in model:
+      if fact != nil and fact.getMagic notin someEq:
+        # XXX 'canon' should not be necessary here, but it is
+        m.add applyReplacements(fact, replacements).canon
+    a = applyReplacements(aa, replacements)
+    b = applyReplacements(bb, replacements)
+  else:
+    # we have to make a copy here, because the model will be modified:
+    m = model
+  result = pleViaModelRec(m, a, b)
+
+proc proveLe*(m: TModel; a, b: PNode): TImplication =
+  let x = canon(opLe.buildCall(a, b))
+  #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
+  result = ple(m, x[1], x[2])
+  if result == impUnknown:
+    # try an alternative:  a <= b  iff  not (b < a)  iff  not (b+1 <= a):
+    let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a))
+    result = ~ple(m, y[1], y[2])
+
+proc addFactLe*(m: var TModel; a, b: PNode) =
+  m.add canon(opLe.buildCall(a, b))
 
 proc settype(n: PNode): PType =
   result = newType(tySet, n.typ.owner)
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 7a73f2bbf..b4cae017e 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -93,7 +93,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
     if hasPattern(s): addPattern(c, s)
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = 
-  let ident = lookups.considerAcc(n)
+  let ident = lookups.considerQuotedIdent(n)
   let s = strTableGet(fromMod.tab, ident)
   if s == nil:
     localError(n.info, errUndeclaredIdentifier, ident.s)
@@ -193,7 +193,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
     addDecl(c, m)               # add symbol to symbol table of module
     var exceptSet = initIntSet()
     for i in countup(1, sonsLen(n) - 1): 
-      let ident = lookups.considerAcc(n.sons[i])
+      let ident = lookups.considerQuotedIdent(n.sons[i])
       exceptSet.incl(ident.id)
     importAllSymbolsExcept(c, m, exceptSet)
     importForwarded(c, m.ast, exceptSet)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 373a11e9a..6687e2e8e 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -136,18 +136,6 @@ proc mapType(typ: PType): TJSTypeKind =
   of tyProc: result = etyProc
   of tyCString: result = etyString
   
-proc mangle(name: string): string = 
-  result = ""
-  for i in countup(0, len(name) - 1): 
-    case name[i]
-    of 'A'..'Z': 
-      add(result, chr(ord(name[i]) - ord('A') + ord('a')))
-    of '_': 
-      discard
-    of 'a'..'z', '0'..'9': 
-      add(result, name[i])
-    else: add(result, 'X' & toHex(ord(name[i]), 2))
-  
 proc mangleName(s: PSym): PRope = 
   result = s.loc.r
   if result == nil: 
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 2bfd8d1eb..0e4dfc2ac 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -347,7 +347,7 @@ proc getNumber(L: var TLexer): TToken =
         result.base = base2
         while true: 
           case L.buf[pos]
-          of 'A'..'Z', 'a'..'z', '2'..'9', '.': 
+          of '2'..'9', '.': 
             lexMessage(L, errInvalidNumber, result.literal)
             inc(pos)
           of '_': 
@@ -363,7 +363,7 @@ proc getNumber(L: var TLexer): TToken =
         result.base = base8
         while true: 
           case L.buf[pos]
-          of 'A'..'Z', 'a'..'z', '8'..'9', '.': 
+          of '8'..'9', '.': 
             lexMessage(L, errInvalidNumber, result.literal)
             inc(pos)
           of '_': 
@@ -377,25 +377,22 @@ proc getNumber(L: var TLexer): TToken =
           else: break 
       of 'O': 
         lexMessage(L, errInvalidNumber, result.literal)
-      of 'x', 'X': 
+      of 'x', 'X':
         result.base = base16
-        while true: 
+        while true:
           case L.buf[pos]
-          of 'G'..'Z', 'g'..'z': 
-            lexMessage(L, errInvalidNumber, result.literal)
-            inc(pos)
-          of '_': 
-            if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}: 
+          of '_':
+            if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}:
               lexMessage(L, errInvalidToken, "_")
               break
             inc(pos)
-          of '0'..'9': 
+          of '0'..'9':
             xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
             inc(pos)
-          of 'a'..'f': 
+          of 'a'..'f':
             xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10)
             inc(pos)
-          of 'A'..'F': 
+          of 'A'..'F':
             xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
             inc(pos)
           else: break 
@@ -424,8 +421,14 @@ proc getNumber(L: var TLexer): TToken =
       if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
         if result.tokType == tkIntLit:
           result.tokType = tkInt64Lit
-        elif result.tokType in {tkInt8Lit, tkInt16Lit}:
-          lexMessage(L, errInvalidNumber, result.literal)
+        elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}:
+          lexMessage(L, errNumberOutOfRange, result.literal)
+      elif result.tokType == tkInt8Lit and
+          (result.iNumber < int8.low or result.iNumber > int8.high):
+        lexMessage(L, errNumberOutOfRange, result.literal)
+      elif result.tokType == tkInt16Lit and
+          (result.iNumber < int16.low or result.iNumber > int16.high):
+        lexMessage(L, errNumberOutOfRange, result.literal)
   except EInvalidValue:
     lexMessage(L, errInvalidNumber, result.literal)
   except EOverflow, EOutOfRange:
@@ -537,6 +540,10 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
     tok.tokType = tkTripleStrLit # long string literal:
     inc(pos, 2)               # skip ""
     # skip leading newline:
+    if buf[pos] in {' ', '\t'}:
+      var newpos = pos+1
+      while buf[newpos] in {' ', '\t'}: inc newpos
+      if buf[newpos] in {CR, LF}: pos = newpos
     pos = handleCRLF(L, pos)
     buf = L.buf
     while true: 
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 510880ffd..86bfeaabd 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -82,6 +82,9 @@ when not defined(readLineFromStdin):
   proc readLineFromStdin(prompt: string, line: var string): bool =
     stdout.write(prompt)
     result = readLine(stdin, line)
+    if not result:
+      stdout.write("\n")
+      quit(0)
 
 proc endsWith*(x: string, s: set[char]): bool =
   var i = x.len-1
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 60125177c..aee64f52f 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -15,14 +15,15 @@ import
 
 proc ensureNoMissingOrUnusedSymbols(scope: PScope)
 
-proc considerAcc*(n: PNode): PIdent = 
+proc considerQuotedIdent*(n: PNode): PIdent =
+  ## Retrieve a PIdent from a PNode, taking into account accent nodes.
   case n.kind
   of nkIdent: result = n.ident
   of nkSym: result = n.sym.name
   of nkAccQuoted:
     case n.len
     of 0: globalError(n.info, errIdentifierExpected, renderTree(n))
-    of 1: result = considerAcc(n.sons[0])
+    of 1: result = considerQuotedIdent(n.sons[0])
     else:
       var id = ""
       for i in 0.. <n.len:
@@ -82,10 +83,10 @@ proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
 proc errorSym*(c: PContext, n: PNode): PSym =
   ## creates an error symbol to avoid cascading errors (for IDE support)
   var m = n
-  # ensure that 'considerAcc' can't fail:
+  # ensure that 'considerQuotedIdent' can't fail:
   if m.kind == nkDotExpr: m = m.sons[1]
   let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}: 
-      considerAcc(m)
+      considerQuotedIdent(m)
     else:
       getIdent("err:" & renderTree(m))
   result = newSym(skError, ident, getCurrOwner(), n.info)
@@ -189,7 +190,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
   of nkSym:
     result = n.sym
   of nkAccQuoted:
-    var ident = considerAcc(n)
+    var ident = considerQuotedIdent(n)
     result = searchInScopes(c, ident)
     if result == nil:
       localError(n.info, errUndeclaredIdentifier, ident.s)
@@ -208,7 +209,7 @@ type
 proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = 
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerAcc(n)
+    var ident = considerQuotedIdent(n)
     result = searchInScopes(c, ident)
     if result == nil and checkUndeclared in flags: 
       localError(n.info, errUndeclaredIdentifier, ident.s)
@@ -228,7 +229,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
       if n.sons[1].kind == nkIdent: 
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted: 
-        ident = considerAcc(n.sons[1])
+        ident = considerQuotedIdent(n.sons[1])
       if ident != nil: 
         if m == c.module: 
           result = strTableGet(c.topLevelScope.symbols, ident)
@@ -251,7 +252,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerAcc(n)
+    var ident = considerQuotedIdent(n)
     o.scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
@@ -272,7 +273,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       if n.sons[1].kind == nkIdent: 
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted:
-        ident = considerAcc(n.sons[1])
+        ident = considerQuotedIdent(n.sons[1])
       if ident != nil: 
         if o.m == c.module: 
           # a module may access its private members:
@@ -354,5 +355,5 @@ when false:
       if sfImmediate in a.flags: return a
       a = nextOverloadIter(o, c, n)
     if result == nil and checkUndeclared in flags: 
-      localError(n.info, errUndeclaredIdentifier, n.considerAcc.s)
+      localError(n.info, errUndeclaredIdentifier, n.considerQuotedIdent.s)
       result = errorSym(c, n)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 1b9e5fe0f..e2afa4362 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -13,6 +13,8 @@ const
   genPrefix* = ":tmp"         # prefix for generated names
 
 import ast, astalgo, types, idents, magicsys, msgs, options
+from guards import createMagic
+from trees import getMagic
 
 proc newTupleAccess*(tup: PNode, i: int): PNode =
   result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
@@ -68,6 +70,7 @@ proc addField*(obj: PType; s: PSym) =
   var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
   let t = skipIntLit(s.typ)
   field.typ = t
+  assert t.kind != tyStmt
   field.position = sonsLen(obj.n)
   addSon(obj.n, newSymNode(field))
 
@@ -79,19 +82,30 @@ proc newDotExpr(obj, b: PSym): PNode =
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
-proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = 
+proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode = 
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.sons[0]
-  assert deref.typ.kind == tyObject
-  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
-  assert field != nil, b.name.s
+  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  var t = deref.typ.skipTypes(abstractInst)
+  var field: PSym
+  while true:
+    assert t.kind == tyObject
+    field = getSymFromList(t.n, getIdent(b))
+    if field != nil: break
+    t = t.sons[0]
+    if t == nil: break
+    t = t.skipTypes(abstractInst)
+  assert field != nil, b
   addSon(deref, a)
   result = newNodeI(nkDotExpr, info)
   addSon(result, deref)
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
+proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = 
+  # returns a[].b as a node
+  result = indirectAccess(a, b.name.s & $b.id, info)
+
 proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
   result = indirectAccess(newSymNode(a), b, info)
 
@@ -101,6 +115,11 @@ proc genAddrOf*(n: PNode): PNode =
   result.typ = newType(tyPtr, n.typ.owner)
   result.typ.rawAddSon(n.typ)
 
+proc genDeref*(n: PNode): PNode =
+  result = newNodeIT(nkHiddenDeref, n.info, 
+                     n.typ.skipTypes(abstractInst).sons[0])
+  result.add n
+
 proc callCodegenProc*(name: string, arg1: PNode; 
                       arg2, arg3: PNode = nil): PNode =
   result = newNodeI(nkCall, arg1.info)
@@ -112,13 +131,120 @@ proc callCodegenProc*(name: string, arg1: PNode;
     result.add arg1
     if arg2 != nil: result.add arg2
     if arg3 != nil: result.add arg3
+    result.typ = sym.typ.sons[0]
+
+proc callProc(a: PNode): PNode =
+  result = newNodeI(nkCall, a.info)
+  result.add a
+  result.typ = a.typ.sons[0]
+
+# we have 4 cases to consider:
+# - a void proc --> nothing to do
+# - a proc returning GC'ed memory --> requires a flowVar
+# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
+# - not in a parallel environment --> requires a flowVar for memory safety
+type
+  TSpawnResult = enum
+    srVoid, srFlowVar, srByVar
+  TFlowVarKind = enum
+    fvInvalid # invalid type T for 'FlowVar[T]'
+    fvGC      # FlowVar of a GC'ed type
+    fvBlob    # FlowVar of a blob type
+
+proc spawnResult(t: PType; inParallel: bool): TSpawnResult =
+  if t.isEmptyType: srVoid
+  elif inParallel and not containsGarbageCollectedRef(t): srByVar
+  else: srFlowVar
+
+proc flowVarKind(t: PType): TFlowVarKind =
+  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
+  elif containsGarbageCollectedRef(t): fvInvalid
+  else: fvBlob
+
+proc addLocalVar(varSection: PNode; owner: PSym; typ: PType; v: PNode): PSym =
+  result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
+  result.typ = typ
+  incl(result.flags, sfFromGeneric)
+
+  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
+  vpart.sons[0] = newSymNode(result)
+  vpart.sons[1] = ast.emptyNode
+  vpart.sons[2] = v
+  varSection.add vpart
+
+discard """
+We generate roughly this:
+
+proc f_wrapper(thread, args) =
+  barrierEnter(args.barrier)  # for parallel statement
+  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
+                 # depending on whether we're in a 'parallel' statement
+  var b = args.b
+  var fv = args.fv
+
+  fv.owner = thread # optional
+  nimArgsPassingDone() # signal parent that the work is done
+  # 
+  args.fv.blob = f(a, b, ...)
+  nimFlowVarSignal(args.fv)
+  
+  # - or -
+  f(a, b, ...)
+  barrierLeave(args.barrier)  # for parallel statement
+
+stmtList:
+  var scratchObj
+  scratchObj.a = a
+  scratchObj.b = b
+
+  nimSpawn(f_wrapper, addr scratchObj)
+  scratchObj.fv # optional
+
+"""
 
 proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
-                       varSection, call: PNode): PSym =
+                       varSection, call, barrier, fv: PNode;
+                       spawnKind: TSpawnResult): PSym =
   var body = newNodeI(nkStmtList, f.info)
+  var threadLocalBarrier: PSym
+  if barrier != nil:
+    var varSection = newNodeI(nkVarSection, barrier.info)
+    threadLocalBarrier = addLocalVar(varSection, argsParam.owner, 
+                                     barrier.typ, barrier)
+    body.add varSection
+    body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode)
+  var threadLocalProm: PSym
+  if spawnKind == srByVar:
+    threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv)
+  elif fv != nil:
+    internalAssert fv.typ.kind == tyGenericInst
+    threadLocalProm = addLocalVar(varSection, argsParam.owner, fv.typ, fv)
+    
   body.add varSection
-  body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam))
-  body.add call
+  if fv != nil and spawnKind != srByVar:
+    # generate:
+    #   fv.owner = threadParam
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      "owner", fv.info), threadParam.newSymNode)
+
+  body.add callCodeGenProc("nimArgsPassingDone", threadParam.newSymNode)
+  if spawnKind == srByVar:
+    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
+  elif fv != nil:
+    let fk = fv.typ.sons[1].flowVarKind
+    if fk == fvInvalid:
+      localError(f.info, "cannot create a flowVar of type: " & 
+        typeToString(fv.typ.sons[1]))
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      if fk == fvGC: "data" else: "blob", fv.info), call)
+    if barrier == nil:
+      # by now 'fv' is shared and thus might have beeen overwritten! we need
+      # to use the thread-local view instead:
+      body.add callCodeGenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
+  else:
+    body.add call
+  if barrier != nil:
+    body.add callCodeGenProc("barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
   params.add emptyNode
@@ -146,10 +272,152 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
   result.typ = newType(tyPtr, objType.owner)
   result.typ.rawAddSon(objType)
 
-proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
-  result = newNodeI(nkStmtList, n.info)
-  if n.kind notin nkCallKinds or not n.typ.isEmptyType:
-    localError(n.info, "'spawn' takes a call expression of type void")
+proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym, 
+                             castExpr, call, varSection, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(genPrefix)
+  for i in 1 .. <n.len:
+    # we pick n's type here, which hopefully is 'tyArray' and not
+    # 'tyOpenArray':
+    var argType = n[i].typ.skipTypes(abstractInst)
+    if i < formals.len and formals[i].typ.kind == tyVar:
+      localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
+    elif containsTyRef(argType):
+      localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, objType.owner, n.info)
+    field.typ = argType
+    objType.addField(field)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
+
+    let temp = addLocalVar(varSection, objType.owner, argType,
+                           indirectAccess(castExpr, field, n.info))    
+    call.add(newSymNode(temp))
+
+proc getRoot*(n: PNode): PSym =
+  ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
+  ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
+  ## determined as the owner; ``obj`` in the example.
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
+      result = n.sym
+  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
+      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
+    result = getRoot(n.sons[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    result = getRoot(n.sons[1])
+  of nkCallKinds:
+    if getMagic(n) == mSlice: result = getRoot(n.sons[1])
+  else: discard
+
+proc newIntLit(value: BiggestInt): PNode =
+  result = nkIntLit.newIntNode(value)
+  result.typ = getSysType(tyInt)
+
+proc genHigh(n: PNode): PNode =
+  if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
+    result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
+  else:
+    result = newNodeI(nkCall, n.info, 2)
+    result.typ = getSysType(tyInt)
+    result.sons[0] = newSymNode(createMagic("high", mHigh))
+    result.sons[1] = n
+
+proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
+                             castExpr, call, varSection, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(genPrefix)
+  # we need to copy the foreign scratch object fields into local variables
+  # for correctness: These are called 'threadLocal' here.
+  for i in 1 .. <n.len:
+    let n = n[i]
+    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
+                            abstractInst)
+    if containsTyRef(argType):
+      localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, objType.owner, n.info)
+
+    if argType.kind in {tyVarargs, tyOpenArray}:
+      # important special case: we always create a zero-copy slice:
+      let slice = newNodeI(nkCall, n.info, 4)
+      slice.typ = n.typ
+      slice.sons[0] = newSymNode(createMagic("slice", mSlice))
+      var fieldB = newSym(skField, tmpName, objType.owner, n.info)
+      fieldB.typ = getSysType(tyInt)
+      objType.addField(fieldB)
+      
+      if getMagic(n) == mSlice:
+        let a = genAddrOf(n[1])
+        field.typ = a.typ
+        objType.addField(field)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+
+        var fieldA = newSym(skField, tmpName, objType.owner, n.info)
+        fieldA.typ = getSysType(tyInt)
+        objType.addField(fieldA)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
+
+        let threadLocal = addLocalVar(varSection, objType.owner, fieldA.typ,
+                                      indirectAccess(castExpr, fieldA, n.info))
+        slice.sons[2] = threadLocal.newSymNode
+      else:
+        let a = genAddrOf(n)
+        field.typ = a.typ
+        objType.addField(field)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
+
+        slice.sons[2] = newIntLit(0)
+      # the array itself does not need to go through a thread local variable:
+      slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
+
+      let threadLocal = addLocalVar(varSection, objType.owner, fieldB.typ,
+                                    indirectAccess(castExpr, fieldB, n.info))
+      slice.sons[3] = threadLocal.newSymNode
+      call.add slice
+    elif (let size = computeSize(argType); size < 0 or size > 16) and
+        n.getRoot != nil:
+      # it is more efficient to pass a pointer instead:
+      let a = genAddrOf(n)
+      field.typ = a.typ
+      objType.addField(field)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+      let threadLocal = addLocalVar(varSection, objType.owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info))
+      call.add(genDeref(threadLocal.newSymNode))
+    else:
+      # boring case
+      field.typ = argType
+      objType.addField(field)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
+      let threadLocal = addLocalVar(varSection, objType.owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info))
+      call.add(threadLocal.newSymNode)
+
+proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; 
+                       barrier, dest: PNode = nil): PNode =
+  # if 'barrier' != nil, then it is in a 'parallel' section and we
+  # generate quite different code
+  let n = spawnExpr[1]
+  let spawnKind = spawnResult(retType, barrier!=nil)
+  case spawnKind
+  of srVoid:
+    internalAssert dest == nil
+    result = newNodeI(nkStmtList, n.info)
+  of srFlowVar:
+    internalAssert dest == nil
+    result = newNodeIT(nkStmtListExpr, n.info, retType)
+  of srByVar:
+    if dest == nil: localError(n.info, "'spawn' must not be discarded")
+    result = newNodeI(nkStmtList, n.info)
+
+  if n.kind notin nkCallKinds:
+    localError(n.info, "'spawn' takes a call expression")
     return
   if optThreadAnalysis in gGlobalOptions:
     if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
@@ -162,6 +430,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
     threadParam.typ = ptrType
     argsParam.typ = ptrType
     argsParam.position = 1
+
   var objType = createObj(owner, n.info)
   incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType)
@@ -174,7 +443,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
     varSectionB.addVar(scratchObj.newSymNode)
     result.add varSectionB
 
-  var call = newNodeI(nkCall, n.info)
+  var call = newNodeIT(nkCall, n.info, n.typ)
   var fn = n.sons[0]
   # templates and macros are in fact valid here due to the nature of
   # the transformation:
@@ -194,35 +463,44 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
 
   call.add(fn)
   var varSection = newNodeI(nkVarSection, n.info)
-  let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
-  for i in 1 .. <n.len:
-    # we pick n's type here, which hopefully is 'tyArray' and not
-    # 'tyOpenArray':
-    var argType = n[i].typ.skipTypes(abstractInst)
-    if i < formals.len and formals[i].typ.kind == tyVar:
-      localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
-    elif containsTyRef(argType):
-      localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
+  if barrier.isNil:
+    setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, varSection, result)
+  else: 
+    setupArgsForParallelism(n, objType, scratchObj, castExpr, call, varSection, result)
 
-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, owner, n.info)
-    field.typ = argType
+  var barrierAsExpr: PNode = nil
+  if barrier != nil:
+    let typ = newType(tyPtr, owner)
+    typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
+    var field = newSym(skField, getIdent"barrier", owner, n.info)
+    field.typ = typ
     objType.addField(field)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
-
-    var temp = newSym(skTemp, tmpName, owner, n.info)
-    temp.typ = argType
-    incl(temp.flags, sfFromGeneric)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
+    barrierAsExpr = indirectAccess(castExpr, field, n.info)
 
-    var vpart = newNodeI(nkIdentDefs, n.info, 3)
-    vpart.sons[0] = newSymNode(temp)
-    vpart.sons[1] = ast.emptyNode
-    vpart.sons[2] = indirectAccess(castExpr, field, n.info)
-    varSection.add vpart
+  var fvField, fvAsExpr: PNode = nil
+  if spawnKind == srFlowVar:
+    var field = newSym(skField, getIdent"fv", owner, n.info)
+    field.typ = retType
+    objType.addField(field)
+    fvField = newDotExpr(scratchObj, field)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    # create flowVar:
+    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2]))
+    if barrier == nil:
+      result.add callCodeGenProc("nimFlowVarCreateCondVar", fvField)
 
-    call.add(newSymNode(temp))
+  elif spawnKind == srByVar:
+    var field = newSym(skField, getIdent"fv", owner, n.info)
+    field.typ = newType(tyPtr, objType.owner)
+    field.typ.rawAddSon(retType)
+    objType.addField(field)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
 
-  let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call)
+  let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call,
+                                  barrierAsExpr, fvAsExpr, spawnKind)
   result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
                              genAddrOf(scratchObj.newSymNode))
+
+  if spawnKind == srFlowVar: result.add fvField
diff --git a/compiler/main.nim b/compiler/main.nim
index f6d11d960..b4af49248 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -69,6 +69,7 @@ proc commandCompileToC =
     # echo "BEFORE CHECK DEP"
     # discard checkDepMem(gProjectMainIdx)
     # echo "CHECK DEP COMPLETE"
+    discard
 
   compileProject()
   cgenWriteModules()
@@ -283,11 +284,11 @@ proc resetMemory =
     echo GC_getStatistics()
 
 const
-  SimiluateCaasMemReset = false
+  SimulateCaasMemReset = false
   PrintRopeCacheStats = false
 
 proc mainCommand* =
-  when SimiluateCaasMemReset:
+  when SimulateCaasMemReset:
     gGlobalOptions.incl(optCaasEnabled)
 
   # In "nimrod serve" scenario, each command must reset the registered passes
@@ -309,7 +310,7 @@ proc mainCommand* =
   of "cpp", "compiletocpp":
     extccomp.cExt = ".cpp"
     gCmd = cmdCompileToCpp
-    if cCompiler == ccGcc: setCC("gpp")
+    if cCompiler == ccGcc: setCC("gcc")
     wantMainModule()
     defineSymbol("cpp")
     commandCompileToC()
@@ -453,6 +454,6 @@ proc mainCommand* =
     echo "  efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
                                        ffDecimal, 3)
 
-  when SimiluateCaasMemReset:
+  when SimulateCaasMemReset:
     resetMemory()
 
diff --git a/compiler/modules.nim b/compiler/modules.nim
index fb1940741..b102224cd 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -115,7 +115,7 @@ proc newModule(fileIdx: int32): PSym =
   new(result)
   result.id = - 1             # for better error checking
   result.kind = skModule
-  let filename = fileIdx.toFilename
+  let filename = fileIdx.toFullPath
   result.name = getIdent(splitFile(filename).name)
   if not isNimrodIdentifier(result.name.s):
     rawMessage(errInvalidModuleName, result.name.s)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 8374c92a7..730cb9605 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -116,7 +116,7 @@ type
     warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, 
     warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, 
     warnNilStatement, warnAnalysisLoophole,
-    warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
+    warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
     warnEachIdentIsTuple, warnShadowIdent, 
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnUser,
@@ -380,7 +380,7 @@ const
     warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]",
     warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
     warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
-    warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]",
+    warnUnsafeCode: "unsafe code: '$1' [UnsafeCode]",
     warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
     warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]",
     warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]",
@@ -416,7 +416,7 @@ const
     "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", 
     "CommentXIgnored", "NilStmt",
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
-    "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", 
+    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", 
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
     "GcMem", "User"]
 
diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini
index 0dc44a7c9..44e16cec8 100644
--- a/compiler/nimrod.ini
+++ b/compiler/nimrod.ini
@@ -3,7 +3,7 @@ Name: "Nimrod"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips
+  linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
   macosx: i386;amd64;powerpc64
   solaris: i386;amd64;sparc
   freebsd: i386;amd64
@@ -79,6 +79,7 @@ Files: "lib/system/*.nim"
 Files: "lib/core/*.nim"
 Files: "lib/pure/*.nim"
 Files: "lib/pure/collections/*.nim"
+Files: "lib/pure/concurrency/*.nim"
 Files: "lib/impure/*.nim"
 Files: "lib/wrappers/*.nim"
 
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index b996d0b9b..3c868ed2a 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -12,10 +12,9 @@
 
 const 
   MaxSetElements* = 1 shl 16  # (2^16) to support unicode character sets?
-  defaultAsmMarkerSymbol* = '!'
   VersionMajor* = 0
   VersionMinor* = 9
-  VersionPatch* = 4
+  VersionPatch* = 5
   VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch
 
   RodFileVersion* = "1215"       # modify this if the rod-format changes!
diff --git a/compiler/options.nim b/compiler/options.nim
index 36d343d1b..58a340d21 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -16,7 +16,7 @@ const
   hasFFI* = defined(useFFI)
   newScopeForIf* = true
   useCaas* = not defined(noCaas)
-  noTimeMachine = defined(avoidTimeMachine) and defined(macosx)
+  noTimeMachine* = defined(avoidTimeMachine) and defined(macosx)
 
 type                          # please make sure we have under 32 options
                               # (improves code efficiency a lot!)
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index e94068776..bbdba8c22 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,7 +10,7 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg
+import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
 
 # we precompile the pattern here for efficiency into some internal
 # stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
@@ -215,6 +215,9 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
     result = arLValue
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: 
     result = isAssignable(owner, n.sons[0])
+  of nkCallKinds:
+    # builtin slice keeps lvalue-ness:
+    if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1])
   else:
     discard
 
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 5c7b86240..6ff0c2dfc 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -28,25 +28,20 @@ import
   llstream, lexer, idents, strutils, ast, astalgo, msgs
 
 type
-  TParser*{.final.} = object  # a TParser object represents a module that
+  TParser*{.final.} = object  # A TParser object represents a module that
                               # is being parsed
-    currInd: int              # current indentation
-    firstTok, strongSpaces: bool
-    lex*: TLexer              # the lexer that is used for parsing
-    tok*: TToken              # the current token
-    inPragma: int
+    currInd: int              # current indentation level
+    firstTok, strongSpaces: bool # Has the first token been read?
+                                 # Is strongSpaces on?
+    lex*: TLexer              # The lexer that is used for parsing
+    tok*: TToken              # The current token
+    inPragma: int             # Pragma level
     inSemiStmtList: int
 
 proc parseAll*(p: var TParser): PNode
 proc closeParser*(p: var TParser)
 proc parseTopLevelStmt*(p: var TParser): PNode
-  # implements an iterator. Returns the next top-level statement or
-  # emtyNode if end of stream.
-
 proc parseString*(s: string, filename: string = "", line: int = 0): PNode
-  # filename and line could be set optionally, when the string originates 
-  # from a certain source file. This way, the compiler could generate
-  # correct error messages referring to the original source.
   
 # helpers for the other parsers
 proc isOperator*(tok: TToken): bool
@@ -68,15 +63,19 @@ proc optInd*(p: var TParser, n: PNode)
 proc indAndComment*(p: var TParser, n: PNode)
 proc setBaseFlags*(n: PNode, base: TNumericalBase)
 proc parseSymbol*(p: var TParser, allowNil = false): PNode
-proc parseTry(p: var TParser): PNode
+proc parseTry(p: var TParser; isExpr: bool): PNode
 proc parseCase(p: var TParser): PNode
 # implementation
 
-proc getTok(p: var TParser) = 
+proc getTok(p: var TParser) =
+  ## Get the next token from the parser's lexer, and store it in the parser's
+  ## `tok` member.
   rawGetTok(p.lex, p.tok)
 
 proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
                  strongSpaces=false) =
+  ## Open a parser, using the given arguments to set up its internal state.
+  ## 
   initToken(p.tok)
   openLexer(p.lex, fileIdx, inputStream)
   getTok(p)                   # read the first token
@@ -87,13 +86,16 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
                  strongSpaces=false) =
   openParser(p, filename.fileInfoIdx, inputstream, strongSpaces)
 
-proc closeParser(p: var TParser) = 
+proc closeParser(p: var TParser) =
+  ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
 
-proc parMessage(p: TParser, msg: TMsgKind, arg: string = "") = 
+proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
+  ## Produce and emit the parser message `arg` to output.
   lexMessage(p.lex, msg, arg)
 
-proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) = 
+proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
+  ## Produce and emit a parser message to output about the token `tok`
   lexMessage(p.lex, msg, prettyTok(tok))
 
 template withInd(p: expr, body: stmt) {.immediate.} =
@@ -143,10 +145,15 @@ proc expectIdent(p: TParser) =
     lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
   
 proc eat(p: var TParser, tokType: TTokType) =
-  if p.tok.tokType == tokType: getTok(p)
-  else: lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
+  ## Move the parser to the next token if the current token is of type
+  ## `tokType`, otherwise error.
+  if p.tok.tokType == tokType:
+    getTok(p)
+  else:
+    lexMessage(p.lex, errTokenExpected, TokTypeToStr[tokType])
   
 proc parLineInfo(p: TParser): TLineInfo =
+  ## Retrieve the line information associated with the parser's current state.
   result = getLineInfo(p.lex, p.tok)
 
 proc indAndComment(p: var TParser, n: PNode) =
@@ -192,9 +199,11 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
   result = tok.tokType == tkOpr and relevantOprChar(tok.ident) == '@'
 
 proc isLeftAssociative(tok: TToken): bool {.inline.} =
+  ## Determines whether the token is left assocative.
   result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^'
 
 proc getPrecedence(tok: TToken, strongSpaces: bool): int =
+  ## Calculates the precedence of the given token.
   template considerStrongSpaces(x): expr =
     x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
 
@@ -224,22 +233,26 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
   else: result = -10
 
 proc isOperator(tok: TToken): bool =
+  ## Determines if the given token is an operator type token.
   tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
                   tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
 
 proc isUnary(p: TParser): bool =
+  ## Check if the current parser token is a unary operator
   p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and
     p.tok.strongSpaceB == 0 and
     p.tok.strongSpaceA > 0
 
 proc checkBinary(p: TParser) {.inline.} =
+  ## Check if the current parser token is a binary operator.
   # we don't check '..' here as that's too annoying
   if p.strongSpaces and p.tok.tokType == tkOpr:
     if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
-      parMessage(p, errGenerated, "number of spaces around '$#' not consistent"%
-        prettyTok(p.tok))
+      parMessage(p, errGenerated,
+                 "Number of spaces around '$#' not consistent" %
+                 prettyTok(p.tok))
     elif p.tok.strongSpaceA notin {0,1,2,4,8}:
-      parMessage(p, errGenerated, "number of spaces must be 0,1,2,4 or 8")
+      parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8")
 
 #| module = stmt ^* (';' / IND{=})
 #|
@@ -274,7 +287,7 @@ proc colcom(p: var TParser, n: PNode) =
   skipComment(p, n)
 
 proc parseSymbol(p: var TParser, allowNil = false): PNode =
-  #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
+  #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
   #|        | IDENT
   case p.tok.tokType
   of tkSymbol: 
@@ -285,31 +298,22 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
     getTok(p)
     while true:
       case p.tok.tokType
-      of tkBracketLe: 
-        add(result, newIdentNodeP(getIdent"[]", p))
-        getTok(p)
-        eat(p, tkBracketRi)
-      of tkEquals:
-        add(result, newIdentNodeP(getIdent"=", p))
-        getTok(p)
-      of tkParLe:
-        add(result, newIdentNodeP(getIdent"()", p))
-        getTok(p)
-        eat(p, tkParRi)
-      of tkCurlyLe:
-        add(result, newIdentNodeP(getIdent"{}", p))
-        getTok(p)
-        eat(p, tkCurlyRi)
-      of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
-        add(result, newIdentNodeP(p.tok.ident, p))
-        getTok(p)
-      of tkIntLit..tkCharLit:
-        add(result, newIdentNodeP(getIdent(tokToStr(p.tok)), p))
-        getTok(p)
-      else:
+      of tkAccent:
         if result.len == 0: 
           parMessage(p, errIdentifierExpected, p.tok)
         break
+      of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
+        var accm = ""
+        while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
+                                tkParLe..tkParDotRi}:
+          accm.add(tokToStr(p.tok))
+          getTok(p)
+        result.add(newIdentNodeP(getIdent(accm), p))
+      of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
+        result.add(newIdentNodeP(getIdent(tokToStr(p.tok)), p))
+        getTok(p)
+      else:
+        parMessage(p, errIdentifierExpected, p.tok)
     eat(p, tkAccent)
   else:
     if allowNil and p.tok.tokType == tkNil:
@@ -841,7 +845,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
     addSon(result, parseTypeDesc(p))
   else: 
     addSon(result, ast.emptyNode)
-    if (p.tok.tokType != tkEquals) and not (withBothOptional in flags): 
+    if p.tok.tokType != tkEquals and withBothOptional notin flags: 
       parMessage(p, errColonOrEqualsExpected, p.tok)
   if p.tok.tokType == tkEquals: 
     getTok(p)
@@ -982,6 +986,7 @@ proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
 
 proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
                        mode: TPrimaryMode): PNode =
+  #| distinct = 'distinct' optInd typeDesc
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
@@ -999,13 +1004,13 @@ proc parseExpr(p: var TParser): PNode =
   #| expr = (ifExpr
   #|       | whenExpr
   #|       | caseExpr
-  #|       | tryStmt)
+  #|       | tryExpr)
   #|       / simpleExpr
   case p.tok.tokType:
   of tkIf: result = parseIfExpr(p, nkIfExpr)
   of tkWhen: result = parseIfExpr(p, nkWhenExpr)
   of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p)
+  of tkTry: result = parseTry(p, isExpr=true)
   else: result = simpleExpr(p)
 
 proc parseEnum(p: var TParser): PNode
@@ -1108,6 +1113,7 @@ proc parseTypeDefAux(p: var TParser): PNode =
   result = simpleExpr(p, pmTypeDef)
 
 proc makeCall(n: PNode): PNode =
+  ## Creates a call if the given node isn't already a call.
   if n.kind in nkCallKinds:
     result = n
   else:
@@ -1357,22 +1363,25 @@ proc parseCase(p: var TParser): PNode =
   if wasIndented:
     p.currInd = oldInd
     
-proc parseTry(p: var TParser): PNode =
+proc parseTry(p: var TParser; isExpr: bool): PNode =
   #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
   #|            (IND{=}? 'except' exprList colcom stmt)*
   #|            (IND{=}? 'finally' colcom stmt)?
+  #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
+  #|            (optInd 'except' exprList colcom stmt)*
+  #|            (optInd 'finally' colcom stmt)?
   result = newNodeP(nkTryStmt, p)
   getTok(p)
   eat(p, tkColon)
   skipComment(p, result)
   addSon(result, parseStmt(p))
   var b: PNode = nil
-  while sameOrNoInd(p):
+  while sameOrNoInd(p) or isExpr:
     case p.tok.tokType
-    of tkExcept: 
+    of tkExcept:
       b = newNodeP(nkExceptBranch, p)
       exprList(p, tkColon, b)
-    of tkFinally: 
+    of tkFinally:
       b = newNodeP(nkFinally, p)
       getTokNoInd(p)
       eat(p, tkColon)
@@ -1871,7 +1880,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkIf: result = parseIfOrWhen(p, nkIfStmt)
   of tkWhile: result = parseWhile(p)
   of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p)
+  of tkTry: result = parseTry(p, isExpr=false)
   of tkFinally: result = parseExceptBlock(p, nkFinally)
   of tkExcept: result = parseExceptBlock(p, nkExceptBranch)
   of tkFor: result = parseFor(p)
@@ -1952,7 +1961,8 @@ proc parseStmt(p: var TParser): PNode =
           if p.tok.tokType != tkSemiColon: break
           getTok(p)
   
-proc parseAll(p: var TParser): PNode = 
+proc parseAll(p: var TParser): PNode =
+  ## Parses the rest of the input stream held by the parser into a PNode.
   result = newNodeP(nkStmtList, p)
   while p.tok.tokType != tkEof: 
     var a = complexOrSimpleStmt(p)
@@ -1966,6 +1976,8 @@ proc parseAll(p: var TParser): PNode =
       parMessage(p, errInvalidIndentation)
 
 proc parseTopLevelStmt(p: var TParser): PNode =
+  ## Implements an iterator which, when called repeatedly, returns the next
+  ## top-level statement or emptyNode if end of stream.
   result = ast.emptyNode
   while true:
     if p.tok.indent != 0: 
@@ -1984,6 +1996,10 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       break
 
 proc parseString(s: string, filename: string = "", line: int = 0): PNode =
+  ## Parses a string into an AST, returning the top node.
+  ## `filename` and `line`, although optional, provide info so that the
+  ## compiler can generate correct error messages referring to the original
+  ## source.
   var stream = llStreamOpen(s)
   stream.lineOffset = line
 
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index d262790ab..5e21289b5 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -287,7 +287,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
         # constraint not fullfilled:
         if not ok: return nil
 
-  markUsed(n, s)
+  markUsed(n.info, s)
   if ctx.subMatch:
     assert m.len == 3
     m.sons[1] = result
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index db9fe7cbe..a17773aa4 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -517,7 +517,7 @@ proc pragmaUses(c: PContext, n: PNode) =
   proc processExc(c: PContext, x: PNode): PNode =
     if x.kind in {nkAccQuoted, nkIdent, nkSym,
                   nkOpenSymChoice, nkClosedSymChoice}:
-      if considerAcc(x).s == "*":
+      if considerQuotedIdent(x).s == "*":
         return newSymNode(ast.anyGlobal)
     result = c.semExpr(c, x)
     if result.kind != nkSym or sfGlobal notin result.sym.flags:
@@ -644,12 +644,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           incl(sym.flags, sfNoReturn)
         of wDynlib: 
           processDynLib(c, it, sym)
-        of wCompilerproc: 
+        of wCompilerproc:
           noVal(it)           # compilerproc may not get a string!
-          makeExternExport(sym, "$1", it.info)
-          incl(sym.flags, sfCompilerProc)
-          incl(sym.flags, sfUsed) # suppress all those stupid warnings
-          registerCompilerProc(sym)
+          if sfFromGeneric notin sym.flags:
+            makeExternExport(sym, "$1", it.info)
+            incl(sym.flags, sfCompilerProc)
+            incl(sym.flags, sfUsed) # suppress all those stupid warnings
+            registerCompilerProc(sym)
         of wProcVar: 
           noVal(it)
           incl(sym.flags, sfProcvar)
diff --git a/compiler/pretty.nim b/compiler/pretty.nim
index 3a5bfe197..17311f9e6 100644
--- a/compiler/pretty.nim
+++ b/compiler/pretty.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -149,8 +149,8 @@ proc checkDef(c: PGen; n: PNode) =
   if n.kind != nkSym: return
   checkDef(n, n.sym)
 
-proc checkUse*(n: PNode, s: PSym) =
-  if n.info.fileIndex < 0: return
+proc checkUse*(info: TLineInfo; s: PSym) =
+  if info.fileIndex < 0: return
   # we simply convert it to what it looks like in the definition
   # for consistency
   
@@ -159,10 +159,10 @@ proc checkUse*(n: PNode, s: PSym) =
   if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
   let newName = s.name.s
   
-  loadFile(n.info)
+  loadFile(info)
   
-  let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1]
-  var first = min(n.info.col.int, line.len)
+  let line = gSourceFiles[info.fileIndex].lines[info.line-1]
+  var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
   while first > 0 and line[first-1] in Letters: dec first
@@ -179,8 +179,8 @@ proc checkUse*(n: PNode, s: PSym) =
       if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"):
         x = ""
     
-    system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x)
-    gSourceFiles[n.info.fileIndex].dirty = true
+    system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
+    gSourceFiles[info.fileIndex].dirty = true
 
 when false:
   var cannotRename = initIntSet()
@@ -220,53 +220,9 @@ when false:
         result.add s[i]
       inc i
 
-  proc checkUse(c: PGen; n: PNode) =
-    if n.info.fileIndex < 0: return
-    let s = n.sym
-    # operators stay as they are:
-    if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return
-    if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
-    
-    if s.id in cannotRename: return
-    
-    let newName = if rules.hasKey(s.name.s): rules[s.name.s]
-                  else: beautifyName(s.name.s, n.sym.kind)
-    
-    loadFile(n.info)
-    
-    let line = gSourceFiles[n.info.fileIndex].lines[n.info.line-1]
-    var first = min(n.info.col.int, line.len)
-    if first < 0: return
-    #inc first, skipIgnoreCase(line, "proc ", first)
-    while first > 0 and line[first-1] in Letters: dec first
-    if first < 0: return
-    if line[first] == '`': inc first
-    
-    if {sfImportc, sfExportc} * s.flags != {}:
-      # careful, we must ensure the resulting name still matches the external
-      # name:
-      if newName != s.name.s and newName != s.loc.r.ropeToStr and
-          lfFullExternalName notin s.loc.flags:
-        #Message(n.info, errGenerated, 
-        #  "cannot rename $# to $# due to external name" % [s.name.s, newName])
-        cannotRename.incl(s.id)
-        return
-    let last = first+identLen(line, first)-1
-    if differ(line, first, last, newName):
-      # last-first+1 != newName.len or 
-      var x = line.subStr(0, first-1) & newName & line.substr(last+1)
-      when removeTP:
-        # the WinAPI module is full of 'TX = X' which after the substitution
-        # becomes 'X = X'. We remove those lines:
-        if x.match(peg"\s* {\ident} \s* '=' \s* y$1 ('#' .*)?"):
-          x = ""
-      
-      system.shallowCopy(gSourceFiles[n.info.fileIndex].lines[n.info.line-1], x)
-      gSourceFiles[n.info.fileIndex].dirty = true
-
 proc check(c: PGen, n: PNode) =
   case n.kind
-  of nkSym: checkUse(n, n.sym)
+  of nkSym: checkUse(n.info, n.sym)
   of nkBlockStmt, nkBlockExpr, nkBlockType:
     checkDef(c, n[0])
     check(c, n.sons[1])
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 6b62c48c5..0b1312ccc 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -146,28 +146,29 @@ proc makeNimString(s: string): string =
   for i in countup(0, len(s)-1): add(result, toNimChar(s[i]))
   add(result, '\"')
 
-proc putComment(g: var TSrcGen, s: string) = 
+proc putComment(g: var TSrcGen, s: string) =
+  if s.isNil: return
   var i = 0
   var comIndent = 1
   var isCode = (len(s) >= 2) and (s[1] != ' ')
   var ind = g.lineLen
   var com = ""
-  while true: 
+  while true:
     case s[i]
-    of '\0': 
-      break 
-    of '\x0D': 
+    of '\0':
+      break
+    of '\x0D':
       put(g, tkComment, com)
       com = ""
       inc(i)
       if s[i] == '\x0A': inc(i)
       optNL(g, ind)
-    of '\x0A': 
+    of '\x0A':
       put(g, tkComment, com)
       com = ""
       inc(i)
       optNL(g, ind)
-    of '#': 
+    of '#':
       add(com, s[i])
       inc(i)
       comIndent = 0
@@ -175,10 +176,10 @@ proc putComment(g: var TSrcGen, s: string) =
         add(com, s[i])
         inc(i)
         inc(comIndent)
-    of ' ', '\x09': 
+    of ' ', '\x09':
       add(com, s[i])
       inc(i)
-    else: 
+    else:
       # we may break the comment into a multi-line comment if the line
       # gets too long:
       # compute length of the following word:
@@ -195,10 +196,10 @@ proc putComment(g: var TSrcGen, s: string) =
   optNL(g)
 
 proc maxLineLength(s: string): int = 
-  result = 0
+  if s.isNil: return 0
   var i = 0
   var lineLen = 0
-  while true: 
+  while true:
     case s[i]
     of '\0': 
       break 
@@ -459,7 +460,7 @@ proc lsub(n: PNode): int =
   of nkBreakStmt: result = lsub(n.sons[0]) + len("break_")
   of nkContinueStmt: result = lsub(n.sons[0]) + len("continue_")
   of nkPragma: result = lcomma(n) + 4
-  of nkCommentStmt: result = len(n.comment)
+  of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
   of nkOfBranch: result = lcomma(n, 0, - 2) + lsub(lastSon(n)) + len("of_:_")
   of nkImportAs: result = lsub(n.sons[0]) + len("_as_") + lsub(n.sons[1])
   of nkElifBranch: result = lsons(n) + len("elif_:_")
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 4433ed4ab..09b92cd8a 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -10,7 +10,7 @@
 ## Serialization utilities for the compiler.
 import strutils
 
-proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.}
+proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
 
 proc toStrMaxPrecision*(f: BiggestFloat): string = 
   if f != f:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 7d129caf4..8025ef70d 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -15,7 +15,8 @@ import
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
-  evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity
+  evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity,
+  semparallel
 
 # implementation
 
@@ -134,7 +135,7 @@ proc isTopLevel(c: PContext): bool {.inline.} =
   result = c.currentScope.depthLevel <= 2
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = 
-  result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
+  result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
   # like newSymS, but considers gensym'ed symbols
@@ -147,7 +148,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # template; we must fix it here: see #909
     result.owner = getCurrOwner()
   else:
-    result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
+    result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
@@ -268,11 +269,15 @@ include hlo, seminst, semcall
 
 proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
                        flags: TExprFlags): PNode =
+  ## Semantically check the output of a macro.
+  ## This involves processes such as re-checking the macro output for type
+  ## coherence, making sure that variables declared with 'let' aren't
+  ## reassigned, and binding the unbound identifiers that the macro output
+  ## contains.
   inc(evalTemplateCounter)
   if evalTemplateCounter > 100:
     globalError(s.info, errTemplateInstantiationTooNested)
-  let oldFriend = c.friendModule
-  c.friendModule = s.owner.getModule
+  c.friendModules.add(s.owner.getModule)
 
   result = n
   if s.typ.sons[0] == nil:
@@ -296,11 +301,13 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
       result = fitNode(c, s.typ.sons[0], result)
       #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
   dec(evalTemplateCounter)
-  c.friendModule = oldFriend
+  discard c.friendModules.pop()
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode =
-  markUsed(n, sym)
+  pushInfoContext(nOrig.info)
+
+  markUsed(n.info, sym)
   if sym == c.p.owner:
     globalError(n.info, errRecursiveDependencyX, sym.name.s)
 
@@ -310,6 +317,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   result = evalMacroCall(c.module, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, result, sym, flags)
+  popInfoContext()
 
 proc forceBool(c: PContext, n: PNode): PNode = 
   result = fitNode(c, getSysType(tyBool), n)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 04d280ce2..65a2d7ab8 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -168,7 +168,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       pickBest(callOp)
    
     if overloadsState == csEmpty and result.state == csEmpty:
-      localError(n.info, errUndeclaredIdentifier, considerAcc(f).s)
+      localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -251,7 +251,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
 proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
-  markUsed(n.sons[0], finalCallee)
+  markUsed(n.sons[0].info, finalCallee)
   if finalCallee.ast == nil:
     internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
   if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
@@ -283,7 +283,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
   var m: TCandidate
   initCandidate(c, m, s, n)
   var newInst = generateInstance(c, s, m.bindings, n.info)
-  markUsed(n, s)
+  markUsed(n.info, s)
   result = newSymNode(newInst, n.info)
 
 proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 987a70a41..abecc1b6d 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -52,7 +52,7 @@ type
     importTable*: PScope       # scope for all imported symbols
     topLevelScope*: PScope     # scope for all top-level symbols
     p*: PProcCon               # procedure context
-    friendModule*: PSym        # current friend module; may access private data;
+    friendModules*: seq[PSym]  # friend modules; may access private data;
                                # this is used so that generic instantiations
                                # can access private object fields
     instCounter*: int          # to prevent endless instantiations
@@ -91,6 +91,7 @@ type
     generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
     lastGenericIdx*: int      # used for the generics stack
     hloLoopDetector*: int     # used to prevent endless loops in the HLO
+    inParallelStmt*: int
    
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
   result.genericSym = s
@@ -168,7 +169,7 @@ proc newContext(module: PSym): PContext =
   initLinkedList(result.libs)
   append(result.optionStack, newOptionEntry())
   result.module = module
-  result.friendModule = module
+  result.friendModules = @[module]
   result.converters = @[]
   result.patterns = @[]
   result.includedFiles = initIntSet()
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 652e1dd4b..7f97124e1 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -12,7 +12,7 @@
 
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
-  markUsed(n, s)
+  markUsed(n.info, s)
   pushInfoContext(n.info)
   result = evalTemplate(n, s, getCurrOwner())
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
@@ -78,7 +78,7 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
 proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 
   case s.kind
   of skConst:
-    markUsed(n, s)
+    markUsed(n.info, s)
     case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
     of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, 
         tyTuple, tySet, tyUInt..tyUInt64:
@@ -101,7 +101,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of skMacro: result = semMacroExpr(c, n, n, s, flags)
   of skTemplate: result = semTemplateExpr(c, n, s, flags)
   of skVar, skLet, skResult, skParam, skForVar:
-    markUsed(n, s)
+    markUsed(n.info, s)
     # if a proc accesses a global variable, it is not side effect free:
     if sfGlobal in s.flags:
       incl(c.p.owner.flags, sfSideEffect)
@@ -123,13 +123,13 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       n.typ = s.typ
       return n
   of skType:
-    markUsed(n, s)
+    markUsed(n.info, s)
     if s.typ.kind == tyStatic and s.typ.n != nil:
       return s.typ.n
     result = newSymNode(s, n.info)
     result.typ = makeTypeDesc(c, s.typ)
   else:
-    markUsed(n, s)
+    markUsed(n.info, s)
     result = newSymNode(s, n.info)
 
 type
@@ -166,6 +166,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
   elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and
       (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes):
     # accept conversion between integral types
+    discard
   else:
     # we use d, s here to speed up that operation a bit:
     case cmpTypes(c, d, s)
@@ -175,21 +176,26 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
     else:
       discard
 
-proc isCastable(dst, src: PType): bool = 
+proc isCastable(dst, src: PType): bool =
+  ## Checks whether the source type can be casted to the destination type.
+  ## Casting is very unrestrictive; casts are allowed as long as 
+  ## castDest.size >= src.size, and typeAllowed(dst, skParam)
   #const
   #  castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, 
   #                       tySequence, tyPointer, tyNil, tyOpenArray,
   #                       tyProc, tySet, tyEnum, tyBool, tyChar}
-  var ds, ss: BiggestInt
-  # this is very unrestrictive; cast is allowed if castDest.size >= src.size
-  ds = computeSize(dst)
-  ss = computeSize(src)
-  if ds < 0: 
+  var dstSize, srcSize: BiggestInt
+
+  dstSize = computeSize(dst)
+  srcSize = computeSize(src)
+  if dstSize < 0: 
     result = false
-  elif ss < 0: 
+  elif srcSize < 0: 
+    result = false
+  elif not typeAllowed(dst, skParam):
     result = false
   else: 
-    result = (ds >= ss) or
+    result = (dstSize >= srcSize) or
         (skipTypes(dst, abstractInst).kind in IntegralTypes) or
         (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
   
@@ -247,12 +253,13 @@ proc semConv(c: PContext, n: PNode): PNode =
       let it = op.sons[i]
       let status = checkConvertible(c, result.typ, it.typ)
       if status in {convOK, convNotNeedeed}:
-        markUsed(n, it.sym)
+        markUsed(n.info, it.sym)
         markIndirect(c, it.sym)
         return it
     localError(n.info, errUseQualifier, op.sons[0].sym.name.s)
 
 proc semCast(c: PContext, n: PNode): PNode = 
+  ## Semantically analyze a casting ("cast[type](param)")
   if optSafeCode in gGlobalOptions: localError(n.info, errCastNotInSafeMode)
   #incl(c.p.owner.flags, sfSideEffect)
   checkSonsLen(n, 2)
@@ -386,7 +393,7 @@ proc semOpAux(c: PContext, n: PNode) =
     var a = n.sons[i]
     if a.kind == nkExprEqExpr and sonsLen(a) == 2: 
       var info = a.sons[0].info
-      a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info)
+      a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
       a.sons[1] = semExprWithType(c, a.sons[1], flags)
       a.typ = a.sons[1].typ
     else:
@@ -785,6 +792,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n.flags.incl nfExprCall
       result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
       if result == nil: return errorNode(c, n)
+    elif result.kind notin nkCallKinds:
+      # the semExpr() in overloadedCallOpr can even break this condition!
+      # See bug #904 of how to trigger it:
+      return result
   #result = afterCallActions(c, result, nOrig, flags)
   fixAbstractType(c, result)
   analyseIfAddressTakenInCall(c, result)
@@ -964,12 +975,12 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
   if s != nil:
-    markUsed(n.sons[1], s)
+    markUsed(n.sons[1].info, s)
     return semSym(c, n, s, flags)
 
   n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
   #restoreOldStyleType(n.sons[0])
-  var i = considerAcc(n.sons[1])
+  var i = considerQuotedIdent(n.sons[1])
   var ty = n.sons[0].typ
   var f: PSym = nil
   result = nil
@@ -987,7 +998,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         result = newSymNode(f)
         result.info = n.info
         result.typ = ty
-        markUsed(n, f)
+        markUsed(n.info, f)
         return
     of tyTypeParamsHolders:
       return readTypeParameter(c, ty, i, n.info)
@@ -1019,7 +1030,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if f != nil:
       if fieldVisible(c, f):
         # is the access to a public field or in the same module or in a friend?
-        markUsed(n.sons[1], f)
+        markUsed(n.sons[1].info, f)
         n.sons[0] = makeDeref(n.sons[0])
         n.sons[1] = newSymNode(f) # we now have the correct field
         n.typ = f.typ
@@ -1032,7 +1043,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   elif ty.kind == tyTuple and ty.n != nil: 
     f = getSymFromList(ty.n, i)
     if f != nil:
-      markUsed(n.sons[1], f)
+      markUsed(n.sons[1].info, f)
       n.sons[0] = makeDeref(n.sons[0])
       n.sons[1] = newSymNode(f)
       n.typ = f.typ
@@ -1050,7 +1061,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
     addSon(result, n.sons[1])
     addSon(result, copyTree(n[0]))
   else:
-    var i = considerAcc(n.sons[1])
+    var i = considerQuotedIdent(n.sons[1])
     result = newNodeI(nkDotCall, n.info)
     result.flags.incl nfDotField
     addSon(result, newIdentNode(i, n[1].info))
@@ -1134,7 +1145,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
-  var id = considerAcc(a[1])
+  var id = considerQuotedIdent(a[1])
   var setterId = newIdentNode(getIdent(id.s & '='), n.info)
   # a[0] is already checked for semantics, that does ``builtinFieldAccess``
   # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
@@ -1368,7 +1379,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
       else: 
         localError(n.sons[1].info, errIdentifierExpected, "")
   of nkAccQuoted:
-    result = lookUpForDefined(c, considerAcc(n), onlyCurrentScope)
+    result = lookUpForDefined(c, considerQuotedIdent(n), onlyCurrentScope)
   of nkSym:
     result = n.sym
   else: 
@@ -1387,11 +1398,6 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
   result.info = n.info
   result.typ = getSysType(tyBool)
 
-proc setMs(n: PNode, s: PSym): PNode = 
-  result = n
-  n.sons[0] = newSymNode(s)
-  n.sons[0].info = n.info
-
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
   ## The argument to the proc should be nkCall(...) or similar
   ## Returns the macro/template symbol
@@ -1448,7 +1454,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
   if expandedSym.kind == skError: return n
 
   macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
-  markUsed(n, expandedSym)
+  markUsed(n.info, expandedSym)
 
   for i in countup(1, macroCall.len-1):
     macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
@@ -1583,6 +1589,27 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     result = semDirectOp(c, n, flags)
 
+proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
+  result = newType(tyGenericInvokation, c.module)
+  addSonSkipIntLit(result, magicsys.getCompilerProc("FlowVar").typ)
+  addSonSkipIntLit(result, t)
+  result = instGenericContainer(c, info, result, allowMetaTypes = false)
+
+proc instantiateCreateFlowVarCall(c: PContext; t: PType;
+                                  info: TLineInfo): PSym =
+  let sym = magicsys.getCompilerProc("nimCreateFlowVar")
+  if sym == nil:
+    localError(info, errSystemNeeds, "nimCreateFlowVar")
+  var bindings: TIdTable 
+  initIdTable(bindings)
+  bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
+  result = c.semGenerateInstance(c, sym, bindings, info)
+
+proc setMs(n: PNode, s: PSym): PNode = 
+  result = n
+  n.sons[0] = newSymNode(s)
+  n.sons[0].info = n.info
+
 proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 
   # this is a hotspot in the compiler!
   # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
@@ -1604,6 +1631,22 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     checkSonsLen(n, 2)
     result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
     result.typ = getSysType(tyString)
+  of mParallel:
+    result = setMs(n, s)
+    var x = n.lastSon
+    if x.kind == nkDo: x = x.sons[bodyPos]
+    inc c.inParallelStmt
+    result.sons[1] = semStmt(c, x)
+    dec c.inParallelStmt
+  of mSpawn:
+    result = setMs(n, s)
+    result.sons[1] = semExpr(c, n.sons[1])
+    if not result[1].typ.isEmptyType:
+      if c.inParallelStmt > 0:
+        result.typ = result[1].typ
+      else:
+        result.typ = createFlowVar(c, result[1].typ, n.info)
+      result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode
   else: result = semDirectOp(c, n, flags)
 
 proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
@@ -1842,7 +1885,7 @@ proc semBlock(c: PContext, n: PNode): PNode =
     if sfGenSym notin labl.flags:
       addDecl(c, labl)
       n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0], labl)
+    suggestSym(n.sons[0].info, labl)
   n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
   if isEmptyType(n.typ): n.kind = nkBlockStmt
@@ -1962,7 +2005,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     var s = qualifiedLookUp(c, n.sons[0], mode)
     if s != nil: 
       if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
-        pretty.checkUse(n.sons[0].sons[1], s)
+        pretty.checkUse(n.sons[0].sons[1].info, s)
       case s.kind
       of skMacro:
         if sfImmediate notin s.flags:
@@ -2060,6 +2103,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkClosedSymChoice, nkOpenSymChoice:
     # handling of sym choices is context dependent
     # the node is left intact for now
+    discard
   of nkStaticExpr:
     result = semStaticExpr(c, n)
   of nkAsgn: result = semAsgn(c, n)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 79abfaf4d..30e02dcc9 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -380,6 +380,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mInSet: result = newIntNodeT(ord(inSet(a, b)), n)
   of mRepr:
     # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
+    discard
   of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
   of mBoolToStr: 
     if getOrdValue(a) == 0: result = newStrNodeT("false", n)
@@ -404,10 +405,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
      mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
      mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
-     mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn:
+     mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, mParallel:
     discard
-  of mRand:
-    result = newIntNodeT(math.random(a.getInt.int), n)
   else: internalError(a.info, "evalOp(" & $m & ')')
   
 proc getConstIfExpr(c: PSym, n: PNode): PNode = 
@@ -538,17 +537,18 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
   var idx = getOrdValue(y)
   case x.kind
   of nkPar: 
-    if (idx >= 0) and (idx < sonsLen(x)): 
+    if idx >= 0 and idx < sonsLen(x):
       result = x.sons[int(idx)]
       if result.kind == nkExprColonExpr: result = result.sons[1]
     else:
       localError(n.info, errIndexOutOfBounds)
-  of nkBracket: 
-    if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)]
+  of nkBracket:
+    idx = idx - x.typ.firstOrd
+    if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
     else: localError(n.info, errIndexOutOfBounds)
-  of nkStrLit..nkTripleStrLit: 
+  of nkStrLit..nkTripleStrLit:
     result = newNodeIT(nkCharLit, x.info, n.typ)
-    if (idx >= 0) and (idx < len(x.strVal)): 
+    if idx >= 0 and idx < len(x.strVal): 
       result.intVal = ord(x.strVal[int(idx)])
     elif idx == len(x.strVal): 
       discard
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index da4a2a132..934434951 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -69,7 +69,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
             ctx: var TIntSet): PNode =
   result = n
-  let ident = considerAcc(n)
+  let ident = considerQuotedIdent(n)
   var s = searchInScopes(c, ident)
   if s == nil:
     if ident.id notin ctx and withinMixin notin flags:
@@ -82,7 +82,37 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     else:
       result = semGenericStmtSymbol(c, n, s)
   # else: leave as nkIdent
+
+proc newDot(n, b: PNode): PNode =
+  result = newNodeI(nkDotExpr, n.info)
+  result.add(n.sons[0])
+  result.add(b)
+
+proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
+                 ctx: var TIntSet): PNode =
+  assert n.kind == nkDotExpr
+  let luf = if withinMixin notin flags: {checkUndeclared} else: {}
   
+  var s = qualifiedLookUp(c, n, luf)
+  if s != nil:
+    result = semGenericStmtSymbol(c, n, s)
+  else:
+    result = n
+    let n = n[1]
+    let ident = considerQuotedIdent(n)
+    var s = searchInScopes(c, ident)
+    if s != nil and s.kind in routineKinds:
+      if withinBind in flags:
+        result = newDot(result, symChoice(c, n, s, scClosed))
+      elif s.name.id in ctx:
+        result = newDot(result, symChoice(c, n, s, scForceOpen))
+      else:
+        let sym = semGenericStmtSymbol(c, n, s)
+        if sym.kind == nkSym:
+          result = newDot(result, symChoice(c, n, s, scForceOpen))
+        else:
+          result = newDot(result, sym)
+
 proc semGenericStmt(c: PContext, n: PNode, 
                     flags: TSemGenericFlags, ctx: var TIntSet): PNode =
   result = n
@@ -91,10 +121,11 @@ proc semGenericStmt(c: PContext, n: PNode,
   of nkIdent, nkAccQuoted:
     result = lookup(c, n, flags, ctx)
   of nkDotExpr:
-    let luf = if withinMixin notin flags: {checkUndeclared} else: {}
-    var s = qualifiedLookUp(c, n, luf)
-    if s != nil: result = semGenericStmtSymbol(c, n, s)
+    #let luf = if withinMixin notin flags: {checkUndeclared} else: {}
+    #var s = qualifiedLookUp(c, n, luf)
+    #if s != nil: result = semGenericStmtSymbol(c, n, s)
     # XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
+    result = fuzzyLookup(c, n, flags, ctx)
   of nkEmpty, nkSym..nkNilLit:
     # see tests/compile/tgensymgeneric.nim:
     # We need to open the gensym'ed symbol again so that the instantiation
@@ -114,7 +145,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     let fn = n.sons[0]
     var s = qualifiedLookUp(c, fn, {})
     if s == nil and withinMixin notin flags and
-        fn.kind in {nkIdent, nkAccQuoted} and considerAcc(fn).id notin ctx:
+        fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx:
       localError(n.info, errUndeclaredIdentifier, fn.renderTree)
     
     var first = 0
@@ -141,6 +172,7 @@ proc semGenericStmt(c: PContext, n: PNode,
         # symbol lookup ...
       of skUnknown, skParam: 
         # Leave it as an identifier.
+        discard
       of skProc, skMethod, skIterators, skConverter:
         result.sons[0] = symChoice(c, n.sons[0], s, scOption)
         first = 1
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index f7d5fa6f8..b93d7ca15 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -190,6 +190,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
 
 proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
                       info: TLineInfo): PSym =
+  ## Generates a new instance of a generic procedure.
+  ## The `pt` parameter is a type-unsafe mapping table used to link generic
+  ## parameters to their concrete types within the generic instance.
   # no need to instantiate generic templates/macros:
   if fn.kind in {skTemplate, skMacro}: return fn
   # generates an instantiated proc
@@ -199,8 +202,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
-  var oldFriend = c.friendModule
-  c.friendModule = getModule(fn)
+  c.friendModules.add(getModule(fn))
   #let oldScope = c.currentScope
   #c.currentScope = fn.scope
   result = copySym(fn, false)
@@ -236,6 +238,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   closeScope(c)           # close scope for parameters
   popOwner()
   #c.currentScope = oldScope
-  c.friendModule = oldFriend
+  discard c.friendModules.pop()
   dec(c.instCounter)
   if result.kind == skMethod: finishMethod(c, result)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 4caf1fb8e..f943e7006 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -131,4 +131,3 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mNBindSym: result = semBindSym(c, n)
   of mLocals: result = semLocals(c, n)
   else: result = n
-
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
new file mode 100644
index 000000000..c594a4788
--- /dev/null
+++ b/compiler/semparallel.nim
@@ -0,0 +1,465 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2014 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Semantic checking for 'parallel'.
+
+# - codegen needs to support mSlice (+)
+# - lowerings must not perform unnecessary copies (+)
+# - slices should become "nocopy" to openArray (+)
+#   - need to perform bound checks (+)
+#
+# - parallel needs to insert a barrier (+)
+# - passed arguments need to be ensured to be "const"
+#   - what about 'f(a)'? --> f shouldn't have side effects anyway
+# - passed arrays need to be ensured not to alias
+# - passed slices need to be ensured to be disjoint (+)
+# - output slices need special logic (+)
+
+import
+  ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
+  renderer
+from trees import getMagic
+from strutils import `%`
+
+discard """
+
+one major problem:
+  spawn f(a[i])
+  inc i
+  spawn f(a[i])
+is valid, but
+  spawn f(a[i])
+  spawn f(a[i])
+  inc i
+is not! However, 
+  spawn f(a[i])
+  if guard: inc i
+  spawn f(a[i])
+is not valid either! --> We need a flow dependent analysis here.
+
+However:
+  while foo:
+    spawn f(a[i])
+    inc i
+    spawn f(a[i])
+
+Is not valid either! --> We should really restrict 'inc' to loop endings?
+
+The heuristic that we implement here (that has no false positives) is: Usage
+of 'i' in a slice *after* we determined the stride is invalid!
+"""
+
+type
+  TDirection = enum
+    ascending, descending
+  MonotonicVar = object
+    v, alias: PSym        # to support the ordinary 'countup' iterator
+                          # we need to detect aliases
+    lower, upper, stride: PNode
+    dir: TDirection
+    blacklisted: bool     # blacklisted variables that are not monotonic
+  AnalysisCtx = object
+    locals: seq[MonotonicVar]
+    slices: seq[tuple[x,a,b: PNode, spawnId: int, inLoop: bool]]
+    guards: TModel      # nested guards
+    args: seq[PSym]     # args must be deeply immutable
+    spawns: int         # we can check that at last 1 spawn is used in
+                        # the 'parallel' section
+    currentSpawnId: int
+    inLoop: int
+
+let opSlice = createMagic("slice", mSlice)
+
+proc initAnalysisCtx(): AnalysisCtx =
+  result.locals = @[]
+  result.slices = @[]
+  result.args = @[]
+  result.guards = @[]
+
+proc lookupSlot(c: AnalysisCtx; s: PSym): int =
+  for i in 0.. <c.locals.len:
+    if c.locals[i].v == s or c.locals[i].alias == s: return i
+  return -1
+
+proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar =
+  let s = lookupSlot(c, v)
+  if s >= 0: return addr(c.locals[s])
+  let L = c.locals.len
+  c.locals.setLen(L+1)
+  c.locals[L].v = v
+  return addr(c.locals[L])
+
+proc gatherArgs(c: var AnalysisCtx; n: PNode) =
+  for i in 0.. <n.safeLen:
+    let root = getRoot n[i]
+    if root != nil:
+      block addRoot:
+        for r in items(c.args):
+          if r == root: break addRoot
+        c.args.add root
+    gatherArgs(c, n[i])
+
+proc isSingleAssignable(n: PNode): bool =
+  n.kind == nkSym and (let s = n.sym;
+    s.kind in {skTemp, skForVar, skLet} and
+          {sfAddrTaken, sfGlobal} * s.flags == {})
+
+proc isLocal(n: PNode): bool =
+  n.kind == nkSym and (let s = n.sym;
+    s.kind in {skResult, skTemp, skForVar, skVar, skLet} and
+          {sfAddrTaken, sfGlobal} * s.flags == {})
+
+proc checkLocal(c: AnalysisCtx; n: PNode) =
+  if isLocal(n):
+    let s = c.lookupSlot(n.sym)
+    if s >= 0 and c.locals[s].stride != nil:
+      localError(n.info, "invalid usage of counter after increment")
+  else:
+    for i in 0 .. <n.safeLen: checkLocal(c, n.sons[i])
+
+template `?`(x): expr = x.renderTree
+
+proc checkLe(c: AnalysisCtx; a, b: PNode) =
+  case proveLe(c.guards, a, b)
+  of impUnknown:
+    localError(a.info, "cannot prove: " & ?a & " <= " & ?b)
+  of impYes: discard
+  of impNo:
+    localError(a.info, "can prove: " & ?a & " > " & ?b)
+
+proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
+  checkLe(c, arr.lowBound, idx)
+  checkLe(c, idx, arr.highBound)
+
+proc addLowerBoundAsFacts(c: var AnalysisCtx) =
+  for v in c.locals:
+    if not v.blacklisted:
+      c.guards.addFactLe(v.lower, newSymNode(v.v))
+
+proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
+  checkLocal(c, n)
+  let le = le.canon
+  let ri = ri.canon
+  # perform static bounds checking here; and not later!
+  let oldState = c.guards.len
+  addLowerBoundAsFacts(c)
+  c.checkBounds(x, le)
+  c.checkBounds(x, ri)
+  c.guards.setLen(oldState)
+  c.slices.add((x, le, ri, c.currentSpawnId, c.inLoop > 0))
+
+proc overlap(m: TModel; x,y,c,d: PNode) =
+  #  X..Y and C..D overlap iff (X <= D and C <= Y)
+  case proveLe(m, x, d)
+  of impUnknown:
+    localError(x.info,
+      "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
+        [?x, ?d, ?x, ?y, ?c, ?d])
+  of impYes:
+    case proveLe(m, c, y)
+    of impUnknown:
+      localError(x.info,
+        "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
+          [?c, ?y, ?x, ?y, ?c, ?d])
+    of impYes:
+      localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d])
+    of impNo: discard
+  of impNo: discard
+
+proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
+  if isLocal(n):
+    let s = c.lookupSlot(n.sym)
+    if s >= 0 and c.locals[s].stride != nil:
+      result = c.locals[s].stride.intVal
+  else:
+    for i in 0 .. <n.safeLen: result += stride(c, n.sons[i])
+
+proc subStride(c: AnalysisCtx; n: PNode): PNode =
+  # substitute with stride:
+  if isLocal(n):
+    let s = c.lookupSlot(n.sym)
+    if s >= 0 and c.locals[s].stride != nil:
+      result = n +@ c.locals[s].stride.intVal
+    else:
+      result = n
+  elif n.safeLen > 0:
+    result = shallowCopy(n)
+    for i in 0 .. <n.len: result.sons[i] = subStride(c, n.sons[i])
+  else:
+    result = n
+
+proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
+  # this is the only thing that we need to perform after we have traversed
+  # the whole tree so that the strides are available.
+  # First we need to add all the computed lower bounds:
+  addLowerBoundAsFacts(c)
+  # Every slice used in a loop needs to be disjoint with itself:
+  for x,a,b,id,inLoop in items(c.slices):
+    if inLoop: overlap(c.guards, a,b, c.subStride(a), c.subStride(b))
+  # Another tricky example is:
+  #   while true:
+  #     spawn f(a[i])
+  #     spawn f(a[i+1])
+  #     inc i  # inc i, 2  would be correct here
+  #
+  # Or even worse:
+  #   while true:
+  #     spawn f(a[i+1 .. i+3])
+  #     spawn f(a[i+4 .. i+5])
+  #     inc i, 4
+  # Prove that i*k*stride + 3 != i*k'*stride + 5
+  # For the correct example this amounts to
+  #   i*k*2 != i*k'*2 + 1
+  # which is true.
+  # For now, we don't try to prove things like that at all, even though it'd
+  # be feasible for many useful examples. Instead we attach the slice to
+  # a spawn and if the attached spawns differ, we bail out:
+  for i in 0 .. high(c.slices):
+    for j in i+1 .. high(c.slices):
+      let x = c.slices[i]
+      let y = c.slices[j]
+      if x.spawnId != y.spawnId and guards.sameTree(x.x, y.x):
+        if not x.inLoop or not y.inLoop:
+          # XXX strictly speaking, 'or' is not correct here and it needs to
+          # be 'and'. However this prevents too many obviously correct programs
+          # like f(a[0..x]); for i in x+1 .. a.high: f(a[i])
+          overlap(c.guards, x.a, x.b, y.a, y.b)
+        elif (let k = simpleSlice(x.a, x.b); let m = simpleSlice(y.a, y.b);
+              k >= 0 and m >= 0):
+          # ah I cannot resist the temptation and add another sweet heuristic:
+          # if both slices have the form (i+k)..(i+k)  and (i+m)..(i+m) we
+          # check they are disjoint and k < stride and m < stride:
+          overlap(c.guards, x.a, x.b, y.a, y.b)
+          let stride = min(c.stride(x.a), c.stride(y.a))
+          if k < stride and m < stride:
+            discard
+          else:
+            localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
+              [?x.a, ?x.b, ?y.a, ?y.b])
+        else:
+          localError(x.x.info, "cannot prove ($#)..($#) disjoint from ($#)..($#)" %
+            [?x.a, ?x.b, ?y.a, ?y.b])
+
+proc analyse(c: var AnalysisCtx; n: PNode)
+
+proc analyseSons(c: var AnalysisCtx; n: PNode) =
+  for i in 0 .. <safeLen(n): analyse(c, n[i])
+
+proc min(a, b: PNode): PNode =
+  if a.isNil: result = b
+  elif a.intVal < b.intVal: result = a
+  else: result = b
+
+proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
+
+proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
+  if op.magic == mSpawn:
+    inc c.spawns
+    let oldSpawnId = c.currentSpawnId
+    c.currentSpawnId = c.spawns
+    gatherArgs(c, n[1])
+    analyseSons(c, n)
+    c.currentSpawnId = oldSpawnId
+  elif op.magic == mInc or (op.name.s == "+=" and op.fromSystem):
+    if n[1].isLocal:
+      let incr = n[2].skipConv
+      if incr.kind in {nkCharLit..nkUInt32Lit} and incr.intVal > 0:
+        let slot = c.getSlot(n[1].sym)
+        slot.stride = min(slot.stride, incr)
+    analyseSons(c, n)
+  elif op.name.s == "[]" and op.fromSystem:
+    c.addSlice(n, n[1], n[2][1], n[2][2])
+    analyseSons(c, n)
+  elif op.name.s == "[]=" and op.fromSystem:
+    c.addSlice(n, n[1], n[2][1], n[2][2])
+    analyseSons(c, n)
+  else:
+    analyseSons(c, n)
+
+proc analyseCase(c: var AnalysisCtx; n: PNode) =
+  analyse(c, n.sons[0])
+  let oldFacts = c.guards.len
+  for i in 1.. <n.len:
+    let branch = n.sons[i]
+    setLen(c.guards, oldFacts)
+    addCaseBranchFacts(c.guards, n, i)
+    for i in 0 .. <branch.len:
+      analyse(c, branch.sons[i])
+  setLen(c.guards, oldFacts)
+
+proc analyseIf(c: var AnalysisCtx; n: PNode) =
+  analyse(c, n.sons[0].sons[0])
+  let oldFacts = c.guards.len
+  addFact(c.guards, canon(n.sons[0].sons[0]))
+
+  analyse(c, n.sons[0].sons[1])
+  for i in 1.. <n.len:
+    let branch = n.sons[i]
+    setLen(c.guards, oldFacts)
+    for j in 0..i-1:
+      addFactNeg(c.guards, canon(n.sons[j].sons[0]))
+    if branch.len > 1:
+      addFact(c.guards, canon(branch.sons[0]))
+    for i in 0 .. <branch.len:
+      analyse(c, branch.sons[i])
+  setLen(c.guards, oldFacts)
+
+proc analyse(c: var AnalysisCtx; n: PNode) =
+  case n.kind
+  of nkAsgn, nkFastAsgn:
+    if n[0].isSingleAssignable and n[1].isLocal:
+      let slot = c.getSlot(n[1].sym)
+      slot.alias = n[0].sym
+    elif n[0].isLocal:
+      # since we already ensure sfAddrTaken is not in s.flags, we only need to
+      # prevent direct assignments to the monotonic variable:
+      let slot = c.getSlot(n[0].sym)
+      slot.blackListed = true
+    invalidateFacts(c.guards, n[0])
+    analyseSons(c, n)
+    addAsgnFact(c.guards, n[0], n[1])
+  of nkCallKinds:
+    # direct call:
+    if n[0].kind == nkSym: analyseCall(c, n, n[0].sym)
+    else: analyseSons(c, n)
+  of nkBracketExpr:
+    c.addSlice(n, n[0], n[1], n[1])
+    analyseSons(c, n)
+  of nkReturnStmt, nkRaiseStmt, nkTryStmt:
+    localError(n.info, "invalid control flow for 'parallel'")
+    # 'break' that leaves the 'parallel' section is not valid either
+    # or maybe we should generate a 'try' XXX
+  of nkVarSection:
+    for it in n:
+      let value = it.lastSon
+      if value.kind != nkEmpty:
+        for j in 0 .. it.len-3:
+          if it[j].isLocal:
+            let slot = c.getSlot(it[j].sym)
+            if slot.lower.isNil: slot.lower = value
+            else: internalError(it.info, "slot already has a lower bound")
+        analyse(c, value)
+  of nkCaseStmt: analyseCase(c, n)
+  of nkIfStmt, nkIfExpr: analyseIf(c, n)
+  of nkWhileStmt:
+    analyse(c, n.sons[0])
+    # 'while true' loop?
+    inc c.inLoop
+    if isTrue(n.sons[0]):
+      analyseSons(c, n.sons[1])
+    else:
+      # loop may never execute:
+      let oldState = c.locals.len
+      let oldFacts = c.guards.len
+      addFact(c.guards, canon(n.sons[0]))
+      analyse(c, n.sons[1])
+      setLen(c.locals, oldState)
+      setLen(c.guards, oldFacts)
+      # we know after the loop the negation holds:
+      if not hasSubnodeWith(n.sons[1], nkBreakStmt):
+        addFactNeg(c.guards, canon(n.sons[0]))
+    dec c.inLoop
+  of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
+      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma:
+    discard
+  else:
+    analyseSons(c, n)
+
+proc transformSlices(n: PNode): PNode =
+  if n.kind in nkCallKinds and n[0].kind == nkSym:
+    let op = n[0].sym
+    if op.name.s == "[]" and op.fromSystem:
+      result = copyNode(n)
+      result.add opSlice.newSymNode
+      result.add n[1]
+      result.add n[2][1]
+      result.add n[2][2]
+      return result
+  if n.safeLen > 0:
+    result = shallowCopy(n)
+    for i in 0 .. < n.len:
+      result.sons[i] = transformSlices(n.sons[i])
+  else:
+    result = n
+
+proc transformSpawn(owner: PSym; n, barrier: PNode): PNode
+proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode =
+  result = shallowCopy(n)
+  for i in 0 .. < n.len:
+    result.sons[i] = transformSpawn(owner, n.sons[i], barrier)
+
+proc transformSpawn(owner: PSym; n, barrier: PNode): PNode =
+  case n.kind
+  of nkVarSection:
+    result = nil
+    for it in n:
+      let b = it.lastSon
+      if getMagic(b) == mSpawn:
+        if it.len != 3: localError(it.info, "invalid context for 'spawn'")
+        let m = transformSlices(b)
+        if result.isNil:
+          result = newNodeI(nkStmtList, n.info)
+          result.add n
+        result.add wrapProcForSpawn(owner, m, b.typ, barrier, it[0])
+        it.sons[it.len-1] = emptyNode
+    if result.isNil: result = n
+  of nkAsgn, nkFastAsgn:
+    let b = n[1]
+    if getMagic(b) == mSpawn:
+      let m = transformSlices(b)
+      return wrapProcForSpawn(owner, m, b.typ, barrier, n[0])
+    result = transformSpawnSons(owner, n, barrier)
+  of nkCallKinds:
+    if getMagic(n) == mSpawn:
+      result = transformSlices(n)
+      return wrapProcForSpawn(owner, result, n.typ, barrier, nil)
+    result = transformSpawnSons(owner, n, barrier)
+  elif n.safeLen > 0:
+    result = transformSpawnSons(owner, n, barrier)
+  else:
+    result = n
+
+proc checkArgs(a: var AnalysisCtx; n: PNode) =
+  discard "too implement"
+
+proc generateAliasChecks(a: AnalysisCtx; result: PNode) =
+  discard "too implement"
+
+proc liftParallel*(owner: PSym; n: PNode): PNode =
+  # this needs to be called after the 'for' loop elimination
+
+  # first pass:
+  # - detect monotonic local integer variables
+  # - detect used slices
+  # - detect used arguments
+  #echo "PAR ", renderTree(n)
+  
+  var a = initAnalysisCtx()
+  let body = n.lastSon
+  analyse(a, body)
+  if a.spawns == 0:
+    localError(n.info, "'parallel' section without 'spawn'")
+  checkSlicesAreDisjoint(a)
+  checkArgs(a, body)
+
+  var varSection = newNodeI(nkVarSection, n.info)
+  var temp = newSym(skTemp, getIdent"barrier", owner, n.info)
+  temp.typ = magicsys.getCompilerProc("Barrier").typ
+  incl(temp.flags, sfFromGeneric)
+  let tempNode = newSymNode(temp)
+  varSection.addVar tempNode
+
+  let barrier = genAddrOf(tempNode)
+  result = newNodeI(nkStmtList, n.info)
+  generateAliasChecks(a, result)
+  result.add varSection
+  result.add callCodeGenProc("openBarrier", barrier)
+  result.add transformSpawn(owner, body, barrier)
+  result.add callCodeGenProc("closeBarrier", barrier)
+
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 6235fb76a..c8ce5e787 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -89,7 +89,7 @@ proc initVarViaNew(a: PEffects, n: PNode) =
   if n.kind != nkSym: return
   let s = n.sym
   if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
-    # 'x' is not nil, but that doesn't mean it's not nil children
+    # 'x' is not nil, but that doesn't mean its "not nil" children
     # are initialized:
     initVar(a, n)
 
@@ -478,13 +478,18 @@ proc trackBlock(tracked: PEffects, n: PNode) =
   else:
     track(tracked, n)
 
-proc isTrue(n: PNode): bool =
+proc isTrue*(n: PNode): bool =
   n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
     n.kind == nkIntLit and n.intVal != 0
 
 proc paramType(op: PType, i: int): PType =
   if op != nil and i < op.len: result = op.sons[i]
 
+proc cstringCheck(tracked: PEffects; n: PNode) =
+  if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
+      a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
+    message(n.info, warnUnsafeCode, renderTree(n))
+
 proc track(tracked: PEffects, n: PNode) =
   case n.kind
   of nkSym:
@@ -541,6 +546,7 @@ proc track(tracked: PEffects, n: PNode) =
     track(tracked, n.sons[0])
     addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
     notNilCheck(tracked, n.sons[1], n.sons[0].typ)
+    when false: cstringCheck(tracked, n)
   of nkVarSection:
     for child in n:
       let last = lastSon(child)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 2429e4183..3cb9691eb 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -33,7 +33,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
       x.info = n.info
       incl(s.flags, sfUsed)
       n.sons[0] = x
-      suggestSym(x, s)
+      suggestSym(x.info, s)
     else:
       localError(n.info, errInvalidControlFlowX, s.name.s)
   elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): 
@@ -66,10 +66,16 @@ proc toCover(t: PType): BiggestInt =
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
 proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
+  ## Checks that the given symbol is a proper procedure variable, meaning
+  ## that it 
   var smoduleId = getModule(s).id
   if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
-      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
-    localError(n.info, errXCannotBePassedToProcVar, s.name.s)
+      smoduleId != c.module.id:
+    block outer:
+      for module in c.friendModules:
+        if smoduleId == module.id:
+          break outer
+      localError(n.info, errXCannotBePassedToProcVar, s.name.s)
 
 proc semProcvarCheck(c: PContext, n: PNode) =
   let n = n.skipConv
@@ -190,7 +196,7 @@ proc semCase(c: PContext, n: PNode): PNode =
   var typ = commonTypeBegin
   var hasElse = false
   case skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc}).kind
-  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32:
+  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
     chckCovered = true
   of tyFloat..tyFloat128, tyString, tyError:
     discard
@@ -313,7 +319,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     incl(result.flags, sfGlobal)
   else:
     result = semIdentWithPragma(c, kind, n, {})
-  suggestSym(n, result)
+  suggestSym(n.info, result)
 
 proc checkNilable(v: PSym) =
   if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
@@ -655,7 +661,7 @@ proc semFor(c: PContext, n: PNode): PNode =
   n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
   var call = n.sons[length-2]
   let isCallExpr = call.kind in nkCallKinds
-  if isCallExpr and call.sons[0].sym.magic != mNone:
+  if isCallExpr and call[0].kind == nkSym and call[0].sym.magic != mNone:
     if call.sons[0].sym.magic == mOmpParFor:
       result = semForVars(c, n)
       result.kind = nkParForStmt
@@ -822,6 +828,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
                               getCurrOwner(), s.info)
 
 proc semTypeSection(c: PContext, n: PNode): PNode =
+  ## Processes a type section. This must be done in separate passes, in order
+  ## to allow the type definitions in the section to reference each other
+  ## without regard for the order of their definitions.
   typeSectionLeftSidePass(c, n)
   typeSectionRightSidePass(c, n)
   typeSectionFinalPass(c, n)
@@ -868,7 +877,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
     result = n.sym
     if result.kind notin {skMacro, skTemplate}: result = nil
   else:
-    result = searchInScopes(c, considerAcc(n), {skMacro, skTemplate})
+    result = searchInScopes(c, considerQuotedIdent(n), {skMacro, skTemplate})
 
 proc semProcAnnotation(c: PContext, prc: PNode): PNode =
   var n = prc.sons[pragmasPos]
@@ -879,7 +888,7 @@ proc semProcAnnotation(c: PContext, prc: PNode): PNode =
     let m = lookupMacro(c, key)
     if m == nil:
       if key.kind == nkIdent and key.ident.id == ord(wDelegator):
-        if considerAcc(prc.sons[namePos]).s == "()":
+        if considerQuotedIdent(prc.sons[namePos]).s == "()":
           prc.sons[namePos] = newIdentNode(idDelegator, prc.info)
           prc.sons[pragmasPos] = copyExcept(n, i)
         else:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index ad34eea65..ee8b1ccb8 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -59,7 +59,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
     result = newSymNode(s, n.info)
-    markUsed(n, s)
+    markUsed(n.info, s)
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
@@ -93,7 +93,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var TIntSet): PNode =
   for i in 0 .. < n.len:
-    toMixin.incl(considerAcc(n.sons[i]).id)
+    toMixin.incl(considerQuotedIdent(n.sons[i]).id)
   result = newNodeI(nkEmpty, n.info)
   
 proc replaceIdentBySym(n: var PNode, s: PNode) =
@@ -151,7 +151,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
       result.sons[i] = onlyReplaceParams(c, n.sons[i])
 
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerAcc(n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 6289ecc85..53fe4e266 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -70,9 +70,10 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       counter = x
     of nkSym: 
       e = n.sons[i].sym
-    of nkIdent: 
+    of nkIdent, nkAccQuoted: 
       e = newSymS(skEnumField, n.sons[i], c)
-    else: illFormedAst(n)
+    else:
+      illFormedAst(n[i])
     e.typ = result
     e.position = int(counter)
     if e.position == 0: hasNull = true
@@ -116,7 +117,7 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
     var base = semTypeNode(c, n.sons[1], nil)
     addSonSkipIntLit(result, base)
     if sonsLen(n) == 3:
-      result.n = newIdentNode(considerAcc(n.sons[2]), n.sons[2].info)
+      result.n = newIdentNode(considerQuotedIdent(n.sons[2]), n.sons[2].info)
   else:
     localError(n.info, errXExpectsOneTypeParam, "varargs")
     addSonSkipIntLit(result, errorType(c))
@@ -280,7 +281,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
   else:
     result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
     if result != nil:
-      markUsed(n, result)
+      markUsed(n.info, result)
       if result.kind == skParam and result.typ.kind == tyTypeDesc:
         # This is a typedesc param. is it already bound?
         # it's not bound when it's used multiple times in the
@@ -385,6 +386,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
     case kind
     of skType: 
       # process pragmas later, because result.typ has not been set yet
+      discard
     of skField: pragma(c, result, n.sons[1], fieldPragmas)
     of skVar:   pragma(c, result, n.sons[1], varPragmas)
     of skLet:   pragma(c, result, n.sons[1], letPragmas)
@@ -441,14 +443,14 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
     elif isRange(b):
       branch.sons[i] = semCaseBranchRange(c, t, b, covered)
     else:
+      # constant sets and arrays are allowed:
       var r = semConstExpr(c, b)
       # for ``{}`` we want to trigger the type mismatch in ``fitNode``:
-      if r.kind != nkCurly or len(r) == 0:
+      if r.kind notin {nkCurly, nkBracket} or len(r) == 0:
         checkMinSonsLen(t, 1)
         branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r))
         inc(covered)
       else:
-        # constant sets have special rules
         # first element is special and will overwrite: branch.sons[i]:
         branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered)
         # other elements have to be added to ``branch``
@@ -560,7 +562,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
     let rec = rectype.sym
     for i in countup(0, sonsLen(n)-3):
       var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
-      suggestSym(n.sons[i], f)
+      suggestSym(n.sons[i].info, f)
       f.typ = typ
       f.position = pos
       if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
@@ -825,7 +827,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       result = addImplicitGeneric(newTypeS(tyAnything, c))
   
   of tyGenericParam:
-    markUsed(genericParams, paramType.sym)
+    markUsed(info, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
       paramType.sym.kind = skType
@@ -862,7 +864,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   var counter = 0
   for i in countup(1, n.len - 1):
     var a = n.sons[i]
-    if a.kind != nkIdentDefs: illFormedAst(a)
+    if a.kind != nkIdentDefs:
+      # for some generic instantiations the passed ':env' parameter
+      # for closures has already been produced (see bug #898). We simply
+      # skip this parameter here. It'll then be re-generated in another LL
+      # pass over this instantiation:
+      if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
+      illFormedAst(a)
     checkMinSonsLen(a, 3)
     var
       typ: PType = nil
@@ -1083,8 +1091,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkCallKinds:
     if isRange(n):
       result = semRangeAux(c, n, prev)
-    elif n[0].kind == nkIdent:
-      let op = n.sons[0].ident
+    elif n[0].kind notin nkIdentKinds:
+      result = semTypeExpr(c, n)
+    else:
+      let op = considerQuotedIdent(n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
         checkSonsLen(n, 3)
         var
@@ -1119,8 +1129,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         result = semAnyRef(c, n, tyRef, prev)
       else:
         result = semTypeExpr(c, n)
-    else:
-      result = semTypeExpr(c, n)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
@@ -1179,7 +1187,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else: 
         assignType(prev, t)
         result = prev
-      markUsed(n, n.sym)
+      markUsed(n.info, n.sym)
     else:
       if n.sym.kind != skError: localError(n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 4b91a067e..e5bf08097 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -62,7 +62,7 @@ type
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
     
-proc markUsed*(n: PNode, s: PSym)
+proc markUsed*(info: TLineInfo, s: PSym)
 
 proc initCandidateAux(ctx: PContext,
                       c: var TCandidate, callee: PType) {.inline.} =
@@ -497,11 +497,11 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
 proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
   if rules.kind == nkWith:
     for r in rules:
-      if r.considerAcc == callIdent: return true
+      if r.considerQuotedIdent == callIdent: return true
     return false
   else:
     for r in rules:
-      if r.considerAcc == callIdent: return false
+      if r.considerQuotedIdent == callIdent: return false
     return true
 
 proc maybeSkipDistinct(t: PType, callee: PSym): PType =
@@ -1058,7 +1058,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
     if fdest in {isEqual, isGeneric}: 
-      markUsed(arg, c.converters[i])
+      markUsed(arg.info, c.converters[i])
       var s = newSymNode(c.converters[i])
       s.typ = c.converters[i].typ
       s.info = arg.info
@@ -1271,7 +1271,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
       result = nil
     else: 
       # only one valid interpretation found:
-      markUsed(arg, arg.sons[best].sym)
+      markUsed(arg.info, arg.sons[best].sym)
       result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
                                   argOrig)
 
@@ -1302,7 +1302,7 @@ proc prepareOperand(c: PContext; a: PNode): PNode =
 proc prepareNamedParam(a: PNode) =
   if a.sons[0].kind != nkIdent:
     var info = a.sons[0].info
-    a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info)
+    a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
 
 proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArrayConstr, c)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index fc6ba2f77..c2bdfc5c3 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -63,8 +63,11 @@ proc filterSym(s: PSym): bool {.inline.} =
 
 proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
   let fmoduleId = getModule(f).id
-  result = sfExported in f.flags or fmoduleId == c.module.id or
-      fmoduleId == c.friendModule.id
+  result = sfExported in f.flags or fmoduleId == c.module.id
+  for module in c.friendModules:
+    if fmoduleId == module.id:
+      result = true
+      break
 
 proc suggestField(c: PContext, s: PSym, outputs: var int) = 
   if filterSym(s) and fieldVisible(c, s):
@@ -243,18 +246,18 @@ var
   usageSym*: PSym
   lastLineInfo: TLineInfo
 
-proc findUsages(node: PNode, s: PSym) =
-  if usageSym == nil and isTracked(node.info, s.name.s.len):
+proc findUsages(info: TLineInfo; s: PSym) =
+  if usageSym == nil and isTracked(info, s.name.s.len):
     usageSym = s
     suggestWriteln(symToStr(s, isLocal=false, sectionUsage))
   elif s == usageSym:
-    if lastLineInfo != node.info:
-      suggestWriteln(symToStr(s, isLocal=false, sectionUsage, node.info))
-    lastLineInfo = node.info
+    if lastLineInfo != info:
+      suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info))
+    lastLineInfo = info
 
-proc findDefinition(node: PNode, s: PSym) =
-  if node.isNil or s.isNil: return
-  if isTracked(node.info, s.name.s.len):
+proc findDefinition(info: TLineInfo; s: PSym) =
+  if s.isNil: return
+  if isTracked(info, s.name.s.len):
     suggestWriteln(symToStr(s, isLocal=false, sectionDef))
     suggestQuit()
 
@@ -313,26 +316,26 @@ proc defFromSourceMap*(i: TLineInfo) =
   
   defFromLine(gSourceMaps[i.fileIndex].lines[i.line].entries, i.col)
 
-proc suggestSym*(n: PNode, s: PSym) {.inline.} =
+proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
   ## misnamed: should be 'symDeclared'
   if optUsages in gGlobalOptions:
-    findUsages(n, s)
+    findUsages(info, s)
   if optDef in gGlobalOptions:
-    findDefinition(n, s)
+    findDefinition(info, s)
   if isServing:
-    addToSourceMap(s, n.info)
+    addToSourceMap(s, info)
 
-proc markUsed(n: PNode, s: PSym) =
+proc markUsed(info: TLineInfo; s: PSym) =
   incl(s.flags, sfUsed)
   if {sfDeprecated, sfError} * s.flags != {}:
-    if sfDeprecated in s.flags: message(n.info, warnDeprecated, s.name.s)
-    if sfError in s.flags: localError(n.info, errWrongSymbolX, s.name.s)
-  suggestSym(n, s)
-  if gCmd == cmdPretty: checkUse(n, s)
+    if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s)
+    if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
+  suggestSym(info, s)
+  if gCmd == cmdPretty: checkUse(info, s)
 
 proc useSym*(sym: PSym): PNode =
   result = newSymNode(sym)
-  markUsed(result, sym)
+  markUsed(result.info, sym)
 
 proc suggestExpr*(c: PContext, node: PNode) = 
   var cp = msgs.inCheckpoint(node.info)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index fb5e321b6..dece1ac18 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -546,7 +546,7 @@ proc flattenTree(root: PNode): PNode =
     flattenTreeAux(result, root, op)
   else: 
     result = root
-  
+
 proc transformCall(c: PTransf, n: PNode): PTransNode = 
   var n = flattenTree(n)
   var op = getMergeOp(n)
@@ -565,6 +565,9 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
           inc(j)
       add(result, a.PTransNode)
     if len(result) == 2: result = result[1]
+  elif getMagic(n) == mNBindSym:
+    # for bindSym(myconst) we MUST NOT perform constant folding:
+    result = n.PTransNode
   else:
     let s = transformSons(c, n).PNode
     # bugfix: check after 'transformSons' if it's still a method call:
diff --git a/compiler/types.nim b/compiler/types.nim
index 1f266d64f..786eea14e 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1118,6 +1118,11 @@ proc typeAllowed(t: PType, kind: TSymKind): bool =
 proc align(address, alignment: BiggestInt): BiggestInt = 
   result = (address + (alignment - 1)) and not (alignment - 1)
 
+const
+  szNonConcreteType* = -3
+  szIllegalRecursion* = -2
+  szUnknownSize* = -1
+
 proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt
 proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = 
   var maxAlign, maxSize, b, res: BiggestInt
@@ -1151,14 +1156,9 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
   of nkSym: 
     result = computeSizeAux(n.sym.typ, a)
     n.sym.offset = int(currOffset)
-  else: 
-    internalError("computeRecSizeAux()")
+  else:
     a = 1
-    result = - 1
-
-const 
-  szIllegalRecursion* = -2
-  szUnknownSize* = -1
+    result = szNonConcreteType
 
 proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
   var res, maxAlign, length, currOffset: BiggestInt
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index 790bd1047..f3121a0b4 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -1,4 +1,4 @@
-import renderer, strutils, ast, msgs, types
+import renderer, strutils, ast, msgs, types, astalgo
 
 const defaultParamSeparator* = ","
 
@@ -92,7 +92,7 @@ proc renderParamTypes(found: var seq[string], n: PNode) =
       if not typ.isNil: typeStr = typeToString(typ, preferExported)
       if typeStr.len < 1: return
     for i in 0 .. <typePos:
-      assert n[i].kind == nkIdent
+      assert ((n[i].kind == nkIdent) or (n[i].kind == nkAccQuoted))
       found.add(typeStr)
   else:
     internalError(n.info, "renderParamTypes(found,n) with " & $n.kind)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 836f90967..66595856a 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -127,15 +127,20 @@ proc createStrKeepNode(x: var TFullReg) =
   elif x.node.kind == nkNilLit:
     system.reset(x.node[])
     x.node.kind = nkStrLit
-  elif x.node.kind notin {nkStrLit..nkTripleStrLit}:
+  elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
+      nfAllConst in x.node.flags:
     # XXX this is hacky; tests/txmlgen triggers it:
     x.node = newNode(nkStrLit)
-    #  debug x.node
-    #assert x.node.kind in {nkStrLit..nkTripleStrLit}
+    # It not only hackey, it is also wrong for tgentemplate. The primary
+    # cause of bugs like these is that the VM does not properly distinguish
+    # between variable defintions (var foo = e) and variable updates (foo = e).
 
 template createStr(x) =
   x.node = newNode(nkStrLit)
 
+template createSet(x) =
+  x.node = newNode(nkCurly)
+
 proc moveConst(x: var TFullReg, y: TFullReg) =
   if x.kind != y.kind:
     myreset(x)
@@ -433,7 +438,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if regs[rc].intVal > high(int):
         stackTrace(c, tos, pc, errIndexOutOfBounds)
       let idx = regs[rc].intVal.int
-      # XXX what if the array is not 0-based? -> codegen should insert a sub
       let src = regs[rb].node
       if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
         regs[ra].node = src.sons[idx]
@@ -499,13 +503,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
-      # a[] = b
+      # a[] = c; b unused
       let ra = instr.regA
-      let rb = instr.regB
+      let rc = instr.regC
       case regs[ra].kind
-      of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb])
-      of rkRegisterAddr: regs[ra].regAddr[] = regs[rb]
-      of rkNode: putIntoNode(regs[ra].node, regs[rb])
+      of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc])
+      of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
+      of rkNode: putIntoNode(regs[ra].node, regs[rc])
       else: stackTrace(c, tos, pc, errNilAccess)
     of opcAddInt:
       decodeBC(rkInt)
@@ -667,14 +671,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLtu:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
-    of opcEqRef:
+    of opcEqRef, opcEqNimrodNode:
       decodeBC(rkInt)
       regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and
                              regs[rc].node.kind == nkNilLit) or
                              regs[rb].node == regs[rc].node)
-    of opcEqNimrodNode:
-      decodeBC(rkInt)
-      regs[ra].intVal = ord(regs[rb].node == regs[rc].node)
     of opcXor:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
@@ -720,18 +721,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b))
     of opcMulSet:
       decodeBC(rkNode)
+      createSet(regs[ra])
       move(regs[ra].node.sons, 
             nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
     of opcPlusSet: 
       decodeBC(rkNode)
+      createSet(regs[ra])
       move(regs[ra].node.sons,
            nimsets.unionSets(regs[rb].node, regs[rc].node).sons)
     of opcMinusSet:
       decodeBC(rkNode)
+      createSet(regs[ra])
       move(regs[ra].node.sons,
            nimsets.diffSets(regs[rb].node, regs[rc].node).sons)
     of opcSymdiffSet:
       decodeBC(rkNode)
+      createSet(regs[ra])
       move(regs[ra].node.sons,
            nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)    
     of opcConcatStr:
@@ -742,11 +747,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node.strVal.add getstr(regs[i])
     of opcAddStrCh:
       decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      #createStrKeepNode regs[ra]
       regs[ra].node.strVal.add(regs[rb].intVal.chr)
     of opcAddStrStr:
       decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      #createStrKeepNode regs[ra]
       regs[ra].node.strVal.add(regs[rb].node.strVal)
     of opcAddSeqElem:
       decodeB(rkNode)
@@ -897,10 +902,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       c.exceptionInstr = pc
       let (newPc, newTos) = cleanUpOnException(c, tos)
       # -1 because of the following 'inc'
-      if pc-1 < 0:
+      if newPc-1 < 0:
         bailOut(c, tos)
         return
-      pc = newPc -1
+      pc = newPc-1
       if tos != newTos:
         tos = newTos
         move(regs, tos.slots)
@@ -983,7 +988,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         return TFullReg(kind: rkNone)
     of opcSetLenStr:
       decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      #createStrKeepNode regs[ra]
       regs[ra].node.strVal.setLen(regs[rb].intVal.int)
     of opcOf:
       decodeBC(rkInt)
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index d0c38a2ad..c391d8415 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -207,7 +207,7 @@ const
   largeInstrs* = { # instructions which use 2 int32s instead of 1:
     opcSubStr, opcConv, opcCast, opcNewSeq, opcOf}
   slotSomeTemp* = slotTempUnknown
-  relativeJumps* = {opcTJmp, opcFJmp, opcJmp}
+  relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
 
 template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32)
 template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 0e01f5031..9a213d813 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -15,6 +15,8 @@ proc readOutput(p: PProcess): string =
   discard p.waitForExit
   while not output.atEnd:
     result.add(output.readLine)
+    result.add("\n")
+  result.setLen(result.len - "\n".len)
 
 proc opGorge*(cmd, input: string): string =
   var p = startCmd(cmd)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 84577bb22..c1ec637dd 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -9,6 +9,24 @@
 
 ## This module implements the code generator for the VM.
 
+# Important things to remember:
+# - The VM does not distinguish between definitions ('var x = y') and
+#   assignments ('x = y'). For simple data types that fit into a register
+#   this doesn't matter. However it matters for strings and other complex
+#   types that use the 'node' field; the reason is that slots are
+#   re-used in a register based VM. Example:
+# 
+# .. code-block:: nimrod
+#   let s = a & b  # no matter what, create fresh node
+#   s = a & b  # no matter what, keep the node
+#
+# Also *stores* into non-temporary memory need to perform deep copies:
+# a.b = x.y
+# We used to generate opcAsgn for the *load* of 'x.y' but this is clearly
+# wrong! We need to produce opcAsgn (the copy) for the *store*. This also
+# solves the opcLdConst vs opcAsgnConst issue. Of course whether we need
+# this copy depends on the involved types.
+
 import
   unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, 
   trees, intsets, rodread, magicsys, options, lowerings
@@ -84,21 +102,27 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
   # Takes the `b` register and the immediate `imm`, appies the operation `opc`,
   # and stores the output value into `a`.
   # `imm` is signed and must be within [-127, 128]
-  assert(imm >= -127 and imm <= 128)
-  let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
-                           (b.uint32 shl 16'u32) or
-                           (imm+byteExcess).uint32 shl 24'u32).TInstr
-  c.code.add(ins)
-  c.debug.add(n.info)
+  if imm >= -127 and imm <= 128:
+    let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
+                             (b.uint32 shl 16'u32) or
+                             (imm+byteExcess).uint32 shl 24'u32).TInstr
+    c.code.add(ins)
+    c.debug.add(n.info)
+  else:
+    localError(n.info, errGenerated,
+      "VM: immediate value does not fit into an int8")
 
 proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
   # Applies `opc` to `bx` and stores it into register `a`
   # `bx` must be signed and in the range [-32767, 32768]
-  assert(bx >= -32767 and bx <= 32768)
-  let ins = (opc.uint32 or a.uint32 shl 8'u32 or 
-            (bx+wordExcess).uint32 shl 16'u32).TInstr
-  c.code.add(ins)
-  c.debug.add(n.info)
+  if bx >= -32767 and bx <= 32768:
+    let ins = (opc.uint32 or a.uint32 shl 8'u32 or 
+              (bx+wordExcess).uint32 shl 16'u32).TInstr
+    c.code.add(ins)
+    c.debug.add(n.info)
+  else:
+    localError(n.info, errGenerated,
+      "VM: immediate value does not fit into an int16")
 
 proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
   #assert opc in {opcJmp, opcFJmp, opcTJmp}
@@ -331,6 +355,7 @@ proc canonValue*(n: PNode): PNode =
 proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len
   assert(n.kind != nkCall)
+  n.flags.incl nfAllConst
   c.constants.add n.canonValue
   internalAssert result < 0x7fff
 
@@ -484,11 +509,22 @@ proc genField(n: PNode): TRegister =
         "too large offset! cannot generate code for: " & s.name.s)
   result = s.position
 
+proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
+  if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr);
+      x != 0):
+    let tmp = c.genx(n)
+    # freeing the temporary here means we can produce:  regA = regA - Imm
+    c.freeTemp(tmp)
+    result = c.getTemp(n.typ)
+    c.gABI(n, opcSubImmInt, result, tmp, x.int)
+  else:
+    result = c.genx(n)
+
 proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   case le.kind
   of nkBracketExpr:
     let dest = c.genx(le.sons[0], {gfAddrOf})
-    let idx = c.genx(le.sons[1])
+    let idx = c.genIndex(le.sons[1], le.sons[0].typ)
     c.gABC(le, opcWrArr, dest, idx, value)
     c.freeTemp(dest)
     c.freeTemp(idx)
@@ -501,12 +537,12 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
     c.freeTemp(dest)
   of nkDerefExpr, nkHiddenDeref:
     let dest = c.genx(le.sons[0], {gfAddrOf})
-    c.gABC(le, opcWrDeref, dest, value)
+    c.gABC(le, opcWrDeref, dest, 0, value)
     c.freeTemp(dest)
   of nkSym:
     if le.sym.isGlobal:
       let dest = c.genx(le, {gfAddrOf})
-      c.gABC(le, opcWrDeref, dest, value)
+      c.gABC(le, opcWrDeref, dest, 0, value)
       c.freeTemp(dest)
   else:
     discard
@@ -786,10 +822,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
     c.freeTemp(tmp)
   of mSwap: 
     unused(n, dest)
-    var d = c.genx(n.sons[1])
-    var tmp = c.genx(n.sons[2])
-    c.gABC(n, opcSwap, d, tmp)
-    c.freeTemp(tmp)
+    var
+      d1 = c.genx(n.sons[1])
+      d2 = c.genx(n.sons[2])
+    c.gABC(n, opcSwap, d1, d2)
+    c.genAsgnPatch(n.sons[1], d1)
+    c.genAsgnPatch(n.sons[2], d2)
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mCopyStr:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -968,7 +1006,7 @@ const
 
 proc fitsRegister*(t: PType): bool =
   t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
-    tyRange, tyEnum, tyBool, tyInt..tyUInt64}
+    tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
 
 proc requiresCopy(n: PNode): bool =
   if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
@@ -1065,17 +1103,36 @@ proc checkCanEval(c: PCtx; n: PNode) =
         not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
       cannotEval(n)
 
+proc isTemp(c: PCtx; dest: TDest): bool =
+  result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown
+
+template needsAdditionalCopy(n): expr =
+  not c.isTemp(dest) and not fitsRegister(n.typ)
+
+proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
+                       dest, idx, value: TRegister) =
+  # opcLdObj et al really means "load address". We sometimes have to create a
+  # copy in order to not introduce false aliasing:
+  # mylocal = a.b  # needs a copy of the data!
+  if needsAdditionalCopy(n):
+    var cc = c.getTemp(n.typ)
+    c.gABC(n, whichAsgnOpc(n), cc, value)
+    c.gABC(n, opc, dest, idx, cc)
+    c.freeTemp(cc)
+  else:
+    c.gABC(n, opc, dest, idx, value)
+
 proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   case le.kind
   of nkBracketExpr:
     let dest = c.genx(le.sons[0], {gfAddrOf})
-    let idx = c.genx(le.sons[1])
+    let idx = c.genIndex(le.sons[1], le.sons[0].typ)
     let tmp = c.genx(ri)
     if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
         tyString, tyCString}:
-      c.gABC(le, opcWrStrIdx, dest, idx, tmp)
+      c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
     else:
-      c.gABC(le, opcWrArr, dest, idx, tmp)
+      c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
     c.freeTemp(tmp)
   of nkDotExpr, nkCheckedFieldExpr:
     # XXX field checks here
@@ -1083,12 +1140,12 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
     let dest = c.genx(left.sons[0], {gfAddrOf})
     let idx = genField(left.sons[1])
     let tmp = c.genx(ri)
-    c.gABC(left, opcWrObj, dest, idx, tmp)
+    c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
     c.freeTemp(tmp)
   of nkDerefExpr, nkHiddenDeref:
     let dest = c.genx(le.sons[0], {gfAddrOf})
     let tmp = c.genx(ri)
-    c.gABC(le, opcWrDeref, dest, tmp)
+    c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp)
     c.freeTemp(tmp)
   of nkSym:
     let s = le.sym
@@ -1097,24 +1154,32 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
       withTemp(tmp, le.typ):
         c.gen(le, tmp, {gfAddrOf})
         let val = c.genx(ri)
-        c.gABC(le, opcWrDeref, tmp, val)
+        c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
         c.freeTemp(val)
     else:
       if s.kind == skForVar: c.setSlot s
       internalAssert s.position > 0 or (s.position == 0 and
                                         s.kind in {skParam,skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
-      gen(c, ri, dest)
+      if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}:
+        var cc = c.getTemp(le.typ)
+        gen(c, ri, cc)
+        c.gABC(le, whichAsgnOpc(le), dest, cc)
+        c.freeTemp(cc)
+      else:
+        gen(c, ri, dest)
   else:
     let dest = c.genx(le, {gfAddrOf})
     genAsgn(c, dest, ri, requiresCopy)
 
 proc genLit(c: PCtx; n: PNode; dest: var TDest) =
-  var opc = opcLdConst
+  # opcLdConst is now always valid. We produce the necessary copy in the
+  # assignments now:
+  #var opc = opcLdConst
   if dest < 0: dest = c.getTemp(n.typ)
-  elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
+  #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
   let lit = genLiteral(c, n)
-  c.gABx(n, opc, dest, lit)
+  c.gABx(n, opcLdConst, dest, lit)
 
 proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
   var n = newNode(nkType)
@@ -1143,7 +1208,7 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
   let dest = c.getTemp(s.typ)
   c.gABx(n, opcLdGlobal, dest, s.position)
   let tmp = c.genx(s.ast)
-  c.gABC(n, opcWrDeref, dest, tmp)
+  c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp)
   c.freeTemp(dest)
   c.freeTemp(tmp)
 
@@ -1179,17 +1244,22 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       # see tests/t99bott for an example that triggers it:
       cannotEval(n)
 
+template needsRegLoad(): expr =
+  gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar}))
+
 proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
                    flags: TGenFlags) =
   let a = c.genx(n.sons[0], flags)
-  let b = c.genx(n.sons[1], {})
+  let b = c.genIndex(n.sons[1], n.sons[0].typ)
   if dest < 0: dest = c.getTemp(n.typ)
-  if gfAddrOf notin flags and fitsRegister(n.typ):
+  if needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opc, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
     c.freeTemp(cc)
   else:
+    #message(n.info, warnUser, "argh")
+    #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ)
     c.gABC(n, opc, dest, a, b)
   c.freeTemp(a)
   c.freeTemp(b)
@@ -1198,7 +1268,7 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   let a = c.genx(n.sons[0], flags)
   let b = genField(n.sons[1])
   if dest < 0: dest = c.getTemp(n.typ)
-  if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})):
+  if needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opcLdObj, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
@@ -1298,7 +1368,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind != nkEmpty:
           let tmp = c.genx(a.sons[0], {gfAddrOf})
           let val = c.genx(a.sons[2])
-          c.gABC(a, opcWrDeref, tmp, val)
+          c.preventFalseAlias(a, opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else:
@@ -1306,7 +1376,16 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind == nkEmpty:
           c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
         else:
-          gen(c, a.sons[2], s.position.TRegister)
+          if not fitsRegister(s.typ):
+            c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
+          let le = a.sons[0]
+          if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}:
+            var cc = c.getTemp(le.typ)
+            gen(c, a.sons[2], cc)
+            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc)
+            c.freeTemp(cc)
+          else:
+            gen(c, a.sons[2], s.position.TRegister)
     else:
       # assign to a.sons[0]; happens for closures
       if a.sons[2].kind == nkEmpty:
@@ -1334,7 +1413,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
     c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
     for x in n:
       let a = c.genx(x)
-      c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
+      c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
       c.gABI(n, opcAddImmInt, tmp, tmp, 1)
       c.freeTemp(a)
     c.freeTemp(tmp)
@@ -1366,7 +1445,8 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
     if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym:
       let idx = genField(it.sons[0])
       let tmp = c.genx(it.sons[1])
-      c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
+      c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
+                          dest, idx, tmp)
       c.freeTemp(tmp)
     else:
       internalError(n.info, "invalid object constructor")
@@ -1380,11 +1460,12 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
     if it.kind == nkExprColonExpr:
       let idx = genField(it.sons[0])
       let tmp = c.genx(it.sons[1])
-      c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
+      c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
+                          dest, idx, tmp)
       c.freeTemp(tmp)
     else:
       let tmp = c.genx(it)
-      c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp)
+      c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp)
       c.freeTemp(tmp)
 
 proc genProc*(c: PCtx; s: PSym): int
@@ -1622,7 +1703,7 @@ proc genProc(c: PCtx; s: PSym): int =
     c.gABC(body, opcEof, eofInstr.regA)
     c.optimizeJumps(result)
     s.offset = c.prc.maxSlots
-    #if s.name.s == "addStuff":
+    #if s.name.s == "calc":
     #  echo renderTree(body)
     #  c.echoCode(result)
     c.prc = oldPrc