summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@gmail.com>2015-10-27 23:36:00 +0100
committerDominik Picheta <dominikpicheta@gmail.com>2015-10-27 23:36:00 +0100
commit3892969af4a3c6f509b553f07b2a7f37aa8de967 (patch)
treef4ac158a72b3f520848a479ee4faa8dd68792738 /compiler
parentc7eaa8ae034fc22fcb91770f94e08f7c1ebb9963 (diff)
parentd9415fd5cebdc44385cdd092f2c49060c09e2631 (diff)
downloadNim-3892969af4a3c6f509b553f07b2a7f37aa8de967.tar.gz
Merge branch 'devel'
Diffstat (limited to 'compiler')
-rw-r--r--compiler/aliases.nim62
-rw-r--r--compiler/ast.nim191
-rw-r--r--compiler/astalgo.nim50
-rw-r--r--compiler/bitsets.nim40
-rw-r--r--compiler/canonicalizer.nim132
-rw-r--r--compiler/ccgcalls.nim38
-rw-r--r--compiler/ccgexprs.nim233
-rw-r--r--compiler/ccgstmts.nim15
-rw-r--r--compiler/ccgtypes.nim9
-rw-r--r--compiler/ccgutils.nim5
-rw-r--r--compiler/cgen.nim58
-rw-r--r--compiler/cgmeth.nim121
-rw-r--r--compiler/commands.nim73
-rw-r--r--compiler/condsyms.nim5
-rw-r--r--compiler/crc.nim147
-rw-r--r--compiler/docgen.nim10
-rw-r--r--compiler/docgen2.nim10
-rw-r--r--compiler/evaltempl.nim61
-rw-r--r--compiler/extccomp.nim142
-rw-r--r--compiler/filter_tmpl.nim98
-rw-r--r--compiler/filters.nim38
-rw-r--r--compiler/forloops.nim14
-rw-r--r--compiler/guards.nim152
-rw-r--r--compiler/hlo.nim4
-rw-r--r--compiler/idents.nim42
-rw-r--r--compiler/idgen.nim22
-rw-r--r--compiler/importer.nim4
-rw-r--r--compiler/installer.ini16
-rw-r--r--compiler/jsgen.nim298
-rw-r--r--compiler/jstypes.nim9
-rw-r--r--compiler/lambdalifting.nim22
-rw-r--r--compiler/lexer.nim388
-rw-r--r--compiler/lists.nim48
-rw-r--r--compiler/llstream.nim95
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/lowerings.nim59
-rw-r--r--compiler/magicsys.nim56
-rw-r--r--compiler/main.nim31
-rw-r--r--compiler/modules.nim105
-rw-r--r--compiler/msgs.nim377
-rw-r--r--compiler/nim.cfg (renamed from compiler/nim.nim.cfg)1
-rw-r--r--compiler/nim.nim27
-rw-r--r--compiler/nimblecmd.nim10
-rw-r--r--compiler/nimconf.nim108
-rw-r--r--compiler/nimeval.nim2
-rw-r--r--compiler/nimfix/nimfix.nim16
-rw-r--r--compiler/nimfix/pretty.nim20
-rw-r--r--compiler/nimfix/prettybase.nim7
-rw-r--r--compiler/nimlexbase.nim70
-rw-r--r--compiler/nimsets.nim82
-rw-r--r--compiler/nimsuggest/nimsuggest.nim190
-rw-r--r--compiler/nimsuggest/nimsuggest.nim.cfg17
-rw-r--r--compiler/nodejs.nim2
-rw-r--r--compiler/nversion.nim2
-rw-r--r--compiler/options.nim79
-rw-r--r--compiler/parampatterns.nim23
-rw-r--r--compiler/parser.nim38
-rw-r--r--compiler/passaux.nim26
-rw-r--r--compiler/passes.nim19
-rw-r--r--compiler/patterns.nim14
-rw-r--r--compiler/pbraces.nim6
-rw-r--r--compiler/platform.nim10
-rw-r--r--compiler/plugins/locals/locals.nim3
-rw-r--r--compiler/pragmas.nim579
-rw-r--r--compiler/procfind.nim22
-rw-r--r--compiler/renderer.nim26
-rw-r--r--compiler/rodread.nim472
-rw-r--r--compiler/rodutils.nim34
-rw-r--r--compiler/rodwrite.nim208
-rw-r--r--compiler/ropes.nim2
-rw-r--r--compiler/saturate.nim2
-rw-r--r--compiler/scriptconfig.nim146
-rw-r--r--compiler/sem.nim12
-rw-r--r--compiler/semcall.nim35
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semdestruct.nim14
-rw-r--r--compiler/semexprs.nim172
-rw-r--r--compiler/semfields.nim10
-rw-r--r--compiler/semfold.nim72
-rw-r--r--compiler/semgnrc.nim169
-rw-r--r--compiler/seminst.nim56
-rw-r--r--compiler/semmacrosanity.nim18
-rw-r--r--compiler/semmagic.nim65
-rw-r--r--compiler/semparallel.nim35
-rw-r--r--compiler/sempass2.nim67
-rw-r--r--compiler/semstmts.nim125
-rw-r--r--compiler/semtempl.nim191
-rw-r--r--compiler/semtypes.nim87
-rw-r--r--compiler/semtypinst.nim61
-rw-r--r--compiler/service.nim2
-rw-r--r--compiler/sigmatch.nim146
-rw-r--r--compiler/suggest.nim196
-rw-r--r--compiler/syntaxes.nim111
-rw-r--r--compiler/tccgen.nim22
-rw-r--r--compiler/transf.nim134
-rw-r--r--compiler/trees.nim12
-rw-r--r--compiler/treetab.nim84
-rw-r--r--compiler/types.nim42
-rw-r--r--compiler/vm.nim161
-rw-r--r--compiler/vmdef.nim8
-rw-r--r--compiler/vmdeps.nim43
-rw-r--r--compiler/vmgen.nim212
-rw-r--r--compiler/vmhooks.nim20
-rw-r--r--compiler/vmmarshal.nim23
-rw-r--r--compiler/vmops.nim2
-rw-r--r--compiler/wordrecg.nim16
-rw-r--r--compiler/writetracking.nim272
107 files changed, 5047 insertions, 3199 deletions
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index 3f3d45ff7..4b592ee60 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -11,7 +11,7 @@
 
 import
   ast, astalgo, types, trees, intsets, msgs
-  
+
 type
   TAnalysisResult* = enum
     arNo, arMaybe, arYes
@@ -21,42 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult
 proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
   result = arNo
   case n.kind
-  of nkRecList: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkRecList:
+    for i in countup(0, sonsLen(n) - 1):
       result = isPartOfAux(n.sons[i], b, marker)
       if result == arYes: return
   of nkRecCase:
     assert(n.sons[0].kind == nkSym)
     result = isPartOfAux(n.sons[0], b, marker)
     if result == arYes: return
-    for i in countup(1, sonsLen(n) - 1): 
+    for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
-      of nkOfBranch, nkElse: 
+      of nkOfBranch, nkElse:
         result = isPartOfAux(lastSon(n.sons[i]), b, marker)
         if result == arYes: return
       else: internalError("isPartOfAux(record case branch)")
   of nkSym:
     result = isPartOfAux(n.sym.typ, b, marker)
   else: internalError(n.info, "isPartOfAux()")
-  
-proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = 
+
+proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
   result = arNo
-  if a == nil or b == nil: return 
-  if containsOrIncl(marker, a.id): return 
+  if a == nil or b == nil: return
+  if containsOrIncl(marker, a.id): return
   if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
   case a.kind
-  of tyObject: 
+  of tyObject:
     result = isPartOfAux(a.sons[0], b, marker)
     if result == arNo: result = isPartOfAux(a.n, b, marker)
   of tyGenericInst, tyDistinct:
     result = isPartOfAux(lastSon(a), b, marker)
-  of tyArray, tyArrayConstr, tySet, tyTuple: 
-    for i in countup(0, sonsLen(a) - 1): 
+  of tyArray, tyArrayConstr, tySet, tyTuple:
+    for i in countup(0, sonsLen(a) - 1):
       result = isPartOfAux(a.sons[i], b, marker)
-      if result == arYes: return 
+      if result == arYes: return
   else: discard
 
-proc isPartOf(a, b: PType): TAnalysisResult = 
+proc isPartOf(a, b: PType): TAnalysisResult =
   ## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
   var marker = initIntSet()
   # watch out: parameters reversed because I'm too lazy to change the code...
@@ -70,14 +70,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
   ## type. Since however type analysis is more expensive, we perform it only
   ## if necessary.
   ##
-  ## cases: 
+  ## cases:
   ##
   ## YES-cases:
   ##  x    <| x   # for general trees
   ##  x[]  <| x
   ##  x[i] <| x
   ##  x.f  <| x
-  ##  
+  ##
   ## NO-cases:
   ## x           !<| y    # depending on type and symbol kind
   ## x[constA]   !<| x[constB]
@@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
   ##
   ##  x[] ?<| y[]   iff compatible type
   ##
-  ## 
+  ##
   ##  x[]  ?<| y  depending on type
-  ##  
+  ##
   if a.kind == b.kind:
     case a.kind
     of nkSym:
       const varKinds = {skVar, skTemp, skProc}
       # same symbol: aliasing:
       if a.sym.id == b.sym.id: result = arYes
-      elif a.sym.kind in varKinds or b.sym.kind in varKinds: 
+      elif a.sym.kind in varKinds or b.sym.kind in varKinds:
         # actually, a param could alias a var but we know that cannot happen
         # here. XXX make this more generic
         result = arNo
@@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
       if len(a) >= 2 and len(b) >= 2:
         # array accesses:
         if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
-          # we know it's the same array and we have 2 constant indexes; 
-          # if they are 
+          # we know it's the same array and we have 2 constant indexes;
+          # if they are
           var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
           var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
-          
+
           if sameValue(x, y): result = arYes
           else: result = arNo
         # else: maybe and no are accurate
@@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         # pointer derefs:
         if result != arYes:
           if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
-      
+
     of nkDotExpr:
       result = isPartOf(a[0], b[0])
       if result != arNo:
@@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
       # weaken because of indirection:
       if result != arYes:
         if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
-      
+
     of nkHiddenStdConv, nkHiddenSubConv, nkConv:
       result = isPartOf(a[1], b[1])
     of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
@@ -144,26 +144,26 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
     # Calls return a new location, so a default of ``arNo`` is fine.
   else:
     # go down recursively; this is quite demanding:
-    const 
+    const
       Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
-                  nkCheckedFieldExpr}
+                  nkCheckedFieldExpr, nkHiddenAddr}
       Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
       DerefKinds = {nkHiddenDeref, nkDerefExpr}
     case b.kind
     of Ix0Kinds:
       # a* !<| b.f  iff  a* !<| b
       result = isPartOf(a, b[0])
-    
+
     of DerefKinds:
-      # a* !<| b[] iff 
+      # a* !<| b[] iff
       if isPartOf(a.typ, b.typ) != arNo:
         result = isPartOf(a, b[0])
         if result == arNo: result = arMaybe
-    
+
     of Ix1Kinds:
       # a* !<| T(b)  iff a* !<| b
       result = isPartOf(a, b[1])
-    
+
     of nkSym:
       # b is an atom, so we have to check a:
       case a.kind
@@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         result = isPartOf(a[0], b)
       of Ix1Kinds:
         result = isPartOf(a[1], b)
-      
+
       of DerefKinds:
         if isPartOf(a.typ, b.typ) != arNo:
           result = isPartOf(a[0], b)
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 3798410e8..25958f580 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,7 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists,
+  msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists,
   intsets, idgen
 
 type
@@ -291,12 +291,13 @@ const
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
 
-  sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't
-                       # require RC ops
   sfCompileToCpp* = sfInfixCall       # compile the module as C++ code
   sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
   sfExperimental* = sfOverriden       # module uses the .experimental switch
   sfGoto* = sfOverriden               # var is used for 'goto' code generation
+  sfWrittenTo* = sfBorrow             # param is assigned to
+  sfEscapes* = sfProcvar              # param escapes
+  sfBase* = sfDiscriminant
 
 const
   # getting ready for the future expr/stmt merge
@@ -423,6 +424,7 @@ type
                 # but unfortunately it has measurable impact for compilation
                 # efficiency
     nfTransf,   # node has been transformed
+    nfNoRewrite # node should not be transformed anymore
     nfSem       # node has been checked for semantics
     nfLL        # node has gone through lambda lifting
     nfDotField  # the call can use a dot operator
@@ -475,6 +477,8 @@ type
                       # wildcard type.
     tfHasAsgn         # type has overloaded assignment operator
     tfBorrowDot       # distinct type borrows '.'
+    tfTriggersCompileTime # uses the NimNode type which make the proc
+                          # implicitly '.compiletime'
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -521,6 +525,12 @@ const
   tfUnion* = tfNoSideEffect
   tfGcSafe* = tfThread
   tfObjHasKids* = tfEnumHasHoles
+  tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
+    # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
+    # deprecated and this mess can be cleaned up.
+  tfVoid* = tfVarargs # for historical reasons we conflated 'void' with
+                      # 'empty' ('@[]' has the type 'seq[empty]').
+  tfReturnsNew* = tfInheritable
   skError* = skUnknown
 
   # type flags that are essential for type equality:
@@ -529,40 +539,52 @@ const
 type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
-    mDefined, mDefinedInScope, mCompiles,
+    mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
     mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
     mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
-    mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
-    mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
+    mUnaryLt, mInc, mDec, mOrd,
+    mNew, mNewFinalize, mNewSeq,
+    mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
+    mXLenStr, mXLenSeq,
     mIncl, mExcl, mCard, mChr,
     mGCref, mGCunref,
-
-    mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
-    mDivI64, mModI64, mSucc, mPred,
+    mAddI, mSubI, mMulI, mDivI, mModI,
+    mSucc, mPred,
     mAddF64, mSubF64, mMulF64, mDivF64,
-
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
-    mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
-    mMinF64, mMaxF64, mAddU, mSubU, mMulU,
-    mDivU, mModU, mEqI, mLeI,
-    mLtI,
-    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
-    mLeU, mLtU, mLeU64, mLtU64,
-    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
-    mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
-    mUnaryMinusI64, mAbsI, mAbsI64, mNot,
+    mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
+    mMinI, mMaxI,
+    mMinF64, mMaxF64,
+    mAddU, mSubU, mMulU, mDivU, mModU,
+    mEqI, mLeI, mLtI,
+    mEqF64, mLeF64, mLtF64,
+    mLeU, mLtU,
+    mLeU64, mLtU64,
+    mEqEnum, mLeEnum, mLtEnum,
+    mEqCh, mLeCh, mLtCh,
+    mEqB, mLeB, mLtB,
+    mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString,
+    mXor, mEqProc,
+    mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
     mUnaryPlusI, mBitnotI,
-    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
-    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
-    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
-    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
-    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
-    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
+    mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
+    mZe8ToI, mZe8ToI64,
+    mZe16ToI, mZe16ToI64,
+    mZe32ToI64, mZeIToI64,
+    mToU8, mToU16, mToU32,
+    mToFloat, mToBiggestFloat,
+    mToInt, mToBiggestInt,
+    mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
+    mStrToStr, mEnumToStr,
+    mAnd, mOr,
+    mEqStr, mLeStr, mLtStr,
+    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
+    mConStrStr, mSlice,
     mDotDot, # this one is only necessary to give nice compile time warnings
     mFields, mFieldPairs, mOmpParFor,
     mAppendStrCh, mAppendStrStr, mAppendSeqElem,
-    mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
+    mInRange, mInSet, mRepr, mExit,
+    mSetLengthStr, mSetLengthSeq,
     mIsPartOf, mAstToStr, mParallel,
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
@@ -584,42 +606,53 @@ type
     mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
     mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
     mNBindSym, mLocals, mNCallSite,
-    mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError,
-    mInstantiationInfo, mGetTypeInfo, mNGenSym
+    mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
+    mNHint, mNWarning, mNError,
+    mInstantiationInfo, mGetTypeInfo, mNGenSym,
+    mNimvm
 
 # things that we can evaluate safely at compile time, even if not asked for it:
 const
   ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
     mPred, mInc, mDec, mOrd, mLengthOpenArray,
     mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
+    mArrGet, mArrPut, mAsgn,
     mIncl, mExcl, mCard, mChr,
-    mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
-    mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
-    mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
-    mMinF64, mMaxF64, mAddU, mSubU, mMulU,
-    mDivU, mModU, mEqI, mLeI,
-    mLtI,
-    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
-    mLeU, mLtU, mLeU64, mLtU64,
-    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
-    mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
-    mUnaryMinusI64, mAbsI, mAbsI64, mNot,
-    mUnaryPlusI, mBitnotI,
-    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
-    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
-    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
-    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
-    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
-    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr,
-    mAppendStrCh, mAppendStrStr, mAppendSeqElem,
+    mAddI, mSubI, mMulI, mDivI, mModI,
+    mAddF64, mSubF64, mMulF64, mDivF64,
+    mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
+    mMinI, mMaxI,
+    mMinF64, mMaxF64,
+    mAddU, mSubU, mMulU, mDivU, mModU,
+    mEqI, mLeI, mLtI,
+    mEqF64, mLeF64, mLtF64,
+    mLeU, mLtU,
+    mLeU64, mLtU64,
+    mEqEnum, mLeEnum, mLtEnum,
+    mEqCh, mLeCh, mLtCh,
+    mEqB, mLeB, mLtB,
+    mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor,
+    mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
+    mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
+    mZe8ToI, mZe8ToI64,
+    mZe16ToI, mZe16ToI64,
+    mZe32ToI64, mZeIToI64,
+    mToU8, mToU16, mToU32,
+    mToFloat, mToBiggestFloat,
+    mToInt, mToBiggestInt,
+    mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
+    mStrToStr, mEnumToStr,
+    mAnd, mOr,
+    mEqStr, mLeStr, mLtStr,
+    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
+    mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mInRange, mInSet, mRepr,
     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, mParallel, mSpawn, mAstToStr}
+    mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
 
 type
   PNode* = ref TNode
@@ -679,6 +712,7 @@ type
     lfSingleUse               # no location yet and will only be used once
   TStorageLoc* = enum
     OnUnknown,                # location is unknown (stack, heap or static)
+    OnStatic,                 # in a static section
     OnStack,                  # location is on hardware stack
     OnHeap                    # location is on heap or global
                               # (reference counting needed)
@@ -704,6 +738,8 @@ type
     name*: Rope
     path*: PNode              # can be a string literal!
 
+  CompilesId* = int ## id that is used for the caching logic within
+                    ## ``system.compiles``. See the seminst module.
   TInstantiation* = object
     sym*: PSym
     concreteTypes*: seq[PType]
@@ -711,6 +747,7 @@ type
                               # needed in caas mode for purging the cache
                               # XXX: it's possible to switch to a
                               # simple ref count here
+    compilesId*: CompilesId
 
   PInstantiation* = ref TInstantiation
 
@@ -747,6 +784,7 @@ type
       tab*: TStrTable         # interface table for modules
     of skLet, skVar, skField, skForVar:
       guard*: PSym
+      bitsize*: int
     else: nil
     magic*: TMagic
     typ*: PType
@@ -842,7 +880,7 @@ type
     data*: TIdNodePairSeq
 
   TNodePair* = object
-    h*: THash                 # because it is expensive to compute!
+    h*: Hash                 # because it is expensive to compute!
     key*: PNode
     val*: int
 
@@ -947,6 +985,9 @@ proc add*(father, son: PNode) =
 proc `[]`*(n: PNode, i: int): PNode {.inline.} =
   result = n.sons[i]
 
+template `-|`*(b, s: expr): expr =
+  (if b >= 0: b else: s.len + b)
+
 # son access operators with support for negative indices
 template `{}`*(n: PNode, i: int): expr = n[i -| n]
 template `{}=`*(n: PNode, i: int, s: PNode): stmt =
@@ -1000,7 +1041,8 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   result.id = getID()
   when debugIds:
     registerId(result)
-  #if result.id < 2000:
+  #if result.id == 93289:
+  #  writeStacktrace()
   #  MessageOut(name.s & " has id: " & toString(result.id))
 
 var emptyNode* = newNode(nkEmpty)
@@ -1198,23 +1240,11 @@ proc newSons*(father: PType, length: int) =
   else:
     setLen(father.sons, length)
 
-proc sonsLen*(n: PType): int =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-
-proc len*(n: PType): int =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-
-proc sonsLen*(n: PNode): int =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-
-proc lastSon*(n: PNode): PNode =
-  result = n.sons[sonsLen(n) - 1]
-
-proc lastSon*(n: PType): PType =
-  result = n.sons[sonsLen(n) - 1]
+proc sonsLen*(n: PType): int = n.sons.len
+proc len*(n: PType): int = n.sons.len
+proc sonsLen*(n: PNode): int = n.sons.len
+proc lastSon*(n: PNode): PNode = n.sons[^1]
+proc lastSon*(n: PType): PType = n.sons[^1]
 
 proc assignType*(dest, src: PType) =
   dest.kind = src.kind
@@ -1356,8 +1386,11 @@ proc propagateToOwner*(owner, elem: PType) =
       o2.flags.incl tfHasAsgn
       owner.flags.incl tfHasAsgn
 
+  if tfTriggersCompileTime in elem.flags:
+    owner.flags.incl tfTriggersCompileTime
+
   if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
-                       tyGenericInvocation}:
+                       tyGenericInvocation, tyPtr}:
     let elemB = elem.skipTypes({tyGenericInst})
     if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
       # for simplicity, we propagate this flag even to generics. We then
@@ -1496,6 +1529,9 @@ proc getFloat*(a: PNode): BiggestFloat =
 proc getStr*(a: PNode): string =
   case a.kind
   of nkStrLit..nkTripleStrLit: result = a.strVal
+  of nkNilLit:
+    # let's hope this fixes more problems than it creates:
+    result = nil
   else:
     internalError(a.info, "getStr")
     result = ""
@@ -1553,6 +1589,14 @@ proc makeStmtList*(n: PNode): PNode =
     result = newNodeI(nkStmtList, n.info)
     result.add n
 
+proc skipStmtList*(n: PNode): PNode =
+  if n.kind in {nkStmtList, nkStmtListExpr}:
+    for i in 0 .. n.len-2:
+      if n[i].kind notin {nkEmpty, nkCommentStmt}: return n
+    result = n.lastSon
+  else:
+    result = n
+
 proc createMagic*(name: string, m: TMagic): PSym =
   result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
   result.magic = m
@@ -1560,3 +1604,10 @@ proc createMagic*(name: string, m: TMagic): PSym =
 let
   opNot* = createMagic("not", mNot)
   opContains* = createMagic("contains", mInSet)
+
+when false:
+  proc containsNil*(n: PNode): bool =
+    # only for debugging
+    if n.isNil: return true
+    for i in 0 ..< n.safeLen:
+      if n[i].containsNil: return true
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 1707718d7..3ba43b4c5 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -14,7 +14,7 @@
 import
   ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
 
-proc hashNode*(p: RootRef): THash
+proc hashNode*(p: RootRef): Hash
 proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
   # Convert a tree into its YAML representation; this is used by the
   # YAML code generator and it is invaluable for debugging purposes.
@@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
 
 type
   TTabIter*{.final.} = object # consider all fields here private
-    h*: THash                 # current hash
+    h*: Hash                  # current hash
 
 proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
 proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
@@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
 
 type
   TIdentIter*{.final.} = object # iterator over all syms with same identifier
-    h*: THash                   # current hash
+    h*: Hash                    # current hash
     name*: PIdent
 
 
@@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
 proc lookupInRecord*(n: PNode, field: PIdent): PSym
 proc getModule*(s: PSym): PSym
 proc mustRehash*(length, counter: int): bool
-proc nextTry*(h, maxHash: THash): THash {.inline.}
+proc nextTry*(h, maxHash: Hash): Hash {.inline.}
 
 # ------------- table[int, int] ---------------------------------------------
 const
@@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
     else: internalError(list.info, "getSymFromList")
   result = nil
 
-proc hashNode(p: RootRef): THash =
+proc hashNode(p: RootRef): Hash =
   result = hash(cast[pointer](p))
 
 proc mustRehash(length, counter: int): bool =
@@ -452,7 +452,7 @@ proc debug(n: PSym) =
   elif n.kind == skUnknown:
     msgWriteln("skUnknown")
   else:
-    #writeln(stdout, $symToYaml(n, 0, 1))
+    #writeLine(stdout, $symToYaml(n, 0, 1))
     msgWriteln("$1_$2: $3, $4, $5, $6" % [
       n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
       $lineInfoToStr(n.info), $n.kind])
@@ -466,7 +466,7 @@ proc debug(n: PNode) =
 const
   EmptySeq = @[]
 
-proc nextTry(h, maxHash: THash): THash =
+proc nextTry(h, maxHash: Hash): Hash =
   result = ((5 * h) + 1) and maxHash
   # For any initial h in range(maxHash), repeating that maxHash times
   # generates each int in range(maxHash) exactly once (see any text on
@@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
 
 proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
   # returns true whether n is in t
-  var h: THash = hashNode(obj) and high(t.data) # start with real hash value
+  var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
   while t.data[h] != nil:
     if t.data[h] == obj:
       return true
@@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
   result = false
 
 proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
-  var h: THash = hashNode(obj) and high(data)
+  var h: Hash = hashNode(obj) and high(data)
   while data[h] != nil:
     assert(data[h] != obj)
     h = nextTry(h, high(data))
@@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
 
 proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
   # returns true if obj is already in the string table:
-  var h: THash = hashNode(obj) and high(t.data)
+  var h: Hash = hashNode(obj) and high(t.data)
   while true:
     var it = t.data[h]
     if it == nil: break
@@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
   result = false
 
 proc tableRawGet(t: TTable, key: RootRef): int =
-  var h: THash = hashNode(key) and high(t.data) # start with real hash value
+  var h: Hash = hashNode(key) and high(t.data) # start with real hash value
   while t.data[h].key != nil:
     if t.data[h].key == key:
       return h
@@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
 
 proc tableSearch(t: TTable, key, closure: RootRef,
                  comparator: TCmpProc): RootRef =
-  var h: THash = hashNode(key) and high(t.data) # start with real hash value
+  var h: Hash = hashNode(key) and high(t.data) # start with real hash value
   while t.data[h].key != nil:
     if t.data[h].key == key:
       if comparator(t.data[h].val, closure):
@@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
   else: result = nil
 
 proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
-  var h: THash = hashNode(key) and high(data)
+  var h: Hash = hashNode(key) and high(data)
   while data[h].key != nil:
     assert(data[h].key != key)
     h = nextTry(h, high(data))
@@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
     inc(t.counter)
 
 proc strTableContains(t: TStrTable, n: PSym): bool =
-  var h: THash = n.name.h and high(t.data) # start with real hash value
+  var h: Hash = n.name.h and high(t.data) # start with real hash value
   while t.data[h] != nil:
     if (t.data[h] == n):
       return true
@@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
   result = false
 
 proc strTableRawInsert(data: var TSymSeq, n: PSym) =
-  var h: THash = n.name.h and high(data)
+  var h: Hash = n.name.h and high(data)
   if sfImmediate notin n.flags:
     # fast path:
     while data[h] != nil:
@@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
 
 proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
   assert prevSym.name.h == newSym.name.h
-  var h: THash = prevSym.name.h and high(data)
+  var h: Hash = prevSym.name.h and high(data)
   while data[h] != nil:
     if data[h] == prevSym:
       data[h] = newSym
@@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
   # It is essential that `n` is written nevertheless!
   # This way the newest redefinition is picked by the semantic analyses!
   assert n.name != nil
-  var h: THash = n.name.h and high(t.data)
+  var h: Hash = n.name.h and high(t.data)
   var replaceSlot = -1
   while true:
     var it = t.data[h]
@@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
   result = false
 
 proc strTableGet(t: TStrTable, name: PIdent): PSym =
-  var h: THash = name.h and high(t.data)
+  var h: Hash = name.h and high(t.data)
   while true:
     result = t.data[h]
     if result == nil: break
@@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
 
 proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
                          excluding: IntSet): PSym =
-  var h: THash = ti.h and high(tab.data)
+  var h: Hash = ti.h and high(tab.data)
   var start = h
   result = tab.data[h]
   while result != nil:
@@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
   result = false
 
 proc idTableRawGet(t: TIdTable, key: int): int =
-  var h: THash
+  var h: Hash
   h = key and high(t.data)    # start with real hash value
   while t.data[h].key != nil:
     if t.data[h].key.id == key:
@@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
       yield (t.data[i].key.id, t.data[i].val)
 
 proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
-  var h: THash
+  var h: Hash
   h = key.id and high(data)
   while data[h].key != nil:
     assert(data[h].key.id != key.id)
@@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
     if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
 
 proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
-  var h: THash
+  var h: Hash
   h = key.id and high(t.data) # start with real hash value
   while t.data[h].key != nil:
     if t.data[h].key.id == key.id:
@@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
     result = idNodeTableGet(t, key)
 
 proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
-  var h: THash
+  var h: Hash
   h = key.id and high(data)
   while data[h].key != nil:
     assert(data[h].key.id != key.id)
@@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
   for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
 
 proc iiTableRawGet(t: TIITable, key: int): int =
-  var h: THash
+  var h: Hash
   h = key and high(t.data)    # start with real hash value
   while t.data[h].key != InvalidKey:
     if t.data[h].key == key: return h
@@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
   else: result = InvalidKey
 
 proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
-  var h: THash
+  var h: Hash
   h = key and high(data)
   while data[h].key != InvalidKey:
     assert(data[h].key != key)
diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim
index a2324f4e2..5454ef5e7 100644
--- a/compiler/bitsets.nim
+++ b/compiler/bitsets.nim
@@ -10,12 +10,12 @@
 # this unit handles Nim sets; it implements bit sets
 # the code here should be reused in the Nim standard library
 
-type 
+type
   TBitSet* = seq[int8]        # we use byte here to avoid issues with
                               # cross-compiling; uint would be more efficient
                               # however
 
-const 
+const
   ElemSize* = sizeof(int8) * 8
 
 proc bitSetInit*(b: var TBitSet, length: int)
@@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool
 proc bitSetContains*(x, y: TBitSet): bool
 # implementation
 
-proc bitSetIn(x: TBitSet, e: BiggestInt): bool = 
+proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
   result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
       toU8(0)
 
-proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = 
+proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
   assert(elem >= 0)
   x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
       toU8(int(1 shl (elem mod ElemSize)))
 
-proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = 
+proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
   x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
       not toU8(int(1 shl (elem mod ElemSize)))
 
-proc bitSetInit(b: var TBitSet, length: int) = 
+proc bitSetInit(b: var TBitSet, length: int) =
   newSeq(b, length)
 
-proc bitSetUnion(x: var TBitSet, y: TBitSet) = 
+proc bitSetUnion(x: var TBitSet, y: TBitSet) =
   for i in countup(0, high(x)): x[i] = x[i] or y[i]
-  
-proc bitSetDiff(x: var TBitSet, y: TBitSet) = 
+
+proc bitSetDiff(x: var TBitSet, y: TBitSet) =
   for i in countup(0, high(x)): x[i] = x[i] and not y[i]
-  
-proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = 
+
+proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
   for i in countup(0, high(x)): x[i] = x[i] xor y[i]
-  
-proc bitSetIntersect(x: var TBitSet, y: TBitSet) = 
+
+proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
   for i in countup(0, high(x)): x[i] = x[i] and y[i]
-  
-proc bitSetEquals(x, y: TBitSet): bool = 
-  for i in countup(0, high(x)): 
-    if x[i] != y[i]: 
+
+proc bitSetEquals(x, y: TBitSet): bool =
+  for i in countup(0, high(x)):
+    if x[i] != y[i]:
       return false
   result = true
 
-proc bitSetContains(x, y: TBitSet): bool = 
-  for i in countup(0, high(x)): 
-    if (x[i] and not y[i]) != int8(0): 
+proc bitSetContains(x, y: TBitSet): bool =
+  for i in countup(0, high(x)):
+    if (x[i] and not y[i]) != int8(0):
       return false
   result = true
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
index 6fcc57a91..dc6445035 100644
--- a/compiler/canonicalizer.nim
+++ b/compiler/canonicalizer.nim
@@ -30,39 +30,39 @@ type
 #
 # This is a good compromise between correctness and brevity. ;-)
 
-const

+const
   cb64 = [
     "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
-    "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", 
-    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", 
-    "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", 
+    "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
+    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
+    "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
     "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-    "_A", "_B"]

-

-proc toBase64a(s: cstring, len: int): string =

-  ## encodes `s` into base64 representation. After `lineLen` characters, a 

-  ## `newline` is added.

-  result = newStringOfCap(((len + 2) div 3) * 4)

-  var i = 0

-  while i < s.len - 2:

-    let a = ord(s[i])

-    let b = ord(s[i+1])

-    let c = ord(s[i+2])

-    result.add cb64[a shr 2]

-    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]

-    result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]

-    result.add cb64[c and 0x3F]

-    inc(i, 3)

-  if i < s.len-1:

-    let a = ord(s[i])

-    let b = ord(s[i+1])

-    result.add cb64[a shr 2]

-    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]

-    result.add cb64[((b and 0x0F) shl 2)]

-  elif i < s.len:

-    let a = ord(s[i])

-    result.add cb64[a shr 2]

-    result.add cb64[(a and 3) shl 4]

+    "_A", "_B"]
+
+proc toBase64a(s: cstring, len: int): string =
+  ## encodes `s` into base64 representation. After `lineLen` characters, a
+  ## `newline` is added.
+  result = newStringOfCap(((len + 2) div 3) * 4)
+  var i = 0
+  while i < s.len - 2:
+    let a = ord(s[i])
+    let b = ord(s[i+1])
+    let c = ord(s[i+2])
+    result.add cb64[a shr 2]
+    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
+    result.add cb64[c and 0x3F]
+    inc(i, 3)
+  if i < s.len-1:
+    let a = ord(s[i])
+    let b = ord(s[i+1])
+    result.add cb64[a shr 2]
+    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result.add cb64[((b and 0x0F) shl 2)]
+  elif i < s.len:
+    let a = ord(s[i])
+    result.add cb64[a shr 2]
+    result.add cb64[(a and 3) shl 4]
 
 proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
 
@@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) =
     c &= ":anon"
   else:
     var it = s.owner
-    while it != nil: 
+    while it != nil:
       hashSym(c, it)
       c &= "."
       it = s.owner
@@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) =
 
 proc hashType(c: var MD5Context, t: PType) =
   # modelled after 'typeToString'
-  if t == nil: 
+  if t == nil:
     c &= "\254"
     return
 
   var k = t.kind
   md5Update(c, cast[cstring](addr(k)), 1)
-  
+
   if t.sym != nil and sfAnon notin t.sym.flags:
     # t.n for literals, but not for e.g. objects!
     if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
     c.hashSym(t.sym)
-    
+
   case t.kind
   of tyGenericBody, tyGenericInst, tyGenericInvocation:
     for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
@@ -135,10 +135,10 @@ proc hashType(c: var MD5Context, t: PType) =
   of tyArrayConstr:
     c.hashTree(t.sons[0].n)
     c.hashType(t.sons[1])
-  of tyTuple: 
+  of tyTuple:
     if t.n != nil:
       assert(sonsLen(t.n) == sonsLen(t))
-      for i in countup(0, sonsLen(t.n) - 1): 
+      for i in countup(0, sonsLen(t.n) - 1):
         assert(t.n.sons[i].kind == nkSym)
         c &= t.n.sons[i].sym.name.s
         c &= ":"
@@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
   if iiTableGet(w.index.tab, s.id) == InvalidKey:
     w.sstack.add(s)
 
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, 
-                result: var string) = 
-  if n == nil: 
+proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
+                result: var string) =
+  if n == nil:
     # nil nodes have to be stored too:
     result.add("()")
     return
   result.add('(')
-  encodeVInt(ord(n.kind), result) 
+  encodeVInt(ord(n.kind), result)
   # we do not write comments for now
   # Line information takes easily 20% or more of the filesize! Therefore we
   # omit line information if it is the same as the father's line information:
-  if fInfo.fileIndex != n.info.fileIndex: 
+  if fInfo.fileIndex != n.info.fileIndex:
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
@@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     result.add('?')
     encodeVInt(n.info.col, result)
   var f = n.flags * PersistentNodeFlags
-  if f != {}: 
+  if f != {}:
     result.add('$')
     encodeVInt(cast[int32](f), result)
   if n.typ != nil:
@@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     encodeVInt(n.typ.id, result)
     pushType(w, n.typ)
   case n.kind
-  of nkCharLit..nkInt64Lit: 
+  of nkCharLit..nkInt64Lit:
     if n.intVal != 0:
       result.add('!')
       encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit: 
-    if n.floatVal != 0.0: 
+  of nkFloatLit..nkFloat64Lit:
+    if n.floatVal != 0.0:
       result.add('!')
       encodeStr($n.floatVal, result)
   of nkStrLit..nkTripleStrLit:
-    if n.strVal != "": 
+    if n.strVal != "":
       result.add('!')
       encodeStr(n.strVal, result)
   of nkIdent:
@@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     encodeVInt(n.sym.id, result)
     pushSym(w, n.sym)
   else:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       encodeNode(w, n.info, n.sons[i], result)
   add(result, ')')
 
@@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
     setLen(result, oldLen)
   else:
     add(result, '>')
-  
-proc encodeType(w: PRodWriter, t: PType, result: var string) = 
-  if t == nil: 
+
+proc encodeType(w: PRodWriter, t: PType, result: var string) =
+  if t == nil:
     # nil nodes have to be stored too:
     result.add("[]")
     return
@@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   encodeVInt(ord(t.kind), result)
   add(result, '+')
   encodeVInt(t.id, result)
-  if t.n != nil: 
+  if t.n != nil:
     encodeNode(w, unknownLineInfo(), t.n, result)
-  if t.flags != {}: 
+  if t.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv): 
+  if t.callConv != low(t.callConv):
     add(result, '?')
     encodeVInt(ord(t.callConv), result)
-  if t.owner != nil: 
+  if t.owner != nil:
     add(result, '*')
     encodeVInt(t.owner.id, result)
     pushSym(w, t.owner)
-  if t.sym != nil: 
+  if t.sym != nil:
     add(result, '&')
     encodeVInt(t.sym.id, result)
     pushSym(w, t.sym)
-  if t.size != - 1: 
+  if t.size != - 1:
     add(result, '/')
     encodeVBiggestInt(t.size, result)
-  if t.align != 2: 
+  if t.align != 2:
     add(result, '=')
     encodeVInt(t.align, result)
   encodeLoc(w, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1): 
-    if t.sons[i] == nil: 
+  for i in countup(0, sonsLen(t) - 1):
+    if t.sons[i] == nil:
       add(result, "^()")
-    else: 
-      add(result, '^') 
+    else:
+      add(result, '^')
       encodeVInt(t.sons[i].id, result)
       pushType(w, t.sons[i])
 
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = 
+proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
   add(result, '|')
   encodeVInt(ord(lib.kind), result)
   add(result, '|')
@@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   if s.magic != mNone:
     result.add('@')
     encodeVInt(ord(s.magic), result)
-  if s.options != w.options: 
+  if s.options != w.options:
     result.add('!')
     encodeVInt(cast[int32](s.options), result)
-  if s.position != 0: 
+  if s.position != 0:
     result.add('%')
     encodeVInt(s.position, result)
   if s.offset != - 1:
@@ -383,7 +383,7 @@ proc createDb() =
       fullpath varchar(256) not null,
       interfHash varchar(256) not null,
       fullHash varchar(256) not null,
-      
+
       created timestamp not null default (DATETIME('now')),
     );""")
 
@@ -397,7 +397,7 @@ proc createDb() =
 
       foreign key (module) references module(id)
     );""")
-    
+
   db.exec(sql"""
     create table if not exists Type(
       id integer primary key,
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 2dacc25e9..86ecc9db8 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -159,6 +159,17 @@ proc genArgNoParam(p: BProc, n: PNode): Rope =
     initLocExprSingleUse(p, n, a)
     result = rdLoc(a)
 
+template genParamLoop(params) {.dirty.} =
+  if i < sonsLen(typ):
+    assert(typ.n.sons[i].kind == nkSym)
+    let paramType = typ.n.sons[i]
+    if not paramType.typ.isCompileTimeOnly:
+      if params != nil: add(params, ~", ")
+      add(params, genArg(p, ri.sons[i], paramType.sym, ri))
+  else:
+    if params != nil: add(params, ~", ")
+    add(params, genArgNoParam(p, ri.sons[i]))
+
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var op: TLoc
   # this is a hotspot in the compiler
@@ -170,13 +181,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   assert(sonsLen(typ) == sonsLen(typ.n))
   var length = sonsLen(ri)
   for i in countup(1, length - 1):
-    if ri.sons[i].typ.isCompileTimeOnly: continue
-    if params != nil: add(params, ~", ")
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
-    else:
-      add(params, genArgNoParam(p, ri.sons[i]))
+    genParamLoop(params)
   fixupCall(p, le, ri, d, op.r, params)
 
 proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
@@ -198,13 +203,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var length = sonsLen(ri)
   for i in countup(1, length - 1):
     assert(sonsLen(typ) == sonsLen(typ.n))
-    if ri.sons[i].typ.isCompileTimeOnly: continue
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
-    else:
-      add(pl, genArgNoParam(p, ri.sons[i]))
-    if i < length - 1: add(pl, ~", ")
+    genParamLoop(pl)
 
   template genCallPattern {.dirty.} =
     lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
@@ -241,13 +240,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
     genCallPattern()
 
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
-  if ri.sons[i].typ.isCompileTimeOnly:
-    result = nil
-  elif i < sonsLen(typ):
+  if i < sonsLen(typ):
     # 'var T' is 'T&' in C++. This means we ignore the request of
     # any nkHiddenAddr when it's a 'var T'.
-    assert(typ.n.sons[i].kind == nkSym)
-    if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
+    let paramType = typ.n.sons[i]
+    assert(paramType.kind == nkSym)
+    if paramType.typ.isCompileTimeOnly:
+      result = nil
+    elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
       result = genArgNoParam(p, ri.sons[i][0])
     else:
       result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 05a3602d1..388b6d047 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -221,7 +221,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
 
 proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   let newflags =
-    if src.k == locData:
+    if src.s == OnStatic:
       flags + {needToCopy}
     elif tfShallow in dest.t.flags:
       flags - {needToCopy}
@@ -238,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
                       t: PNode) =
   if t == nil: return
   let newflags =
-    if src.k == locData:
+    if src.s == OnStatic:
       flags + {needToCopy}
     elif tfShallow in dest.t.flags:
       flags - {needToCopy}
@@ -287,13 +287,13 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyRef:
     genRefAssign(p, dest, src, flags)
   of tySequence:
-    if needToCopy notin flags and src.k != locData:
+    if needToCopy notin flags and src.s != OnStatic:
       genRefAssign(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
               addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
   of tyString:
-    if needToCopy notin flags and src.k != locData:
+    if needToCopy notin flags and src.s != OnStatic:
       genRefAssign(p, dest, src, flags)
     else:
       if dest.s == OnStack or not usesNativeGC():
@@ -347,8 +347,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       useStringh(p.module)
       linefmt(p, cpsStmts,
-           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1));$n",
-           rdLoc(dest), rdLoc(src))
+           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+           rdLoc(dest), rdLoc(src), getTypeDesc(p.module, ty))
   of tyOpenArray, tyVarargs:
     # open arrays are always on the stack - really? What if a sequence is
     # passed to an open array?
@@ -413,7 +413,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
   var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locData, t, OnUnknown)
+    initLoc(a, locData, t, OnStatic)
     a.r = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
@@ -424,11 +424,11 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
     d.t = t
     d.r = r
 
-proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
+proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
   var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locExpr, t, OnUnknown)
+    initLoc(a, locExpr, t, s)
     a.r = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
@@ -503,15 +503,15 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
       "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
       "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
       "$# = #modInt($#, $#);$n",
+      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
+    prc64: array[mAddI..mPred, string] = [
       "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
       "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
       "$# = #modInt64($#, $#);$n",
-      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
+      "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
     opr: array[mAddI..mPred, string] = [
       "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
       "($#)($# / $#)", "($#)($# % $#)",
-      "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
-      "($#)($# / $#)", "($#)($# % $#)",
       "($#)($# + $#)", "($#)($# - $#)"]
   var a, b: TLoc
   assert(e.sons[1].typ != nil)
@@ -525,16 +525,16 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)]
     putIntoDest(p, d, e.typ, res)
   else:
-    let res = binaryArithOverflowRaw(p, t, a, b, prc[m])
+    let res = binaryArithOverflowRaw(p, t, a, b,
+                                   if t.kind == tyInt64: prc64[m] else: prc[m])
     putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res])
 
 proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   const
-    opr: array[mUnaryMinusI..mAbsI64, string] = [
+    opr: array[mUnaryMinusI..mAbsI, string] = [
       mUnaryMinusI: "((NI$2)-($1))",
       mUnaryMinusI64: "-($1)",
-      mAbsI: "($1 > 0? ($1) : -($1))",
-      mAbsI64: "($1 > 0? ($1) : -($1))"]
+      mAbsI: "($1 > 0? ($1) : -($1))"]
   var
     a: TLoc
     t: PType
@@ -561,11 +561,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "($4)($1 ^ $2)",      # BitxorI
       "(($1 <= $2) ? $1 : $2)", # MinI
       "(($1 >= $2) ? $1 : $2)", # MaxI
-      "($4)((NU64)($1) >> (NU64)($2))", # ShrI64
-      "($4)((NU64)($1) << (NU64)($2))", # ShlI64
-      "($4)($1 & $2)",            # BitandI64
-      "($4)($1 | $2)",            # BitorI64
-      "($4)($1 ^ $2)",            # BitxorI64
       "(($1 <= $2) ? $1 : $2)", # MinF64
       "(($1 >= $2) ? $1 : $2)", # MaxF64
       "($4)((NU$3)($1) + (NU$3)($2))", # AddU
@@ -576,9 +571,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "($1 == $2)",           # EqI
       "($1 <= $2)",           # LeI
       "($1 < $2)",            # LtI
-      "($1 == $2)",           # EqI64
-      "($1 <= $2)",           # LeI64
-      "($1 < $2)",            # LtI64
       "($1 == $2)",           # EqF64
       "($1 <= $2)",           # LeF64
       "($1 < $2)",            # LtF64
@@ -638,7 +630,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
       "$1",                   # UnaryPlusI
       "($3)((NU$2) ~($1))",   # BitnotI
-      "($3)((NU$2) ~($1))",   # BitnotI64
       "$1",                   # UnaryPlusF64
       "-($1)",                # UnaryMinusF64
       "($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems
@@ -694,7 +685,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
         d.s = OnUnknown
         if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
             e.kind == nkHiddenDeref:
-          putIntoDest(p, d, e.typ, rdLoc(a))
+          putIntoDest(p, d, e.typ, rdLoc(a), a.s)
           return
       of tyPtr:
         d.s = OnUnknown         # BUGFIX!
@@ -703,7 +694,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       let typ = skipTypes(a.t, abstractInst)
       if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
            e.kind == nkHiddenDeref:
-        putIntoDest(p, d, e.typ, rdLoc(a))
+        putIntoDest(p, d, e.typ, rdLoc(a), a.s)
         return
     if enforceDeref and mt == ctPtrToArray:
       # we lie about the type for better C interop: 'ptr array[3,T]' is
@@ -711,23 +702,23 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       # See tmissingderef. So we get rid of the deref instead. The codegen
       # ends up using 'memcpy' for the array assignment,
       # so the '&' and '*' cancel out:
-      putIntoDest(p, d, a.t.sons[0], rdLoc(a))
+      putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s)
     else:
-      putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)])
+      putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s)
 
 proc genAddr(p: BProc, e: PNode, d: var TLoc) =
   # careful  'addr(myptrToArray)' needs to get the ampersand:
   if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
     var a: TLoc
     initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e.typ, "&" & a.r)
+    putIntoDest(p, d, e.typ, "&" & a.r, a.s)
     #Message(e.info, warnUser, "HERE NEW &")
   elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
     expr(p, e.sons[0], d)
   else:
     var a: TLoc
     initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e.typ, addrLoc(a))
+    putIntoDest(p, d, e.typ, addrLoc(a), a.s)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
   if d.k == locNone: d.s = a.s
@@ -754,7 +745,18 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
   else: internalError(e.info, "genTupleElem")
   addf(r, ".Field$1", [rope(i)])
-  putIntoDest(p, d, ty.sons[i], r)
+  putIntoDest(p, d, ty.sons[i], r, a.s)
+
+proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
+  var ty = ty
+  assert r != nil
+  while ty != nil:
+    assert(ty.kind in {tyTuple, tyObject})
+    result = lookupInRecord(ty.n, field.name)
+    if result != nil: break
+    if not p.module.compileToCpp: add(r, ".Sup")
+    ty = getUniqueType(ty.sons[0])
+  if result == nil: internalError(field.info, "genCheckedRecordField")
 
 proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
   var a: TLoc
@@ -765,25 +767,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     # we found a unique tuple type which lacks field information
     # so we use Field$i
     addf(r, ".Field$1", [rope(f.position)])
-    putIntoDest(p, d, f.typ, r)
+    putIntoDest(p, d, f.typ, r, a.s)
   else:
-    var field: PSym = nil
-    while ty != nil:
-      if ty.kind notin {tyTuple, tyObject}:
-        internalError(e.info, "genRecordField")
-      field = lookupInRecord(ty.n, f.name)
-      if field != nil: break
-      if not p.module.compileToCpp: add(r, ".Sup")
-      ty = getUniqueType(ty.sons[0])
-    if field == nil: internalError(e.info, "genRecordField 2 ")
+    let field = lookupFieldAgain(p, ty, f, r)
     if field.loc.r == nil: internalError(e.info, "genRecordField 3")
     addf(r, ".$1", [field.loc.r])
-    putIntoDest(p, d, field.typ, r)
+    putIntoDest(p, d, field.typ, r, a.s)
   #d.s = a.s
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
 
-proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
+proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
+                   origTy: PType) =
   var test, u, v: TLoc
   for i in countup(1, sonsLen(e) - 1):
     var it = e.sons[i]
@@ -795,8 +790,12 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
     assert(disc.kind == nkSym)
     initLoc(test, locNone, it.typ, OnStack)
     initLocExpr(p, it.sons[1], u)
-    initLoc(v, locExpr, disc.typ, OnUnknown)
-    v.r = "$1.$2" % [obj, disc.sym.loc.r]
+    var o = obj
+    let d = lookupFieldAgain(p, origTy, disc.sym, o)
+    initLoc(v, locExpr, d.typ, OnUnknown)
+    v.r = o
+    v.r.add(".")
+    v.r.add(d.loc.r)
     genInExprAux(p, it, u, v, test)
     let id = nodeTableTestOrSet(p.module.dataCache,
                                newStrNode(nkStrLit, field.name.s), gBackendId)
@@ -813,27 +812,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
 
 proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
   if optFieldCheck in p.options:
-    var
-      a: TLoc
-      f, field: PSym
-      ty: PType
-      r: Rope
-    ty = genRecordFieldAux(p, e.sons[0], d, a)
-    r = rdLoc(a)
-    f = e.sons[0].sons[1].sym
-    field = nil
-    while ty != nil:
-      assert(ty.kind in {tyTuple, tyObject})
-      field = lookupInRecord(ty.n, f.name)
-      if field != nil: break
-      if not p.module.compileToCpp: add(r, ".Sup")
-      ty = getUniqueType(ty.sons[0])
-    if field == nil: internalError(e.info, "genCheckedRecordField")
+    var a: TLoc
+    let ty = genRecordFieldAux(p, e.sons[0], d, a)
+    var r = rdLoc(a)
+    let f = e.sons[0].sons[1].sym
+    let field = lookupFieldAgain(p, ty, f, r)
     if field.loc.r == nil:
       internalError(e.info, "genCheckedRecordField") # generate the checks:
-    genFieldCheck(p, e, r, field)
+    genFieldCheck(p, e, r, field, ty)
     add(r, rfmt(nil, ".$1", field.loc.r))
-    putIntoDest(p, d, field.typ, r)
+    putIntoDest(p, d, field.typ, r, a.s)
   else:
     genRecordField(p, e.sons[0], d)
 
@@ -860,7 +848,7 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
         localError(x.info, errIndexOutOfBounds)
   d.inheritLocation(a)
   putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
-              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first))
+              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
 
 proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -869,7 +857,7 @@ proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
   var ty = skipTypes(a.t, abstractVarRange)
   if d.k == locNone: d.s = a.s
   putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)))
+              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
 
 proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -880,7 +868,7 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
             rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
   if d.k == locNone: d.s = a.s
   putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)))
+              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
 
 proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -903,7 +891,7 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
     a.r = rfmt(nil, "(*$1)", a.r)
   putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
-              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)))
+              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
 
 proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   var ty = skipTypes(n.sons[0].typ, abstractVarRange)
@@ -1061,9 +1049,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
   #    seq->data[seq->len-1] = x;
   let seqAppendPattern = if not p.module.compileToCpp:
-                           "$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
                          else:
-                           "$1 = ($2) #incrSeq($1, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
   var a, b, dest: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
@@ -1073,8 +1061,9 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
       getTypeDesc(p.module, skipTypes(e.sons[2].typ, abstractVar))])
   keepAlive(p, a)
   initLoc(dest, locExpr, b.t, OnHeap)
-  dest.r = rfmt(nil, "$1->data[$1->$2-1]", rdLoc(a), lenField(p))
+  dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p))
   genAssignment(p, dest, b, {needToCopy, afDestIsNil})
+  lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p))
   gcUsage(e)
 
 proc genReset(p: BProc, n: PNode) =
@@ -1158,20 +1147,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
   else:
     constructLoc(p, tmp)
   discard getTypeDesc(p.module, t)
+  let ty = getUniqueType(t)
   for i in 1 .. <e.len:
     let it = e.sons[i]
     var tmp2: TLoc
     tmp2.r = r
-    var field: PSym = nil
-    var ty = getUniqueType(t)
-    while ty != nil:
-      field = lookupInRecord(ty.n, it.sons[0].sym.name)
-      if field != nil: break
-      if not p.module.compileToCpp: add(tmp2.r, ".Sup")
-      ty = getUniqueType(ty.sons[0])
-    if field == nil or field.loc.r == nil: internalError(e.info, "genObjConstr")
+    let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r)
+    if field.loc.r == nil: internalError(e.info, "genObjConstr")
     if it.len == 3 and optFieldCheck in p.options:
-      genFieldCheck(p, it.sons[2], tmp2.r, field)
+      genFieldCheck(p, it.sons[2], r, field, ty)
     add(tmp2.r, ".")
     add(tmp2.r, field.loc.r)
     tmp2.k = locTemp
@@ -1280,7 +1264,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
     r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
   else:
     r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
-  putIntoDest(p, d, getSysType(tyBool), r)
+  putIntoDest(p, d, getSysType(tyBool), r, a.s)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
   genOf(p, n.sons[1], n.sons[2].typ, d)
@@ -1292,45 +1276,47 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
   case t.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     putIntoDest(p, d, e.typ,
-                ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]))
+                ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s)
   of tyFloat..tyFloat128:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]))
+    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s)
   of tyBool:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]))
+    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s)
   of tyChar:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]))
+    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s)
   of tyEnum, tyOrdinal:
     putIntoDest(p, d, e.typ,
                 ropecg(p.module, "#reprEnum($1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t)]))
+                rdLoc(a), genTypeInfo(p.module, t)]), a.s)
   of tyString:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]))
+    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
   of tySet:
     putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [
-                addrLoc(a), genTypeInfo(p.module, t)]))
+                addrLoc(a), genTypeInfo(p.module, t)]), a.s)
   of tyOpenArray, tyVarargs:
     var b: TLoc
     case a.t.kind
     of tyOpenArray, tyVarargs:
-      putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)])
+      putIntoDest(p, b, e.typ, "$1, $1Len0" % [rdLoc(a)], a.s)
     of tyString, tySequence:
       putIntoDest(p, b, e.typ,
-                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)])
+                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s)
     of tyArray, tyArrayConstr:
       putIntoDest(p, b, e.typ,
-                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))])
+                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s)
     else: internalError(e.sons[0].info, "genRepr()")
     putIntoDest(p, d, e.typ,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
-        genTypeInfo(p.module, elemType(t))]))
+        genTypeInfo(p.module, elemType(t))]), a.s)
   of tyCString, tyArray, tyArrayConstr, tyRef, tyPtr, tyPointer, tyNil,
      tySequence:
     putIntoDest(p, d, e.typ,
                 ropecg(p.module, "#reprAny($1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t)]))
+                rdLoc(a), genTypeInfo(p.module, t)]), a.s)
+  of tyEmpty:
+    localError(e.info, "'repr' doesn't support 'void' type")
   else:
     putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
-                                   [addrLoc(a), genTypeInfo(p.module, t)]))
+                                   [addrLoc(a), genTypeInfo(p.module, t)]), a.s)
   gcUsage(e)
 
 proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
@@ -1492,11 +1478,11 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of 1, 2, 4, 8:
     case op
     of mIncl:
-      var ts = "NI" & $(size * 8)
+      var ts = "NU" & $(size * 8)
       binaryStmtInExcl(p, e, d,
           "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
     of mExcl:
-      var ts = "NI" & $(size * 8)
+      var ts = "NU" & $(size * 8)
       binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
           ts & ")*8)));$n")
     of mCard:
@@ -1555,13 +1541,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   let etyp = skipTypes(e.typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
     putIntoDest(p, d, e.typ, "(*($1*) ($2))" %
-        [getTypeDesc(p.module, e.typ), addrLoc(a)])
+        [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s)
   elif etyp.kind == tyProc and etyp.callConv == ccClosure:
     putIntoDest(p, d, e.typ, "(($1) ($2))" %
-        [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)])
+        [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s)
   else:
     putIntoDest(p, d, e.typ, "(($1) ($2))" %
-        [getTypeDesc(p.module, e.typ), rdCharLoc(a)])
+        [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
 
 proc genCast(p: BProc, e: PNode, d: var TLoc) =
   const floatTypes = {tyFloat..tyFloat128}
@@ -1581,7 +1567,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     tmp.s = OnStack
     tmp.flags = {}
     expr(p, e.sons[1], tmp)
-    putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl])
+    putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s)
   else:
     # I prefer the shorter cast version for pointer types -> generate less
     # C code; plus it's the right thing to do for closures:
@@ -1591,16 +1577,17 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
   var a: TLoc
   var dest = skipTypes(n.typ, abstractVar)
   # range checks for unsigned turned out to be buggy and annoying:
-  if optRangeCheck notin p.options or dest.kind in {tyUInt..tyUInt64}:
+  if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
+                                             {tyUInt..tyUInt64}:
     initLocExpr(p, n.sons[0], a)
     putIntoDest(p, d, n.typ, "(($1) ($2))" %
-        [getTypeDesc(p.module, dest), rdCharLoc(a)])
+        [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s)
   else:
     initLocExpr(p, n.sons[0], a)
     putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
         getTypeDesc(p.module, dest), rdCharLoc(a),
         genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
-        rope(magic)]))
+        rope(magic)]), a.s)
 
 proc genConv(p: BProc, e: PNode, d: var TLoc) =
   let destType = e.typ.skipTypes({tyVar, tyGenericInst})
@@ -1612,13 +1599,13 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)])
+  putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
   putIntoDest(p, d, skipTypes(n.typ, abstractVar),
-              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]))
+              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s)
   gcUsage(n)
 
 proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
@@ -1661,7 +1648,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   case op
   of mOr, mAnd: genAndOr(p, e, d, op)
   of mNot..mToBiggestInt: unaryArith(p, e, d, op)
-  of mUnaryMinusI..mAbsI64: unaryArithOverflow(p, e, d, op)
+  of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
   of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
   of mShrI..mXor: binaryArith(p, e, d, op)
   of mEqProc: genEqProc(p, e, d)
@@ -1746,6 +1733,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
+    echo "from here ", p.prc.name.s, " ", p.prc.info
+    writestacktrace()
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
   of mSpawn:
     let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil)
@@ -1768,7 +1757,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
     var t = getUniqueType(n.typ)
     discard getTypeDesc(p.module, t) # so that any fields are initialized
     var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
-    fillLoc(d, locData, t, "TMP" & rope(id), OnHeap)
+    fillLoc(d, locData, t, "TMP" & rope(id), OnStatic)
     if id == gBackendId:
       # expression not found in the cache:
       inc(gBackendId)
@@ -1807,7 +1796,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
                [rdLoc(d), rdSetElemLoc(a, e.typ)])
     else:
       # small set
-      var ts = "NI" & $(getSize(e.typ) * 8)
+      var ts = "NU" & $(getSize(e.typ) * 8)
       lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
       for i in countup(0, sonsLen(e) - 1):
         if e.sons[i].kind == nkRange:
@@ -1815,13 +1804,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, e.sons[i].sons[0], a)
           initLocExpr(p, e.sons[i].sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
-              "$2 |=(1<<((" & ts & ")($1)%(sizeof(" & ts & ")*8)));$n", [
+              "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
               rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ),
               rdSetElemLoc(b, e.typ)])
         else:
           initLocExpr(p, e.sons[i], a)
           lineF(p, cpsStmts,
-               "$1 |=(1<<((" & ts & ")($2)%(sizeof(" & ts & ")*8)));$n",
+               "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
                [rdLoc(d), rdSetElemLoc(a, e.typ)])
 
 proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
@@ -1854,7 +1843,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
     var tmp = "LOC" & rope(p.labels)
     addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
         [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
-    putIntoDest(p, d, n.typ, tmp)
+    putIntoDest(p, d, n.typ, tmp, OnStatic)
   else:
     var tmp, a, b: TLoc
     initLocExpr(p, n.sons[0], a)
@@ -1908,10 +1897,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
               r, genTypeInfo(p.module, dest))
   if n.sons[0].typ.kind != tyObject:
     putIntoDest(p, d, n.typ,
-                "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)])
+                "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s)
   else:
     putIntoDest(p, d, n.typ, "(*($1*) ($2))" %
-                             [getTypeDesc(p.module, dest), addrLoc(a)])
+                             [getTypeDesc(p.module, dest), addrLoc(a)], a.s)
 
 proc downConv(p: BProc, n: PNode, d: var TLoc) =
   if p.module.compileToCpp:
@@ -1943,9 +1932,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
         linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
       else:
         r = "&" & r
-        putIntoDest(p, d, n.typ, r)
+        putIntoDest(p, d, n.typ, r, a.s)
     else:
-      putIntoDest(p, d, n.typ, r)
+      putIntoDest(p, d, n.typ, r, a.s)
 
 proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   var t = getUniqueType(n.typ)
@@ -1960,7 +1949,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
          [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
 
   if d.k == locNone:
-    fillLoc(d, locData, t, tmp, OnHeap)
+    fillLoc(d, locData, t, tmp, OnStatic)
   else:
     putDataIntoDest(p, d, t, tmp)
 
@@ -1978,6 +1967,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         genProc(p.module, sym)
       putLocIntoDest(p, d, sym.loc)
     of skProc, skConverter, skIterators:
+      if sfCompileTime in sym.flags:
+        localError(n.info, "request to generate code for .compileTime proc: " &
+           sym.name.s)
       genProc(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
         internalError(n.info, "expr: proc not init " & sym.name.s)
@@ -1987,7 +1979,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
         putLocIntoDest(p, d, sym.loc)
       elif isSimpleConst(sym.typ):
-        putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ))
+        putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
       else:
         genComplexConst(p, sym, d)
     of skEnumField:
@@ -2074,6 +2066,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkStmtList:
     for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
   of nkIfExpr, nkIfStmt: genIf(p, n, d)
+  of nkWhen:
+    # This should be a "when nimvm" node.
+    expr(p, n.sons[1].sons[0], d)
   of nkObjDownConv: downConv(p, n, d)
   of nkObjUpConv: upConv(p, n, d)
   of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
@@ -2128,7 +2123,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       # due to a bug/limitation in the lambda lifting, unused inner procs
       # are not transformed correctly. We work around this issue (#411) here
       # by ensuring it's no inner proc (owner is a module):
-      if prc.skipGenericOwner.kind == skModule:
+      if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
         if (optDeadCodeElim notin gGlobalOptions and
             sfDeadCodeElim notin getModule(prc).flags) or
             ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
@@ -2150,7 +2145,7 @@ proc genNamedConstExpr(p: BProc, n: PNode): Rope =
 proc genConstSimpleList(p: BProc, n: PNode): Rope =
   var length = sonsLen(n)
   result = rope("{")
-  for i in countup(0, length - 2):
+  for i in countup(ord(n.kind == nkObjConstr), length - 2):
     addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
   if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1]))
   addf(result, "}$n", [])
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 6d29b1684..f4a7c4400 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} =
 proc blockBody(b: var TBlock): Rope =
   result = b.sections[cpsLocals]
   if b.frameLen > 0:
-    result.addf("F.len+=$1;$n", [b.frameLen.rope])
+    result.addf("FR.len+=$1;$n", [b.frameLen.rope])
   result.add(b.sections[cpsInit])
   result.add(b.sections[cpsStmts])
 
@@ -123,7 +123,7 @@ proc endBlock(p: BProc) =
       ~"}$n"
   let frameLen = p.blocks[topBlock].frameLen
   if frameLen > 0:
-    blockEnd.addf("F.len-=$1;$n", [frameLen.rope])
+    blockEnd.addf("FR.len-=$1;$n", [frameLen.rope])
   endBlock(p, blockEnd)
 
 proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@@ -1002,8 +1002,10 @@ proc genAsmStmt(p: BProc, t: PNode) =
 proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
   if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
-    if n.sons[0].strVal.startsWith("/*TYPESECTION*/"): result = cfsTypes
-    elif n.sons[0].strVal.startsWith("/*VARSECTION*/"): result = cfsVars
+    let sec = n.sons[0].strVal
+    if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
+    elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
+    elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
 
 proc genEmit(p: BProc, t: PNode) =
   var s = genAsmOrEmitStmt(p, t.sons[1])
@@ -1099,7 +1101,10 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
     genGotoVar(p, e.sons[1])
   elif not fieldDiscriminantCheckNeeded(p, e):
     var a: TLoc
-    initLocExpr(p, e.sons[0], a)
+    if e[0].kind in {nkDerefExpr, nkHiddenDeref}:
+      genDeref(p, e[0], a, enforceDeref=true)
+    else:
+      initLocExpr(p, e.sons[0], a)
     if fastAsgn: incl(a.flags, lfNoDeepCopy)
     assert(a.t != nil)
     loadInto(p, e.sons[0], e.sons[1], a)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 3742fd2fd..1ed9ce113 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -323,7 +323,8 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
     result = OnUnknown
 
 proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
-                   check: var IntSet, declareEnvironment=true) =
+                   check: var IntSet, declareEnvironment=true;
+                   weakDep=false) =
   params = nil
   if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
     rettype = ~"void"
@@ -341,6 +342,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
       add(params, ~"*")
       incl(param.loc.flags, lfIndirect)
       param.loc.s = OnUnknown
+    elif weakDep:
+      add(params, getTypeDescWeak(m, param.typ, check))
     else:
       add(params, getTypeDescAux(m, param.typ, check))
     add(params, ~" ")
@@ -438,6 +441,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
       elif fieldType.kind == tySequence:
         # we need to use a weak dependency here for trecursive_table.
         addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
+      elif field.bitsize != 0:
+        addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)])
       else:
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
@@ -577,7 +582,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
     result = getTypeName(t)
     idTablePut(m.typeCache, t, result)
     var rettype, desc: Rope
-    genProcParams(m, t, rettype, desc, check)
+    genProcParams(m, t, rettype, desc, check, true, true)
     if not isImportedType(t):
       if t.callConv != ccClosure: # procedure vars may need a closure!
         addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 4ba6643ec..6dfd7b52c 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -99,7 +99,10 @@ proc getUniqueType*(key: PType): PType =
       gCanonicalTypes[k] = key
       result = key
   of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
-    internalError("getUniqueType")
+    if key.sym != nil:
+      internalError(key.sym.info, "metatype not eliminated")
+    else:
+      internalError("metatype not eliminated")
   of tyDistinct:
     if key.deepCopy != nil: result = key
     else: result = getUniqueType(lastSon(key))
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index da9c6f653..f63134b66 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -10,12 +10,11 @@
 ## This module implements the C code generator.
 
 import
-  ast, astalgo, hashes, trees, platform, magicsys, extccomp,
-  options, intsets,
-  nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
-  ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
-  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
-  semparallel
+  ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
+  nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
+  ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
+  condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
+  lowerings, semparallel
 
 import strutils except `%` # collides with ropes.`%`
 
@@ -376,7 +375,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
   var a = "&" & s.loc.r
   if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
   lineF(p, cpsInit,
-       "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
+       "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n",
        [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
         genTypeInfo(p.module, s.loc.t)])
   inc(p.maxFrameLen)
@@ -506,8 +505,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
     if lib.path.kind in {nkStrLit..nkTripleStrLit}:
       var s: TStringSeq = @[]
       libCandidates(lib.path.strVal, s)
-      if gVerbosity >= 2:
-        msgWriteln("Dependency: " & lib.path.strVal)
+      rawMessage(hintDependency, lib.path.strVal)
       var loadlib: Rope = nil
       for i in countup(0, high(s)):
         inc(m.labels)
@@ -599,7 +597,7 @@ proc cgsym(m: BModule, name: string): Rope =
     of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
     of skVar, skResult, skLet: genVarPrototype(m, sym)
     of skType: discard getTypeDesc(m, sym.typ)
-    else: internalError("cgsym: " & name)
+    else: internalError("cgsym: " & name & ": " & $sym.kind)
   else:
     # we used to exclude the system module from this check, but for DLL
     # generation support this sloppyness leads to hard to detect bugs, so
@@ -611,10 +609,12 @@ proc generateHeaders(m: BModule) =
   add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
   var it = PStrEntry(m.headerFiles.head)
   while it != nil:
-    if it.data[0] notin {'\"', '<'}:
-      addf(m.s[cfsHeaders], "$N#include \"$1\"$N", [rope(it.data)])
+    if it.data[0] == '#':
+      add(m.s[cfsHeaders], rope(it.data.replace('`', '"') & tnl))
+    elif it.data[0] notin {'\"', '<'}:
+      addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it.data)])
     else:
-      addf(m.s[cfsHeaders], "$N#include $1$N", [rope(it.data)])
+      addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)])
     it = PStrEntry(it.next)
 
 proc retIsNotVoid(s: PSym): bool =
@@ -623,7 +623,7 @@ proc retIsNotVoid(s: PSym): bool =
 proc initFrame(p: BProc, procname, filename: Rope): Rope =
   discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
-    discard cgsym(p.module, "TVarSlot")
+    discard cgsym(p.module, "VarSlot")
     result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
                   procname, filename, p.maxFrameLen.rope,
                   p.blocks[0].frameLen.rope)
@@ -676,8 +676,11 @@ proc genProcAux(m: BModule, prc: PSym) =
   closureSetup(p, prc)
   genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
   var generatedProc: Rope
+  if sfNoReturn in prc.flags:
+    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+      header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasNakedDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
       header = "__declspec(naked) " & header
     generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
                          header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
@@ -718,10 +721,14 @@ proc genProcPrototype(m: BModule, sym: PSym) =
                         getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
   elif not containsOrIncl(m.declaredProtos, sym.id):
     var header = genProcHeader(m, sym)
+    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
+      header = "__declspec(noreturn) " & header
     if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
       header = "extern \"C\" " & header
-    if sfPure in sym.flags and hasNakedAttribute in CC[cCompiler].props:
+    if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
       header.add(" __attribute__((naked))")
+    if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
+      header.add(" __attribute__((noreturn))")
     add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
 
 proc genProcNoForward(m: BModule, prc: PSym) =
@@ -753,7 +760,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
   var m = p.module
   useHeader(m, sym)
   if sym.loc.k == locNone:
-    fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown)
+    fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnStatic)
   if lfNoDecl in sym.loc.flags: return
   # declare implementation:
   var q = findPendingModule(m, sym)
@@ -808,7 +815,7 @@ proc genVarPrototype(m: BModule, sym: PSym) =
   genVarPrototypeAux(m, sym)
 
 proc addIntTypes(result: var Rope) {.inline.} =
-  addf(result, "#define NIM_INTBITS $1", [
+  addf(result, "#define NIM_INTBITS $1" & tnl, [
     platform.CPU[targetCPU].intSize.rope])
 
 proc getCopyright(cfile: string): Rope =
@@ -846,14 +853,14 @@ proc genMainProc(m: BModule) =
     # functions, which might otherwise merge their stack frames.
     PreMainBody =
       "void PreMainInner() {$N" &
-      "\tsystemInit();$N" &
+      "\tsystemInit000();$N" &
       "$1" &
       "$2" &
       "$3" &
       "}$N$N" &
       "void PreMain() {$N" &
       "\tvoid (*volatile inner)();$N" &
-      "\tsystemDatInit();$N" &
+      "\tsystemDatInit000();$N" &
       "\tinner = PreMainInner;$N" &
       "$4$5" &
       "\t(*inner)();$N" &
@@ -943,7 +950,7 @@ proc genMainProc(m: BModule) =
     gBreakpoints.add(m.genFilenames)
 
   let initStackBottomCall =
-    if platform.targetOS == osStandalone: "".rope
+    if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
   inc(m.labels)
   appcg(m, m.s[cfsProcs], PreMainBody, [
@@ -967,8 +974,8 @@ proc getSomeInitName(m: PSym, suffix: string): Rope =
   result.add m.name.s
   result.add suffix
 
-proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init")
-proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit")
+proc getInitName(m: PSym): Rope = getSomeInitName(m, "Init000")
+proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
 
 proc registerModuleToMain(m: PSym) =
   var
@@ -1010,7 +1017,7 @@ proc genInitCode(m: BModule) =
       var procname = makeCString(m.module.name.s)
       add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
-      add(prc, ~"\tTFrame F; F.len = 0;$N")
+      add(prc, ~"\tTFrame F; FR.len = 0;$N")
 
   add(prc, genSectionStart(cpsInit))
   add(prc, m.preInitProc.s(cpsInit))
@@ -1103,7 +1110,7 @@ proc rawNewModule(module: PSym, filename: string): BModule =
 
 proc nullify[T](arr: var T) =
   for i in low(arr)..high(arr):
-    arr[i] = nil
+    arr[i] = Rope(nil)
 
 proc resetModule*(m: BModule) =
   # between two compilations in CAAS mode, we can throw
@@ -1322,4 +1329,3 @@ proc cgenWriteModules* =
   if generatedHeader != nil: writeHeader(generatedHeader)
 
 const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
-
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 6c997b983..3c2c51b76 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -9,33 +9,33 @@
 
 ## This module implements code generation for multi methods.
 
-import 
+import
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
   sempass2, strutils
 
-proc genConv(n: PNode, d: PType, downcast: bool): PNode = 
+proc genConv(n: PNode, d: PType, downcast: bool): PNode =
   var dest = skipTypes(d, abstractPtrs)
   var source = skipTypes(n.typ, abstractPtrs)
-  if (source.kind == tyObject) and (dest.kind == tyObject): 
+  if (source.kind == tyObject) and (dest.kind == tyObject):
     var diff = inheritanceDiff(dest, source)
     if diff == high(int): internalError(n.info, "cgmeth.genConv")
-    if diff < 0: 
+    if diff < 0:
       result = newNodeIT(nkObjUpConv, n.info, d)
       addSon(result, n)
       if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
-    elif diff > 0: 
+    elif diff > 0:
       result = newNodeIT(nkObjDownConv, n.info, d)
       addSon(result, n)
-      if not downcast: 
+      if not downcast:
         internalError(n.info, "cgmeth.genConv: no downcast allowed")
-    else: 
+    else:
       result = n
-  else: 
+  else:
     result = n
-  
-proc methodCall*(n: PNode): PNode = 
+
+proc methodCall*(n: PNode): PNode =
   result = n
-  # replace ordinary method by dispatcher method: 
+  # replace ordinary method by dispatcher method:
   var disp = lastSon(result.sons[0].sym.ast).sym
   assert sfDispatcher in disp.flags
   result.sons[0].sym = disp
@@ -47,30 +47,35 @@ proc methodCall*(n: PNode): PNode =
 var
   gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
 
-proc sameMethodBucket(a, b: PSym): bool = 
-  result = false
-  if a.name.id != b.name.id: return 
-  if sonsLen(a.typ) != sonsLen(b.typ): 
+type
+  MethodResult = enum No, Invalid, Yes
+
+proc sameMethodBucket(a, b: PSym): MethodResult =
+  if a.name.id != b.name.id: return
+  if sonsLen(a.typ) != sonsLen(b.typ):
     return                    # check for return type:
-  if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return 
-  for i in countup(1, sonsLen(a.typ) - 1): 
+  if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
+  for i in countup(1, sonsLen(a.typ) - 1):
     var aa = a.typ.sons[i]
     var bb = b.typ.sons[i]
-    while true: 
+    while true:
       aa = skipTypes(aa, {tyGenericInst})
       bb = skipTypes(bb, {tyGenericInst})
-      if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): 
+      if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
         aa = aa.lastSon
         bb = bb.lastSon
-      else: 
-        break 
-    if sameType(aa, bb) or
-        (aa.kind == tyObject) and (bb.kind == tyObject) and
-        (inheritanceDiff(bb, aa) < 0):
-      discard
+      else:
+        break
+    if sameType(aa, bb):
+      if aa.kind == tyObject and result != Invalid: result = Yes
+    elif aa.kind == tyObject and bb.kind == tyObject:
+      let diff = inheritanceDiff(bb, aa)
+      if diff < 0:
+        if result != Invalid: result = Yes
+      elif diff != high(int):
+        result = Invalid
     else:
-      return
-  result = true
+      return No
 
 proc attachDispatcher(s: PSym, dispatcher: PNode) =
   var L = s.ast.len-1
@@ -133,18 +138,31 @@ proc fixupDispatcher(meth, disp: PSym) =
 
 proc methodDef*(s: PSym, fromCache: bool) =
   var L = len(gMethods)
+  var witness: PSym
   for i in countup(0, L - 1):
     var disp = gMethods[i].dispatcher
-    if sameMethodBucket(disp, s):
+    case sameMethodBucket(disp, s)
+    of Yes:
       add(gMethods[i].methods, s)
       attachDispatcher(s, lastSon(disp.ast))
       fixupDispatcher(s, disp)
       when useEffectSystem: checkMethodEffects(disp, s)
-      return 
+      if sfBase in s.flags and gMethods[i].methods[0] != s:
+        # already exists due to forwarding definition?
+        localError(s.info, "method is not a base")
+      return
+    of No: discard
+    of Invalid:
+      if witness.isNil: witness = gMethods[i].methods[0]
   # create a new dispatcher:
   add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
   if fromCache:
     internalError(s.info, "no method dispatcher found")
+  if witness != nil:
+    localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
+                       "' to method defined here: " & $witness.info)
+  elif sfBase notin s.flags:
+    message(s.info, warnUseBase)
 
 proc relevantCol(methods: TSymSeq, col: int): bool =
   # returns true iff the position is relevant
@@ -154,35 +172,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool =
       let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
       if not sameType(t2, t):
         return true
-  
-proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = 
-  for col in countup(1, sonsLen(a.typ) - 1): 
-    if contains(relevantCols, col): 
+
+proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
+  for col in countup(1, sonsLen(a.typ) - 1):
+    if contains(relevantCols, col):
       var aa = skipTypes(a.typ.sons[col], skipPtrs)
       var bb = skipTypes(b.typ.sons[col], skipPtrs)
       var d = inheritanceDiff(aa, bb)
-      if (d != high(int)): 
+      if (d != high(int)):
         return d
-  
-proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = 
+
+proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
   # we use shellsort here; fast and simple
   var n = len(a)
   var h = 1
-  while true: 
+  while true:
     h = 3 * h + 1
-    if h > n: break 
-  while true: 
+    if h > n: break
+  while true:
     h = h div 3
-    for i in countup(h, n - 1): 
+    for i in countup(h, n - 1):
       var v = a[i]
       var j = i
-      while cmpSignatures(a[j - h], v, relevantCols) >= 0: 
+      while cmpSignatures(a[j - h], v, relevantCols) >= 0:
         a[j] = a[j - h]
         j = j - h
-        if j < h: break 
+        if j < h: break
       a[j] = v
-    if h == 1: break 
-  
+    if h == 1: break
+
 proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
   var base = lastSon(methods[0].ast).sym
   result = base
@@ -199,7 +217,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
         addSon(isn, newSymNode(iss))
         addSon(isn, newSymNode(base.typ.n.sons[col].sym))
         addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
-        if cond != nil: 
+        if cond != nil:
           var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
           addSon(a, newSymNode(ands))
           addSon(a, cond)
@@ -209,12 +227,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
           cond = isn
     var call = newNodeI(nkCall, base.info)
     addSon(call, newSymNode(curr))
-    for col in countup(1, paramLen - 1): 
-      addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), 
+    for col in countup(1, paramLen - 1):
+      addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
                            curr.typ.sons[col], false))
     var ret: PNode
     if base.typ.sons[0] != nil:
-      var a = newNodeI(nkAsgn, base.info)
+      var a = newNodeI(nkFastAsgn, base.info)
       addSon(a, newSymNode(base.ast.sons[resultPos].sym))
       addSon(a, call)
       ret = newNodeI(nkReturnStmt, base.info)
@@ -230,13 +248,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
       disp = ret
   result.ast.sons[bodyPos] = disp
 
-proc generateMethodDispatchers*(): PNode = 
+proc generateMethodDispatchers*(): PNode =
   result = newNode(nkStmtList)
-  for bucket in countup(0, len(gMethods) - 1): 
+  for bucket in countup(0, len(gMethods) - 1):
     var relevantCols = initIntSet()
-    for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): 
+    for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
       if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
     sortBucket(gMethods[bucket].methods, relevantCols)
     addSon(result,
            newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))
-
diff --git a/compiler/commands.nim b/compiler/commands.nim
index b6ebb6bcb..b38e52bc5 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -17,11 +17,12 @@ template bootSwitch(name, expr, userString: expr): expr =
   const name = if expr: " " & userString else: ""
 
 bootSwitch(usedRelease, defined(release), "-d:release")
-bootSwitch(usedGnuReadline, defined(useGnuReadline), "-d:useGnuReadline")
+bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
 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(usedGoGC, defined(gogc), "--gc:go")
 bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
@@ -64,14 +65,15 @@ proc getCommandLineDesc(): string =
 
 proc helpOnError(pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(getCommandLineDesc())
+    msgWriteln(getCommandLineDesc(), {msgStdout})
     msgQuit(0)
 
 proc writeAdvancedUsage(pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(`%`(HelpMessage, [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]) & AdvancedUsage)
+                                 CPU[platform.hostCPU].name]) & AdvancedUsage,
+               {msgStdout})
     msgQuit(0)
 
 proc writeVersionInfo(pass: TCmdLinePass) =
@@ -86,7 +88,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
 
     msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
       usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
-      usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedNoGC)
+      usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC)
     msgQuit(0)
 
 var
@@ -94,7 +96,7 @@ var
 
 proc writeCommandLineUsage() =
   if not helpWritten:
-    msgWriteln(getCommandLineDesc())
+    msgWriteln(getCommandLineDesc(), {msgStdout})
     helpWritten = true
 
 proc addPrefix(switch: string): string =
@@ -127,6 +129,18 @@ proc processOnOffSwitch(op: TOptions, arg: string, pass: TCmdLinePass,
   of wOff: gOptions = gOptions - op
   else: localError(info, errOnOrOffExpectedButXFound, arg)
 
+proc processOnOffSwitchOrList(op: TOptions, arg: string, pass: TCmdLinePass,
+                              info: TLineInfo): bool =
+  result = false
+  case whichKeyword(arg)
+  of wOn: gOptions = gOptions + op
+  of wOff: gOptions = gOptions - op
+  else:
+    if arg == "list":
+      result = true
+    else:
+      localError(info, errOnOffOrListExpectedButXFound, arg)
+
 proc processOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdLinePass,
                          info: TLineInfo) =
   case whichKeyword(arg)
@@ -140,6 +154,10 @@ proc expectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
 proc expectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   if arg != "": localError(info, errCmdLineNoArgExpected, addPrefix(switch))
 
+var
+  enableNotes: TNoteKinds
+  disableNotes: TNoteKinds
+
 proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
                          info: TLineInfo; orig: string) =
   var id = ""  # arg = "X]:on|off"
@@ -161,8 +179,12 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
     if x >= 0: n = TNoteKind(x + ord(warnMin))
     else: localError(info, "unknown warning: " & id)
   case whichKeyword(substr(arg, i))
-  of wOn: incl(gNotes, n)
-  of wOff: excl(gNotes, n)
+  of wOn:
+    incl(gNotes, n)
+    incl(enableNotes, n)
+  of wOff:
+    excl(gNotes, n)
+    incl(disableNotes, n)
   else: localError(info, errOnOrOffExpectedButXFound, arg)
 
 proc processCompile(filename: string) =
@@ -181,6 +203,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
     of "v2":           result = gSelectedGC == gcV2
     of "markandsweep": result = gSelectedGC == gcMarkAndSweep
     of "generational": result = gSelectedGC == gcGenerational
+    of "go":           result = gSelectedGC == gcGo
     of "none":         result = gSelectedGC == gcNone
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
   of "opt":
@@ -229,7 +252,8 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of "experimental": result = gExperimentalMode
   else: invalidCmdLineOption(passCmd1, switch, info)
 
-proc processPath(path: string, notRelativeToProj = false): string =
+proc processPath(path: string, notRelativeToProj = false,
+                               cfginfo = unknownLineInfo()): string =
   let p = if notRelativeToProj or os.isAbsolute(path) or
               '$' in path or path[0] == '.':
             path
@@ -239,6 +263,7 @@ proc processPath(path: string, notRelativeToProj = false): string =
     "nim", getPrefixDir(),
     "lib", libpath,
     "home", removeTrailingDirSep(os.getHomeDir()),
+    "config", cfginfo.toFullPath().splitFile().dir,
     "projectname", options.gProjectName,
     "projectpath", options.gProjectPath])
 
@@ -281,7 +306,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   case switch.normalize
   of "path", "p":
     expectArg(switch, arg, pass, info)
-    addPath(processPath(arg), info)
+    addPath(processPath(arg, cfginfo=info), info)
   of "nimblepath", "babelpath":
     # keep the old name for compat
     if pass in {passCmd2, passPP} and not options.gNoNimblePath:
@@ -363,14 +388,19 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     of "generational":
       gSelectedGC = gcGenerational
       defineSymbol("gcgenerational")
+    of "go":
+      gSelectedGC = gcGo
+      defineSymbol("gogc")
     of "none":
       gSelectedGC = gcNone
       defineSymbol("nogc")
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
-  of "warnings", "w": processOnOffSwitch({optWarns}, arg, pass, info)
+  of "warnings", "w":
+    if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
   of "warning": processSpecificNote(arg, wWarning, pass, info, switch)
   of "hint": processSpecificNote(arg, wHint, pass, info, switch)
-  of "hints": processOnOffSwitch({optHints}, arg, pass, info)
+  of "hints":
+    if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
   of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
   of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
   of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
@@ -405,6 +435,8 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info)
   of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info)
   of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
+  of "reportconceptfailures":
+    processOnOffSwitchG({optReportConceptFailures}, arg, pass, info)
   of "threads":
     processOnOffSwitchG({optThreads}, arg, pass, info)
     #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe)
@@ -501,6 +533,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "verbosity":
     expectArg(switch, arg, pass, info)
     gVerbosity = parseInt(arg)
+    gNotes = NotesVerbosity[gVerbosity]
+    incl(gNotes, enableNotes)
+    excl(gNotes, disableNotes)
   of "parallelbuild":
     expectArg(switch, arg, pass, info)
     gNumberOfProcessors = parseInt(arg)
@@ -530,6 +565,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "genscript":
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optGenScript)
+  of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
   of "lib":
     expectArg(switch, arg, pass, info)
     libpath = processPath(arg, notRelativeToProj=true)
@@ -579,6 +615,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "experimental":
     expectNoArg(switch, arg, pass, info)
     gExperimentalMode = true
+  of "assembler":
+    cAssembler = nameToCC(arg)
+    if cAssembler notin cValidAssemblers:
+      localError(info, errGenerated, "'$1' is not a valid assembler." % [arg])
   else:
     if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
     else: invalidCmdLineOption(pass, switch, info)
@@ -608,11 +648,18 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
 proc processArgument*(pass: TCmdLinePass; p: OptParser;
                       argsCount: var int): bool =
   if argsCount == 0:
-    options.command = p.key
+    # nim filename.nims  is the same as "nim e filename.nims":
+    if p.key.endswith(".nims"):
+      options.command = "e"
+      options.gProjectName = unixToNativePath(p.key)
+      arguments = cmdLineRest(p)
+      result = true
+    elif pass != passCmd2:
+      options.command = p.key
   else:
     if pass == passCmd1: options.commandArgs.add p.key
     if argsCount == 1:
-      # support UNIX style filenames anywhere for portable build scripts:
+      # support UNIX style filenames everywhere for portable build scripts:
       options.gProjectName = unixToNativePath(p.key)
       arguments = cmdLineRest(p)
       result = true
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index ad7d80c85..60e8f2826 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -88,3 +88,8 @@ proc initDefines*() =
   defineSymbol("nimalias")
   defineSymbol("nimlocks")
   defineSymbol("nimnode")
+  defineSymbol("nimnomagic64")
+  defineSymbol("nimvarargstyped")
+  defineSymbol("nimtypedescfixed")
+  defineSymbol("nimKnowsNimvm")
+  defineSymbol("nimArrIdx")
diff --git a/compiler/crc.nim b/compiler/crc.nim
deleted file mode 100644
index a8b61f2a5..000000000
--- a/compiler/crc.nim
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import 
-  strutils
-
-type 
-  TCrc32* = int32
-
-const 
-  InitCrc32* = TCrc32(- 1)
-  InitAdler32* = int32(1)
-
-proc updateCrc32*(val: int8, crc: TCrc32): TCrc32 {.inline.}
-proc updateCrc32*(val: char, crc: TCrc32): TCrc32 {.inline.}
-proc crcFromBuf*(buf: pointer, length: int): TCrc32
-proc strCrc32*(s: string): TCrc32
-proc crcFromFile*(filename: string): TCrc32
-proc updateAdler32*(adler: int32, buf: pointer, length: int): int32
-# implementation
-
-type 
-  TCRC_TabEntry = int
-
-const 
-  crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508, 
-    - 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274, 
-    2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249, 
-    - 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861, 
-    1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254, 
-    - 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096, 
-    1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667, 
-    - 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451, 
-    1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992, 
-    - 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974, 
-    1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021, 
-    - 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465, 
-    112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922, 
-    - 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444, 
-    476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271, 
-    - 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271, 
-    366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204, 
-    - 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506, 
-    879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665, 
-    - 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909, 
-    544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846, 
-    - 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912, 
-    - 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997, 
-    1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541, 
-    - 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072, 
-    1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770, 
-    - 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763, 
-    1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311, 
-    - 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462, 
-    1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492, 
-    - 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233, 
-    1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705, 
-    - 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972, 
-    40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662, 
-    - 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071, 
-    198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755, 
-    - 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554, 
-    285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400, 
-    - 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301, 
-    1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397, 
-    - 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552, 
-    615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682, 
-    - 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235, 
-    755167117]
-
-proc updateCrc32(val: int8, crc: TCrc32): TCrc32 = 
-  result = TCrc32(crc32table[(int(crc) xor (int(val) and 0x000000FF)) and
-      0x000000FF]) xor (crc shr TCrc32(8))
-
-proc updateCrc32(val: char, crc: TCrc32): TCrc32 = 
-  result = updateCrc32(toU8(ord(val)), crc)
-
-proc strCrc32(s: string): TCrc32 = 
-  result = InitCrc32
-  for i in countup(0, len(s) - 1): result = updateCrc32(s[i], result)
-  
-proc `><`*(c: TCrc32, s: string): TCrc32 = 
-  result = c
-  for i in 0..len(s)-1: result = updateCrc32(s[i], result)  
-  
-type 
-  TByteArray = array[0..10000000, int8]
-  PByteArray = ref TByteArray
-
-proc crcFromBuf(buf: pointer, length: int): TCrc32 = 
-  var p = cast[PByteArray](buf)
-  result = InitCrc32
-  for i in countup(0, length - 1): result = updateCrc32(p[i], result)
-  
-proc crcFromFile(filename: string): TCrc32 = 
-  const 
-    bufSize = 8000 # don't use 8K for the memory allocator!
-  var 
-    bin: File
-  result = InitCrc32
-  if not open(bin, filename): 
-    return                    # not equal if file does not exist
-  var buf = alloc(bufSize)
-  var p = cast[PByteArray](buf)
-  while true: 
-    var readBytes = readBuffer(bin, buf, bufSize)
-    for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
-    if readBytes != bufSize: break 
-  dealloc(buf)
-  close(bin)
-
-const 
-  base = int32(65521) # largest prime smaller than 65536 
-                      # NMAX = 5552; original code with unsigned 32 bit integer 
-                      # NMAX is the largest n 
-                      # such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 
-  nmax = 3854 # code with signed 32 bit integer 
-              # NMAX is the largest n such that 
-              # 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1 
-              # The penalty is the time loss in the extra MOD-calls. 
-
-proc updateAdler32(adler: int32, buf: pointer, length: int): int32 = 
-  var 
-    s1, s2: int32
-    L, k, b: int
-  s1 = adler and int32(0x0000FFFF)
-  s2 = (adler shr int32(16)) and int32(0x0000FFFF)
-  L = length
-  b = 0
-  while (L > 0): 
-    if L < nmax: k = L
-    else: k = nmax
-    dec(L, k)
-    while (k > 0): 
-      s1 = s1 +% int32((cast[cstring](buf))[b])
-      s2 = s2 +% s1
-      inc(b)
-      dec(k)
-    s1 = `%%`(s1, base)
-    s2 = `%%`(s2, base)
-  result = (s2 shl int32(16)) or s1
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index f8489d825..8536cc619 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -18,7 +18,7 @@ import
 
 type
   TSections = array[TSymKind, Rope]
-  TDocumentor = object of rstgen.TRstGenerator
+  TDocumentor = object of rstgen.RstGenerator
     modDesc: Rope           # module description
     id: int                  # for generating IDs
     toc, section: TSections
@@ -29,7 +29,7 @@ type
   PDoc* = ref TDocumentor ## Alias to type less.
 
 proc compilerMsgHandler(filename: string, line, col: int,
-                        msgKind: rst.TMsgKind, arg: string) {.procvar.} =
+                        msgKind: rst.MsgKind, arg: string) {.procvar.} =
   # translate msg kind:
   var k: msgs.TMsgKind
   case msgKind
@@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} =
 
 proc parseRst(text, filename: string,
               line, column: int, hasToc: var bool,
-              rstOptions: TRstParseOptions): PRstNode =
+              rstOptions: RstParseOptions): PRstNode =
   result = rstParse(text, filename, line, column, hasToc, rstOptions,
                     docgenFindFile, compilerMsgHandler)
 
@@ -75,7 +75,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
   ga('send', 'pageview');
 
 </script>
-    """ % [config["doc.googleAnalytics"]]
+    """ % [config.getOrDefault"doc.googleAnalytics"]
   else:
     result.analytics = ""
 
@@ -158,7 +158,7 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
   if n == nil: return nil
   result = genComment(d, n).rope
   if result == nil:
-    if n.kind notin {nkEmpty..nkNilLit}:
+    if n.kind notin {nkEmpty..nkNilLit, nkEnumTy}:
       for i in countup(0, len(n)-1):
         result = genRecComment(d, n.sons[i])
         if result != nil: return
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index aa832b6df..27de06811 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -10,10 +10,10 @@
 # This module implements a new documentation generator that runs after
 # semantic checking.
 
-import 
+import
   os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
 
-type 
+type
   TGen = object of TPassContext
     doc: PDoc
     module: PSym
@@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode =
     except IOError:
       discard
 
-proc processNode(c: PPassContext, n: PNode): PNode = 
+proc processNode(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
   generateDoc(g.doc, n)
 
-proc myOpen(module: PSym): PPassContext = 
+proc myOpen(module: PSym): PPassContext =
   var g: PGen
   new(g)
   g.module = module
@@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext =
 
 const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
 
-proc finishDoc2Pass*(project: string) = 
+proc finishDoc2Pass*(project: string) =
   discard
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 8959aa4df..c33e5be86 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   if ctx.instLines: result.info = b.info
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
+  template handleParam(param) =
+    let x = param
+    if x.kind == nkArgList:
+      for y in items(x): result.add(y)
+    else:
+      result.add copyTree(x)
+
   case templ.kind
   of nkSym:
     var s = templ.sym
     if s.owner.id == c.owner.id:
       if s.kind == skParam and sfGenSym notin s.flags:
-        let x = actual.sons[s.position]
-        if x.kind == nkArgList:
-          for y in items(x): result.add(y)
-        else:
-          result.add copyTree(x)
+        handleParam actual.sons[s.position]
+      elif s.kind == skGenericParam:
+        handleParam actual.sons[s.owner.typ.len + s.position - 1]
       else:
         internalAssert sfGenSym in s.flags
         var x = PSym(idTableGet(c.mapping, s))
@@ -56,20 +61,44 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
 proc evalTemplateArgs(n: PNode, s: PSym): PNode =
   # if the template has zero arguments, it can be called without ``()``
   # `n` is then a nkSym or something similar
-  var a: int
-  case n.kind
-  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
-    a = sonsLen(n)
-  else: a = 0
-  var f = s.typ.sonsLen
-  if a > f: globalError(n.info, errWrongNumberOfArguments)
+  var totalParams = case n.kind
+    of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1
+    else: 0
+
+  var
+    # XXX: Since immediate templates are not subjected to the
+    # standard sigmatching algorithm, they will have a number
+    # of deficiencies when it comes to generic params:
+    # Type dependencies between the parameters won't be honoured
+    # and the bound generic symbols won't be resolvable within
+    # their bodies. We could try to fix this, but it may be
+    # wiser to just deprecate immediate templates and macros
+    # now that we have working untyped parameters.
+    genericParams = if sfImmediate in s.flags: 0
+                    else: s.ast[genericParamsPos].len
+    expectedRegularParams = <s.typ.len
+    givenRegularParams = totalParams - genericParams
+
+  if totalParams > expectedRegularParams + genericParams:
+    globalError(n.info, errWrongNumberOfArguments)
 
   result = newNodeI(nkArgList, n.info)
-  for i in countup(1, f - 1):
-    var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
-    if arg == nil or arg.kind == nkEmpty:
+  for i in 1 .. givenRegularParams:
+    result.addSon n.sons[i]
+
+  # handle parameters with default values, which were
+  # not supplied by the user
+  for i in givenRegularParams+1 .. expectedRegularParams:
+    let default = s.typ.n.sons[i].sym.ast
+    if default.isNil or default.kind == nkEmpty:
       localError(n.info, errWrongNumberOfArguments)
-    addSon(result, arg)
+      addSon(result, ast.emptyNode)
+    else:
+      addSon(result, default.copyTree)
+
+  # add any generic paramaters
+  for i in 1 .. genericParams:
+    result.addSon n.sons[givenRegularParams + i]
 
 var evalTemplateCounter* = 0
   # to prevent endless recursion in templates instantiation
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 26f0318ee..3882bdd03 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -13,12 +13,13 @@
 # nim files.
 
 import
-  lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
+  lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
+  securehash, streams
 
 type
   TSystemCC* = enum
     ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
-    ccTcc, ccPcc, ccUcc, ccIcl
+    ccTcc, ccPcc, ccUcc, ccIcl, asmFasm
   TInfoCCProp* = enum         # properties of the C compiler:
     hasSwitchRange,           # CC allows ranges in switch statements (GNU C)
     hasComputedGoto,          # CC has computed goto (GNU C extension)
@@ -26,8 +27,8 @@ type
     hasAssume,                # CC has __assume (Visual C extension)
     hasGcGuard,               # CC supports GC_GUARD to keep stack roots
     hasGnuAsm,                # CC's asm uses the absurd GNU assembler syntax
-    hasNakedDeclspec,         # CC has __declspec(naked)
-    hasNakedAttribute         # CC has __attribute__((naked))
+    hasDeclspec,              # CC has __declspec(X)
+    hasAttribute,             # CC has __attribute__((X))
   TInfoCCProps* = set[TInfoCCProp]
   TInfoCC* = tuple[
     name: string,        # the short name of the compiler
@@ -85,7 +86,7 @@ compiler gcc:
     structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
     packedPragma: "__attribute__((__packed__))",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
-            hasNakedAttribute})
+            hasAttribute})
 
 # LLVM Frontend for GCC/G++
 compiler llvmGcc:
@@ -122,12 +123,12 @@ compiler vcc:
     includeCmd: " /I",
     linkDirCmd: " /LIBPATH:",
     linkLibCmd: " $1.lib",
-    debug: " /GZ /Zi ",
+    debug: " /RTC1 /Z7 ",
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$3$n$1 $2",
     packedPragma: "#pragma pack(1)",
-    props: {hasCpp, hasAssume, hasNakedDeclspec})
+    props: {hasCpp, hasAssume, hasDeclspec})
 
 # Intel C/C++ Compiler
 compiler icl:
@@ -317,6 +318,31 @@ compiler ucc:
     packedPragma: "", # XXX: not supported yet
     props: {})
 
+# fasm assembler
+compiler fasm:
+  result = (
+    name: "fasm",
+    objExt: "o",
+    optSpeed: "",
+    optSize: "",
+    compilerExe: "fasm",
+    cppCompiler: "fasm",
+    compileTmpl: "$file $objfile",
+    buildGui: "",
+    buildDll: "",
+    buildLib: "",
+    linkerExe: "",
+    linkTmpl: "",
+    includeCmd: "",
+    linkDirCmd: "",
+    linkLibCmd: "",
+    debug: "",
+    pic: "",
+    asmStmtFrmt: "",
+    structStmtFmt: "",
+    packedPragma: "",
+    props: {})
+
 const
   CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
     gcc(),
@@ -330,17 +356,22 @@ const
     tcc(),
     pcc(),
     ucc(),
-    icl()]
+    icl(),
+    fasm()]
 
   hExt* = ".h"
 
 var
   cCompiler* = ccGcc # the used compiler
+  cAssembler* = ccNone
   gMixedMode*: bool  # true if some module triggered C++ codegen
   cIncludes*: seq[string] = @[]   # directories to search for included files
   cLibs*: seq[string] = @[]       # directories to search for lib files
   cLinkedLibs*: seq[string] = @[] # libraries to link
 
+const
+  cValidAssemblers* = {asmFasm}
+
 # implementation
 
 proc libNameTmpl(): string {.inline.} =
@@ -428,7 +459,7 @@ proc resetCompilationLists* =
   initLinkedList(toCompile)
   ## XXX: we must associate these with their originating module
   # when the module is loaded/unloaded it adds/removes its items
-  # That's because we still need to CRC check the external files
+  # That's because we still need to hash check the external files
   # Maybe we can do that in checkDep on the other hand?
   initLinkedList(externalToCompile)
   initLinkedList(toLink)
@@ -437,17 +468,13 @@ proc addFileToLink*(filename: string) =
   prependStr(toLink, filename)
   # BUGFIX: was ``appendStr``
 
-proc execWithEcho(cmd: string, prettyCmd = ""): int =
-  if optListCmd in gGlobalOptions or gVerbosity > 0:
-    if prettyCmd != "":
-      msgWriteln(prettyCmd)
-    else:
-      msgWriteln(cmd)
+proc execWithEcho(cmd: string, msg = hintExecuting): int =
+  rawMessage(msg, cmd)
   result = execCmd(cmd)
 
-proc execExternalProgram*(cmd: string, prettyCmd = "") =
-  if execWithEcho(cmd, prettyCmd) != 0:
-    rawMessage(errExecutionOfProgramFailed, "")
+proc execExternalProgram*(cmd: string, msg = hintExecuting) =
+  if execWithEcho(cmd, msg) != 0:
+    rawMessage(errExecutionOfProgramFailed, cmd)
 
 proc generateScript(projectFile: string, script: Rope) =
   let (dir, name, ext) = splitFile(projectFile)
@@ -530,6 +557,21 @@ proc getLinkerExe(compiler: TSystemCC): string =
 
 proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
   var c = cCompiler
+  if cfilename.endswith(".asm"):
+    var customAssembler = getConfigVar("assembler")
+    if customAssembler.len > 0:
+      c = nameToCC(customAssembler)
+    else:
+      if targetCPU == cpuI386 or targetCPU == cpuAmd64:
+        c = asmFasm
+      else:
+        c = ccNone
+
+    if c == ccNone:
+      rawMessage(errExternalAssemblerNotFound, "")
+    elif c notin cValidAssemblers:
+      rawMessage(errExternalAssemblerNotValid, customAssembler)
+
   var options = cFileSpecificOptions(cfilename)
   var exe = getConfigVar(c, ".exe")
   if exe.len == 0: exe = c.getCompilerExe
@@ -572,32 +614,30 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
     "nim", quoteShell(getPrefixDir()),
     "lib", quoteShell(libpath)])
 
-proc footprint(filename: string): TCrc32 =
-  # note, '><' further modifies a crc value with a string.
-  result = crcFromFile(filename) ><
-      platform.OS[targetOS].name ><
-      platform.CPU[targetCPU].name ><
-      extccomp.CC[extccomp.cCompiler].name ><
-      getCompileCFileCmd(filename, true)
+proc footprint(filename: string): SecureHash =
+  result = secureHash(
+    $secureHashFile(filename) &
+    platform.OS[targetOS].name &
+    platform.CPU[targetCPU].name &
+    extccomp.CC[extccomp.cCompiler].name &
+    getCompileCFileCmd(filename, true))
 
 proc externalFileChanged(filename: string): bool =
   if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
     return false
 
-  var crcFile = toGeneratedFile(filename.withPackageName, "crc")
-  var currentCrc = int(footprint(filename))
+  var hashFile = toGeneratedFile(filename.withPackageName, "sha1")
+  var currentHash = footprint(filename)
   var f: File
-  if open(f, crcFile, fmRead):
-    var line = newStringOfCap(40)
-    if not f.readLine(line): line = "0"
+  if open(f, hashFile, fmRead):
+    let oldHash = parseSecureHash(f.readLine())
     close(f)
-    var oldCrc = parseInt(line)
-    result = oldCrc != currentCrc
+    result = oldHash != currentHash
   else:
     result = true
   if result:
-    if open(f, crcFile, fmWrite):
-      f.writeln($currentCrc)
+    if open(f, hashFile, fmWrite):
+      f.writeLine($currentHash)
       close(f)
 
 proc addExternalFileToCompile*(filename: string) =
@@ -632,6 +672,12 @@ proc callCCompiler*(projectfile: string) =
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
     echo prettyCmds[idx]
+  let runCb = proc (idx: int, p: Process) =
+    let exitCode = p.peekExitCode
+    if exitCode != 0:
+      rawMessage(errGenerated, "execution of an external compiler program '" &
+        cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
+        p.outputStream.readAll.strip)
   compileCFile(toCompile, script, cmds, prettyCmds, false)
   compileCFile(externalToCompile, script, cmds, prettyCmds, true)
   if optCompileOnly notin gGlobalOptions:
@@ -640,22 +686,19 @@ proc callCCompiler*(projectfile: string) =
     if gNumberOfProcessors <= 1:
       for i in countup(0, high(cmds)):
         res = execWithEcho(cmds[i])
-        if res != 0: rawMessage(errExecutionOfProgramFailed, [])
+        if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
     elif optListCmd in gGlobalOptions or gVerbosity > 1:
-      res = execProcesses(cmds, {poEchoCmd, poUsePath, poParentStreams},
-                          gNumberOfProcessors)
+      res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
+                          gNumberOfProcessors, afterRunEvent=runCb)
     elif gVerbosity == 1:
-      res = execProcesses(cmds, {poUsePath, poParentStreams},
-                          gNumberOfProcessors, prettyCb)
+      res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                          gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
     else:
-      res = execProcesses(cmds, {poUsePath, poParentStreams},
-                          gNumberOfProcessors)
+      res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                          gNumberOfProcessors, afterRunEvent=runCb)
     if res != 0:
       if gNumberOfProcessors <= 1:
-        rawMessage(errExecutionOfProgramFailed, [])
-      else:
-        rawMessage(errGenerated, " execution of an external program failed; " &
-                   "rerun with --parallelBuild:1 to see the error message")
+        rawMessage(errExecutionOfProgramFailed, cmds.join())
   if optNoLinking notin gGlobalOptions:
     # call the linker:
     var it = PStrEntry(toLink.head)
@@ -668,7 +711,8 @@ proc callCCompiler*(projectfile: string) =
       it = PStrEntry(it.next)
 
     if optGenStaticLib in gGlobalOptions:
-      linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName),
+      let name = splitFile(gProjectName).name
+      linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % name),
                                   "objfiles", objfiles]
     else:
       var linkerExe = getConfigVar(c, ".linkerexe")
@@ -703,10 +747,8 @@ proc callCCompiler*(projectfile: string) =
           "nim", quoteShell(getPrefixDir()),
           "lib", quoteShell(libpath)])
     if optCompileOnly notin gGlobalOptions:
-      if gVerbosity == 1:
-        execExternalProgram(linkCmd, "[Linking]")
-      else:
-        execExternalProgram(linkCmd)
+      execExternalProgram(linkCmd,
+                          if gVerbosity > 1: hintExecuting else: hintLinking)
   else:
     linkCmd = ""
   if optGenScript in gGlobalOptions:
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 5d1f73be4..21810adb9 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -9,18 +9,18 @@
 
 # This module implements Nim's standard template filter.
 
-import 
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, 
+import
+  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
   renderer, filters
 
 proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
   # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
 # implementation
 
-type 
-  TParseState = enum 
+type
+  TParseState = enum
     psDirective, psTempl
-  TTmplParser{.final.} = object 
+  TTmplParser{.final.} = object
     inp: PLLStream
     state: TParseState
     info: TLineInfo
@@ -33,18 +33,18 @@ type
     pendingExprLine: bool
 
 
-const 
+const
   PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
 
-proc newLine(p: var TTmplParser) = 
+proc newLine(p: var TTmplParser) =
   llStreamWrite(p.outp, repeat(')', p.emitPar))
   p.emitPar = 0
   if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
   if p.pendingExprLine:
     llStreamWrite(p.outp, spaces(2))
     p.pendingExprLine = false
-  
-proc scanPar(p: var TTmplParser, d: int) = 
+
+proc scanPar(p: var TTmplParser, d: int) =
   var i = d
   while true:
     case p.x[i]
@@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) =
     else: discard
     inc(i)
 
-proc withInExpr(p: TTmplParser): bool {.inline.} = 
+proc withInExpr(p: TTmplParser): bool {.inline.} =
   result = p.par > 0 or p.bracket > 0 or p.curly > 0
-  
-proc parseLine(p: var TTmplParser) = 
-  var 
+
+proc parseLine(p: var TTmplParser) =
+  var
     d, j, curly: int
     keyw: string
   j = 0
   while p.x[j] == ' ': inc(j)
-  if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'): 
+  if p.x[0] == p.nimDirective and p.x[1] in {'?', '!'}:
     newLine(p)
-  elif (p.x[j] == p.nimDirective): 
+  elif p.x[j] == p.nimDirective:
     newLine(p)
     inc(j)
     while p.x[j] == ' ': inc(j)
     d = j
     keyw = ""
-    while p.x[j] in PatternChars: 
+    while p.x[j] in PatternChars:
       add(keyw, p.x[j])
       inc(j)
-    
+
     scanPar(p, j)
     p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
     case whichKeyword(keyw)
-    of wEnd: 
-      if p.indent >= 2: 
+    of wEnd:
+      if p.indent >= 2:
         dec(p.indent, 2)
-      else: 
+      else:
         p.info.col = int16(j)
         localError(p.info, errXNotAllowedHere, "end")
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, "#end")
-    of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, 
-       wConverter, wMacro, wTemplate, wMethod: 
+    of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
+       wConverter, wMacro, wTemplate, wMethod:
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, substr(p.x, d))
       inc(p.indent, 2)
-    of wElif, wOf, wElse, wExcept, wFinally: 
+    of wElif, wOf, wElse, wExcept, wFinally:
       llStreamWrite(p.outp, spaces(p.indent - 2))
       llStreamWrite(p.outp, substr(p.x, d))
     of wLet, wVar, wConst, wType:
@@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, substr(p.x, d))
     p.state = psDirective
-  else: 
+  else:
     # data line
     # reset counters
     p.par = 0
@@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) =
     p.bracket = 0
     j = 0
     case p.state
-    of psTempl: 
+    of psTempl:
       # next line of string literal:
       llStreamWrite(p.outp, p.conc)
       llStreamWrite(p.outp, "\n")
       llStreamWrite(p.outp, spaces(p.indent + 2))
       llStreamWrite(p.outp, "\"")
-    of psDirective: 
+    of psDirective:
       newLine(p)
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, p.emit)
       llStreamWrite(p.outp, "(\"")
       inc(p.emitPar)
     p.state = psTempl
-    while true: 
+    while true:
       case p.x[j]
-      of '\0': 
-        break 
-      of '\x01'..'\x1F', '\x80'..'\xFF': 
+      of '\0':
+        break
+      of '\x01'..'\x1F', '\x80'..'\xFF':
         llStreamWrite(p.outp, "\\x")
         llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
         inc(j)
-      of '\\': 
+      of '\\':
         llStreamWrite(p.outp, "\\\\")
         inc(j)
-      of '\'': 
+      of '\'':
         llStreamWrite(p.outp, "\\\'")
         inc(j)
-      of '\"': 
+      of '\"':
         llStreamWrite(p.outp, "\\\"")
         inc(j)
-      else: 
-        if p.x[j] == p.subsChar: 
+      else:
+        if p.x[j] == p.subsChar:
           # parse Nim expression:
           inc(j)
           case p.x[j]
-          of '{': 
+          of '{':
             p.info.col = int16(j)
             llStreamWrite(p.outp, '\"')
             llStreamWrite(p.outp, p.conc)
@@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) =
             llStreamWrite(p.outp, '(')
             inc(j)
             curly = 0
-            while true: 
+            while true:
               case p.x[j]
-              of '\0': 
+              of '\0':
                 localError(p.info, errXExpected, "}")
                 break
-              of '{': 
+              of '{':
                 inc(j)
                 inc(curly)
                 llStreamWrite(p.outp, '{')
-              of '}': 
+              of '}':
                 inc(j)
-                if curly == 0: break 
+                if curly == 0: break
                 if curly > 0: dec(curly)
                 llStreamWrite(p.outp, '}')
-              else: 
+              else:
                 llStreamWrite(p.outp, p.x[j])
                 inc(j)
             llStreamWrite(p.outp, ')')
             llStreamWrite(p.outp, p.conc)
             llStreamWrite(p.outp, '\"')
-          of 'a'..'z', 'A'..'Z', '\x80'..'\xFF': 
+          of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
             llStreamWrite(p.outp, '\"')
             llStreamWrite(p.outp, p.conc)
             llStreamWrite(p.outp, p.toStr)
             llStreamWrite(p.outp, '(')
-            while p.x[j] in PatternChars: 
+            while p.x[j] in PatternChars:
               llStreamWrite(p.outp, p.x[j])
               inc(j)
             llStreamWrite(p.outp, ')')
             llStreamWrite(p.outp, p.conc)
             llStreamWrite(p.outp, '\"')
-          else: 
-            if p.x[j] == p.subsChar: 
+          else:
+            if p.x[j] == p.subsChar:
               llStreamWrite(p.outp, p.subsChar)
               inc(j)
-            else: 
+            else:
               p.info.col = int16(j)
               localError(p.info, errInvalidExpression, "$")
-        else: 
+        else:
           llStreamWrite(p.outp, p.x[j])
           inc(j)
     llStreamWrite(p.outp, "\\n\"")
 
-proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = 
+proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var p: TTmplParser
   p.info = newLineInfo(filename, 0, 0)
   p.outp = llStreamOpen("")
diff --git a/compiler/filters.nim b/compiler/filters.nim
index 783a320a4..adafe464e 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -10,7 +10,7 @@
 # This module implements Nim's simple filters and helpers for filters.
 
 import
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, 
+  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
   renderer
 
 proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
@@ -21,40 +21,40 @@ proc strArg*(n: PNode, name: string, pos: int, default: string): string
 proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
 # implementation
 
-proc invalidPragma(n: PNode) = 
+proc invalidPragma(n: PNode) =
   localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
 
-proc getArg(n: PNode, name: string, pos: int): PNode = 
+proc getArg(n: PNode, name: string, pos: int): PNode =
   result = nil
-  if n.kind in {nkEmpty..nkNilLit}: return 
-  for i in countup(1, sonsLen(n) - 1): 
-    if n.sons[i].kind == nkExprEqExpr: 
+  if n.kind in {nkEmpty..nkNilLit}: return
+  for i in countup(1, sonsLen(n) - 1):
+    if n.sons[i].kind == nkExprEqExpr:
       if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
-      if identEq(n.sons[i].sons[0].ident, name): 
+      if identEq(n.sons[i].sons[0].ident, name):
         return n.sons[i].sons[1]
-    elif i == pos: 
+    elif i == pos:
       return n.sons[i]
-  
-proc charArg(n: PNode, name: string, pos: int, default: char): char = 
+
+proc charArg(n: PNode, name: string, pos: int, default: char): char =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif x.kind == nkCharLit: result = chr(int(x.intVal))
   else: invalidPragma(n)
-  
-proc strArg(n: PNode, name: string, pos: int, default: string): string = 
+
+proc strArg(n: PNode, name: string, pos: int, default: string): string =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
   else: invalidPragma(n)
-  
-proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = 
+
+proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
   elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
   else: invalidPragma(n)
-  
-proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = 
+
+proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var pattern = strArg(call, "startswith", 1, "")
   var leading = boolArg(call, "leading", 2, true)
   var trailing = boolArg(call, "trailing", 3, true)
@@ -62,13 +62,13 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var line = newStringOfCap(80)
   while llStreamReadLine(stdin, line):
     var stripped = strip(line, leading, trailing)
-    if (len(pattern) == 0) or startsWith(stripped, pattern): 
+    if (len(pattern) == 0) or startsWith(stripped, pattern):
       llStreamWriteln(result, stripped)
-    else: 
+    else:
       llStreamWriteln(result, line)
   llStreamClose(stdin)
 
-proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = 
+proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var sub = strArg(call, "sub", 1, "")
   if len(sub) == 0: invalidPragma(call)
   var by = strArg(call, "by", 2, "")
diff --git a/compiler/forloops.nim b/compiler/forloops.nim
index efe000968..5074d79d5 100644
--- a/compiler/forloops.nim
+++ b/compiler/forloops.nim
@@ -12,13 +12,13 @@
 import ast, astalgo
 
 const
-  someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
-    mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
-    mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, 
+  someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
+    mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
+    mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
     mLtCh, mLtB, mLtPtr}
 
 proc isCounter(s: PSym): bool {.inline.} =
-  s.kind in {skResult, skVar, skLet, skTemp} and 
+  s.kind in {skResult, skVar, skLet, skTemp} and
   {sfGlobal, sfAddrTaken} * s.flags == {}
 
 proc isCall(n: PNode): bool {.inline.} =
@@ -29,7 +29,7 @@ proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
 proc getCounter(lastStmt: PNode): PSym =
   if lastStmt.isCall:
     let op = lastStmt.sym
-    if op.magic in {mDec, mInc} or 
+    if op.magic in {mDec, mInc} or
         ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
       if op[1].kind == nkSym and isCounter(op[1].sym):
         result = op[1].sym
@@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
 
   if not cond.isCall: return
   if cond[0].sym.magic notin someCmp: return
-  
+
   var lastStmt = loop[1]
   while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
     lastStmt = lastStmt.lastSon
@@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
   if counter.isNil or counter.ast.isNil: return
 
   template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
-  
+
   if cond[1] =~ counter or cond[2] =~ counter:
     # ok, now check 'counter' is not used *after* the loop
     if counterInTree(fullTree, loop, counter): return
diff --git a/compiler/guards.nim b/compiler/guards.nim
index df2c1dd75..5ad932e48 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -13,13 +13,13 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
   saturate
 
 const
-  someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
+  someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
     mEqUntracedRef, mEqStr, mEqSet, mEqCString}
 
   # set excluded here as the semantics are vastly different:
-  someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
+  someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
             mLeCh, mLeB, mLePtr, mLeStr}
-  someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
+  someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
             mLtCh, mLtB, mLtPtr, mLtStr}
 
   someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
@@ -30,13 +30,14 @@ const
   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}
+  someAdd = {mAddI, mAddF64, mSucc}
+  someSub = {mSubI, mSubF64, mPred}
+  someMul = {mMulI, mMulF64}
+  someDiv = {mDivI, mDivF64}
+  someMod = {mModI}
   someMax = {mMaxI, mMaxF64}
   someMin = {mMinI, mMinF64}
+  someBinaryOp = someAdd+someSub+someMul+someMax+someMin
 
 proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
 proc isLocation(n: PNode): bool = not n.isValue
@@ -165,11 +166,21 @@ proc `|+|`(a, b: PNode): PNode =
   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 `|*|`(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 `|div|`(a, b: PNode): PNode =
+  result = copyNode(a)
+  if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div 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)
@@ -213,10 +224,16 @@ proc reassociation(n: PNode): PNode =
     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])
+      if result[2].intVal == 0:
+        result = result[1]
   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])
+      result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
+      if result[2].intVal == 1:
+        result = result[1]
+      elif result[2].intVal == 0:
+        result = zero()
   else: discard
 
 proc pred(n: PNode): PNode =
@@ -234,7 +251,7 @@ proc canon*(n: PNode): PNode =
       result.sons[i] = canon(n.sons[i])
   elif n.kind == nkSym and n.sym.kind == skLet and
       n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
-      someMax + someHigh + {mUnaryLt} + someSub + someLen):
+      someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
     result = n.sym.ast.copyTree
   else:
     result = n
@@ -248,7 +265,7 @@ proc canon*(n: PNode): PNode =
     # high == len+(-1)
     result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
   of mUnaryLt:
-    result = buildCall(opAdd, result[1], newIntNode(nkIntLit, -1))
+    result = buildCall(opAdd, result[1], minusOne())
   of someSub:
     # x - 4  -->  x + (-4)
     result = negate(result[1], result[2], result)
@@ -294,6 +311,16 @@ proc canon*(n: PNode): PNode =
         if plus != nil and not isLetLocation(x, true):
           result = buildCall(result[0].sym, plus, y[1])
       else: discard
+    elif x.isValue and y.getMagic in someAdd and y[2].isValue:
+      # 0 <= a.len + 3
+      # -3 <= a.len
+      result.sons[1] = x |-| y[2]
+      result.sons[2] = y[1]
+    elif x.isValue and y.getMagic in someSub and y[2].isValue:
+      # 0 <= a.len - 3
+      # 3 <= a.len
+      result.sons[1] = x |+| y[2]
+      result.sons[2] = y[1]
   else: discard
 
 proc `+@`*(a: PNode; b: BiggestInt): PNode =
@@ -313,6 +340,9 @@ proc usefulFact(n: PNode): PNode =
     if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
       # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
       result = n
+    elif n[1].getMagic in someLen or n[2].getMagic in someLen:
+      # XXX Rethink this whole idea of 'usefulFact' for semparallel
+      result = n
   of mIsNil:
     if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
       result = n
@@ -366,8 +396,8 @@ proc usefulFact(n: PNode): PNode =
 type
   TModel* = seq[PNode] # the "knowledge base"
 
-proc addFact*(m: var TModel, n: PNode) =
-  let n = usefulFact(n)
+proc addFact*(m: var TModel, nn: PNode) =
+  let n = usefulFact(nn)
   if n != nil: m.add n
 
 proc addFactNeg*(m: var TModel, n: PNode) =
@@ -697,10 +727,57 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
   else:
     result = -1
 
+
+template isMul(x): expr = x.getMagic in someMul
+template isDiv(x): expr = x.getMagic in someDiv
+template isAdd(x): expr = x.getMagic in someAdd
+template isSub(x): expr = x.getMagic in someSub
+template isVal(x): expr = x.kind in {nkCharLit..nkUInt64Lit}
+template isIntVal(x, y): expr = x.intVal == y
+
+import macros
+
+macro `=~`(x: PNode, pat: untyped): bool =
+  proc m(x, pat, conds: NimNode) =
+    case pat.kind
+    of nnkInfix:
+      case $pat[0]
+      of "*": conds.add getAst(isMul(x))
+      of "/": conds.add getAst(isDiv(x))
+      of "+": conds.add getAst(isAdd(x))
+      of "-": conds.add getAst(isSub(x))
+      else:
+        error("invalid pattern")
+      m(newTree(nnkBracketExpr, x, newLit(1)), pat[1], conds)
+      m(newTree(nnkBracketExpr, x, newLit(2)), pat[2], conds)
+    of nnkPar:
+      if pat.len == 1:
+        m(x, pat[0], conds)
+      else:
+        error("invalid pattern")
+    of nnkIdent:
+      let c = newTree(nnkStmtListExpr, newLetStmt(pat, x))
+      conds.add c
+      if ($pat)[^1] == 'c': c.add(getAst(isVal(pat)))
+      else: c.add bindSym"true"
+    of nnkIntLit:
+      conds.add(getAst(isIntVal(pat.intVal)))
+    else:
+      error("invalid pattern")
+
+  var conds = newTree(nnkBracket)
+  m(x, pat, conds)
+  result = nestList(!"and", conds)
+
+
+proc isMinusOne(n: PNode): bool =
+  n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -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
+  template `>=?`(a,b): expr = ple(m, nkIntLit.newIntNode(b), a) == impYes
 
   #   0 <= 3
   if a.isValue and b.isValue:
@@ -721,6 +798,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
     if a.intVal <= 0: return impYes
 
   #   x <= y+c  if 0 <= c and x <= y
+  #   x <= y+(-c)  if c <= 0  and y >= x
   if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
 
   #   x+c <= y  if c <= 0 and x <= y
@@ -730,10 +808,44 @@ proc ple(m: TModel; a, b: PNode): TImplication =
   if b.getMagic in someMul:
     if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes
 
+
+  if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
+      a[1][2].isValue:
+    # simplify   (x div 4) * 2 <= y   to  x div (c div d)  <= y
+    if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
+      return impYes
+
+  # x*3 + x == x*4. It follows that:
+  # x*3 + y <= x*4  if  y <= x  and 3 <= 4
+  if a =~ x*dc + y and b =~ x2*ec:
+    if sameTree(x, x2):
+      let ec1 = opAdd.buildCall(ec, minusOne())
+      if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
+        return impYes
+  elif a =~ x*dc and b =~ x2*ec + y:
+    #echo "BUG cam ehrer e ", a, " <=? ", b
+    if sameTree(x, x2):
+      let ec1 = opAdd.buildCall(ec, minusOne())
+      if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
+        return impYes
+
+  #  x+c <= x+d  if c <= d. Same for *, - etc.
+  if a.getMagic in someBinaryOp and a.getMagic == b.getMagic:
+    if sameTree(a[1], b[1]) and a[2] <=? b[2]: return impYes
+    elif sameTree(a[2], b[2]) and a[1] <=? 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
 
+    #  x div c <= x div d  if d <= c
+    if b.getMagic in someDiv:
+      if sameTree(a[1], b[1]) and b[2] <=? a[2]: return impYes
+
+    # x div z <= x - 1   if  z <= x
+    if a[2].isValue and b.getMagic in someAdd and b[2].isMinusOne:
+      if a[2] <=? a[1] and sameTree(a[1], b[1]): 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
@@ -769,11 +881,19 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
   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
+      # i <= len-100
+      # i <=? len-1
+      # --> true  if  (len-100) <= (len-1)
+      let x = fact[1]
+      let y = fact[2]
+      if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and
+         sameTree(y[1], b[1]):
+        if ple(m, b[2], y[2]) == impYes:
+          return impYes
+
+      # x <= y implies a <= b  if  a <= x and y <= b
       if ple(m, a, x) == impYes:
         if ple(m, y, b) == impYes:
           return impYes
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 363d100be..6cc9567af 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
   else:
     result = semDirectOp(c, n, {})
   if optHints in gOptions and hintPattern in gNotes:
-    message(orig.info, hintPattern, rule & " --> '" & 
+    message(orig.info, hintPattern, rule & " --> '" &
       renderTree(result, {renderNoComments}) & "'")
 
 proc applyPatterns(c: PContext, n: PNode): PNode =
@@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode =
     result = n
   else:
     if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
-        n.sons[0].kind == nkSym and 
+        n.sons[0].kind == nkSym and
         {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
       # do not optimize 'var g {.global} = re(...)' again!
       return n
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 0cca18929..d9b72baf0 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -11,19 +11,19 @@
 # An identifier is a shared immutable string that can be compared by its
 # id. This module is essential for the compiler's performance.
 
-import 
-  hashes, strutils
+import
+  hashes, strutils, etcpriv
 
-type 
+type
   TIdObj* = object of RootObj
     id*: int # unique id; use this for comparisons and not the pointers
-  
+
   PIdObj* = ref TIdObj
   PIdent* = ref TIdent
   TIdent*{.acyclic.} = object of TIdObj
     s*: string
     next*: PIdent             # for hash-table chaining
-    h*: THash                 # hash value of s
+    h*: Hash                 # hash value of s
 
 var firstCharIsCS*: bool = true
 var buckets*: array[0..4096 * 2 - 1, PIdent]
@@ -37,18 +37,20 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
   while j < blen:
     while a[i] == '_': inc(i)
     while b[j] == '_': inc(j)
+    while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth)
+    while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth)
     # tolower inlined:
     var aa = a[i]
     var bb = b[j]
     if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
     if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
     result = ord(aa) - ord(bb)
-    if (result != 0) or (aa == '\0'): break 
+    if (result != 0) or (aa == '\0'): break
     inc(i)
     inc(j)
   if result == 0:
     if a[i] != '\0': result = 1
-  
+
 proc cmpExact(a, b: cstring, blen: int): int =
   var i = 0
   var j = 0
@@ -57,27 +59,27 @@ proc cmpExact(a, b: cstring, blen: int): int =
     var aa = a[i]
     var bb = b[j]
     result = ord(aa) - ord(bb)
-    if (result != 0) or (aa == '\0'): break 
+    if (result != 0) or (aa == '\0'): break
     inc(i)
     inc(j)
-  if result == 0: 
+  if result == 0:
     if a[i] != '\0': result = 1
 
 var wordCounter = 1
 
-proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
+proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
   var idx = h and high(buckets)
   result = buckets[idx]
   var last: PIdent = nil
   var id = 0
-  while result != nil: 
-    if cmpExact(cstring(result.s), identifier, length) == 0: 
-      if last != nil: 
+  while result != nil:
+    if cmpExact(cstring(result.s), identifier, length) == 0:
+      if last != nil:
         # make access to last looked up identifier faster:
         last.next = result.next
         result.next = buckets[idx]
         buckets[idx] = result
-      return 
+      return
     elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
       assert((id == 0) or (id == result.id))
       id = result.id
@@ -89,20 +91,20 @@ proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
   for i in countup(0, length - 1): result.s[i] = identifier[i]
   result.next = buckets[idx]
   buckets[idx] = result
-  if id == 0: 
+  if id == 0:
     inc(wordCounter)
     result.id = -wordCounter
-  else: 
+  else:
     result.id = id
 
-proc getIdent*(identifier: string): PIdent = 
-  result = getIdent(cstring(identifier), len(identifier), 
+proc getIdent*(identifier: string): PIdent =
+  result = getIdent(cstring(identifier), len(identifier),
                     hashIgnoreStyle(identifier))
 
-proc getIdent*(identifier: string, h: THash): PIdent = 
+proc getIdent*(identifier: string, h: Hash): PIdent =
   result = getIdent(cstring(identifier), len(identifier), h)
 
-proc identEq*(id: PIdent, name: string): bool = 
+proc identEq*(id: PIdent, name: string): bool =
   result = id.id == getIdent(name).id
 
 var idAnon* = getIdent":anonymous"
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
index 3c5669b54..c07782fb2 100644
--- a/compiler/idgen.nim
+++ b/compiler/idgen.nim
@@ -18,26 +18,26 @@ const
 
 when debugIds:
   import intsets
-  
+
   var usedIds = initIntSet()
 
-proc registerID*(id: PIdObj) = 
-  when debugIds: 
-    if id.id == -1 or containsOrIncl(usedIds, id.id): 
+proc registerID*(id: PIdObj) =
+  when debugIds:
+    if id.id == -1 or containsOrIncl(usedIds, id.id):
       internalError("ID already used: " & $id.id)
 
-proc getID*(): int {.inline.} = 
+proc getID*(): int {.inline.} =
   result = gFrontEndId
   inc(gFrontEndId)
 
-proc backendId*(): int {.inline.} = 
+proc backendId*(): int {.inline.} =
   result = gBackendId
   inc(gBackendId)
 
-proc setId*(id: int) {.inline.} = 
+proc setId*(id: int) {.inline.} =
   gFrontEndId = max(gFrontEndId, id + 1)
 
-proc idSynchronizationPoint*(idRange: int) = 
+proc idSynchronizationPoint*(idRange: int) =
   gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
 
 proc toGid(f: string): string =
@@ -48,10 +48,10 @@ proc toGid(f: string): string =
 
 proc saveMaxIds*(project: string) =
   var f = open(project.toGid, fmWrite)
-  f.writeln($gFrontEndId)
-  f.writeln($gBackendId)
+  f.writeLine($gFrontEndId)
+  f.writeLine($gBackendId)
   f.close()
-  
+
 proc loadMaxIds*(project: string) =
   var f: File
   if open(f, project.toGid, fmRead):
diff --git a/compiler/importer.nim b/compiler/importer.nim
index d619725db..c121059fd 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -121,7 +121,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
       if s.kind != skEnumField:
         if s.kind notin ExportableSymKinds:
           internalError(s.info, "importAllSymbols: " & $s.kind)
-        if exceptSet.empty or s.name.id notin exceptSet:
+        if exceptSet.isNil or s.name.id notin exceptSet:
           rawImportSymbol(c, s)
     s = nextIter(i, fromMod.tab)
 
@@ -138,7 +138,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
       let s = a.sym
       if s.kind == skModule:
         importAllSymbolsExcept(c, s, exceptSet)
-      elif exceptSet.empty or s.name.id notin exceptSet:
+      elif exceptSet.isNil or s.name.id notin exceptSet:
         rawImportSymbol(c, s)
   of nkExportExceptStmt:
     localError(n.info, errGenerated, "'export except' not implemented")
diff --git a/compiler/installer.ini b/compiler/installer.ini
index fff82cb5b..318bb36b1 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,7 +6,7 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
+  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
   macosx: i386;amd64;powerpc64
   solaris: i386;amd64;sparc
   freebsd: i386;amd64
@@ -99,9 +99,13 @@ Files: "lib/pure/concurrency/*.nim"
 Files: "lib/pure/unidecode/*.nim"
 Files: "lib/pure/concurrency/*.cfg"
 Files: "lib/impure/*.nim"
+Files: "lib/impure/nre/private/*.nim"
 Files: "lib/wrappers/*.nim"
 
 Files: "lib/wrappers/readline/*.nim"
+Files: "lib/wrappers/linenoise/*.nim"
+Files: "lib/wrappers/linenoise/*.c"
+Files: "lib/wrappers/linenoise/*.h"
 Files: "lib/wrappers/sdl/*.nim"
 Files: "lib/wrappers/zip/*.nim"
 Files: "lib/wrappers/zip/libzip_all.c"
@@ -111,6 +115,9 @@ Files: "lib/posix/*.nim"
 Files: "lib/js/*.nim"
 Files: "lib/packages/docutils/*.nim"
 
+Files: "lib/deprecated/core/*.nim"
+Files: "lib/deprecated/pure/*.nim"
+Files: "lib/deprecated/pure/*.cfg"
 
 [Other]
 Files: "examples/*.nim"
@@ -203,7 +210,11 @@ Files: "tests/stdlib/*.nim"
 Files: "tests/system/*.nim"
 Files: "tests/template/*.nim"
 Files: "tests/testament/*.nim"
-Files: "tests/testdata/*.nim"
+Files: "tests/testdata/*.csv"
+Files: "tests/testdata/*.html"
+Files: "tests/testdata/*.json"
+Files: "tests/testdata/*.txt"
+Files: "tests/testdata/*.xml"
 Files: "tests/threads/*.nim"
 Files: "tests/threads/*.cfg"
 Files: "tests/trmacros/*.nim"
@@ -236,6 +247,7 @@ BinPath: r"bin;dist\mingw\bin;dist"
 ;           Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
 Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
 Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
+Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip"
 Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe"
 ; for now only NSIS supports optional downloads
 
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 704713243..f8bf35ed6 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -30,8 +30,8 @@ implements the required case distinction.
 
 
 import
-  ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp,
-  options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
+  ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
+  nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
   times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
   intsets, cgmeth, lowerings
 
@@ -136,7 +136,7 @@ proc mapType(typ: PType): TJSTypeKind =
       result = etyBaseIndex
   of tyPointer:
     # treat a tyPointer like a typed pointer to an array of bytes
-    result = etyInt
+    result = etyBaseIndex
   of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy:
     result = mapType(t.sons[0])
   of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
@@ -197,10 +197,10 @@ proc isSimpleExpr(n: PNode): bool =
   elif n.isAtom:
     result = true
 
-proc getTemp(p: PProc): Rope =
+proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
   inc(p.unique)
   result = "Tmp$1" % [rope(p.unique)]
-  addf(p.locals, "var $1;$n" | "local $1;$n", [result])
+  if defineInLocals: addf(p.locals, "var $1;$n" | "local $1;$n", [result])
 
 proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
@@ -258,11 +258,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
     ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
     ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
-    ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
-    ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
-    ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
-    ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
-    ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
     ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
     ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
     ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
@@ -276,11 +271,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
     ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
     ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
-    ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
-    ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
-    ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
-    ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
-    ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
     ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
     ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
     ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
@@ -291,9 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 == $2)", "($1 == $2)"], # EqI
     ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
     ["", "", "($1 < $2)", "($1 < $2)"], # LtI
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqI64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtI64
     ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
     ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
     ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
@@ -320,11 +307,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
     ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
     ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
-    ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
     ["", "", "!($1)", "!($1)"], # Not
     ["", "", "+($1)", "+($1)"], # UnaryPlusI
     ["", "", "~($1)", "~($1)"], # BitnotI
-    ["", "", "~($1)", "~($1)"], # BitnotI64
     ["", "", "+($1)", "+($1)"], # UnaryPlusF64
     ["", "", "-($1)", "-($1)"], # UnaryMinusF64
     ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
@@ -357,11 +342,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
     ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
     ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
-    ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
-    ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
-    ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
-    ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
-    ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
     ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
     ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
     ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
@@ -375,11 +355,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
     ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
     ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
-    ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
-    ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
-    ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
-    ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
-    ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
     ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
     ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
     ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
@@ -390,9 +365,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 == $2)", "($1 == $2)"], # EqI
     ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
     ["", "", "($1 < $2)", "($1 < $2)"], # LtI
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqI64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtI64
     ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
     ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
     ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
@@ -419,11 +391,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
     ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
     ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
-    ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
     ["", "", "not ($1)", "not ($1)"], # Not
     ["", "", "+($1)", "+($1)"], # UnaryPlusI
     ["", "", "~($1)", "~($1)"], # BitnotI
-    ["", "", "~($1)", "~($1)"], # BitnotI64
     ["", "", "+($1)", "+($1)"], # UnaryPlusF64
     ["", "", "-($1)", "-($1)"], # UnaryMinusF64
     ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
@@ -535,12 +505,12 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
 proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   # code to generate:
   #
-  #  var sp = {prev: excHandler, exc: null};
-  #  excHandler = sp;
+  #  ++excHandler;
   #  try {
   #    stmts;
-  #    TMP = e
-  #  } catch (e) {
+  #  } catch (EXC) {
+  #    var prevJSError = lastJSError; lastJSError = EXC;
+  #    --excHandler;
   #    if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
   #      stmts;
   #    } else if (e.typ && e.typ == NTI32342) {
@@ -548,35 +518,41 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   #    } else {
   #      stmts;
   #    }
+  #    lastJSError = prevJSError;
   #  } finally {
   #    stmts;
-  #    excHandler = excHandler.prev;
   #  }
   genLineDir(p, n)
   if not isEmptyType(n.typ):
     r.kind = resVal
     r.res = getTemp(p)
   inc(p.unique)
+  var i = 1
+  var length = sonsLen(n)
+  var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
+  if catchBranchesExist:
+    add(p.body, "++excHandler;" & tnl)
   var safePoint = "Tmp$1" % [rope(p.unique)]
   addf(p.body,
-       "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" |
+       "" |
        "local $1 = pcall(",
        [safePoint])
   if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
   addf(p.body, "try {$n" | "function()$n", [])
-  var length = sonsLen(n)
   var a: TCompRes
   gen(p, n.sons[0], a)
   moveInto(p, a, r)
-  var i = 1
-  if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch:
-    addf(p.body, "} catch (EXC) {$n  lastJSError = EXC;$n", [])
+  var generalCatchBranchExists = false
+  if p.target == targetJS and catchBranchesExist:
+    addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
+        " lastJSError = EXC;$n --excHandler;$n", [])
   elif p.target == targetLua:
     addf(p.body, "end)$n", [])
   while i < length and n.sons[i].kind == nkExceptBranch:
     let blen = sonsLen(n.sons[i])
     if blen == 1:
       # general except section:
+      generalCatchBranchExists = true
       if i > 1: addf(p.body, "else {$n" | "else$n", [])
       gen(p, n.sons[i].sons[0], a)
       moveInto(p, a, r)
@@ -588,17 +564,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         if n.sons[i].sons[j].kind != nkType:
           internalError(n.info, "genTryStmt")
         if orExpr != nil: add(orExpr, "||" | " or ")
-        addf(orExpr, "isObj($1.exc.m_type, $2)",
-             [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)])
+        addf(orExpr, "isObj(lastJSError.m_type, $1)",
+             [genTypeInfo(p, n.sons[i].sons[j].typ)])
       if i > 1: add(p.body, "else ")
-      addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n",
+      addf(p.body, "if (lastJSError && ($2)) {$n" | "if $1.exc and ($2) then$n",
         [safePoint, orExpr])
       gen(p, n.sons[i].sons[blen - 1], a)
       moveInto(p, a, r)
       addf(p.body, "}$n" | "end$n", [])
     inc(i)
   if p.target == targetJS:
-    add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl)
+    if catchBranchesExist:
+      if not generalCatchBranchExists:
+        useMagic(p, "reraiseException")
+        add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
+      add(p.body, "lastJSError = prevJSError;" & tnl)
+    add(p.body, "} finally {" & tnl)
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
   if p.target == targetJS:
@@ -748,14 +729,13 @@ proc genBreakStmt(p: PProc, n: PNode) =
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
   addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)])
 
-proc genAsmStmt(p: PProc, n: PNode) =
+proc genAsmOrEmitStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
-  assert(n.kind == nkAsmStmt)
   for i in countup(0, sonsLen(n) - 1):
     case n.sons[i].kind
     of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
     of nkSym: add(p.body, mangleName(n.sons[i].sym))
-    else: internalError(n.sons[i].info, "jsgen: genAsmStmt()")
+    else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
   var cond, stmt: TCompRes
@@ -810,19 +790,36 @@ proc needsNoCopy(y: PNode): bool =
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   var a, b: TCompRes
   gen(p, x, a)
+
+  let xtyp = mapType(x.typ)
+
+  if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
+    gen(p, x.sons[0], a)
+    let tmp = p.getTemp(false)
+    addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc])
+    a.res = "$1[0][$1[1]]" % [tmp]
+  else:
+    gen(p, x, a)
+
   gen(p, y, b)
-  case mapType(x.typ)
+
+  case xtyp
   of etyObject:
-    if needsNoCopy(y) or noCopyNeeded:
+    if (needsNoCopy(y) and needsNoCopy(x)) or noCopyNeeded:
       addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
-      addf(p.body, "$1 = nimCopy($2, $3);$n",
+      addf(p.body, "nimCopy($1, $2, $3);$n",
            [a.res, b.res, genTypeInfo(p, y.typ)])
   of etyBaseIndex:
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
-      internalError(x.info, "genAsgn")
-    addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
+      if y.kind == nkCall:
+        let tmp = p.getTemp(false)
+        addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
+      else:
+        internalError(x.info, "genAsgn")
+    else:
+      addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
   else:
     addf(p.body, "$1 = $2;$n", [a.res, b.res])
 
@@ -838,11 +835,9 @@ proc genSwap(p: PProc, n: PNode) =
   var a, b: TCompRes
   gen(p, n.sons[1], a)
   gen(p, n.sons[2], b)
-  inc(p.unique)
-  var tmp = "Tmp$1" % [rope(p.unique)]
+  var tmp = p.getTemp(false)
   if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
-    inc(p.unique)
-    let tmp2 = "Tmp$1" % [rope(p.unique)]
+    let tmp2 = p.getTemp(false)
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
       internalError(n.info, "genSwap")
     addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
@@ -880,7 +875,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
   if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple:
     r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope]
   else:
-    if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAddr")
+    if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess")
     var f = n.sons[1].sym
     if f.loc.r == nil: f.loc.r = mangleName(f)
     r.res = "$1.$2" % [r.res, f.loc.r]
@@ -970,18 +965,35 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
   of nkCheckedFieldExpr:
     genCheckedFieldAddr(p, n, r)
   of nkDotExpr:
-    genFieldAddr(p, n.sons[0], r)
+    if mapType(n.typ) == etyBaseIndex:
+      genFieldAddr(p, n.sons[0], r)
+    else:
+      genFieldAccess(p, n.sons[0], r)
   of nkBracketExpr:
     var ty = skipTypes(n.sons[0].typ, abstractVarRange)
-    if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
-    case ty.kind
-    of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
-       tyVarargs, tyChar:
-      genArrayAddr(p, n.sons[0], r)
-    of tyTuple:
-      genFieldAddr(p, n.sons[0], r)
-    else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')')
-  else: internalError(n.sons[0].info, "genAddr")
+    if ty.kind in MappedToObject:
+      gen(p, n.sons[0], r)
+    else:
+      let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
+      case kindOfIndexedExpr
+      of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
+          tyVarargs:
+        genArrayAddr(p, n.sons[0], r)
+      of tyTuple:
+        genFieldAddr(p, n.sons[0], r)
+      else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
+  of nkObjDownConv:
+    gen(p, n.sons[0], r)
+  else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
+
+proc genProcForSymIfNeeded(p: PProc, s: PSym) =
+  if not p.g.generatedSyms.containsOrIncl(s.id):
+    let newp = genProc(p, s)
+    var owner = p
+    while owner != nil and owner.prc != s.owner:
+      owner = owner.up
+    if owner != nil: add(owner.locals, newp)
+    else: add(p.g.code, newp)
 
 proc genSym(p: PProc, n: PNode, r: var TCompRes) =
   var s = n.sym
@@ -1018,13 +1030,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
       discard
     elif sfForward in s.flags:
       p.g.forwarded.add(s)
-    elif not p.g.generatedSyms.containsOrIncl(s.id):
-      let newp = genProc(p, s)
-      var owner = p
-      while owner != nil and owner.prc != s.owner:
-        owner = owner.up
-      if owner != nil: add(owner.locals, newp)
-      else: add(p.g.code, newp)
+    else:
+      genProcForSymIfNeeded(p, s)
   else:
     if s.loc.r == nil:
       internalError(n.info, "symbol has no generated name: " & s.name.s)
@@ -1037,10 +1044,15 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
   else:
     var a: TCompRes
     gen(p, n.sons[0], a)
-    if a.typ != etyBaseIndex: internalError(n.info, "genDeref")
-    r.res = "$1[$2]" % [a.address, a.res]
+    if a.typ == etyBaseIndex:
+      r.res = "$1[$2]" % [a.address, a.res]
+    elif n.sons[0].kind == nkCall:
+      let tmp = p.getTemp
+      r.res = "($1 = $2, $1[0][$1[1]])" % [tmp, a.res]
+    else:
+      internalError(n.info, "genDeref")
 
-proc genArg(p: PProc, n: PNode, r: var TCompRes) =
+proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
   gen(p, n, a)
   if a.typ == etyBaseIndex:
@@ -1050,13 +1062,42 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) =
   else:
     add(r.res, a.res)
 
+proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) =
+  var a: TCompRes
+  gen(p, n, a)
+  if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
+      a.typ == etyBaseIndex:
+    add(r.res, "$1[$2]" % [a.address, a.res])
+  elif a.typ == etyBaseIndex:
+    add(r.res, a.address)
+    add(r.res, ", ")
+    add(r.res, a.res)
+  else:
+    add(r.res, a.res)
+
+
 proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
   add(r.res, "(")
+  var hasArgs = false
+
+  var typ = skipTypes(n.sons[0].typ, abstractInst)
+  assert(typ.kind == tyProc)
+  assert(sonsLen(typ) == sonsLen(typ.n))
+
   for i in countup(1, sonsLen(n) - 1):
     let it = n.sons[i]
-    if it.typ.isCompileTimeOnly: continue
-    if i > 1: add(r.res, ", ")
-    genArg(p, it, r)
+    var paramType : PNode = nil
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      paramType = typ.n.sons[i]
+      if paramType.typ.isCompileTimeOnly: continue
+
+    if hasArgs: add(r.res, ", ")
+    if paramType.isNil:
+      genArgNoParam(p, it, r)
+    else:
+      genArg(p, it, paramType.sym, r)
+    hasArgs = true
   add(r.res, ")")
   r.kind = resExpr
 
@@ -1080,11 +1121,12 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   add(r.res, "(")
   for i in countup(2, sonsLen(n) - 1):
     if i > 2: add(r.res, ", ")
-    genArg(p, n.sons[i], r)
+    genArgNoParam(p, n.sons[i], r)
   add(r.res, ")")
   r.kind = resExpr
 
 proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
+  useMagic(p, "toJSStr") # Used in rawEcho
   useMagic(p, "rawEcho")
   add(r.res, "rawEcho(")
   let n = n[1].skipConv
@@ -1093,7 +1135,7 @@ proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
     let it = n.sons[i]
     if it.typ.isCompileTimeOnly: continue
     if i > 0: add(r.res, ", ")
-    genArg(p, it, r)
+    genArgNoParam(p, it, r)
   add(r.res, ")")
   r.kind = resExpr
 
@@ -1102,24 +1144,32 @@ proc putToSeq(s: string, indirect: bool): Rope =
   if indirect: result = "[$1]" % [result]
 
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope
-proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope =
-  result = nil
+proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
   case rec.kind
   of nkRecList:
     for i in countup(0, sonsLen(rec) - 1):
-      add(result, createRecordVarAux(p, rec.sons[i], c))
+      createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output)
   of nkRecCase:
-    add(result, createRecordVarAux(p, rec.sons[0], c))
+    createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output)
     for i in countup(1, sonsLen(rec) - 1):
-      add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c))
+      createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
   of nkSym:
-    if c > 0: add(result, ", ")
-    add(result, mangleName(rec.sym))
-    add(result, ": ")
-    add(result, createVar(p, rec.sym.typ, false))
-    inc(c)
+    if rec.sym.id notin excludedFieldIDs:
+      if output.len > 0: output.add(", ")
+      output.add(mangleName(rec.sym))
+      output.add(": ")
+      output.add(createVar(p, rec.sym.typ, false))
   else: internalError(rec.info, "createRecordVarAux")
 
+proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
+  var t = typ
+  if tfFinal notin t.flags or t.sons[0] != nil:
+    if output.len > 0: output.add(", ")
+    addf(output, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)])
+  while t != nil:
+    createRecordVarAux(p, t.n, excludedFieldIDs, output)
+    t = t.sons[0]
+
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   var t = skipTypes(typ, abstractInst)
   case t.kind
@@ -1160,15 +1210,9 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
     add(result, "}")
     if indirect: result = "[$1]" % [result]
   of tyObject:
-    result = rope("{")
-    var c = 0
-    if tfFinal notin t.flags or t.sons[0] != nil:
-      inc(c)
-      addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)])
-    while t != nil:
-      add(result, createRecordVarAux(p, t.n, c))
-      t = t.sons[0]
-    add(result, "}")
+    var initList : Rope
+    createObjInitList(p, t, initIntSet(), initList)
+    result = "{$1}" % [initList]
     if indirect: result = "[$1]" % [result]
   of tyVar, tyPtr, tyRef:
     if mapType(t) == etyBaseIndex:
@@ -1197,7 +1241,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
         s = a.res
       else:
         useMagic(p, "nimCopy")
-        s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)]
+        s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
     of etyBaseIndex:
       if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
       if {sfAddrTaken, sfGlobal} * v.flags != {}:
@@ -1389,6 +1433,12 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
   of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
   of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
+  of mDotDot:
+    genProcForSymIfNeeded(p, n.sons[0].sym)
+    genCall(p, n, r)
+  of mParseBiggestFloat:
+    useMagic(p, "nimParseBiggestFloat")
+    genCall(p, n, r)
   else:
     genCall(p, n, r)
     #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
@@ -1434,19 +1484,22 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
   r.res.add("}")
 
 proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
-  # XXX inheritance?
   var a: TCompRes
-  r.res = rope("{")
   r.kind = resExpr
+  var initList : Rope
+  var fieldIDs = initIntSet()
   for i in countup(1, sonsLen(n) - 1):
-    if i > 1: add(r.res, ", ")
+    if i > 1: add(initList, ", ")
     var it = n.sons[i]
     internalAssert it.kind == nkExprColonExpr
     gen(p, it.sons[1], a)
     var f = it.sons[0].sym
     if f.loc.r == nil: f.loc.r = mangleName(f)
-    addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res])
-  r.res.add("}")
+    fieldIDs.incl(f.id)
+    addf(initList, "$#: $#" | "$# = $#" , [f.loc.r, a.res])
+  let t = skipTypes(n.typ, abstractInst + skipPtrs)
+  createObjInitList(p, t, fieldIDs, initList)
+  r.res = "{$1}" % [initList]
 
 proc genConv(p: PProc, n: PNode, r: var TCompRes) =
   var dest = skipTypes(n.typ, abstractVarRange)
@@ -1551,7 +1604,10 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
         mangleName(resultSym),
         createVar(p, resultSym.typ, isIndirect(resultSym))]
     gen(p, prc.ast.sons[resultPos], a)
-    returnStmt = "return $#;$n" % [a.res]
+    if mapType(resultSym.typ) == etyBaseIndex:
+      returnStmt = "return [$#, $#];$n" % [a.address, a.res]
+    else:
+      returnStmt = "return $#;$n" % [a.res]
   genStmt(p, prc.getBody)
   result = ("function $#($#) {$n$#$#$#$#}$n" |
             "function $#($#) $n$#$#$#$#$nend$n") %
@@ -1565,6 +1621,12 @@ proc genStmt(p: PProc, n: PNode) =
   gen(p, n, r)
   if r.res != nil: addf(p.body, "$#;$n", [r.res])
 
+proc genPragma(p: PProc, n: PNode) =
+  for it in n.sons:
+    case whichPragma(it)
+    of wEmit: genAsmOrEmitStmt(p, it.sons[1])
+    else: discard
+
 proc gen(p: PProc, n: PNode, r: var TCompRes) =
   r.typ = etyNone
   r.kind = resNone
@@ -1648,6 +1710,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       gen(p, lastSon(n), r)
   of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
   of nkIfStmt, nkIfExpr: genIf(p, n, r)
+  of nkWhen:
+    # This is "when nimvm" node
+    gen(p, n.sons[1].sons[0], r)
   of nkWhileStmt: genWhileStmt(p, n)
   of nkVarSection, nkLetSection: genVarStmt(p, n)
   of nkConstSection: discard
@@ -1664,12 +1729,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     if n.sons[0].kind != nkEmpty:
       genLineDir(p, n)
       gen(p, n.sons[0], r)
-  of nkAsmStmt: genAsmStmt(p, n)
+  of nkAsmStmt: genAsmOrEmitStmt(p, n)
   of nkTryStmt: genTry(p, n, r)
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef: discard
+  of nkPragma: genPragma(p, n)
   of nkProcDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
     if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
@@ -1691,7 +1757,7 @@ proc genHeader(): Rope =
   result = ("/* Generated by the Nim Compiler v$1 */$n" &
             "/*   (c) 2015 Andreas Rumpf */$n$n" &
             "var framePtr = null;$n" &
-            "var excHandler = null;$n" &
+            "var excHandler = 0;$n" &
             "var lastJSError = null;$n") %
            [rope(VersionAsString)]
 
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 851938327..367c173ea 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -116,13 +116,12 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
          [name, genTypeInfo(p, typ.sons[0])])
 
 proc genTypeInfo(p: PProc, typ: PType): Rope =
-  var t = typ
-  if t.kind == tyGenericInst: t = lastSon(t)
+  let t = typ.skipTypes({tyGenericInst})
   result = "NTI$1" % [rope(t.id)]
   if containsOrIncl(p.g.typeInfoGenerated, t.id): return
   case t.kind
   of tyDistinct:
-    result = genTypeInfo(p, typ.sons[0])
+    result = genTypeInfo(p, t.sons[0])
   of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
     var s =
       "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
@@ -134,14 +133,14 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
     addf(p.g.typeInfo, "$1.base = $2;$n",
-         [result, genTypeInfo(p, typ.lastSon)])
+         [result, genTypeInfo(p, t.lastSon)])
   of tyArrayConstr, tyArray:
     var s =
       "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
     addf(p.g.typeInfo, "$1.base = $2;$n",
-         [result, genTypeInfo(p, typ.sons[1])])
+         [result, genTypeInfo(p, t.sons[1])])
   of tyEnum: genEnumInfo(p, t, result)
   of tyObject: genObjectInfo(p, t, result)
   of tyTuple: genTupleInfo(p, t, result)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index c68bc352c..cccc94756 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   var params = routine.ast.sons[paramsPos]
   # -1 is correct here as param.position is 0 based but we have at position 0
   # some nkEffect node:
-  param.position = params.len-1
+  param.position = routine.typ.n.len-1
   addSon(params, newSymNode(param))
   incl(routine.typ.flags, tfCapturesEnv)
   assert sfFromGeneric in param.flags
@@ -859,11 +859,17 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
         return indirectAccess(newSymNode(it.closureParam), local, n.info)
 
     if local.kind == skClosureIterator:
+      # bug #3354; allow for
+      #iterator iter(): int {.closure.}=
+      #  s.add(iter)
+      #  yield 1
+
+      #if local == o.fn or local == it.fn:
+      #  message(n.info, errRecursiveDependencyX, local.name.s)
+
       # consider: [i1, i2, i1]  Since we merged the iterator's closure
       # with the captured owning variables, we need to generate the
       # closure generation code again:
-      if local == o.fn or local == it.fn:
-        message(n.info, errRecursiveDependencyX, local.name.s)
       # XXX why doesn't this work?
       var closure = PEnv(idTableGet(o.lambdasToEnv, local))
       if closure.isNil:
@@ -946,7 +952,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
 proc liftLambdas*(fn: PSym, body: PNode): PNode =
   # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
-  if body.kind == nkEmpty or gCmd == cmdCompileToJS or
+
+  # However we can do lifting for the stuff which is *only* compiletime.
+  let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
+
+  if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or
       fn.skipGenericOwner.kind != skModule:
     # ignore forward declaration:
     result = body
@@ -1012,7 +1022,9 @@ proc liftForLoop*(body: PNode): PNode =
         ...
     """
   var L = body.len
-  internalAssert body.kind == nkForStmt and body[L-2].kind in nkCallKinds
+  if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
+    localError(body.info, "ignored invalid for loop")
+    return body
   var call = body[L-2]
 
   result = newNodeI(nkStmtList, body.info)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 8080e0e8c..cea42ad1e 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -17,7 +17,7 @@
 
 import
   hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg
+  wordrecg, etcpriv
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
 proc isNimIdentifier*(s: string): bool =
   if s[0] in SymStartChars:
     var i = 1
-    while i < s.len:
+    var sLen = s.len
+    while i < sLen:
       if s[i] == '_':
         inc(i)
-        if s[i] notin SymChars: return
+      elif isMagicIdentSeparatorRune(cstring s, i):
+        inc(i, magicIdentSeparatorRuneByteWidth)
       if s[i] notin SymChars: return
       inc(i)
     result = true
@@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
   var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
   L.dispMessage(info, msg, arg)
 
-proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
-  var pos = L.bufpos              # use registers for pos, buf
-  var buf = L.buf
-  while true:
-    if buf[pos] in chars:
-      add(tok.literal, buf[pos])
-      inc(pos)
-    else:
-      break
-    if buf[pos] == '_':
-      if buf[pos+1] notin chars:
-        lexMessage(L, errInvalidToken, "_")
-        break
-      add(tok.literal, '_')
-      inc(pos)
-  L.bufpos = pos
-
 proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
   result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
 
@@ -268,136 +253,200 @@ proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
     result = i - start
 {.pop.} # overflowChecks
 
+
+template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
+  add(t.literal, replacementChar)
+  inc(L.bufpos)
+
+template eatChar(L: var TLexer, t: var TToken) =
+  add(t.literal, L.buf[L.bufpos])
+  inc(L.bufpos)
+
 proc getNumber(L: var TLexer): TToken =
+  proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
+    var pos = L.bufpos              # use registers for pos, buf
+    var buf = L.buf
+    while true:
+      if buf[pos] in chars:
+        add(tok.literal, buf[pos])
+        inc(pos)
+      else:
+        break
+      if buf[pos] == '_':
+        if buf[pos+1] notin chars:
+          lexMessage(L, errInvalidToken, "_")
+          break
+        add(tok.literal, '_')
+        inc(pos)
+    L.bufpos = pos
+
+  proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
+    var pos = L.bufpos              # use registers for pos, buf
+    var buf = L.buf
+    while buf[pos] in chars:
+      add(tok.literal, buf[pos])
+      inc(pos)
+    L.bufpos = pos
+
+  proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
+    # Used to get slightly human friendlier err messages.
+    # Note: the erroneous 'O' char in the character set is intentional
+    const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
+      'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'}
+    var msgPos = L.bufpos
+    var t: TToken
+    t.literal = ""
+    L.bufpos = startpos # Use L.bufpos as pos because of matchChars
+    matchChars(L, t, literalishChars)
+    # We must verify +/- specifically so that we're not past the literal
+    if  L.buf[L.bufpos] in {'+', '-'} and
+        L.buf[L.bufpos - 1] in {'e', 'E'}:
+      add(t.literal, L.buf[L.bufpos])
+      inc(L.bufpos)
+      matchChars(L, t, literalishChars)
+    if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
+      inc(L.bufpos)
+      add(t.literal, L.buf[L.bufpos])
+      matchChars(L, t, {'0'..'9'})
+    L.bufpos = msgPos
+    lexMessage(L, msg, t.literal)
+
   var
-    pos, endpos: int
+    startpos, endpos: int
     xi: BiggestInt
-  # get the base:
+    isBase10 = true
+  const
+    baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'}
+    literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''}
+    floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit}
   result.tokType = tkIntLit   # int literal until we know better
   result.literal = ""
-  result.base = base10        # BUGFIX
-  pos = L.bufpos     # make sure the literal is correct for error messages:
-  var eallowed = false
-  if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}:
-    matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'})
+  result.base = base10
+  startpos = L.bufpos
+
+  # First stage: find out base, make verifications, build token literal string
+  if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
+    isBase10 = false
+    eatChar(L, result, '0')
+    case L.buf[L.bufpos]
+    of 'O':
+      lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
+    of 'x', 'X':
+      eatChar(L, result, 'x')
+      matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
+    of 'o', 'c', 'C':
+      eatChar(L, result, 'c')
+      matchUnderscoreChars(L, result, {'0'..'7'})
+    of 'b', 'B':
+      eatChar(L, result, 'b')
+      matchUnderscoreChars(L, result, {'0'..'1'})
+    else:
+      internalError(getLineInfo(L), "getNumber")
   else:
-    matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
-    eallowed = true
-  if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
-    add(result.literal, '.')
-    inc(L.bufpos)
-    matchUnderscoreChars(L, result, {'0'..'9'})
-    eallowed = true
-  if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
-    add(result.literal, 'e')
-    inc(L.bufpos)
-    if L.buf[L.bufpos] in {'+', '-'}:
-      add(result.literal, L.buf[L.bufpos])
-      inc(L.bufpos)
     matchUnderscoreChars(L, result, {'0'..'9'})
+    if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
+      result.tokType = tkFloat64Lit
+      eatChar(L, result, '.')
+      matchUnderscoreChars(L, result, {'0'..'9'})
+    if L.buf[L.bufpos] in {'e', 'E'}:
+      result.tokType = tkFloat64Lit
+      eatChar(L, result, 'e')
+      if L.buf[L.bufpos] in {'+', '-'}:
+        eatChar(L, result)
+      matchUnderscoreChars(L, result, {'0'..'9'})
   endpos = L.bufpos
-  if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}:
-    if L.buf[endpos] == '\'': inc(endpos)
-    L.bufpos = pos            # restore position
-    case L.buf[endpos]
+
+  # Second stage, find out if there's a datatype suffix and handle it
+  var postPos = endpos
+  if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
+    if L.buf[postPos] == '\'':
+      inc(postPos)
+
+    case L.buf[postPos]
     of 'f', 'F':
-      inc(endpos)
-      if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
+      inc(postPos)
+      if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
         result.tokType = tkFloat32Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
         result.tokType = tkFloat64Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '1') and
-           (L.buf[endpos + 1] == '2') and
-           (L.buf[endpos + 2] == '8'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '1') and
+           (L.buf[postPos + 1] == '2') and
+           (L.buf[postPos + 2] == '8'):
         result.tokType = tkFloat128Lit
-        inc(endpos, 3)
-      else:
-        lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos])
+        inc(postPos, 3)
+      else:   # "f" alone defaults to float32
+        result.tokType = tkFloat32Lit
+    of 'd', 'D':  # ad hoc convenience shortcut for f64
+      inc(postPos)
+      result.tokType = tkFloat64Lit
     of 'i', 'I':
-      inc(endpos)
-      if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
+      inc(postPos)
+      if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
         result.tokType = tkInt64Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
         result.tokType = tkInt32Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
         result.tokType = tkInt16Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '8'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '8'):
         result.tokType = tkInt8Lit
-        inc(endpos)
+        inc(postPos)
       else:
-        lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos])
+        lexMessageLitNum(L, errInvalidNumber, startpos)
     of 'u', 'U':
-      inc(endpos)
-      if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
+      inc(postPos)
+      if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
         result.tokType = tkUInt64Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
         result.tokType = tkUInt32Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
         result.tokType = tkUInt16Lit
-        inc(endpos, 2)
-      elif (L.buf[endpos] == '8'):
+        inc(postPos, 2)
+      elif (L.buf[postPos] == '8'):
         result.tokType = tkUInt8Lit
-        inc(endpos)
+        inc(postPos)
       else:
         result.tokType = tkUIntLit
-    else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos])
-  else:
-    L.bufpos = pos            # restore position
+    else:
+      lexMessageLitNum(L, errInvalidNumber, startpos)
+
+  # Is there still a literalish char awaiting? Then it's an error!
+  if  L.buf[postPos] in literalishChars or
+     (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
+    lexMessageLitNum(L, errInvalidNumber, startpos)
+
+  # Third stage, extract actual number
+  L.bufpos = startpos            # restore position
+  var pos: int = startpos
   try:
-    if (L.buf[pos] == '0') and
-        (L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
+    if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars):
       inc(pos, 2)
-      xi = 0                  # it may be a base prefix
-      case L.buf[pos - 1]     # now look at the optional type suffix:
+      xi = 0                  # it is a base prefix
+
+      case L.buf[pos - 1]
       of 'b', 'B':
         result.base = base2
-        while true:
-          case L.buf[pos]
-          of '2'..'9', '.':
-            lexMessage(L, errInvalidNumber, result.literal)
-            inc(pos)
-          of '_':
-            if L.buf[pos+1] notin {'0'..'1'}:
-              lexMessage(L, errInvalidToken, "_")
-              break
-            inc(pos)
-          of '0', '1':
+        while pos < endpos:
+          if L.buf[pos] != '_':
             xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
-            inc(pos)
-          else: break
+          inc(pos)
       of 'o', 'c', 'C':
         result.base = base8
-        while true:
-          case L.buf[pos]
-          of '8'..'9', '.':
-            lexMessage(L, errInvalidNumber, result.literal)
-            inc(pos)
-          of '_':
-            if L.buf[pos+1] notin {'0'..'7'}:
-              lexMessage(L, errInvalidToken, "_")
-              break
-            inc(pos)
-          of '0'..'7':
+        while pos < endpos:
+          if L.buf[pos] != '_':
             xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
-            inc(pos)
-          else: break
-      of 'O':
-        lexMessage(L, errInvalidNumber, result.literal)
+          inc(pos)
       of 'x', 'X':
         result.base = base16
-        while true:
+        while pos < endpos:
           case L.buf[pos]
           of '_':
-            if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}:
-              lexMessage(L, errInvalidToken, "_")
-              break
             inc(pos)
           of '0'..'9':
             xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
@@ -408,51 +457,81 @@ proc getNumber(L: var TLexer): TToken =
           of 'A'..'F':
             xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
             inc(pos)
-          else: break
-      else: internalError(getLineInfo(L), "getNumber")
+          else:
+            break
+      else:
+        internalError(getLineInfo(L), "getNumber")
+
       case result.tokType
       of tkIntLit, tkInt64Lit: result.iNumber = xi
       of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
-      of tkInt16Lit: result.iNumber = BiggestInt(toU16(int(xi)))
-      of tkInt32Lit: result.iNumber = BiggestInt(toU32(xi))
+      of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
+      of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
       of tkUIntLit, tkUInt64Lit: result.iNumber = xi
-      of tkUInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
-      of tkUInt16Lit: result.iNumber = BiggestInt(toU16(int(xi)))
-      of tkUInt32Lit: result.iNumber = BiggestInt(toU32(xi))
+      of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi))))
+      of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi))))
+      of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi))))
       of tkFloat32Lit:
         result.fNumber = (cast[PFloat32](addr(xi)))[]
         # note: this code is endian neutral!
         # XXX: Test this on big endian machine!
       of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
       else: internalError(getLineInfo(L), "getNumber")
-    elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or
-        (result.tokType == tkFloat64Lit):
-      result.fNumber = parseFloat(result.literal)
-      if result.tokType == tkIntLit: result.tokType = tkFloatLit
-    elif result.tokType == tkUint64Lit:
-      xi = 0
-      let len = unsafeParseUInt(result.literal, xi)
-      if len != result.literal.len or len == 0:
-        raise newException(ValueError, "invalid integer: " & $xi)
-      result.iNumber = xi
+
+      # Bounds checks. Non decimal literals are allowed to overflow the range of
+      # the datatype as long as their pattern don't overflow _bitwise_, hence
+      # below checks of signed sizes against uint*.high is deliberate:
+      # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK)
+      if result.tokType notin floatTypes:
+        let outOfRange = case result.tokType:
+        of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi
+        of tkInt8Lit: (xi > BiggestInt(uint8.high))
+        of tkInt16Lit: (xi > BiggestInt(uint16.high))
+        of tkInt32Lit: (xi > BiggestInt(uint32.high))
+        else: false
+
+        if outOfRange:
+          #echo "out of range num: ", result.iNumber, " vs ", xi
+          lexMessageLitNum(L, errNumberOutOfRange, startpos)
+
     else:
-      result.iNumber = parseBiggestInt(result.literal)
+      case result.tokType
+      of floatTypes:
+        result.fNumber = parseFloat(result.literal)
+      of tkUint64Lit:
+        xi = 0
+        let len = unsafeParseUInt(result.literal, xi)
+        if len != result.literal.len or len == 0:
+          raise newException(ValueError, "invalid integer: " & $xi)
+        result.iNumber = xi
+      else:
+        result.iNumber = parseBiggestInt(result.literal)
+
+      # Explicit bounds checks
+      let outOfRange = case result.tokType:
+      of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
+      of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
+                      result.iNumber > BiggestInt(uint8.high))
+      of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
+      of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
+                      result.iNumber > BiggestInt(uint16.high))
+      of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
+      of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
+                      result.iNumber > BiggestInt(uint32.high))
+      else: false
+
+      if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
+
+    # Promote int literal to int64? Not always necessary, but more consistent
+    if result.tokType == tkIntLit:
       if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
-        if result.tokType == tkIntLit:
-          result.tokType = tkInt64Lit
-        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)
+        result.tokType = tkInt64Lit
+
   except ValueError:
-    lexMessage(L, errInvalidNumber, result.literal)
+    lexMessageLitNum(L, errInvalidNumber, startpos)
   except OverflowError, RangeError:
-    lexMessage(L, errNumberOutOfRange, result.literal)
-  L.bufpos = endpos
+    lexMessageLitNum(L, errNumberOutOfRange, startpos)
+  L.bufpos = postPos
 
 proc handleHexChar(L: var TLexer, xi: var int) =
   case L.buf[L.bufpos]
@@ -625,23 +704,34 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
   inc(L.bufpos)               # skip '
 
 proc getSymbol(L: var TLexer, tok: var TToken) =
-  var h: THash = 0
+  var h: Hash = 0
   var pos = L.bufpos
   var buf = L.buf
   while true:
     var c = buf[pos]
     case c
     of 'a'..'z', '0'..'9', '\x80'..'\xFF':
-      h = h !& ord(c)
+      if  c == '\226' and
+          buf[pos+1] == '\128' and
+          buf[pos+2] == '\147':  # It's a 'magic separator' en-dash Unicode
+        if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars:
+          lexMessage(L, errInvalidToken, "–")
+          break
+        inc(pos, magicIdentSeparatorRuneByteWidth)
+      else:
+        h = h !& ord(c)
+        inc(pos)
     of 'A'..'Z':
       c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
       h = h !& ord(c)
+      inc(pos)
     of '_':
       if buf[pos+1] notin SymChars:
         lexMessage(L, errInvalidToken, "_")
         break
+      inc(pos)
+
     else: break
-    inc(pos)
   h = !$h
   tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
   L.bufpos = pos
@@ -652,7 +742,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
     tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
 
 proc endOperator(L: var TLexer, tok: var TToken, pos: int,
-                 hash: THash) {.inline.} =
+                 hash: Hash) {.inline.} =
   var h = !$hash
   tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
   if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
@@ -662,7 +752,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
 proc getOperator(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
   var buf = L.buf
-  var h: THash = 0
+  var h: Hash = 0
   while true:
     var c = buf[pos]
     if c notin OpChars: break
diff --git a/compiler/lists.nim b/compiler/lists.nim
index 1b2b91bff..27e373342 100644
--- a/compiler/lists.nim
+++ b/compiler/lists.nim
@@ -10,7 +10,7 @@
 # This module implements a generic doubled linked list.
 # TODO Remove this and replace it with something sensible
 import os
-type 
+type
   PListEntry* = ref TListEntry
   TListEntry* = object of RootObj
     prev*, next*: PListEntry
@@ -25,68 +25,68 @@ type
 
   TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
 
-proc initLinkedList*(list: var TLinkedList) = 
+proc initLinkedList*(list: var TLinkedList) =
   list.counter = 0
   list.head = nil
   list.tail = nil
 
-proc append*(list: var TLinkedList, entry: PListEntry) = 
+proc append*(list: var TLinkedList, entry: PListEntry) =
   inc(list.counter)
   entry.next = nil
   entry.prev = list.tail
-  if list.tail != nil: 
+  if list.tail != nil:
     assert(list.tail.next == nil)
     list.tail.next = entry
   list.tail = entry
   if list.head == nil: list.head = entry
-  
-proc contains*(list: TLinkedList, data: string): bool = 
+
+proc contains*(list: TLinkedList, data: string): bool =
   var it = list.head
-  while it != nil: 
-    if PStrEntry(it).data == data: 
+  while it != nil:
+    if PStrEntry(it).data == data:
       return true
     it = it.next
-  
-proc newStrEntry(data: string): PStrEntry = 
+
+proc newStrEntry(data: string): PStrEntry =
   new(result)
   result.data = data
 
-proc appendStr*(list: var TLinkedList, data: string) = 
+proc appendStr*(list: var TLinkedList, data: string) =
   append(list, newStrEntry(data))
 
-proc includeStr*(list: var TLinkedList, data: string): bool = 
+proc includeStr*(list: var TLinkedList, data: string): bool =
   if contains(list, data): return true
   appendStr(list, data)       # else: add to list
 
-proc prepend*(list: var TLinkedList, entry: PListEntry) = 
+proc prepend*(list: var TLinkedList, entry: PListEntry) =
   inc(list.counter)
   entry.prev = nil
   entry.next = list.head
-  if list.head != nil: 
+  if list.head != nil:
     assert(list.head.prev == nil)
     list.head.prev = entry
   list.head = entry
   if list.tail == nil: list.tail = entry
 
-proc prependStr*(list: var TLinkedList, data: string) = 
+proc prependStr*(list: var TLinkedList, data: string) =
   prepend(list, newStrEntry(data))
 
-proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) = 
+proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
   assert(pos != nil)
-  if pos == list.head: 
+  if pos == list.head:
     prepend(list, entry)
-  else: 
+  else:
     inc(list.counter)
     entry.next = pos
     entry.prev = pos.prev
     if pos.prev != nil: pos.prev.next = entry
     pos.prev = entry
- 
-proc remove*(list: var TLinkedList, entry: PListEntry) = 
+
+proc remove*(list: var TLinkedList, entry: PListEntry) =
   dec(list.counter)
-  if entry == list.tail: 
+  if entry == list.tail:
     list.tail = entry.prev
-  if entry == list.head: 
+  if entry == list.head:
     list.head = entry.next
   if entry.next != nil: entry.next.prev = entry.prev
   if entry.prev != nil: entry.prev.next = entry.next
@@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) =
       remove(list, it)
     it = nxt
 
-proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = 
+proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
   result = list.head
   while result != nil:
-    if fn(result, closure): return 
+    if fn(result, closure): return
     result = result.next
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 18ca4aec7..0a1e09fc8 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -9,13 +9,14 @@
 
 ## Low-level streams for high performance.
 
-import 
+import
   strutils
 
-when not defined(windows) and defined(useGnuReadline):
+# support '-d:useGnuReadline' for backwards compatibility:
+when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
   import rdstdin
 
-type 
+type
   TLLStreamKind* = enum       # enum of different stream implementations
     llsNone,                  # null stream: reading and writing has no effect
     llsString,                # stream encapsulates a string
@@ -27,42 +28,42 @@ type
     s*: string
     rd*, wr*: int             # for string streams
     lineOffset*: int          # for fake stdin line numbers
-  
+
   PLLStream* = ref TLLStream
 
-proc llStreamOpen*(data: string): PLLStream = 
+proc llStreamOpen*(data: string): PLLStream =
   new(result)
   result.s = data
   result.kind = llsString
 
-proc llStreamOpen*(f: File): PLLStream = 
+proc llStreamOpen*(f: File): PLLStream =
   new(result)
   result.f = f
   result.kind = llsFile
 
-proc llStreamOpen*(filename: string, mode: FileMode): PLLStream = 
+proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
   new(result)
   result.kind = llsFile
   if not open(result.f, filename, mode): result = nil
-  
-proc llStreamOpen*(): PLLStream = 
+
+proc llStreamOpen*(): PLLStream =
   new(result)
   result.kind = llsNone
 
-proc llStreamOpenStdIn*(): PLLStream = 
+proc llStreamOpenStdIn*(): PLLStream =
   new(result)
   result.kind = llsStdIn
   result.s = ""
   result.lineOffset = -1
 
-proc llStreamClose*(s: PLLStream) = 
+proc llStreamClose*(s: PLLStream) =
   case s.kind
-  of llsNone, llsString, llsStdIn: 
+  of llsNone, llsString, llsStdIn:
     discard
-  of llsFile: 
+  of llsFile:
     close(s.f)
 
-when not declared(readLineFromStdin): 
+when not declared(readLineFromStdin):
   # fallback implementation:
   proc readLineFromStdin(prompt: string, line: var string): bool =
     stdout.write(prompt)
@@ -77,7 +78,7 @@ proc endsWith*(x: string, s: set[char]): bool =
   if i >= 0 and x[i] in s:
     result = true
 
-const 
+const
   LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
                           '|', '%', '&', '$', '@', '~', ','}
   AdditionalLineContinuationOprs = {'#', ':', '='}
@@ -104,31 +105,31 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
   s.rd = 0
   var line = newStringOfCap(120)
   var triples = 0
-  while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): 
+  while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
     add(s.s, line)
     add(s.s, "\n")
     inc triples, countTriples(line)
     if not continueLine(line, (triples and 1) == 1): break
   inc(s.lineOffset)
   result = min(bufLen, len(s.s) - s.rd)
-  if result > 0: 
+  if result > 0:
     copyMem(buf, addr(s.s[s.rd]), result)
     inc(s.rd, result)
 
-proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = 
+proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
   case s.kind
-  of llsNone: 
+  of llsNone:
     result = 0
-  of llsString: 
+  of llsString:
     result = min(bufLen, len(s.s) - s.rd)
-    if result > 0: 
+    if result > 0:
       copyMem(buf, addr(s.s[0 + s.rd]), result)
       inc(s.rd, result)
-  of llsFile: 
+  of llsFile:
     result = readBuffer(s.f, buf, bufLen)
-  of llsStdIn: 
+  of llsStdIn:
     result = llReadFromStdin(s, buf, bufLen)
-  
+
 proc llStreamReadLine*(s: PLLStream, line: var string): bool =
   setLen(line, 0)
   case s.kind
@@ -152,60 +153,60 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool =
     result = readLine(s.f, line)
   of llsStdIn:
     result = readLine(stdin, line)
-    
-proc llStreamWrite*(s: PLLStream, data: string) = 
+
+proc llStreamWrite*(s: PLLStream, data: string) =
   case s.kind
-  of llsNone, llsStdIn: 
+  of llsNone, llsStdIn:
     discard
-  of llsString: 
+  of llsString:
     add(s.s, data)
     inc(s.wr, len(data))
-  of llsFile: 
+  of llsFile:
     write(s.f, data)
-  
-proc llStreamWriteln*(s: PLLStream, data: string) = 
+
+proc llStreamWriteln*(s: PLLStream, data: string) =
   llStreamWrite(s, data)
   llStreamWrite(s, "\n")
 
-proc llStreamWrite*(s: PLLStream, data: char) = 
+proc llStreamWrite*(s: PLLStream, data: char) =
   var c: char
   case s.kind
-  of llsNone, llsStdIn: 
+  of llsNone, llsStdIn:
     discard
-  of llsString: 
+  of llsString:
     add(s.s, data)
     inc(s.wr)
-  of llsFile: 
+  of llsFile:
     c = data
     discard writeBuffer(s.f, addr(c), sizeof(c))
 
-proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) = 
+proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
   case s.kind
-  of llsNone, llsStdIn: 
+  of llsNone, llsStdIn:
     discard
-  of llsString: 
-    if buflen > 0: 
+  of llsString:
+    if buflen > 0:
       setLen(s.s, len(s.s) + buflen)
       copyMem(addr(s.s[0 + s.wr]), buf, buflen)
       inc(s.wr, buflen)
-  of llsFile: 
+  of llsFile:
     discard writeBuffer(s.f, buf, buflen)
-  
-proc llStreamReadAll*(s: PLLStream): string = 
-  const 
+
+proc llStreamReadAll*(s: PLLStream): string =
+  const
     bufSize = 2048
   case s.kind
-  of llsNone, llsStdIn: 
+  of llsNone, llsStdIn:
     result = ""
-  of llsString: 
+  of llsString:
     if s.rd == 0: result = s.s
     else: result = substr(s.s, s.rd)
     s.rd = len(s.s)
-  of llsFile: 
+  of llsFile:
     result = newString(bufSize)
     var bytes = readBuffer(s.f, addr(result[0]), bufSize)
     var i = bytes
-    while bytes == bufSize: 
+    while bytes == bufSize:
       setLen(result, i + bufSize)
       bytes = readBuffer(s.f, addr(result[i + 0]), bufSize)
       inc(i, bytes)
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 88e32404a..e88589c3e 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -116,7 +116,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
   # pretend it's imported from some unknown module to prevent cascading errors:
-  if gCmd != cmdInteractive and c.inCompilesContext == 0:
+  if gCmd != cmdInteractive and c.compilesContextId == 0:
     c.importTable.addSym(result)
 
 type
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index b6b01d558..20800b809 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -63,6 +63,52 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
     if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
     result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
 
+proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
+  result = newNodeI(nkBracketExpr, tup.info)
+  addSon(result, copyTree(tup))
+  var lit = newNodeI(nkIntLit, tup.info)
+  lit.intVal = i
+  addSon(result, lit)
+
+proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
+  let value = n.lastSon
+  result = newNodeI(nkStmtList, n.info)
+
+  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
+  var v = newNodeI(nkLetSection, value.info)
+  let tempAsNode = newIdentNode(getIdent(genPrefix & $temp.id), value.info)
+
+  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
+  vpart.sons[0] = tempAsNode
+  vpart.sons[1] = ast.emptyNode
+  vpart.sons[2] = value
+  addSon(v, vpart)
+  result.add(v)
+
+  let lhs = n.sons[0]
+  for i in 0 .. lhs.len-1:
+    result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
+
+proc lowerSwap*(n: PNode; owner: PSym): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
+  var temp = newSym(skVar, getIdent(genPrefix), owner, n.info)
+  temp.typ = n.sons[1].typ
+  incl(temp.flags, sfFromGeneric)
+
+  var v = newNodeI(nkVarSection, n.info)
+  let tempAsNode = newSymNode(temp)
+
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart.sons[0] = tempAsNode
+  vpart.sons[1] = ast.emptyNode
+  vpart.sons[2] = n[1]
+  addSon(v, vpart)
+
+  result.add(v)
+  result.add newFastAsgnStmt(n[1], n[2])
+  result.add newFastAsgnStmt(n[2], tempAsNode)
+
 proc createObj*(owner: PSym, info: TLineInfo): PType =
   result = newType(tyObject, owner)
   rawAddSon(result, nil)
@@ -167,7 +213,7 @@ proc genDeref*(n: PNode): PNode =
   result.add n
 
 proc callCodegenProc*(name: string, arg1: PNode;
-                      arg2, arg3: PNode = nil): PNode =
+                      arg2, arg3, optionalArgs: PNode = nil): PNode =
   result = newNodeI(nkCall, arg1.info)
   let sym = magicsys.getCompilerProc(name)
   if sym == nil:
@@ -177,6 +223,9 @@ proc callCodegenProc*(name: string, arg1: PNode;
     result.add arg1
     if arg2 != nil: result.add arg2
     if arg3 != nil: result.add arg3
+    if optionalArgs != nil:
+      for i in 1..optionalArgs.len-3:
+        result.add optionalArgs[i]
     result.typ = sym.typ.sons[0]
 
 proc callProc(a: PNode): PNode =
@@ -483,7 +532,7 @@ 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 n = spawnExpr[^2]
   let spawnKind = spawnResult(retType, barrier!=nil)
   case spawnKind
   of srVoid:
@@ -569,7 +618,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
     fvField = newDotExpr(scratchObj, field)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     # create flowVar:
-    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[2]))
+    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
     if barrier == nil:
       result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
 
@@ -584,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
   let wrapper = createWrapperProc(fn, threadParam, argsParam,
                                   varSection, varInit, call,
                                   barrierAsExpr, fvAsExpr, spawnKind)
-  result.add callCodegenProc("nimSpawn", wrapper.newSymNode,
-                             genAddrOf(scratchObj.newSymNode))
+  result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
+                             genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
 
   if spawnKind == srFlowVar: result.add fvField
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 6d4c65268..40e0728ae 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -9,43 +9,35 @@
 
 # Built-in types and compilerprocs are registered here.
 
-import 
+import
   ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
 
 var systemModule*: PSym
 
-proc registerSysType*(t: PType)
-  # magic symbols in the system module:
-proc getSysType*(kind: TTypeKind): PType
-proc getCompilerProc*(name: string): PSym
-proc registerCompilerProc*(s: PSym)
-proc finishSystem*(tab: TStrTable)
-proc getSysSym*(name: string): PSym
-# implementation
-
 var
   gSysTypes: array[TTypeKind, PType]
   compilerprocs: TStrTable
+  exposed: TStrTable
 
 proc nilOrSysInt*: PType = gSysTypes[tyInt]
 
-proc registerSysType(t: PType) = 
+proc registerSysType*(t: PType) =
   if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
-  
-proc newSysType(kind: TTypeKind, size: int): PType = 
+
+proc newSysType(kind: TTypeKind, size: int): PType =
   result = newType(kind, systemModule)
   result.size = size
   result.align = size.int16
 
-proc getSysSym(name: string): PSym = 
+proc getSysSym*(name: string): PSym =
   result = strTableGet(systemModule.tab, getIdent(name))
-  if result == nil: 
+  if result == nil:
     rawMessage(errSystemNeeds, name)
     result = newSym(skError, getIdent(name), systemModule, systemModule.info)
     result.typ = newType(tyError, systemModule)
   if result.kind == skStub: loadStub(result)
   if result.kind == skAlias: result = result.owner
-  
+
 proc getSysMagic*(name: string, m: TMagic): PSym =
   var ti: TIdentIter
   let id = getIdent(name)
@@ -57,13 +49,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym =
   rawMessage(errSystemNeeds, name)
   result = newSym(skError, id, systemModule, systemModule.info)
   result.typ = newType(tyError, systemModule)
-  
-proc sysTypeFromName*(name: string): PType = 
+
+proc sysTypeFromName*(name: string): PType =
   result = getSysSym(name).typ
 
-proc getSysType(kind: TTypeKind): PType = 
+proc getSysType*(kind: TTypeKind): PType =
   result = gSysTypes[kind]
-  if result == nil: 
+  if result == nil:
     case kind
     of tyInt: result = sysTypeFromName("int")
     of tyInt8: result = sysTypeFromName("int8")
@@ -87,7 +79,7 @@ proc getSysType(kind: TTypeKind): PType =
     of tyNil: result = newSysType(tyNil, ptrSize)
     else: internalError("request for typekind: " & $kind)
     gSysTypes[kind] = result
-  if result.kind != kind: 
+  if result.kind != kind:
     internalError("wanted: " & $kind & " got: " & $result.kind)
   if result == nil: internalError("type not found: " & $kind)
 
@@ -97,6 +89,7 @@ var
 proc resetSysTypes* =
   systemModule = nil
   initStrTable(compilerprocs)
+  initStrTable(exposed)
   for i in low(gSysTypes)..high(gSysTypes):
     gSysTypes[i] = nil
 
@@ -163,8 +156,8 @@ proc setIntLitType*(result: PNode) =
       result.typ = getSysType(tyInt64)
   else: internalError(result.info, "invalid int size")
 
-proc getCompilerProc(name: string): PSym = 
-  var ident = getIdent(name, hashIgnoreStyle(name))
+proc getCompilerProc*(name: string): PSym =
+  let ident = getIdent(name)
   result = strTableGet(compilerprocs, ident)
   if result == nil:
     result = strTableGet(rodCompilerprocs, ident)
@@ -173,9 +166,22 @@ proc getCompilerProc(name: string): PSym =
       if result.kind == skStub: loadStub(result)
       if result.kind == skAlias: result = result.owner
 
-proc registerCompilerProc(s: PSym) =
+proc registerCompilerProc*(s: PSym) =
   strTableAdd(compilerprocs, s)
 
-proc finishSystem(tab: TStrTable) = discard
+proc registerNimScriptSymbol*(s: PSym) =
+  # Nimscript symbols must be al unique:
+  let conflict = strTableGet(exposed, s.name)
+  if conflict == nil:
+    strTableAdd(exposed, s)
+  else:
+    localError(s.info, "symbol conflicts with other .exportNims symbol at: " &
+      $conflict.info)
+
+proc getNimScriptSymbol*(name: string): PSym =
+  strTableGet(exposed, getIdent(name))
+
+proc resetNimScriptSymbols*() = initStrTable(exposed)
 
 initStrTable(compilerprocs)
+initStrTable(exposed)
diff --git a/compiler/main.nim b/compiler/main.nim
index 0c80c19b7..3d04cc61c 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -15,7 +15,7 @@ import
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  tables, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
+  docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists
 
 from magicsys import systemModule, resetSysTypes
 
@@ -63,14 +63,14 @@ proc commandCompileToC =
   compileProject()
   cgenWriteModules()
   if gCmd != cmdRun:
-    extccomp.callCCompiler(if gProjectName == "-": "stdinfile" else: changeFileExt(gProjectFull, ""))
+    extccomp.callCCompiler(changeFileExt(gProjectFull, ""))
 
   if isServing:
     # caas will keep track only of the compilation commands
     lastCaasCmd = curCaasCmd
     resetCgenModules()
     for i in 0 .. <gMemCacheData.len:
-      gMemCacheData[i].crcStatus = crcCached
+      gMemCacheData[i].hashStatus = hashCached
       gMemCacheData[i].needsRecompile = Maybe
 
       # XXX: clean these global vars
@@ -116,7 +116,7 @@ proc interactivePasses =
   #incl(gGlobalOptions, optSafeCode)
   #setTarget(osNimrodVM, cpuNimrodVM)
   initDefines()
-  defineSymbol("nimrodvm")
+  defineSymbol("nimscript")
   when hasFFI: defineSymbol("nimffi")
   registerPass(verbosePass)
   registerPass(semPass)
@@ -190,7 +190,6 @@ proc resetMemory =
   resetRopeCache()
   resetSysTypes()
   gOwners = @[]
-  rangeDestructorProc = nil
   for i in low(buckets)..high(buckets):
     buckets[i] = nil
   idAnon = nil
@@ -237,7 +236,7 @@ proc mainCommand* =
   when SimulateCaasMemReset:
     gGlobalOptions.incl(optCaasEnabled)
 
-  # In "nimrod serve" scenario, each command must reset the registered passes
+  # In "nim serve" scenario, each command must reset the registered passes
   clearPasses()
   gLastCmdTime = epochTime()
   appendStr(searchPaths, options.libpath)
@@ -318,11 +317,12 @@ proc mainCommand* =
         (key: "lib_paths", val: libpaths)
       ]
 
-      outWriteln($dumpdata)
+      msgWriteln($dumpdata, {msgStdout, msgSkipHook})
     else:
-      outWriteln("-- list of currently defined symbols --")
-      for s in definedSymbolNames(): outWriteln(s)
-      outWriteln("-- end of list --")
+      msgWriteln("-- list of currently defined symbols --",
+                 {msgStdout, msgSkipHook})
+      for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
+      msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
 
       for it in iterSearchPath(searchPaths): msgWriteln(it)
   of "check":
@@ -337,7 +337,7 @@ proc mainCommand* =
     wantMainModule()
     commandScan()
     msgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
-  of "i":
+  of "secret":
     gCmd = cmdInteractive
     commandInteractive()
   of "e":
@@ -356,12 +356,14 @@ proc mainCommand* =
     gGlobalOptions.incl(optCaasEnabled)
     msgs.gErrorMax = high(int)  # do not stop after first error
     serve(mainCommand)
+  of "nop", "help":
+    # prevent the "success" message:
+    gCmd = cmdDump
   else:
     rawMessage(errInvalidCommandX, command)
 
-  if (msgs.gErrorCounter == 0 and
-      gCmd notin {cmdInterpret, cmdRun, cmdDump} and
-      gVerbosity > 0):
+  if msgs.gErrorCounter == 0 and
+     gCmd notin {cmdInterpret, cmdRun, cmdDump}:
     rawMessage(hintSuccessX, [$gLinesCompiled,
                formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
                formatSize(getTotalMem()),
@@ -379,3 +381,4 @@ proc mainCommand* =
   when SimulateCaasMemReset:
     resetMemory()
 
+  resetAttributes()
diff --git a/compiler/modules.nim b/compiler/modules.nim
index a2b739efc..3893d377e 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,19 +10,19 @@
 ## implements the module handling
 
 import
-  ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options, 
-  idents, os, lexer, idgen, passes, syntaxes
+  ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
+  idents, os, lexer, idgen, passes, syntaxes, llstream
 
 type
   TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
-  TCrcStatus* = enum crcNotTaken, crcCached, crcHasChanged, crcNotChanged
+  THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
 
   TModuleInMemory* = object
     compiledAt*: float
-    crc*: TCrc32
+    hash*: SecureHash
     deps*: seq[int32] ## XXX: slurped files are currently not tracked
     needsRecompile*: TNeedRecompile
-    crcStatus*: TCrcStatus
+    hashStatus*: THashStatus
 
 var
   gCompiledModules: seq[PSym] = @[]
@@ -34,36 +34,36 @@ proc getModule(fileIdx: int32): PSym =
   if fileIdx >= 0 and fileIdx < gCompiledModules.len:
     result = gCompiledModules[fileIdx]
 
-template crc(x: PSym): expr =
-  gMemCacheData[x.position].crc
+template hash(x: PSym): expr =
+  gMemCacheData[x.position].hash
 
-proc crcChanged(fileIdx: int32): bool =
+proc hashChanged(fileIdx: int32): bool =
   internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
-  
+
   template updateStatus =
-    gMemCacheData[fileIdx].crcStatus = if result: crcHasChanged
-                                       else: crcNotChanged
-    # echo "TESTING CRC: ", fileIdx.toFilename, " ", result
-  
-  case gMemCacheData[fileIdx].crcStatus:
-  of crcHasChanged:
+    gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
+                                       else: hashNotChanged
+    # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
+
+  case gMemCacheData[fileIdx].hashStatus:
+  of hashHasChanged:
     result = true
-  of crcNotChanged:
+  of hashNotChanged:
     result = false
-  of crcCached:
-    let newCrc = crcFromFile(fileIdx.toFilename)
-    result = newCrc != gMemCacheData[fileIdx].crc
-    gMemCacheData[fileIdx].crc = newCrc
+  of hashCached:
+    let newHash = secureHashFile(fileIdx.toFullPath)
+    result = newHash != gMemCacheData[fileIdx].hash
+    gMemCacheData[fileIdx].hash = newHash
     updateStatus()
-  of crcNotTaken:
-    gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
+  of hashNotTaken:
+    gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
     result = true
     updateStatus()
 
-proc doCRC(fileIdx: int32) =
-  if gMemCacheData[fileIdx].crcStatus == crcNotTaken:
-    # echo "FIRST CRC: ", fileIdx.ToFilename
-    gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
+proc doHash(fileIdx: int32) =
+  if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
+    # echo "FIRST Hash: ", fileIdx.ToFilename
+    gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
 
 proc addDep(x: PSym, dep: int32) =
   growCache gMemCacheData, dep
@@ -78,6 +78,13 @@ proc resetModule*(fileIdx: int32) =
   if fileIdx <% cgendata.gModules.len:
     cgendata.gModules[fileIdx] = nil
 
+proc resetModule*(module: PSym) =
+  let conflict = getModule(module.position.int32)
+  if conflict == nil: return
+  doAssert conflict == module
+  resetModule(module.position.int32)
+  initStrTable(module.tab)
+
 proc resetAllModules* =
   for i in 0..gCompiledModules.high:
     if gCompiledModules[i] != nil:
@@ -85,6 +92,14 @@ proc resetAllModules* =
   resetPackageCache()
   # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
+proc resetAllModulesHard* =
+  resetPackageCache()
+  gCompiledModules.setLen 0
+  gMemCacheData.setLen 0
+  magicsys.resetSysTypes()
+  # XXX
+  #gOwners = @[]
+
 proc checkDepMem(fileIdx: int32): TNeedRecompile =
   template markDirty =
     resetModule(fileIdx)
@@ -94,9 +109,9 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
     return gMemCacheData[fileIdx].needsRecompile
 
   if optForceFullMake in gGlobalOptions or
-     crcChanged(fileIdx):
+     hashChanged(fileIdx):
        markDirty
-  
+
   if gMemCacheData[fileIdx].deps != nil:
     gMemCacheData[fileIdx].needsRecompile = Probing
     for dep in gMemCacheData[fileIdx].deps:
@@ -104,30 +119,30 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
       if d in {Yes, Recompiled}:
         # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
         markDirty
-  
+
   gMemCacheData[fileIdx].needsRecompile = No
   return No
 
 proc newModule(fileIdx: int32): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
-  # mechanism, which we do in order to assign each module a persistent ID. 
+  # mechanism, which we do in order to assign each module a persistent ID.
   new(result)
   result.id = - 1             # for better error checking
   result.kind = skModule
   let filename = fileIdx.toFullPath
   result.name = getIdent(splitFile(filename).name)
-  if result.name.s != "-" and not isNimIdentifier(result.name.s):
+  if not isNimIdentifier(result.name.s):
     rawMessage(errInvalidModuleName, result.name.s)
-  
+
   result.info = newLineInfo(fileIdx, 1, 1)
   result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
                         result.info)
   result.position = fileIdx
-  
+
   growCache gMemCacheData, fileIdx
   growCache gCompiledModules, fileIdx
   gCompiledModules[result.position] = result
-  
+
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
   strTableAdd(result.tab, result) # a module knows itself
@@ -143,16 +158,19 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
     result.flags = result.flags + flags
     if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
       rd = handleSymbolFile(result)
-      if result.id < 0: 
+      if result.id < 0:
         internalError("handleSymbolFile should have set the module\'s ID")
         return
     else:
       result.id = getID()
-    processModule(result, nil, rd)
+    if sfMainModule in flags and gProjectIsStdin:
+      processModule(result, llStreamOpen(stdin), rd)
+    else:
+      processModule(result, nil, rd)
     if optCaasEnabled in gGlobalOptions:
       gMemCacheData[fileIdx].compiledAt = gLastCmdTime
       gMemCacheData[fileIdx].needsRecompile = Recompiled
-      doCRC fileIdx
+      doHash fileIdx
   else:
     if checkDepMem(fileIdx) == Yes:
       result = compileModule(fileIdx, flags)
@@ -171,7 +189,7 @@ proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
   if optCaasEnabled in gGlobalOptions:
     growCache gMemCacheData, fileIdx
     addDep(s, fileIdx)
-    doCRC(fileIdx)
+    doHash(fileIdx)
 
 proc `==^`(a, b: string): bool =
   try:
@@ -202,9 +220,8 @@ proc compileProject*(projectFileIdx = -1'i32) =
     compileSystemModule()
     discard compileModule(projectFile, {sfMainModule})
 
-var stdinModule: PSym
-proc makeStdinModule*(): PSym =
-  if stdinModule == nil:
-    stdinModule = newModule(fileInfoIdx"stdin")
-    stdinModule.id = getID()
-  result = stdinModule
+proc makeModule*(filename: string): PSym =
+  result = newModule(fileInfoIdx filename)
+  result.id = getID()
+
+proc makeStdinModule*(): PSym = makeModule"stdin"
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 041a181be..7b44b4349 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  options, strutils, os, tables, ropes, platform
+  options, strutils, os, tables, ropes, platform, terminal, macros
 
 type
   TMsgKind* = enum
@@ -17,10 +17,9 @@ type
     errIntLiteralExpected, errInvalidCharacterConstant,
     errClosingTripleQuoteExpected, errClosingQuoteExpected,
     errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
-    errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,
-    errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,
-    errNewlineExpected,
-    errInvalidModuleName,
+    errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
+    errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
+    errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
     errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
     errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
     errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
@@ -31,11 +30,14 @@ type
     errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
     errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
     errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
-    errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound,
+    errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound,
+    errNoneBoehmRefcExpectedButXFound,
     errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
     errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
     errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
-    errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
+    errExprExpected, errUndeclaredIdentifier, errUndeclaredField,
+    errUndeclaredRoutine, errUseQualifier,
+    errTypeExpected,
     errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
     errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
     errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
@@ -80,7 +82,7 @@ type
     errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
     errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
     errNoReturnTypeDeclared,
-    errInvalidCommandX, errXOnlyAtModuleScope,
+    errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
     errXNeedsParamObjectType,
     errTemplateInstantiationTooNested, errInstantiationFrom,
     errInvalidIndexValueForTuple, errCommandExpectsFilename,
@@ -106,6 +108,8 @@ type
     errCannotInferReturnType,
     errGenericLambdaNotAllowed,
     errCompilerDoesntSupportTarget,
+    errExternalAssemblerNotFound,
+    errExternalAssemblerNotValid,
     errUser,
     warnCannotOpenFile,
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@@ -114,7 +118,7 @@ type
     warnUnknownSubstitutionX, warnLanguageXNotSupported,
     warnFieldXNotSupported, warnCommentXIgnored,
     warnNilStatement, warnTypelessParam,
-    warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
+    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
     warnEachIdentIsTuple, warnShadowIdent,
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
@@ -124,6 +128,8 @@ type
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
     hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
     hintConditionAlwaysTrue, hintName, hintPattern,
+    hintExecuting, hintLinking, hintDependency,
+    hintSource, hintStackTrace, hintGCStats,
     hintUser
 
 const
@@ -143,6 +149,7 @@ const
     errInvalidToken: "invalid token: $1",
     errLineTooLong: "line too long",
     errInvalidNumber: "$1 is not a valid number",
+    errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
     errNumberOutOfRange: "number $1 out of valid range",
     errNnotAllowedInCharacter: "\\n not allowed in character literal",
     errClosingBracketExpected: "closing ']' expected, but end of file reached",
@@ -179,6 +186,7 @@ const
     errUnknownVar: "unknown variable: \'$1\'",
     errUnknownCcompiler: "unknown C compiler: \'$1\'",
     errOnOrOffExpectedButXFound: "\'on\' or \'off\' expected, but \'$1\' found",
+    errOnOffOrListExpectedButXFound: "\'on\', \'off\' or \'list\' expected, but \'$1\' found",
     errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found",
     errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found",
     errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found",
@@ -190,10 +198,12 @@ const
     errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
     errExprExpected: "expression expected, but found \'$1\'",
     errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
+    errUndeclaredField: "undeclared field: \'$1\'",
+    errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
     errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
     errTypeExpected: "type expected",
     errSystemNeeds: "system module needs \'$1\'",
-    errExecutionOfProgramFailed: "execution of an external program failed",
+    errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
     errNotOverloadable: "overloaded \'$1\' leads to ambiguous calls",
     errInvalidArgForX: "invalid argument for \'$1\'",
     errStmtHasNoEffect: "statement has no effect",
@@ -308,6 +318,7 @@ const
     errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
     errXNeedsReturnType: "$1 needs a return type",
     errNoReturnTypeDeclared: "no return type declared",
+    errNoCommand: "no command given",
     errInvalidCommandX: "invalid command: \'$1\'",
     errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
     errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
@@ -362,55 +373,63 @@ const
                                 "it is used as an operand to another routine and the types " &
                                 "of the generic paramers can be inferred from the expected signature.",
     errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
+    errExternalAssemblerNotFound: "External assembler not found",
+    errExternalAssemblerNotValid: "External assembler '$1' is not a valid assembler",
     errUser: "$1",
-    warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
-    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
-    warnXIsNeverRead: "\'$1\' is never read [XIsNeverRead]",
-    warnXmightNotBeenInit: "\'$1\' might not have been initialized [XmightNotBeenInit]",
-    warnDeprecated: "$1 is deprecated [Deprecated]",
-    warnConfigDeprecated: "config file '$1' is deprecated [ConfigDeprecated]",
-    warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one) [SmallLshouldNotBeUsed]",
-    warnUnknownMagic: "unknown magic \'$1\' might crash the compiler [UnknownMagic]",
-    warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]",
-    warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]",
-    warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]",
-    warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]",
-    warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]",
-    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]",
-    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template' [TypelessParam]",
-    warnDifferentHeaps: "possible inconsistency of thread local heaps [DifferentHeaps]",
-    warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
-    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]",
-    warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
-    warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
-    warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
+    warnCannotOpenFile: "cannot open \'$1\'",
+    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
+    warnXIsNeverRead: "\'$1\' is never read",
+    warnXmightNotBeenInit: "\'$1\' might not have been initialized",
+    warnDeprecated: "$1 is deprecated",
+    warnConfigDeprecated: "config file '$1' is deprecated",
+    warnSmallLshouldNotBeUsed: "\'l\' should not be used as an identifier; may look like \'1\' (one)",
+    warnUnknownMagic: "unknown magic \'$1\' might crash the compiler",
+    warnRedefinitionOfLabel: "redefinition of label \'$1\'",
+    warnUnknownSubstitutionX: "unknown substitution \'$1\'",
+    warnLanguageXNotSupported: "language \'$1\' not supported",
+    warnFieldXNotSupported: "field \'$1\' not supported",
+    warnCommentXIgnored: "comment \'$1\' ignored",
+    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
+    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+    warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
+    warnWriteToForeignHeap: "write to foreign heap",
+    warnUnsafeCode: "unsafe code: '$1'",
+    warnEachIdentIsTuple: "each identifier is a tuple",
+    warnShadowIdent: "shadowed identifier: '$1'",
+    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
+    warnProveField: "cannot prove that field '$1' is accessible",
+    warnProveIndex: "cannot prove index '$1' is valid",
+    warnGcUnsafe: "not GC-safe: '$1'",
     warnGcUnsafe2: "$1",
-    warnUninit: "'$1' might not have been initialized [Uninit]",
-    warnGcMem: "'$1' uses GC'ed memory [GcMem]",
-    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]",
-    warnLockLevel: "$1 [LockLevel]",
-    warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
-    warnUser: "$1 [User]",
-    hintSuccess: "operation successful [Success]",
-    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#) [SuccessX]",
-    hintLineTooLong: "line too long [LineTooLong]",
-    hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]",
-    hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]",
-    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless [ConvFromXtoItselfNotNeeded]",
-    hintExprAlwaysX: "expression evaluates always to \'$1\' [ExprAlwaysX]",
-    hintQuitCalled: "quit() called [QuitCalled]",
-    hintProcessing: "$1 [Processing]",
-    hintCodeBegin: "generated code listing: [CodeBegin]",
-    hintCodeEnd: "end of listing [CodeEnd]",
-    hintConf: "used config file \'$1\' [Conf]",
-    hintPath: "added path: '$1' [Path]",
-    hintConditionAlwaysTrue: "condition is always true: '$1' [CondTrue]",
-    hintName: "name should be: '$1' [Name]",
-    hintPattern: "$1 [Pattern]",
-    hintUser: "$1 [User]"]
+    warnUninit: "'$1' might not have been initialized",
+    warnGcMem: "'$1' uses GC'ed memory",
+    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
+    warnLockLevel: "$1",
+    warnResultShadowed: "Special variable 'result' is shadowed.",
+    warnUser: "$1",
+    hintSuccess: "operation successful",
+    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+    hintLineTooLong: "line too long",
+    hintXDeclaredButNotUsed: "\'$1\' is declared but not used",
+    hintConvToBaseNotNeeded: "conversion to base object is not needed",
+    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
+    hintExprAlwaysX: "expression evaluates always to \'$1\'",
+    hintQuitCalled: "quit() called",
+    hintProcessing: "$1",
+    hintCodeBegin: "generated code listing:",
+    hintCodeEnd: "end of listing",
+    hintConf: "used config file \'$1\'",
+    hintPath: "added path: '$1'",
+    hintConditionAlwaysTrue: "condition is always true: '$1'",
+    hintName: "name should be: '$1'",
+    hintPattern: "$1",
+    hintExecuting: "$1",
+    hintLinking: "",
+    hintDependency: "$1",
+    hintSource: "$1",
+    hintStackTrace: "$1",
+    hintGCStats: "$1",
+    hintUser: "$1"]
 
 const
   WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
@@ -420,15 +439,16 @@ const
     "RedefinitionOfLabel", "UnknownSubstitutionX",
     "LanguageXNotSupported", "FieldXNotSupported",
     "CommentXIgnored", "NilStmt",
-    "TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
+    "TypelessParam", "UseBase", "WriteToForeignHeap",
     "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
     "GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
 
-  HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
+  HintsToStr*: array[0..22, string] = ["Success", "SuccessX", "LineTooLong",
     "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
     "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
-    "Path", "CondTrue", "Name", "Pattern",
+    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
+    "Source", "StackTrace", "GCStats",
     "User"]
 
 const
@@ -449,10 +469,10 @@ type
     fullPath: string           # This is a canonical full filesystem path
     projPath*: string          # This is relative to the project's root
     shortName*: string         # short name of the module
-    quotedName*: Rope         # cached quoted short name for codegen
+    quotedName*: Rope          # cached quoted short name for codegen
                                # purposes
 
-    lines*: seq[Rope]         # the source code of the module
+    lines*: seq[Rope]          # the source code of the module
                                #   used for better error messages and
                                #   embedding the original source in the
                                #   generated code
@@ -480,6 +500,30 @@ type
   ESuggestDone* = object of Exception
 
 const
+  NotesVerbosity*: array[0..3, TNoteKinds] = [
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintSuccessX, hintPath, hintConf,
+                                         hintProcessing,
+                                         hintDependency,
+                                         hintExecuting, hintLinking,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintPath, hintConf,
+                                         hintDependency,
+                                         hintExecuting,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace},
+    {low(TNoteKind)..high(TNoteKind)}]
+
+const
   InvalidFileIDX* = int32(-1)
 
 var
@@ -567,9 +611,7 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
 proc sourceLine*(i: TLineInfo): Rope
 
 var
-  gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
-                        {warnShadowIdent, warnUninit,
-                         warnProveField, warnProveIndex, warnGcUnsafe}
+  gNotes*: TNoteKinds = NotesVerbosity[1] # defaults to verbosity of 1
   gErrorCounter*: int = 0     # counts the number of errors
   gHintCounter*: int = 0
   gWarnCounter*: int = 0
@@ -589,8 +631,11 @@ var
 
 proc suggestWriteln*(s: string) =
   if eStdOut in errorOutputs:
-    if isNil(writelnHook): writeln(stdout, s)
-    else: writelnHook(s)
+    if isNil(writelnHook):
+      writeLine(stdout, s)
+      flushFile(stdout)
+    else:
+      writelnHook(s)
 
 proc msgQuit*(x: int8) = quit x
 proc msgQuit*(x: string) = quit x
@@ -601,13 +646,17 @@ proc suggestQuit*() =
 # this format is understood by many text editors: it is the same that
 # Borland and Freepascal use
 const
-  PosErrorFormat* = "$1($2, $3) Error: $4"
-  PosWarningFormat* = "$1($2, $3) Warning: $4"
-  PosHintFormat* = "$1($2, $3) Hint: $4"
-  PosContextFormat = "$1($2, $3) Info: $4"
-  RawErrorFormat* = "Error: $1"
-  RawWarningFormat* = "Warning: $1"
-  RawHintFormat* = "Hint: $1"
+  PosFormat    = "$1($2, $3) "
+  KindFormat   = " [$1]"
+  KindColor    = fgCyan
+  ErrorTitle   = "Error: "
+  ErrorColor   = fgRed
+  WarningTitle = "Warning: "
+  WarningColor = fgYellow
+  HintTitle    = "Hint: "
+  HintColor    = fgGreen
+  InfoTitle    = "Info: "
+  InfoColor    = fgCyan
 
 proc getInfoContextLen*(): int = return msgContext.len
 proc setInfoContextLen*(L: int) = setLen(msgContext, L)
@@ -678,21 +727,79 @@ proc `??`* (info: TLineInfo, filename: string): bool =
 
 var gTrackPos*: TLineInfo
 
-proc outWriteln*(s: string) =
-  ## Writes to stdout. Always.
-  if eStdOut in errorOutputs: writeln(stdout, s)
+type
+  MsgFlag* = enum  ## flags altering msgWriteln behavior
+    msgStdout,     ## force writing to stdout, even stderr is default
+    msgSkipHook    ## skip message hook even if it is present
+  MsgFlags* = set[MsgFlag]
 
-proc msgWriteln*(s: string) =
-  ## Writes to stdout. If --stdout option is given, writes to stderr instead.
+proc msgWriteln*(s: string, flags: MsgFlags = {}) =
+  ## Writes given message string to stderr by default.
+  ## If ``--stdout`` option is given, writes to stdout instead. If message hook
+  ## is present, then it is used to output message rather than stderr/stdout.
+  ## This behavior can be altered by given optional flags.
 
   #if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
 
-  if not isNil(writelnHook):
+  if not isNil(writelnHook) and msgSkipHook notin flags:
     writelnHook(s)
+  elif optStdout in gGlobalOptions or msgStdout in flags:
+    if eStdOut in errorOutputs:
+      writeLine(stdout, s)
+      flushFile(stdout)
+  else:
+    if eStdErr in errorOutputs:
+      writeLine(stderr, s)
+      # On Windows stderr is fully-buffered when piped, regardless of C std.
+      when defined(windows):
+        flushFile(stderr)
+
+macro callIgnoringStyle(theProc: typed, first: typed,
+                        args: varargs[expr]): stmt =
+  let typForegroundColor = bindSym"ForegroundColor".getType
+  let typBackgroundColor = bindSym"BackgroundColor".getType
+  let typStyle = bindSym"Style".getType
+  let typTerminalCmd = bindSym"TerminalCmd".getType
+  result = newCall(theProc)
+  if first.kind != nnkNilLit: result.add(first)
+  for arg in children(args[0][1]):
+    if arg.kind == nnkNilLit: continue
+    let typ = arg.getType
+    if typ.kind != nnkEnumTy or
+       typ != typForegroundColor and
+       typ != typBackgroundColor and
+       typ != typStyle and
+       typ != typTerminalCmd:
+      result.add(arg)
+
+macro callStyledWriteLineStderr(args: varargs[expr]): stmt =
+  result = newCall(bindSym"styledWriteLine")
+  result.add(bindSym"stderr")
+  for arg in children(args[0][1]):
+    result.add(arg)
+
+template callWritelnHook(args: varargs[string, `$`]) =
+  var s = ""
+  for arg in args:
+    s.add arg
+  writelnHook s
+
+template styledMsgWriteln*(args: varargs[expr]) =
+  if not isNil(writelnHook):
+    callIgnoringStyle(callWritelnHook, nil, args)
   elif optStdout in gGlobalOptions:
-    if eStdErr in errorOutputs: writeln(stderr, s)
+    if eStdOut in errorOutputs:
+      callIgnoringStyle(writeLine, stdout, args)
+      flushFile(stdout)
   else:
-    if eStdOut in errorOutputs: writeln(stdout, s)
+    if eStdErr in errorOutputs:
+      if optUseColors in gGlobalOptions:
+        callStyledWriteLineStderr(args)
+      else:
+        callIgnoringStyle(writeLine, stderr, args)
+      # On Windows stderr is fully-buffered when piped, regardless of C std.
+      when defined(windows):
+        flushFile(stderr)
 
 proc coordToStr(coord: int): string =
   if coord == -1: result = "???"
@@ -710,11 +817,11 @@ type
 
 proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
   template quit =
-    if defined(debug) or gVerbosity >= 3 or msg == errInternal:
+    if defined(debug) or msg == errInternal or hintStackTrace in gNotes:
       if stackTraceAvailable() and isNil(writelnHook):
         writeStackTrace()
       else:
-        msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
+        styledMsgWriteln(fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
     quit 1
 
   if msg >= fatalMin and msg <= fatalMax:
@@ -736,61 +843,85 @@ proc writeContext(lastinfo: TLineInfo) =
   var info = lastinfo
   for i in countup(0, len(msgContext) - 1):
     if msgContext[i] != lastinfo and msgContext[i] != info:
-      msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
-                                     coordToStr(msgContext[i].line),
-                                     coordToStr(msgContext[i].col+1),
-                                     getMessageStr(errInstantiationFrom, "")])
+      styledMsgWriteln(styleBright,
+                       PosFormat % [toMsgFilename(msgContext[i]),
+                                    coordToStr(msgContext[i].line),
+                                    coordToStr(msgContext[i].col+1)],
+                       resetStyle,
+                       getMessageStr(errInstantiationFrom, ""))
     info = msgContext[i]
 
 proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
   msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
 
 proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
-  var frmt: string
+  var
+    title: string
+    color: ForegroundColor
+    kind:  string
   case msg
   of errMin..errMax:
     writeContext(unknownLineInfo())
-    frmt = RawErrorFormat
+    title = ErrorTitle
+    color = ErrorColor
   of warnMin..warnMax:
     if optWarns notin gOptions: return
     if msg notin gNotes: return
     writeContext(unknownLineInfo())
-    frmt = RawWarningFormat
+    title = WarningTitle
+    color = WarningColor
+    kind = WarningsToStr[ord(msg) - ord(warnMin)]
     inc(gWarnCounter)
   of hintMin..hintMax:
     if optHints notin gOptions: return
     if msg notin gNotes: return
-    frmt = RawHintFormat
+    title = HintTitle
+    color = HintColor
+    kind = HintsToStr[ord(msg) - ord(hintMin)]
     inc(gHintCounter)
-  let s = `%`(frmt, `%`(msgKindToString(msg), args))
+  let s = `%`(msgKindToString(msg), args)
   if not ignoreMsgBecauseOfIdeTools(msg):
-    msgWriteln(s)
+    if kind != nil:
+      styledMsgWriteln(color, title, resetStyle, s,
+                       KindColor, `%`(KindFormat, kind))
+    else:
+      styledMsgWriteln(color, title, resetStyle, s)
   handleError(msg, doAbort, s)
 
 proc rawMessage*(msg: TMsgKind, arg: string) =
   rawMessage(msg, [arg])
 
+proc resetAttributes* =
+  if {optUseColors, optStdout} * gGlobalOptions == {optUseColors}:
+    terminal.resetAttributes(stderr)
+
 proc writeSurroundingSrc(info: TLineInfo) =
   const indent = "  "
   msgWriteln(indent & $info.sourceLine)
   msgWriteln(indent & spaces(info.col) & '^')
 
 proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
-  let frmt = case msg
-             of warnMin..warnMax: PosWarningFormat
-             of hintMin..hintMax: PosHintFormat
-             else: PosErrorFormat
-  result = frmt % [toMsgFilename(info), coordToStr(info.line),
-                   coordToStr(info.col+1), getMessageStr(msg, arg)]
+  let title = case msg
+              of warnMin..warnMax: WarningTitle
+              of hintMin..hintMax: HintTitle
+              else: ErrorTitle
+  result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+                        coordToStr(info.col+1)] &
+           title &
+           getMessageStr(msg, arg)
 
 proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
                eh: TErrorHandling) =
-  var frmt: string
-  var ignoreMsg = false
+  var
+    title: string
+    color: ForegroundColor
+    kind:  string
+    ignoreMsg = false
   case msg
   of errMin..errMax:
     writeContext(info)
-    frmt = PosErrorFormat
+    title = ErrorTitle
+    color = ErrorColor
     # we try to filter error messages so that not two error message
     # in the same file and line are produced:
     #ignoreMsg = lastError == info and eh != doAbort
@@ -798,20 +929,29 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
   of warnMin..warnMax:
     ignoreMsg = optWarns notin gOptions or msg notin gNotes
     if not ignoreMsg: writeContext(info)
-    frmt = PosWarningFormat
+    title = WarningTitle
+    color = WarningColor
+    kind = WarningsToStr[ord(msg) - ord(warnMin)]
     inc(gWarnCounter)
   of hintMin..hintMax:
     ignoreMsg = optHints notin gOptions or msg notin gNotes
-    frmt = PosHintFormat
+    title = HintTitle
+    color = HintColor
+    kind = HintsToStr[ord(msg) - ord(hintMin)]
     inc(gHintCounter)
   # NOTE: currently line info line numbers start with 1,
   # but column numbers start with 0, however most editors expect
   # first column to be 1, so we need to +1 here
-  let s = frmt % [toMsgFilename(info), coordToStr(info.line),
-                  coordToStr(info.col+1), getMessageStr(msg, arg)]
+  let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
+                       coordToStr(info.col+1)]
+  let s = getMessageStr(msg, arg)
   if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
-    msgWriteln(s)
-    if optPrintSurroundingSrc and msg in errMin..errMax:
+    if kind != nil:
+      styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
+                       KindColor, `%`(KindFormat, kind))
+    else:
+      styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
+    if msg in errMin..errMax and hintSource in gNotes:
       info.writeSurroundingSrc
   handleError(msg, eh, s)
 
@@ -882,3 +1022,22 @@ ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
   of rCannotOpenFile:
     rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
 
+proc listWarnings*() =
+  msgWriteln("Warnings:")
+  for warn in warnMin..warnMax:
+    msgWriteln("  [$1] $2" % [
+      if warn in gNotes: "x" else: " ",
+      msgs.WarningsToStr[ord(warn) - ord(warnMin)]
+    ])
+
+proc listHints*() =
+  msgWriteln("Hints:")
+  for hint in hintMin..hintMax:
+    msgWriteln("  [$1] $2" % [
+      if hint in gNotes: "x" else: " ",
+      msgs.HintsToStr[ord(hint) - ord(hintMin)]
+    ])
+
+# enable colors by default on terminals
+if terminal.isatty(stderr):
+  incl(gGlobalOptions, optUseColors)
diff --git a/compiler/nim.nim.cfg b/compiler/nim.cfg
index 64631a437..4f9962ea8 100644
--- a/compiler/nim.nim.cfg
+++ b/compiler/nim.cfg
@@ -17,5 +17,4 @@ define:useStdoutAsStdmsg
 
 cs:partial
 #define:useNodeIds
-symbol:nimfix
 #gc:markAndSweep
diff --git a/compiler/nim.nim b/compiler/nim.nim
index b8ba2c6da..1293ec922 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -16,7 +16,7 @@ when defined(gcc) and defined(windows):
 import
   commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
   extccomp, strutils, os, osproc, platform, main, parseopt, service,
-  nodejs
+  nodejs, scriptconfig
 
 when hasTinyCBackend:
   import tccgen
@@ -38,31 +38,44 @@ proc handleCmdLine() =
   else:
     # Process command line arguments:
     processCmdLine(passCmd1, "")
-    if gProjectName != "":
+    if gProjectName == "-":
+      gProjectName = "stdinfile"
+      gProjectFull = "stdinfile"
+      gProjectPath = getCurrentDir()
+      gProjectIsStdin = true
+    elif gProjectName != "":
       try:
         gProjectFull = canonicalizePath(gProjectName)
       except OSError:
         gProjectFull = gProjectName
-      var p = splitFile(gProjectFull)
+      let p = splitFile(gProjectFull)
       gProjectPath = p.dir
       gProjectName = p.name
     else:
       gProjectPath = getCurrentDir()
     loadConfigs(DefaultConfig) # load all config files
+    let scriptFile = gProjectFull.changeFileExt("nims")
+    if fileExists(scriptFile):
+      runNimScript(scriptFile)
+      # 'nim foo.nims' means to just run the NimScript file and do nothing more:
+      if scriptFile == gProjectFull: return
+    elif fileExists(gProjectPath / "config.nims"):
+      # directory wide NimScript file
+      runNimScript(gProjectPath / "config.nims")
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
     processCmdLine(passCmd2, "")
+    if options.command == "":
+      rawMessage(errNoCommand, command)
     mainCommand()
-    if gVerbosity >= 2: echo(GC_getStatistics())
+    if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
     if msgs.gErrorCounter == 0:
       when hasTinyCBackend:
         if gCmd == cmdRun:
           tccgen.run(commands.arguments)
       if optRun in gGlobalOptions:
-        if gProjectName == "-":
-          gProjectFull = "stdinfile"
         if gCmd == cmdCompileToJS:
           var ex: string
           if options.outFile.len > 0:
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 23f3331a2..c6c2ab058 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -11,8 +11,8 @@
 
 import parseutils, strutils, strtabs, os, options, msgs, lists
 
-proc addPath*(path: string, info: TLineInfo) = 
-  if not contains(options.searchPaths, path): 
+proc addPath*(path: string, info: TLineInfo) =
+  if not contains(options.searchPaths, path):
     lists.prependStr(options.searchPaths, path)
 
 proc versionSplitPos(s: string): int =
@@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int =
 const
   latest = "head"
 
-proc `<.`(a, b: string): bool = 
+proc `<.`(a, b: string): bool =
   # wether a has a smaller version than b:
   if a == latest: return false
   var i = 0
@@ -48,7 +48,7 @@ proc addPackage(packages: StringTableRef, p: string) =
   let name = p.substr(0, x-1)
   if x < p.len:
     let version = p.substr(x+1)
-    if packages[name] <. version:
+    if packages.getOrDefault(name) <. version:
       packages[name] = version
   else:
     packages[name] = latest
@@ -60,7 +60,7 @@ iterator chosen(packages: StringTableRef): string =
 
 proc addNimblePath(p: string, info: TLineInfo) =
   if not contains(options.searchPaths, p):
-    if gVerbosity >= 1: message(info, hintPath, p)
+    message(info, hintPath, p)
     lists.prependStr(options.lazyPaths, p)
 
 proc addPathWithNimFiles(p: string, info: TLineInfo) =
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 711b476e6..496bd0123 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -9,106 +9,106 @@
 
 # This module handles the reading of the config file.
 
-import 
-  llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, 
+import
+  llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
   options, idents, wordrecg, strtabs
 
 # ---------------- configuration file parser -----------------------------
 # we use Nim's scanner here to save space and work
 
-proc ppGetTok(L: var TLexer, tok: var TToken) = 
+proc ppGetTok(L: var TLexer, tok: var TToken) =
   # simple filter
   rawGetTok(L, tok)
   while tok.tokType in {tkComment}: rawGetTok(L, tok)
-  
+
 proc parseExpr(L: var TLexer, tok: var TToken): bool
-proc parseAtom(L: var TLexer, tok: var TToken): bool = 
-  if tok.tokType == tkParLe: 
+proc parseAtom(L: var TLexer, tok: var TToken): bool =
+  if tok.tokType == tkParLe:
     ppGetTok(L, tok)
     result = parseExpr(L, tok)
     if tok.tokType == tkParRi: ppGetTok(L, tok)
     else: lexMessage(L, errTokenExpected, "\')\'")
-  elif tok.ident.id == ord(wNot): 
+  elif tok.ident.id == ord(wNot):
     ppGetTok(L, tok)
     result = not parseAtom(L, tok)
   else:
     result = isDefined(tok.ident)
     ppGetTok(L, tok)
 
-proc parseAndExpr(L: var TLexer, tok: var TToken): bool = 
+proc parseAndExpr(L: var TLexer, tok: var TToken): bool =
   result = parseAtom(L, tok)
-  while tok.ident.id == ord(wAnd): 
+  while tok.ident.id == ord(wAnd):
     ppGetTok(L, tok)          # skip "and"
     var b = parseAtom(L, tok)
     result = result and b
 
-proc parseExpr(L: var TLexer, tok: var TToken): bool = 
+proc parseExpr(L: var TLexer, tok: var TToken): bool =
   result = parseAndExpr(L, tok)
-  while tok.ident.id == ord(wOr): 
+  while tok.ident.id == ord(wOr):
     ppGetTok(L, tok)          # skip "or"
     var b = parseAndExpr(L, tok)
     result = result or b
 
-proc evalppIf(L: var TLexer, tok: var TToken): bool = 
+proc evalppIf(L: var TLexer, tok: var TToken): bool =
   ppGetTok(L, tok)            # skip 'if' or 'elif'
   result = parseExpr(L, tok)
   if tok.tokType == tkColon: ppGetTok(L, tok)
   else: lexMessage(L, errTokenExpected, "\':\'")
-  
+
 var condStack: seq[bool] = @[]
 
-proc doEnd(L: var TLexer, tok: var TToken) = 
+proc doEnd(L: var TLexer, tok: var TToken) =
   if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
   ppGetTok(L, tok)            # skip 'end'
   setLen(condStack, high(condStack))
 
-type 
-  TJumpDest = enum 
+type
+  TJumpDest = enum
     jdEndif, jdElseEndif
 
 proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest)
-proc doElse(L: var TLexer, tok: var TToken) = 
+proc doElse(L: var TLexer, tok: var TToken) =
   if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
   ppGetTok(L, tok)
   if tok.tokType == tkColon: ppGetTok(L, tok)
   if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
-  
-proc doElif(L: var TLexer, tok: var TToken) = 
+
+proc doElif(L: var TLexer, tok: var TToken) =
   if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
   var res = evalppIf(L, tok)
   if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
   else: condStack[high(condStack)] = true
-  
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) = 
+
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
   var nestedIfs = 0
-  while true: 
+  while true:
     if tok.ident != nil and tok.ident.s == "@":
       ppGetTok(L, tok)
       case whichKeyword(tok.ident)
-      of wIf: 
+      of wIf:
         inc(nestedIfs)
-      of wElse: 
+      of wElse:
         if dest == jdElseEndif and nestedIfs == 0:
           doElse(L, tok)
-          break 
-      of wElif: 
+          break
+      of wElif:
         if dest == jdElseEndif and nestedIfs == 0:
           doElif(L, tok)
-          break 
-      of wEnd: 
-        if nestedIfs == 0: 
+          break
+      of wEnd:
+        if nestedIfs == 0:
           doEnd(L, tok)
-          break 
+          break
         if nestedIfs > 0: dec(nestedIfs)
-      else: 
+      else:
         discard
       ppGetTok(L, tok)
     elif tok.tokType == tkEof:
       lexMessage(L, errTokenExpected, "@end")
     else:
       ppGetTok(L, tok)
-  
-proc parseDirective(L: var TLexer, tok: var TToken) = 
+
+proc parseDirective(L: var TLexer, tok: var TToken) =
   ppGetTok(L, tok)            # skip @
   case whichKeyword(tok.ident)
   of wIf:
@@ -126,13 +126,13 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
     ppGetTok(L, tok)
   else:
     case tok.ident.s.normalize
-    of "putenv": 
+    of "putenv":
       ppGetTok(L, tok)
       var key = tokToStr(tok)
       ppGetTok(L, tok)
       os.putEnv(key, tokToStr(tok))
       ppGetTok(L, tok)
-    of "prependenv": 
+    of "prependenv":
       ppGetTok(L, tok)
       var key = tokToStr(tok)
       ppGetTok(L, tok)
@@ -145,17 +145,17 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
       os.putEnv(key, os.getEnv(key) & tokToStr(tok))
       ppGetTok(L, tok)
     else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
-  
-proc confTok(L: var TLexer, tok: var TToken) = 
+
+proc confTok(L: var TLexer, tok: var TToken) =
   ppGetTok(L, tok)
-  while tok.ident != nil and tok.ident.s == "@": 
+  while tok.ident != nil and tok.ident.s == "@":
     parseDirective(L, tok)    # else: give the token to the parser
-  
-proc checkSymbol(L: TLexer, tok: TToken) = 
-  if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: 
+
+proc checkSymbol(L: TLexer, tok: TToken) =
+  if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
     lexMessage(L, errIdentifierExpected, tokToStr(tok))
-  
-proc parseAssignment(L: var TLexer, tok: var TToken) = 
+
+proc parseAssignment(L: var TLexer, tok: var TToken) =
   if tok.ident.id == getIdent("-").id or tok.ident.id == getIdent("--").id:
     confTok(L, tok)           # skip unnecessary prefix
   var info = getLineInfo(L, tok) # save for later in case of an error
@@ -163,13 +163,13 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
   var s = tokToStr(tok)
   confTok(L, tok)             # skip symbol
   var val = ""
-  while tok.tokType == tkDot: 
+  while tok.tokType == tkDot:
     add(s, '.')
     confTok(L, tok)
     checkSymbol(L, tok)
     add(s, tokToStr(tok))
     confTok(L, tok)
-  if tok.tokType == tkBracketLe: 
+  if tok.tokType == tkBracketLe:
     # BUGFIX: val, not s!
     # BUGFIX: do not copy '['!
     confTok(L, tok)
@@ -180,7 +180,7 @@ proc parseAssignment(L: var TLexer, tok: var TToken) =
     else: lexMessage(L, errTokenExpected, "']'")
     add(val, ']')
   let percent = tok.ident.id == getIdent("%=").id
-  if tok.tokType in {tkColon, tkEquals} or percent: 
+  if tok.tokType in {tkColon, tkEquals} or percent:
     if len(val) > 0: add(val, ':')
     confTok(L, tok)           # skip ':' or '=' or '%'
     checkSymbol(L, tok)
@@ -211,7 +211,7 @@ proc readConfigFile(filename: string) =
     while tok.tokType != tkEof: parseAssignment(L, tok)
     if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
     closeLexer(L)
-    if gVerbosity >= 1: rawMessage(hintConf, filename)
+    rawMessage(hintConf, filename)
 
 proc getUserConfigPath(filename: string): string =
   result = joinPath(getConfigDir(), filename)
@@ -226,15 +226,7 @@ proc getSystemConfigPath(filename: string): string =
     if not existsFile(result): result = "/etc/" & filename
 
 proc loadConfigs*(cfg: string) =
-  # set default value (can be overwritten):
-  if libpath == "": 
-    # choose default libpath:
-    var prefix = getPrefixDir()
-    when defined(posix):
-      if prefix == "/usr": libpath = "/usr/lib/nim"
-      elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
-      else: libpath = joinPath(prefix, "lib")
-    else: libpath = joinPath(prefix, "lib")
+  setDefaultLibpath()
 
   if optSkipConfigFile notin gGlobalOptions:
     readConfigFile(getSystemConfigPath(cfg))
@@ -246,10 +238,10 @@ proc loadConfigs*(cfg: string) =
   if optSkipParentConfigFiles notin gGlobalOptions:
     for dir in parentDirs(pd, fromRoot=true, inclusive=false):
       readConfigFile(dir / cfg)
-  
+
   if optSkipProjConfigFile notin gGlobalOptions:
     readConfigFile(pd / cfg)
-    
+
     if gProjectName.len != 0:
       # new project wide config file:
       var projectConfig = changeFileExt(gProjectFull, "nimcfg")
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 197e8bf86..2bddb76e7 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -10,7 +10,7 @@
 ## exposes the Nim VM to clients.
 
 import
-  ast, modules, passes, passaux, condsyms, 
+  ast, modules, passes, passaux, condsyms,
   options, nimconf, lists, sem, semdata, llstream, vm
 
 proc execute*(program: string) =
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index 8caa23ee3..39436702f 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -10,8 +10,10 @@
 ## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
 
 import strutils, os, parseopt
-import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf,
-  extccomp, condsyms, lists
+import compiler/options, compiler/commands, compiler/modules, compiler/sem,
+  compiler/passes, compiler/passaux, compiler/nimfix/pretty,
+  compiler/msgs, compiler/nimconf,
+  compiler/extccomp, compiler/condsyms, compiler/lists
 
 const Usage = """
 Nimfix - Tool to patch Nim code
@@ -24,7 +26,7 @@ Options:
   --wholeProject                   overwrite every processed file.
   --checkExtern:on|off             style check also extern names
   --styleCheck:on|off|auto         performs style checking for identifiers
-                                   and suggests an alternative spelling; 
+                                   and suggests an alternative spelling;
                                    'auto' corrects the spelling.
   --bestEffort                     try to fix the code even when there
                                    are errors.
@@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
   gOnlyMainfile = true
-  while true: 
+  while true:
     parseopt.next(p)
     case p.kind
-    of cmdEnd: break 
-    of cmdLongoption, cmdShortOption: 
+    of cmdEnd: break
+    of cmdLongoption, cmdShortOption:
       case p.key.normalize
       of "overwritefiles":
         case p.val.normalize
@@ -80,7 +82,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
 
 proc handleCmdLine() =
   if paramCount() == 0:
-    stdout.writeln(Usage)
+    stdout.writeLine(Usage)
   else:
     processCmdLine(passCmd1, "")
     if gProjectName != "":
diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim
index d2d5b5e83..1123afb9e 100644
--- a/compiler/nimfix/pretty.nim
+++ b/compiler/nimfix/pretty.nim
@@ -10,9 +10,11 @@
 ## This module implements the code "prettifier". This is part of the toolchain
 ## to convert Nim code into a consistent style.
 
-import 
-  strutils, os, options, ast, astalgo, msgs, ropes, idents,
-  intsets, strtabs, semdata, prettybase
+import
+  strutils, os, intsets, strtabs
+
+import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
+  compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
 
 type
   StyleCheck* {.pure.} = enum None, Warn, Auto
@@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string =
 
 proc replaceInFile(info: TLineInfo; newName: string) =
   loadFile(info)
-  
+
   let line = gSourceFiles[info.fileIndex].lines[info.line-1]
   var first = min(info.col.int, line.len)
   if first < 0: return
@@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) =
   while first > 0 and line[first-1] in prettybase.Letters: dec first
   if first < 0: return
   if line[first] == '`': inc first
-  
+
   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)    
+    # last-first+1 != newName.len or
+    var x = line.substr(0, first-1) & newName & line.substr(last+1)
     system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
     gSourceFiles[info.fileIndex].dirty = true
 
 proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
   let beau = beautifyName(s, k)
   if s != beau:
-    if gStyleCheck == StyleCheck.Auto: 
+    if gStyleCheck == StyleCheck.Auto:
       sym.name = getIdent(beau)
       replaceInFile(info, beau)
     else:
@@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
   if info.fileIndex < 0: return
   # we simply convert it to what it looks like in the definition
   # for consistency
-  
+
   # operators stay as they are:
   if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
     return
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index 5130d1863..0f17cbcb1 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -7,7 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, msgs, strutils, idents, lexbase, streams
+import strutils, lexbase, streams
+import compiler/ast, compiler/msgs, compiler/idents
 from os import splitFile
 
 type
@@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) =
     var pos = lex.bufpos
     while true:
       case lex.buf[pos]
-      of '\c': 
+      of '\c':
         gSourceFiles[i].newline = "\c\L"
         break
       of '\L', '\0':
@@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
   while first > 0 and line[first-1] in Letters: dec first
   if first < 0: return
   if line[first] == '`': inc first
-  
+
   let last = first+identLen(line, first)-1
   if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
     var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim
index f5db5ca4f..047890c44 100644
--- a/compiler/nimlexbase.nim
+++ b/compiler/nimlexbase.nim
@@ -12,10 +12,10 @@
 # handling that exists! Only at line endings checks are necessary
 # if the buffer needs refilling.
 
-import 
+import
   llstream, strutils
 
-const 
+const
   Lrz* = ' '
   Apo* = '\''
   Tabulator* = '\x09'
@@ -27,7 +27,7 @@ const
   BACKSPACE* = '\x08'
   VT* = '\x0B'
 
-const 
+const
   EndOfFile* = '\0'           # end of file marker
                               # A little picture makes everything clear :-)
                               #  buf:
@@ -36,7 +36,7 @@ const
                               #
   NewLines* = {CR, LF}
 
-type 
+type
   TBaseLexer* = object of RootObj
     bufpos*: int
     buf*: cstring
@@ -46,9 +46,9 @@ type
                               # private data:
     sentinel*: int
     lineStart*: int           # index of last line start in buffer
-  
 
-proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream, 
+
+proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
                     bufLen: int = 8192)
   # 8K is a reasonable buffer size
 proc closeBaseLexer*(L: var TBaseLexer)
@@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int
   # of the LF.
 # implementation
 
-const 
+const
   chrSize = sizeof(char)
 
-proc closeBaseLexer(L: var TBaseLexer) = 
+proc closeBaseLexer(L: var TBaseLexer) =
   dealloc(L.buf)
   llStreamClose(L.stream)
 
-proc fillBuffer(L: var TBaseLexer) = 
-  var 
+proc fillBuffer(L: var TBaseLexer) =
+  var
     charsRead, toCopy, s: int # all are in characters,
                               # not bytes (in case this
                               # is not the same)
@@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) =
   assert(L.sentinel < L.bufLen)
   toCopy = L.bufLen - L.sentinel - 1
   assert(toCopy >= 0)
-  if toCopy > 0: 
-    moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) 
+  if toCopy > 0:
+    moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
     # "moveMem" handles overlapping regions
-  charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]), 
+  charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
                            (L.sentinel + 1) * chrSize) div chrSize
   s = toCopy + charsRead
-  if charsRead < L.sentinel + 1: 
+  if charsRead < L.sentinel + 1:
     L.buf[s] = EndOfFile      # set end marker
     L.sentinel = s
-  else: 
+  else:
     # compute sentinel:
     dec(s)                    # BUGFIX (valgrind)
-    while true: 
+    while true:
       assert(s < L.bufLen)
       while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
-      if s >= 0: 
+      if s >= 0:
         # we found an appropriate character for a sentinel:
         L.sentinel = s
-        break 
-      else: 
+        break
+      else:
         # rather than to give up here because the line is too long,
         # double the buffer's size and try again:
         oldBufLen = L.bufLen
         L.bufLen = L.bufLen * 2
         L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
         assert(L.bufLen - oldBufLen == oldBufLen)
-        charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), 
+        charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
                                  oldBufLen * chrSize) div chrSize
-        if charsRead < oldBufLen: 
+        if charsRead < oldBufLen:
           L.buf[oldBufLen + charsRead] = EndOfFile
           L.sentinel = oldBufLen + charsRead
-          break 
+          break
         s = L.bufLen - 1
 
-proc fillBaseLexer(L: var TBaseLexer, pos: int): int = 
+proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
   assert(pos <= L.sentinel)
-  if pos < L.sentinel: 
+  if pos < L.sentinel:
     result = pos + 1          # nothing to do
-  else: 
+  else:
     fillBuffer(L)
     L.bufpos = 0              # XXX: is this really correct?
     result = 0
   L.lineStart = result
 
-proc handleCR(L: var TBaseLexer, pos: int): int = 
+proc handleCR(L: var TBaseLexer, pos: int): int =
   assert(L.buf[pos] == CR)
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos)
-  if L.buf[result] == LF: 
+  if L.buf[result] == LF:
     result = fillBaseLexer(L, result)
 
-proc handleLF(L: var TBaseLexer, pos: int): int = 
+proc handleLF(L: var TBaseLexer, pos: int): int =
   assert(L.buf[pos] == LF)
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
-  
-proc skipUTF8BOM(L: var TBaseLexer) = 
+
+proc skipUTF8BOM(L: var TBaseLexer) =
   if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
     inc(L.bufpos, 3)
     inc(L.lineStart, 3)
 
-proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = 
+proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
   assert(bufLen > 0)
   L.bufpos = 0
   L.bufLen = bufLen
@@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
   fillBuffer(L)
   skipUTF8BOM(L)
 
-proc getColNumber(L: TBaseLexer, pos: int): int = 
+proc getColNumber(L: TBaseLexer, pos: int): int =
   result = abs(pos - L.lineStart)
 
-proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = 
+proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
   result = ""
   var i = L.lineStart
-  while not (L.buf[i] in {CR, LF, EndOfFile}): 
+  while not (L.buf[i] in {CR, LF, EndOfFile}):
     add(result, L.buf[i])
     inc(i)
   result.add("\n")
-  if marker: 
+  if marker:
     result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index aa7686d30..055bae909 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -9,7 +9,7 @@
 
 # this unit handles Nim sets; it implements symbolic sets
 
-import 
+import
   ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
 
 proc toBitSet*(s: PNode, b: var TBitSet)
@@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool
 proc cardSet*(s: PNode): BiggestInt
 # implementation
 
-proc inSet(s: PNode, elem: PNode): bool = 
-  if s.kind != nkCurly: 
+proc inSet(s: PNode, elem: PNode): bool =
+  if s.kind != nkCurly:
     internalError(s.info, "inSet")
     return false
-  for i in countup(0, sonsLen(s) - 1): 
-    if s.sons[i].kind == nkRange: 
+  for i in countup(0, sonsLen(s) - 1):
+    if s.sons[i].kind == nkRange:
       if leValue(s.sons[i].sons[0], elem) and
-          leValue(elem, s.sons[i].sons[1]): 
+          leValue(elem, s.sons[i].sons[1]):
         return true
-    else: 
-      if sameValue(s.sons[i], elem): 
+    else:
+      if sameValue(s.sons[i], elem):
         return true
   result = false
 
@@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool =
     else:
       result = sameValue(a, b)
 
-proc someInSet(s: PNode, a, b: PNode): bool = 
+proc someInSet(s: PNode, a, b: PNode): bool =
   # checks if some element of a..b is in the set s
   if s.kind != nkCurly:
     internalError(s.info, "SomeInSet")
     return false
-  for i in countup(0, sonsLen(s) - 1): 
-    if s.sons[i].kind == nkRange: 
+  for i in countup(0, sonsLen(s) - 1):
+    if s.sons[i].kind == nkRange:
       if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
-          leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]): 
+          leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
         return true
-    else: 
+    else:
       # a <= elem <= b
-      if leValue(a, s.sons[i]) and leValue(s.sons[i], b): 
+      if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
         return true
   result = false
 
-proc toBitSet(s: PNode, b: var TBitSet) = 
+proc toBitSet(s: PNode, b: var TBitSet) =
   var first, j: BiggestInt
   first = firstOrd(s.typ.sons[0])
   bitSetInit(b, int(getSize(s.typ)))
-  for i in countup(0, sonsLen(s) - 1): 
-    if s.sons[i].kind == nkRange: 
+  for i in countup(0, sonsLen(s) - 1):
+    if s.sons[i].kind == nkRange:
       j = getOrdValue(s.sons[i].sons[0])
-      while j <= getOrdValue(s.sons[i].sons[1]): 
+      while j <= getOrdValue(s.sons[i].sons[1]):
         bitSetIncl(b, j - first)
         inc(j)
-    else: 
+    else:
       bitSetIncl(b, getOrdValue(s.sons[i]) - first)
-  
-proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = 
-  var 
+
+proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
+  var
     a, b, e, first: BiggestInt # a, b are interval borders
     elemType: PType
     n: PNode
@@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
   result.typ = settype
   result.info = info
   e = 0
-  while e < len(s) * ElemSize: 
-    if bitSetIn(s, e): 
+  while e < len(s) * ElemSize:
+    if bitSetIn(s, e):
       a = e
       b = e
-      while true: 
+      while true:
         inc(b)
-        if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break 
+        if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
       dec(b)
-      if a == b: 
+      if a == b:
         addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
-      else: 
+      else:
         n = newNodeI(nkRange, info)
         n.typ = elemType
         addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
@@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
       e = b
     inc(e)
 
-template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = 
+template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
@@ -129,13 +129,13 @@ proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
 proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
 proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
 
-proc containsSets(a, b: PNode): bool = 
+proc containsSets(a, b: PNode): bool =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
   result = bitSetContains(x, y)
 
-proc equalSets(a, b: PNode): bool = 
+proc equalSets(a, b: PNode): bool =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
@@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode =
   for i in countup(0, high(x)): x[i] = not x[i]
   result = toTreeSet(x, a.typ, a.info)
 
-proc cardSet(s: PNode): BiggestInt = 
+proc cardSet(s: PNode): BiggestInt =
   # here we can do better than converting it into a compact set
   # we just count the elements directly
   result = 0
-  for i in countup(0, sonsLen(s) - 1): 
-    if s.sons[i].kind == nkRange: 
+  for i in countup(0, sonsLen(s) - 1):
+    if s.sons[i].kind == nkRange:
       result = result + getOrdValue(s.sons[i].sons[1]) -
           getOrdValue(s.sons[i].sons[0]) + 1
-    else: 
+    else:
       inc(result)
-  
-proc setHasRange(s: PNode): bool = 
+
+proc setHasRange(s: PNode): bool =
   if s.kind != nkCurly:
     internalError(s.info, "SetHasRange")
     return false
-  for i in countup(0, sonsLen(s) - 1): 
-    if s.sons[i].kind == nkRange: 
+  for i in countup(0, sonsLen(s) - 1):
+    if s.sons[i].kind == nkRange:
       return true
   result = false
 
-proc emptyRange(a, b: PNode): bool = 
+proc emptyRange(a, b: PNode): bool =
   result = not leValue(a, b)  # a > b iff not (a <= b)
-  
+
diff --git a/compiler/nimsuggest/nimsuggest.nim b/compiler/nimsuggest/nimsuggest.nim
index 8285d81d9..2be368d68 100644
--- a/compiler/nimsuggest/nimsuggest.nim
+++ b/compiler/nimsuggest/nimsuggest.nim
@@ -7,192 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-## Nimsuggest is a tool that helps to give editors IDE like capabilities.
+## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
 
-import strutils, os, parseopt, parseUtils
-import options, commands, modules, sem, passes, passaux, msgs, nimconf,
-  extccomp, condsyms, lists, net, rdstdin
-
-const Usage = """
-Nimsuggest - Tool to give every editor IDE like capabilities for Nim
-Usage:
-  nimsuggest [options] projectfile.nim
-
-Options:
-  --port:PORT             port, by default 6000
-  --address:HOST          binds to that address, by default ""
-  --stdin                 read commands from stdin and write results to
-                          stdout instead of using sockets
-
-The server then listens to the connection and takes line-based commands.
-
-In addition, all command line options of Nim that do not affect code generation
-are supported.
-"""
-
-var
-  gPort = 6000.Port
-  gAddress = ""
-  gUseStdin: bool
-
-const
-  seps = {':', ';', ' ', '\t'}
-  Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
-         "type 'quit' to quit\n" &
-         "type 'debug' to toggle debug mode on/off\n" &
-         "type 'terse' to toggle terse mode on/off"
-
-proc parseQuoted(cmd: string; outp: var string; start: int): int =
-  var i = start
-  i += skipWhitespace(cmd, i)
-  if cmd[i] == '"':
-    i += parseUntil(cmd, outp, '"', i+1)+2
-  else:
-    i += parseUntil(cmd, outp, seps, i)
-  result = i
-
-proc action(cmd: string) =
-  template toggle(sw) =
-    if sw in gGlobalOptions:
-      excl(gGlobalOptions, sw)
-    else:
-      incl(gGlobalOptions, sw)
-    return
-
-  template err() =
-    echo Help
-    return
-
-  var opc = ""
-  var i = parseIdent(cmd, opc, 0)
-  case opc.normalize
-  of "sug": gIdeCmd = ideSug
-  of "con": gIdeCmd = ideCon
-  of "def": gIdeCmd = ideDef
-  of "use":
-    modules.resetAllModules()
-    gIdeCmd = ideUse
-  of "quit": quit()
-  of "debug": toggle optIdeDebug
-  of "terse": toggle optIdeTerse
-  else: err()
-  var dirtyfile = ""
-  var orig = ""
-  i = parseQuoted(cmd, orig, i)
-  if cmd[i] == ';':
-    i = parseQuoted(cmd, dirtyfile, i+1)
-  i += skipWhile(cmd, seps, i)
-  var line = -1
-  var col = 0
-  i += parseInt(cmd, line, i)
-  i += skipWhile(cmd, seps, i)
-  i += parseInt(cmd, col, i)
-
-  var isKnownFile = true
-  if orig.len == 0: err()
-  let dirtyIdx = orig.fileInfoIdx(isKnownFile)
-
-  if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(dirtyIdx, nil)
-
-  resetModule dirtyIdx
-  if dirtyIdx != gProjectMainIdx:
-    resetModule gProjectMainIdx
-  gTrackPos = newLineInfo(dirtyIdx, line, col-1)
-  #echo dirtyfile, gDirtyBufferIdx, " project ", gProjectMainIdx
-  gErrorCounter = 0
-  if not isKnownFile:
-    compileProject(dirtyIdx)
-  else:
-    compileProject()
-
-proc serve() =
-  # do not stop after the first error:
-  msgs.gErrorMax = high(int)
-  if gUseStdin:
-    echo Help
-    var line = ""
-    while readLineFromStdin("> ", line):
-      action line
-      echo ""
-      flushFile(stdout)
-  else:
-    var server = newSocket()
-    server.bindAddr(gPort, gAddress)
-    var inp = "".TaintedString
-    server.listen()
-
-    while true:
-      var stdoutSocket = newSocket()
-      msgs.writelnHook = proc (line: string) =
-        stdoutSocket.send(line & "\c\L")
-
-      accept(server, stdoutSocket)
-
-      stdoutSocket.readLine(inp)
-      action inp.string
-
-      stdoutSocket.send("\c\L")
-      stdoutSocket.close()
-
-proc mainCommand =
-  registerPass verbosePass
-  registerPass semPass
-  gCmd = cmdIdeTools
-  incl gGlobalOptions, optCaasEnabled
-  isServing = true
-  wantMainModule()
-  appendStr(searchPaths, options.libpath)
-  if gProjectFull.len != 0:
-    # current path is always looked first for modules
-    prependStr(searchPaths, gProjectPath)
-
-  serve()
-
-proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
-  var p = parseopt.initOptParser(cmd)
-  while true:
-    parseopt.next(p)
-    case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      case p.key.normalize
-      of "port": gPort = parseInt(p.val).Port
-      of "address": gAddress = p.val
-      of "stdin": gUseStdin = true
-      else: processSwitch(pass, p)
-    of cmdArgument:
-      options.gProjectName = unixToNativePath(p.key)
-      # if processArgument(pass, p, argsCount): break
-
-proc handleCmdLine() =
-  if paramCount() == 0:
-    stdout.writeln(Usage)
-  else:
-    processCmdLine(passCmd1, "")
-    if gProjectName != "":
-      try:
-        gProjectFull = canonicalizePath(gProjectName)
-      except OSError:
-        gProjectFull = gProjectName
-      var p = splitFile(gProjectFull)
-      gProjectPath = p.dir
-      gProjectName = p.name
-    else:
-      gProjectPath = getCurrentDir()
-    loadConfigs(DefaultConfig) # load all config files
-    # now process command line arguments again, because some options in the
-    # command line can overwite the config file's settings
-    extccomp.initVars()
-    processCmdLine(passCmd2, "")
-    mainCommand()
-
-when false:
-  proc quitCalled() {.noconv.} =
-    writeStackTrace()
-
-  addQuitProc(quitCalled)
-
-condsyms.initDefines()
-defineSymbol "nimsuggest"
-handleCmdline()
+{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}
diff --git a/compiler/nimsuggest/nimsuggest.nim.cfg b/compiler/nimsuggest/nimsuggest.nim.cfg
deleted file mode 100644
index acca17396..000000000
--- a/compiler/nimsuggest/nimsuggest.nim.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-# Special configuration file for the Nim project
-
-gc:markAndSweep
-
-hint[XDeclaredButNotUsed]:off
-path:"$projectPath/../.."
-
-path:"$lib/packages/docutils"
-path:"../../compiler"
-
-define:useStdoutAsStdmsg
-define:nimsuggest
-
-cs:partial
-#define:useNodeIds
-define:booting
-#define:noDocgen
diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim
index e2b79df19..7f9f28aaf 100644
--- a/compiler/nodejs.nim
+++ b/compiler/nodejs.nim
@@ -4,3 +4,5 @@ proc findNodeJs*(): string =
   result = findExe("nodejs")
   if result == "":
     result = findExe("node")
+  if result == "":
+    result = findExe("iojs")
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 4dea62876..adeb0fb6d 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -10,7 +10,7 @@
 # This module contains Nim's version. It is the only place where it needs
 # to be changed.
 
-const 
+const
   MaxSetElements* = 1 shl 16  # (2^16) to support unicode character sets?
   VersionAsString* = system.NimVersion
   RodFileVersion* = "1215"       # modify this if the rod-format changes!
diff --git a/compiler/options.nim b/compiler/options.nim
index 998ab7781..6dd917ad4 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -13,6 +13,7 @@ import
 const
   hasTinyCBackend* = defined(tinyc)
   useEffectSystem* = true
+  useWriteTracking* = false
   hasFFI* = defined(useFFI)
   newScopeForIf* = true
   useCaas* = not defined(noCaas)
@@ -39,7 +40,7 @@ type                          # please make sure we have under 32 options
   TGlobalOption* = enum       # **keep binary compatible**
     gloptNone, optForceFullMake, optDeadCodeElim,
     optListCmd, optCompileOnly, optNoLinking,
-    optSafeCode,              # only allow safe code
+    optReportConceptFailures, # report 'compiles' or 'concept' matching failures
     optCDebug,                # turn on debugging information
     optGenDynLib,             # generate a dynamic library
     optGenStaticLib,          # generate a static library
@@ -54,6 +55,7 @@ type                          # please make sure we have under 32 options
     optSkipUserConfigFile,    # skip the users's config file
     optSkipParentConfigFiles, # skip parent dir's config files
     optNoMain,                # do not generate a "main" proc
+    optUseColors,             # use colors for hints, warnings, and errors
     optThreads,               # support for multi-threading
     optStdout,                # output to stdout
     optThreadAnalysis,        # thread analysis pass
@@ -81,13 +83,13 @@ type                          # please make sure we have under 32 options
     cmdRun                    # run the project via TCC backend
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
-    gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
+    gcNone, gcBoehm, gcGo, gcMarkAndSweep, gcRefc, gcV2, gcGenerational
 
-  TIdeCmd* = enum
-    ideNone, ideSug, ideCon, ideDef, ideUse
+  IdeCmd* = enum
+    ideNone, ideSug, ideCon, ideDef, ideUse, ideDus
 
 var
-  gIdeCmd*: TIdeCmd
+  gIdeCmd*: IdeCmd
 
 const
   ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
@@ -126,9 +128,6 @@ template compilationCachePresent*: expr =
 template optPreserveOrigSource*: expr =
   optEmbedOrigSrc in gGlobalOptions
 
-template optPrintSurroundingSrc*: expr =
-  gVerbosity >= 2
-
 const
   genSubDir* = "nimcache"
   NimExt* = "nim"
@@ -145,10 +144,12 @@ const
 var
   gConfigVars* = newStringTable(modeStyleInsensitive)
   gDllOverrides = newStringTable(modeCaseInsensitive)
+  gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
   libpath* = ""
   gProjectName* = "" # holds a name like 'nimrod'
   gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/
   gProjectFull* = "" # projectPath/projectName
+  gProjectIsStdin* = false # whether we're compiling from stdin
   gProjectMainIdx*: int32 # the canonical path id of the main module
   nimcacheDir* = ""
   command* = "" # the main command (e.g. cc, check, scan, etc)
@@ -172,7 +173,7 @@ proc existsConfigVar*(key: string): bool =
   result = hasKey(gConfigVars, key)
 
 proc getConfigVar*(key: string): string =
-  result = gConfigVars[key]
+  result = gConfigVars.getOrDefault key
 
 proc setConfigVar*(key, val: string) =
   gConfigVars[key] = val
@@ -182,8 +183,24 @@ proc getOutFile*(filename, ext: string): string =
   else: result = changeFileExt(filename, ext)
 
 proc getPrefixDir*(): string =
-  ## gets the application directory
-  result = splitPath(getAppDir()).head
+  ## Gets the prefix dir, usually the parent directory where the binary resides.
+  ##
+  ## This is overrided by some tools (namely nimsuggest) via the ``gPrefixDir``
+  ## global.
+  if gPrefixDir != "": result = gPrefixDir
+  else:
+    result = splitPath(getAppDir()).head
+
+proc setDefaultLibpath*() =
+  # set default value (can be overwritten):
+  if libpath == "":
+    # choose default libpath:
+    var prefix = getPrefixDir()
+    when defined(posix):
+      if prefix == "/usr": libpath = "/usr/lib/nim"
+      elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
+      else: libpath = joinPath(prefix, "lib")
+    else: libpath = joinPath(prefix, "lib")
 
 proc canonicalizePath*(path: string): string =
   when not FileSystemCaseSensitive: result = path.expandFilename.toLower
@@ -205,7 +222,7 @@ proc removeTrailingDirSep*(path: string): string =
   else:
     result = path
 
-proc getGeneratedPath: string =
+proc getNimcacheDir*: string =
   result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
                                                          genSubDir
 
@@ -261,7 +278,7 @@ proc toGeneratedFile*(path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
   var (head, tail) = splitPath(path)
   #if len(head) > 0: head = shortenDir(head & dirSep)
-  result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)])
+  result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
   #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
 
 when noTimeMachine:
@@ -289,14 +306,14 @@ when noTimeMachine:
 proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
   var (head, tail) = splitPath(f)
   #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
-  var subdir = getGeneratedPath() # / head
+  var subdir = getNimcacheDir() # / head
   if createSubDir:
     try:
       createDir(subdir)
       when noTimeMachine:
-       excludeDirFromTimeMachine(subdir)
+        excludeDirFromTimeMachine(subdir)
     except OSError:
-      writeln(stdout, "cannot create directory: " & subdir)
+      writeLine(stdout, "cannot create directory: " & subdir)
       quit(1)
   result = joinPath(subdir, tail)
   #echo "completeGeneratedFilePath(", f, ") = ", result
@@ -325,13 +342,16 @@ proc rawFindFile2(f: string): string =
   result = ""
 
 proc findFile*(f: string): string {.procvar.} =
-  result = f.rawFindFile
-  if result.len == 0:
-    result = f.toLower.rawFindFile
+  if f.isAbsolute:
+    result = if f.existsFile: f else: ""
+  else:
+    result = f.rawFindFile
     if result.len == 0:
-      result = f.rawFindFile2
+      result = f.toLower.rawFindFile
       if result.len == 0:
-        result = f.toLower.rawFindFile2
+        result = f.rawFindFile2
+        if result.len == 0:
+          result = f.toLower.rawFindFile2
 
 proc findModule*(modulename, currentModule: string): string =
   # returns path to module
@@ -395,3 +415,20 @@ template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx
 template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx
 template lnimdbg*: expr = L.fileIdx == gProjectMainIdx
 
+proc parseIdeCmd*(s: string): IdeCmd =
+  case s:
+  of "sug": ideSug
+  of "con": ideCon
+  of "def": ideDef
+  of "use": ideUse
+  of "dus": ideDus
+  else: ideNone
+
+proc `$`*(c: IdeCmd): string =
+  case c:
+  of ideSug: "sug"
+  of ideCon: "con"
+  of ideDef: "def"
+  of ideUse: "use"
+  of ideDus: "dus"
+  of ideNone: "none"
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index b7fe269df..978583c14 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -178,13 +178,14 @@ type
     arDiscriminant,           # is a discriminant
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
-proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
+proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
   ## 'owner' can be nil!
   result = arNone
   case n.kind
   of nkSym:
-    # don't list 'skLet' here:
-    if n.sym.kind in {skVar, skResult, skTemp}:
+    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+                else: {skVar, skResult, skTemp}
+    if n.sym.kind in kinds:
       if owner != nil and owner.id == n.sym.owner.id and
           sfGlobal notin n.sym.flags:
         result = arLocalLValue
@@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
     if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
       result = arDiscriminant
   of nkBracketExpr:
@@ -208,23 +209,27 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     # Object and tuple conversions are still addressable, so we skip them
     # XXX why is 'tyOpenArray' allowed here?
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
     elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
       # types that are equal modulo distinction preserve l-value:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
   of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
     result = arLValue
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = isAssignable(owner, n.sons[0])
+    result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkCallKinds:
     # builtin slice keeps lvalue-ness:
-    if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1])
+    if getMagic(n) in {mArrGet, mSlice}:
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
+  of nkStmtList, nkStmtListExpr:
+    if n.typ != nil:
+      result = isAssignable(owner, n.lastSon, isUnsafeAddr)
   else:
     discard
 
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 0d2ba7cfc..dbf9706ea 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase)
 proc parseSymbol*(p: var TParser, allowNil = false): PNode
 proc parseTry(p: var TParser; isExpr: bool): PNode
 proc parseCase(p: var TParser): PNode
+proc parseStmtPragma(p: var TParser): PNode
 # implementation
 
 proc getTok(p: var TParser) =
@@ -124,6 +125,9 @@ proc rawSkipComment(p: var TParser, node: PNode) =
 proc skipComment(p: var TParser, node: PNode) =
   if p.tok.indent < 0: rawSkipComment(p, node)
 
+proc flexComment(p: var TParser, node: PNode) =
+  if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
+
 proc skipInd(p: var TParser) =
   if p.tok.indent >= 0:
     if not realInd(p): parMessage(p, errInvalidIndentation)
@@ -499,10 +503,13 @@ proc parsePar(p: var TParser): PNode =
   #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
   #|         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
   #|         | 'when' | 'var' | 'mixin'
-  #| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
-  #|                  | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
-  #|                             | (':' expr)? (',' (exprColonEqExpr comma?)*)?  )?
-  #|         optPar ')'
+  #| par = '(' optInd
+  #|           ( &parKeyw complexOrSimpleStmt ^+ ';'
+  #|           | ';' complexOrSimpleStmt ^+ ';'
+  #|           | pragmaStmt
+  #|           | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
+  #|                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
+  #|           optPar ')'
   #
   # unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
   # leading ';' could be used to enforce a 'stmt' context ...
@@ -521,6 +528,8 @@ proc parsePar(p: var TParser): PNode =
     getTok(p)
     optInd(p, result)
     semiStmtList(p, result)
+  elif p.tok.tokType == tkCurlyDotLe:
+    result.add(parseStmtPragma(p))
   elif p.tok.tokType != tkParRi:
     var a = simpleExpr(p)
     if p.tok.tokType == tkEquals:
@@ -881,12 +890,13 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
     skipComment(p, result)
     if realInd(p):
       withInd(p):
-        skipComment(p, result)
+        rawSkipComment(p, result)
         while true:
           case p.tok.tokType
           of tkSymbol, tkAccent:
             var a = parseIdentColonEquals(p, {})
-            skipComment(p, a)
+            if p.tok.indent < 0 or p.tok.indent >= p.currInd:
+              rawSkipComment(p, a)
             addSon(result, a)
           of tkEof: break
           else:
@@ -1602,6 +1612,7 @@ proc parseEnum(p: var TParser): PNode =
   getTok(p)
   addSon(result, ast.emptyNode)
   optInd(p, result)
+  flexComment(p, result)
   while true:
     var a = parseSymbol(p)
     if a.kind == nkEmpty: return
@@ -1615,12 +1626,14 @@ proc parseEnum(p: var TParser): PNode =
       a = newNodeP(nkEnumFieldDef, p)
       addSon(a, b)
       addSon(a, parseExpr(p))
-      skipComment(p, a)
+      if p.tok.indent < 0 or p.tok.indent >= p.currInd:
+        rawSkipComment(p, a)
     if p.tok.tokType == tkComma and p.tok.indent < 0:
       getTok(p)
       rawSkipComment(p, a)
     else:
-      skipComment(p, a)
+      if p.tok.indent < 0 or p.tok.indent >= p.currInd:
+        rawSkipComment(p, a)
     addSon(result, a)
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
         p.tok.tokType == tkEof:
@@ -1641,7 +1654,7 @@ proc parseObjectWhen(p: var TParser): PNode =
     addSon(branch, parseExpr(p))
     colcom(p, branch)
     addSon(branch, parseObjectPart(p))
-    skipComment(p, branch)
+    flexComment(p, branch)
     addSon(result, branch)
     if p.tok.tokType != tkElif: break
   if p.tok.tokType == tkElse and sameInd(p):
@@ -1649,7 +1662,7 @@ proc parseObjectWhen(p: var TParser): PNode =
     eat(p, tkElse)
     colcom(p, branch)
     addSon(branch, parseObjectPart(p))
-    skipComment(p, branch)
+    flexComment(p, branch)
     addSon(result, branch)
 
 proc parseObjectCase(p: var TParser): PNode =
@@ -1669,7 +1682,7 @@ proc parseObjectCase(p: var TParser): PNode =
   addSon(a, ast.emptyNode)
   addSon(result, a)
   if p.tok.tokType == tkColon: getTok(p)
-  skipComment(p, result)
+  flexComment(p, result)
   var wasIndented = false
   let oldInd = p.currInd
   if realInd(p):
@@ -1718,7 +1731,8 @@ proc parseObjectPart(p: var TParser): PNode =
       result = parseObjectCase(p)
     of tkSymbol, tkAccent:
       result = parseIdentColonEquals(p, {withPragma})
-      skipComment(p, result)
+      if p.tok.indent < 0 or p.tok.indent >= p.currInd:
+        rawSkipComment(p, result)
     of tkNil, tkDiscard:
       result = newNodeP(nkNilLit, p)
       getTok(p)
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index f754c80e5..9b9fdca4e 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -9,38 +9,38 @@
 
 ## implements some little helper passes
 
-import 
+import
   strutils, ast, astalgo, passes, msgs, options, idgen
 
 proc verboseOpen(s: PSym): PPassContext =
   #MessageOut('compiling ' + s.name.s);
   result = nil                # we don't need a context
-  if gVerbosity > 0: rawMessage(hintProcessing, s.name.s)
-  
-proc verboseProcess(context: PPassContext, n: PNode): PNode = 
+  rawMessage(hintProcessing, s.name.s)
+
+proc verboseProcess(context: PPassContext, n: PNode): PNode =
   result = n
   if context != nil: internalError("logpass: context is not nil")
-  if gVerbosity == 3: 
+  if gVerbosity == 3:
     # system.nim deactivates all hints, for verbosity:3 we want the processing
     # messages nonetheless, so we activate them again unconditionally:
     incl(msgs.gNotes, hintProcessing)
     message(n.info, hintProcessing, $idgen.gBackendId)
-  
+
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
 
-proc cleanUp(c: PPassContext, n: PNode): PNode = 
+proc cleanUp(c: PPassContext, n: PNode): PNode =
   result = n
   # we cannot clean up if dead code elimination is activated
-  if optDeadCodeElim in gGlobalOptions or n == nil: return 
+  if optDeadCodeElim in gGlobalOptions or n == nil: return
   case n.kind
-  of nkStmtList: 
+  of nkStmtList:
     for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
-  of nkProcDef, nkMethodDef: 
-    if n.sons[namePos].kind == nkSym: 
+  of nkProcDef, nkMethodDef:
+    if n.sons[namePos].kind == nkSym:
       var s = n.sons[namePos].sym
-      if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): 
+      if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
         s.ast.sons[bodyPos] = ast.emptyNode # free the memory
-  else: 
+  else:
     discard
 
 const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 129d8ad47..ceb3e2b8a 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -170,11 +170,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
     openPasses(a, module)
     if stream == nil:
       let filename = fileIdx.toFullPathConsiderDirty
-      if module.name.s == "-":
-        module.name.s = "stdinfile"
-        s = llStreamOpen(stdin)
-      else:
-        s = llStreamOpen(filename, fmRead)
+      s = llStreamOpen(filename, fmRead)
       if s == nil:
         rawMessage(errCannotOpenFile, filename)
         return
@@ -194,8 +190,17 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
       while true:
         var n = parseTopLevelStmt(p)
         if n.kind == nkEmpty: break
-        if not processTopLevelStmt(n, a): break
-
+        if sfNoForward in module.flags:
+          # read everything, no streaming possible
+          var sl = newNodeI(nkStmtList, n.info)
+          sl.add n
+          while true:
+            var n = parseTopLevelStmt(p)
+            if n.kind == nkEmpty: break
+            sl.add n
+          discard processTopLevelStmt(sl, a)
+          break
+        elif not processTopLevelStmt(n, a): break
       closeParsers(p)
       if s.kind != llsStdIn: break
     closePasses(a)
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 368b0b37b..604d3521d 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool =
   elif sc.kind == nkOpenSymChoice:
     # same name suffices for open sym choices!
     result = sc.sons[0].sym.name.id == x.sym.name.id
-  
+
 proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
   # check param constraints first here as this is quite optimized:
   if p.constraint != nil:
@@ -115,13 +115,13 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
       if rpn: arglist.add(n.sons[0])
     elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
       let n = n.sons[1]
-      for i in 0.. <n.len: 
+      for i in 0.. <n.len:
         if not matchStarAux(c, op, n[i], arglist, rpn): return false
     elif checkTypes(c, p.sons[2].sym, n):
       add(arglist, n)
     else:
       result = false
-    
+
   if n.kind notin nkCallKinds: return false
   if matches(c, p.sons[1], n.sons[0]):
     var arglist = newNodeI(nkArgList, n.info)
@@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
 
 proc matches(c: PPatternContext, p, n: PNode): bool =
   # hidden conversions (?)
-  if isPatternParam(c, p):
+  if nfNoRewrite in n.flags:
+    result = false
+  elif isPatternParam(c, p):
     result = bindOrCheck(c, p.sym, n)
   elif n.kind == nkSym and p.kind == nkIdent:
     result = p.ident.id == n.sym.name.id
@@ -149,7 +151,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     of "**": result = matchNested(c, p, n, rpn=true)
     of "~": result = not matches(c, p.sons[1], n)
     else: internalError(p.info, "invalid pattern")
-    # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = 
+    # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
     #   add(a, b)
   elif p.kind == nkCurlyExpr:
     if p.sons[1].kind == nkPrefix:
@@ -210,7 +212,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
         if not isNil(c.mapping): c.mapping = nil
         return false
     result = true
-  
+
   if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
     let n = flattenStmts(n)
     # no need to flatten 'p' here as that has already been done
diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim
index cf4dbffc5..00f83a11e 100644
--- a/compiler/pbraces.nim
+++ b/compiler/pbraces.nim
@@ -7,12 +7,12 @@
 #    distribution, for details about the copyright.
 #
 
-import 
+import
   llstream, lexer, parser, idents, strutils, ast, msgs
 
-proc parseAll*(p: var TParser): PNode = 
+proc parseAll*(p: var TParser): PNode =
   result = nil
 
-proc parseTopLevelStmt*(p: var TParser): PNode = 
+proc parseTopLevelStmt*(p: var TParser): PNode =
   result = nil
 
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 4dd5d8836..8376c2b32 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -158,8 +158,8 @@ type
   TSystemCPU* = enum # Also add CPU for in initialization section and
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
-    cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm,
-    cpuJS, cpuNimrodVM, cpuAVR
+    cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
+    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430
 
 type
   TEndian* = enum
@@ -175,15 +175,19 @@ const
     (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
+    (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
     (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+    (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
     (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
-    (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
+    (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
+    (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
 
 var
   targetCPU*, hostCPU*: TSystemCPU
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim
index d89149f33..59e3d677d 100644
--- a/compiler/plugins/locals/locals.nim
+++ b/compiler/plugins/locals/locals.nim
@@ -9,7 +9,8 @@
 
 ## The builtin 'system.locals' implemented as a plugin.
 
-import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
+import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
+  compiler/lookups, compiler/semdata, compiler/lowerings
 
 proc semLocals(c: PContext, n: PNode): PNode =
   var counter = 0
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index c048d78e9..79d7884fa 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -25,19 +25,20 @@ const
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
     wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
     wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
-    wOverride, wConstructor}
+    wOverride, wConstructor, wExportNims}
   converterPragmas* = procPragmas
-  methodPragmas* = procPragmas
+  methodPragmas* = procPragmas+{wBase}
   templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
-    wDelegator}
+    wDelegator, wExportNims}
   macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
     wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
-    wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator}
+    wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
+    wExportNims}
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
     wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
-    wTags, wLocks, wGcSafe}
-  exprPragmas* = {wLine, wLocks}
+    wTags, wLocks, wGcSafe, wExportNims}
+  exprPragmas* = {wLine, wLocks, wNoRewrite}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
     wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
     wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@@ -54,15 +55,15 @@ const
     wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
     wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
     wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
-    wBorrow, wGcSafe}
+    wBorrow, wGcSafe, wExportNims}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
-    wImportCpp, wImportObjC, wError, wGuard}
+    wImportCpp, wImportObjC, wError, wGuard, wBitsize}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
     wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
     wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
-    wGensym, wInject, wCodegenDecl, wGuard, wGoto}
+    wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
-    wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
+    wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
                       wThread, wRaises, wLocks, wTags, wGcSafe}
@@ -276,7 +277,8 @@ proc processNote(c: PContext, n: PNode) =
   if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
       (n.sons[0].kind == nkBracketExpr) and
       (n.sons[0].sons[1].kind == nkIdent) and
-      (n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent):
+      (n.sons[0].sons[0].kind == nkIdent):
+      #and (n.sons[1].kind == nkIdent):
     var nk: TNoteKind
     case whichKeyword(n.sons[0].sons[0].ident)
     of wHint:
@@ -394,6 +396,8 @@ proc processCompile(c: PContext, n: PNode) =
   var found = findFile(s)
   if found == "": found = s
   var trunc = changeFileExt(found, "")
+  if not isAbsolute(found):
+    found = parentDir(n.info.toFullPath) / found
   extccomp.addExternalFileToCompile(found)
   extccomp.addFileToLink(completeCFilePath(trunc, false))
 
@@ -590,278 +594,293 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
                   validPragmas: TSpecialWords): bool =
   var it = n.sons[i]
   var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
-  if key.kind == nkIdent:
-    var userPragma = strTableGet(c.userPragmas, key.ident)
-    if userPragma != nil:
-      inc c.instCounter
-      if c.instCounter > 100:
-        globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
-      pragma(c, sym, userPragma.ast, validPragmas)
-      # ensure the pragma is also remember for generic instantiations in other
-      # modules:
-      n.sons[i] = userPragma.ast
-      dec c.instCounter
-    else:
-      var k = whichKeyword(key.ident)
-      if k in validPragmas:
-        case k
-        of wExportc:
-          makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
-          incl(sym.flags, sfUsed) # avoid wrong hints
-        of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
-        of wImportCompilerProc:
-          processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
-        of wExtern: setExternName(sym, expectStrLit(c, it))
-        of wImmediate:
-          if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
-          else: invalidPragma(it)
-        of wDirty:
-          if sym.kind == skTemplate: incl(sym.flags, sfDirty)
-          else: invalidPragma(it)
-        of wImportCpp:
-          processImportCpp(sym, getOptionalStr(c, it, "$1"))
-        of wImportObjC:
-          processImportObjC(sym, getOptionalStr(c, it, "$1"))
-        of wAlign:
-          if sym.typ == nil: invalidPragma(it)
-          var align = expectIntLit(c, it)
-          if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.align = align.int16
-        of wSize:
-          if sym.typ == nil: invalidPragma(it)
-          var size = expectIntLit(c, it)
-          if not isPowerOfTwo(size) or size <= 0 or size > 8:
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.size = size
-        of wNodecl:
-          noVal(it)
-          incl(sym.loc.flags, lfNoDecl)
-        of wPure, wAsmNoStackFrame:
-          noVal(it)
-          if sym != nil:
-            if k == wPure and sym.kind in routineKinds: invalidPragma(it)
-            else: incl(sym.flags, sfPure)
-        of wVolatile:
-          noVal(it)
-          incl(sym.flags, sfVolatile)
-        of wRegister:
-          noVal(it)
-          incl(sym.flags, sfRegister)
-        of wThreadVar:
-          noVal(it)
-          incl(sym.flags, sfThread)
-        of wDeadCodeElim: pragmaDeadCodeElim(c, it)
-        of wNoForward: pragmaNoForward(c, it)
-        of wMagic: processMagic(c, it, sym)
-        of wCompileTime:
-          noVal(it)
-          incl(sym.flags, sfCompileTime)
-          incl(sym.loc.flags, lfNoDecl)
-        of wGlobal:
-          noVal(it)
-          incl(sym.flags, sfGlobal)
-          incl(sym.flags, sfPure)
-        of wMerge:
-          # only supported for backwards compat, doesn't do anything anymore
-          noVal(it)
-        of wConstructor:
-          noVal(it)
-          incl(sym.flags, sfConstructor)
-        of wHeader:
-          var lib = getLib(c, libHeader, getStrLitNode(c, it))
-          addToLib(lib, sym)
-          incl(sym.flags, sfImportc)
-          incl(sym.loc.flags, lfHeader)
-          incl(sym.loc.flags, lfNoDecl)
-          # implies nodecl, because otherwise header would not make sense
-          if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
-        of wDestructor:
-          sym.flags.incl sfOverriden
-          if sym.name.s.normalize != "destroy":
-            localError(n.info, errGenerated, "destructor has to be named 'destroy'")
-        of wOverride:
-          sym.flags.incl sfOverriden
-        of wNosideeffect:
-          noVal(it)
-          incl(sym.flags, sfNoSideEffect)
-          if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
-        of wSideeffect:
-          noVal(it)
-          incl(sym.flags, sfSideEffect)
-        of wNoreturn:
-          noVal(it)
-          incl(sym.flags, sfNoReturn)
-        of wDynlib:
-          processDynLib(c, it, sym)
-        of wCompilerproc:
-          noVal(it)           # compilerproc may not get a string!
-          if sfFromGeneric notin sym.flags: markCompilerProc(sym)
-        of wProcVar:
-          noVal(it)
-          incl(sym.flags, sfProcvar)
-        of wDeprecated:
-          if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
-          elif sym != nil: incl(sym.flags, sfDeprecated)
-          else: incl(c.module.flags, sfDeprecated)
-        of wVarargs:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfVarargs)
-        of wBorrow:
-          if sym.kind == skType:
-            typeBorrow(sym, it)
-          else:
-            noVal(it)
-            incl(sym.flags, sfBorrow)
-        of wFinal:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfFinal)
-        of wInheritable:
-          noVal(it)
-          if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
-          else: incl(sym.typ.flags, tfInheritable)
-        of wAcyclic:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfAcyclic)
-        of wShallow:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfShallow)
-        of wThread:
-          noVal(it)
-          incl(sym.flags, sfThread)
-          incl(sym.flags, sfProcvar)
-          if sym.typ != nil: incl(sym.typ.flags, tfThread)
-        of wGcSafe:
-          noVal(it)
-          if sym.kind != skType: incl(sym.flags, sfThread)
-          if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
-          else: invalidPragma(it)
-        of wPacked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfPacked)
-        of wHint: message(it.info, hintUser, expectStrLit(c, it))
-        of wWarning: message(it.info, warnUser, expectStrLit(c, it))
-        of wError:
-          if sym != nil and sym.isRoutine:
-            # This is subtle but correct: the error *statement* is only
-            # allowed for top level statements. Seems to be easier than
-            # distinguishing properly between
-            # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
-            noVal(it)
-            incl(sym.flags, sfError)
-          else:
-            localError(it.info, errUser, expectStrLit(c, it))
-        of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
-        of wDefine: processDefine(c, it)
-        of wUndef: processUndef(c, it)
-        of wCompile: processCompile(c, it)
-        of wLink: processCommonLink(c, it, linkNormal)
-        of wLinksys: processCommonLink(c, it, linkSys)
-        of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
-        of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
-        of wBreakpoint: pragmaBreakpoint(c, it)
-        of wWatchPoint: pragmaWatchpoint(c, it)
-        of wPush:
-          processPush(c, n, i + 1)
-          result = true
-        of wPop: processPop(c, it)
-        of wPragma:
-          processPragma(c, n, i)
-          result = true
-        of wDiscardable:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfDiscardable)
-        of wNoInit:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfNoInit)
-        of wCodegenDecl: processCodegenDecl(c, it, sym)
-        of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
-           wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-           wLinedir, wStacktrace, wLinetrace, wOptimization,
-           wCallconv,
-           wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
-           wPatterns:
-          if processOption(c, it):
-            # calling conventions (boring...):
-            localError(it.info, errOptionExpected)
-        of FirstCallConv..LastCallConv:
-          assert(sym != nil)
-          if sym.typ == nil: invalidPragma(it)
-          else: sym.typ.callConv = wordToCallConv(k)
-        of wEmit: pragmaEmit(c, it)
-        of wUnroll: pragmaUnroll(c, it)
-        of wLinearScanEnd, wComputedGoto: noVal(it)
-        of wEffects:
-          # is later processed in effect analysis:
-          noVal(it)
-        of wIncompleteStruct:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfIncompleteStruct)
-        of wUnchecked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUncheckedArray)
-        of wUnion:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUnion)
-        of wRequiresInit:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfNeedsInit)
-        of wByRef:
-          noVal(it)
-          if sym == nil or sym.typ == nil:
-            if processOption(c, it): localError(it.info, errOptionExpected)
-          else:
-            incl(sym.typ.flags, tfByRef)
-        of wByCopy:
-          noVal(it)
-          if sym.kind != skType or sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfByCopy)
-        of wInject, wGensym:
-          # We check for errors, but do nothing with these pragmas otherwise
-          # as they are handled directly in 'evalTemplate'.
-          noVal(it)
-          if sym == nil: invalidPragma(it)
-        of wLine: pragmaLine(c, it)
-        of wRaises, wTags: pragmaRaisesOrTags(c, it)
-        of wLocks:
-          if sym == nil: pragmaLockStmt(c, it)
-          elif sym.typ == nil: invalidPragma(it)
-          else: sym.typ.lockLevel = pragmaLocks(c, it)
-        of wGuard:
-          if sym == nil or sym.kind notin {skVar, skLet, skField}:
-            invalidPragma(it)
-          else:
-            sym.guard = pragmaGuard(c, it, sym.kind)
-        of wGoto:
-          if sym == nil or sym.kind notin {skVar, skLet}:
-            invalidPragma(it)
-          else:
-            sym.flags.incl sfGoto
-        of wInjectStmt:
-          if it.kind != nkExprColonExpr:
-            localError(it.info, errExprExpected)
-          else:
-            it.sons[1] = c.semExpr(c, it.sons[1])
-        of wExperimental:
+  if key.kind == nkBracketExpr:
+    processNote(c, it)
+    return
+  let ident = considerQuotedIdent(key)
+  var userPragma = strTableGet(c.userPragmas, ident)
+  if userPragma != nil:
+    inc c.instCounter
+    if c.instCounter > 100:
+      globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
+    pragma(c, sym, userPragma.ast, validPragmas)
+    # ensure the pragma is also remember for generic instantiations in other
+    # modules:
+    n.sons[i] = userPragma.ast
+    dec c.instCounter
+  else:
+    var k = whichKeyword(ident)
+    if k in validPragmas:
+      case k
+      of wExportc:
+        makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
+        incl(sym.flags, sfUsed) # avoid wrong hints
+      of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
+      of wImportCompilerProc:
+        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
+      of wExtern: setExternName(sym, expectStrLit(c, it))
+      of wImmediate:
+        if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
+        else: invalidPragma(it)
+      of wDirty:
+        if sym.kind == skTemplate: incl(sym.flags, sfDirty)
+        else: invalidPragma(it)
+      of wImportCpp:
+        processImportCpp(sym, getOptionalStr(c, it, "$1"))
+      of wImportObjC:
+        processImportObjC(sym, getOptionalStr(c, it, "$1"))
+      of wAlign:
+        if sym.typ == nil: invalidPragma(it)
+        var align = expectIntLit(c, it)
+        if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.align = align.int16
+      of wSize:
+        if sym.typ == nil: invalidPragma(it)
+        var size = expectIntLit(c, it)
+        if not isPowerOfTwo(size) or size <= 0 or size > 8:
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.size = size
+      of wNodecl:
+        noVal(it)
+        incl(sym.loc.flags, lfNoDecl)
+      of wPure, wAsmNoStackFrame:
+        noVal(it)
+        if sym != nil:
+          if k == wPure and sym.kind in routineKinds: invalidPragma(it)
+          else: incl(sym.flags, sfPure)
+      of wVolatile:
+        noVal(it)
+        incl(sym.flags, sfVolatile)
+      of wRegister:
+        noVal(it)
+        incl(sym.flags, sfRegister)
+      of wThreadVar:
+        noVal(it)
+        incl(sym.flags, sfThread)
+      of wDeadCodeElim: pragmaDeadCodeElim(c, it)
+      of wNoForward: pragmaNoForward(c, it)
+      of wMagic: processMagic(c, it, sym)
+      of wCompileTime:
+        noVal(it)
+        incl(sym.flags, sfCompileTime)
+        incl(sym.loc.flags, lfNoDecl)
+      of wGlobal:
+        noVal(it)
+        incl(sym.flags, sfGlobal)
+        incl(sym.flags, sfPure)
+      of wMerge:
+        # only supported for backwards compat, doesn't do anything anymore
+        noVal(it)
+      of wConstructor:
+        noVal(it)
+        incl(sym.flags, sfConstructor)
+      of wHeader:
+        var lib = getLib(c, libHeader, getStrLitNode(c, it))
+        addToLib(lib, sym)
+        incl(sym.flags, sfImportc)
+        incl(sym.loc.flags, lfHeader)
+        incl(sym.loc.flags, lfNoDecl)
+        # implies nodecl, because otherwise header would not make sense
+        if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
+      of wDestructor:
+        sym.flags.incl sfOverriden
+        if sym.name.s.normalize != "destroy":
+          localError(n.info, errGenerated, "destructor has to be named 'destroy'")
+      of wOverride:
+        sym.flags.incl sfOverriden
+      of wNosideeffect:
+        noVal(it)
+        incl(sym.flags, sfNoSideEffect)
+        if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
+      of wSideeffect:
+        noVal(it)
+        incl(sym.flags, sfSideEffect)
+      of wNoreturn:
+        noVal(it)
+        incl(sym.flags, sfNoReturn)
+      of wDynlib:
+        processDynLib(c, it, sym)
+      of wCompilerproc:
+        noVal(it)           # compilerproc may not get a string!
+        if sfFromGeneric notin sym.flags: markCompilerProc(sym)
+      of wProcVar:
+        noVal(it)
+        incl(sym.flags, sfProcvar)
+      of wDeprecated:
+        if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
+        elif sym != nil: incl(sym.flags, sfDeprecated)
+        else: incl(c.module.flags, sfDeprecated)
+      of wVarargs:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfVarargs)
+      of wBorrow:
+        if sym.kind == skType:
+          typeBorrow(sym, it)
+        else:
           noVal(it)
-          if isTopLevel(c):
-            c.module.flags.incl sfExperimental
-          else:
-            localError(it.info, "'experimental' pragma only valid as toplevel statement")
+          incl(sym.flags, sfBorrow)
+      of wFinal:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfFinal)
+      of wInheritable:
+        noVal(it)
+        if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
+        else: incl(sym.typ.flags, tfInheritable)
+      of wAcyclic:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfAcyclic)
+      of wShallow:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfShallow)
+      of wThread:
+        noVal(it)
+        incl(sym.flags, sfThread)
+        incl(sym.flags, sfProcvar)
+        if sym.typ != nil: incl(sym.typ.flags, tfThread)
+      of wGcSafe:
+        noVal(it)
+        if sym.kind != skType: incl(sym.flags, sfThread)
+        if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
         else: invalidPragma(it)
+      of wPacked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfPacked)
+      of wHint: message(it.info, hintUser, expectStrLit(c, it))
+      of wWarning: message(it.info, warnUser, expectStrLit(c, it))
+      of wError:
+        if sym != nil and sym.isRoutine:
+          # This is subtle but correct: the error *statement* is only
+          # allowed for top level statements. Seems to be easier than
+          # distinguishing properly between
+          # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
+          noVal(it)
+          incl(sym.flags, sfError)
+        else:
+          localError(it.info, errUser, expectStrLit(c, it))
+      of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
+      of wDefine: processDefine(c, it)
+      of wUndef: processUndef(c, it)
+      of wCompile: processCompile(c, it)
+      of wLink: processCommonLink(c, it, linkNormal)
+      of wLinksys: processCommonLink(c, it, linkSys)
+      of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
+      of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
+      of wBreakpoint: pragmaBreakpoint(c, it)
+      of wWatchPoint: pragmaWatchpoint(c, it)
+      of wPush:
+        processPush(c, n, i + 1)
+        result = true
+      of wPop: processPop(c, it)
+      of wPragma:
+        processPragma(c, n, i)
+        result = true
+      of wDiscardable:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfDiscardable)
+      of wNoInit:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfNoInit)
+      of wCodegenDecl: processCodegenDecl(c, it, sym)
+      of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
+         wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
+         wLinedir, wStacktrace, wLinetrace, wOptimization,
+         wCallconv,
+         wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
+         wPatterns:
+        if processOption(c, it):
+          # calling conventions (boring...):
+          localError(it.info, errOptionExpected)
+      of FirstCallConv..LastCallConv:
+        assert(sym != nil)
+        if sym.typ == nil: invalidPragma(it)
+        else: sym.typ.callConv = wordToCallConv(k)
+      of wEmit: pragmaEmit(c, it)
+      of wUnroll: pragmaUnroll(c, it)
+      of wLinearScanEnd, wComputedGoto: noVal(it)
+      of wEffects:
+        # is later processed in effect analysis:
+        noVal(it)
+      of wIncompleteStruct:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfIncompleteStruct)
+      of wUnchecked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUncheckedArray)
+      of wUnion:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUnion)
+      of wRequiresInit:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfNeedsInit)
+      of wByRef:
+        noVal(it)
+        if sym == nil or sym.typ == nil:
+          if processOption(c, it): localError(it.info, errOptionExpected)
+        else:
+          incl(sym.typ.flags, tfByRef)
+      of wByCopy:
+        noVal(it)
+        if sym.kind != skType or sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfByCopy)
+      of wInject, wGensym:
+        # We check for errors, but do nothing with these pragmas otherwise
+        # as they are handled directly in 'evalTemplate'.
+        noVal(it)
+        if sym == nil: invalidPragma(it)
+      of wLine: pragmaLine(c, it)
+      of wRaises, wTags: pragmaRaisesOrTags(c, it)
+      of wLocks:
+        if sym == nil: pragmaLockStmt(c, it)
+        elif sym.typ == nil: invalidPragma(it)
+        else: sym.typ.lockLevel = pragmaLocks(c, it)
+      of wBitsize:
+        if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr:
+          invalidPragma(it)
+        else:
+          sym.bitsize = expectIntLit(c, it)
+      of wGuard:
+        if sym == nil or sym.kind notin {skVar, skLet, skField}:
+          invalidPragma(it)
+        else:
+          sym.guard = pragmaGuard(c, it, sym.kind)
+      of wGoto:
+        if sym == nil or sym.kind notin {skVar, skLet}:
+          invalidPragma(it)
+        else:
+          sym.flags.incl sfGoto
+      of wExportNims:
+        if sym == nil: invalidPragma(it)
+        else: magicsys.registerNimScriptSymbol(sym)
+      of wInjectStmt:
+        if it.kind != nkExprColonExpr:
+          localError(it.info, errExprExpected)
+        else:
+          it.sons[1] = c.semExpr(c, it.sons[1])
+      of wExperimental:
+        noVal(it)
+        if isTopLevel(c):
+          c.module.flags.incl sfExperimental
+        else:
+          localError(it.info, "'experimental' pragma only valid as toplevel statement")
+      of wNoRewrite:
+        noVal(it)
+      of wBase:
+        noVal(it)
+        sym.flags.incl sfBase
       else: invalidPragma(it)
-  else: processNote(c, it)
+    else: invalidPragma(it)
 
 proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
                       validPragmas: TSpecialWords) =
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 473965a3d..523ea2e2f 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
         discard
 
     result = nextIdentIter(it, scope.symbols)
-  
+
   return nil
 
 proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
@@ -99,17 +99,17 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
       debug fn.typ
       debug if result != nil: result.typ else: nil
       debug if old != nil: old.typ else: nil
- 
+
 when false:
-  proc paramsFitBorrow(child, parent: PNode): bool = 
+  proc paramsFitBorrow(child, parent: PNode): bool =
     var length = sonsLen(child)
     result = false
-    if length == sonsLen(parent): 
-      for i in countup(1, length - 1): 
+    if length == sonsLen(parent):
+      for i in countup(1, length - 1):
         var m = child.sons[i].sym
         var n = parent.sons[i].sym
         assert((m.kind == skParam) and (n.kind == skParam))
-        if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return 
+        if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
       if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
                           dcEqOrDistinctOf): return
       result = true
@@ -120,10 +120,10 @@ when false:
     var it: TIdentIter
     for scope in walkScopes(startScope):
       result = initIdentIter(it, scope.symbols, fn.Name)
-      while result != nil: 
+      while result != nil:
         # watchout! result must not be the same as fn!
-        if (result.Kind == fn.kind) and (result.id != fn.id): 
-          if equalGenericParams(result.ast.sons[genericParamsPos], 
-                                fn.ast.sons[genericParamsPos]): 
-            if paramsFitBorrow(fn.typ.n, result.typ.n): return 
+        if (result.Kind == fn.kind) and (result.id != fn.id):
+          if equalGenericParams(result.ast.sons[genericParamsPos],
+                                fn.ast.sons[genericParamsPos]):
+            if paramsFitBorrow(fn.typ.n, result.typ.n): return
         result = NextIdentIter(it, scope.symbols)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index ffdb60696..7cd8e25ee 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -31,6 +31,7 @@ type
     buf*: string
     pendingNL*: int        # negative if not active; else contains the
                            # indentation value
+    pendingWhitespace: int
     comStack*: seq[PNode]  # comment stack
     flags*: TRenderFlags
     checkAnon: bool        # we're in a context that can contain sfAnon
@@ -83,6 +84,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
   g.buf = ""
   g.flags = renderFlags
   g.pendingNL = -1
+  g.pendingWhitespace = -1
   g.checkAnon = false
 
 proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
@@ -97,12 +99,21 @@ proc addPendingNL(g: var TSrcGen) =
     addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
     g.lineLen = g.pendingNL
     g.pendingNL = - 1
+    g.pendingWhitespace = -1
+  elif g.pendingWhitespace >= 0:
+    addTok(g, tkSpaces, spaces(g.pendingWhitespace))
+    g.pendingWhitespace = -1
 
 proc putNL(g: var TSrcGen, indent: int) =
   if g.pendingNL >= 0: addPendingNL(g)
   else: addTok(g, tkSpaces, "\n")
   g.pendingNL = indent
   g.lineLen = indent
+  g.pendingWhitespace = -1
+
+proc previousNL(g: TSrcGen): bool =
+  result = g.pendingNL >= 0 or (g.tokens.len > 0 and
+                                g.tokens[^1].kind == tkSpaces)
 
 proc putNL(g: var TSrcGen) =
   putNL(g, g.indent)
@@ -127,10 +138,13 @@ proc dedent(g: var TSrcGen) =
     dec(g.lineLen, IndentWidth)
 
 proc put(g: var TSrcGen, kind: TTokType, s: string) =
-  addPendingNL(g)
-  if len(s) > 0:
-    addTok(g, kind, s)
-    inc(g.lineLen, len(s))
+  if kind != tkSpaces:
+    addPendingNL(g)
+    if len(s) > 0:
+      addTok(g, kind, s)
+      inc(g.lineLen, len(s))
+  else:
+    g.pendingWhitespace = s.len
 
 proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) =
   # use this for tokens over multiple lines.
@@ -767,7 +781,8 @@ proc gasm(g: var TSrcGen, n: PNode) =
   putWithSpace(g, tkAsm, "asm")
   gsub(g, n.sons[0])
   gcoms(g)
-  gsub(g, n.sons[1])
+  if n.sons.len > 1:
+    gsub(g, n.sons[1])
 
 proc gident(g: var TSrcGen, n: PNode) =
   if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
@@ -1195,6 +1210,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     if renderNoPragmas notin g.flags:
       if g.inPragma <= 0:
         inc g.inPragma
+        #if not previousNL(g):
         put(g, tkSpaces, Space)
         put(g, tkCurlyDotLe, "{.")
         gcomma(g, n, emptyContext)
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index e92f7ecfa..2a85c8975 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -12,15 +12,15 @@
 # Reading and writing binary files are really hard to debug. Therefore we use
 # a "creative" text/binary hybrid format. ROD-files are more efficient
 # to process because symbols can be loaded on demand.
-# 
+#
 # A ROD file consists of:
 #
 #  - a header:
 #    NIM:$fileversion\n
 #  - the module's id (even if the module changed, its ID will not!):
 #    ID:Ax3\n
-#  - CRC value of this module:
-#    CRC:CRC-val\n
+#  - HASH value of this module:
+#    HASH:HASH-val\n
 #  - a section containing the compiler options and defines this
 #    module has been compiled with:
 #    OPTIONS:options\n
@@ -33,7 +33,7 @@
 #    )
 #  - an include file dependency section:
 #    INCLUDES(
-#    <fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section!
+#    <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section!
 #    )
 #  - a module dependency section:
 #    DEPS: <fileidx> <fileidx>\n
@@ -44,7 +44,7 @@
 #    )
 #  - a compiler proc section:
 #    COMPILERPROCS(
-#    identifier1 id\n # id is the symbol's id    
+#    identifier1 id\n # id is the symbol's id
 #    )
 #  - an index consisting of (ID, linenumber)-pairs:
 #    INDEX(
@@ -52,7 +52,7 @@
 #    id-diff idx-diff\n
 #    )
 #
-#    Since the whole index has to be read in advance, we compress it by 
+#    Since the whole index has to be read in advance, we compress it by
 #    storing the integer differences to the last entry instead of using the
 #    real numbers.
 #
@@ -88,30 +88,30 @@
 #    by using a mem'mapped file.
 #
 
-import 
-  os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, 
-  ropes, idents, crc, idgen, types, rodutils, memfiles
+import
+  os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
+  ropes, idents, securehash, idgen, types, rodutils, memfiles, tables
 
-type 
+type
   TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
     rrEmpty,                  # dependencies not yet computed
     rrNone,                   # no need to recompile
     rrRodDoesNotExist,        # rod file does not exist
     rrRodInvalid,             # rod file is invalid
-    rrCrcChange,              # file has been edited since last recompilation
+    rrHashChange,             # file has been edited since last recompilation
     rrDefines,                # defines have changed
     rrOptions,                # options have changed
     rrInclDeps,               # an include has changed
     rrModDeps                 # a module this module depends on has been changed
 
-const 
-  reasonToFrmt*: array[TReasonForRecompile, string] = ["", 
-    "no need to recompile: $1", "symbol file for $1 does not exist", 
-    "symbol file for $1 has the wrong version", 
-    "file edited since last compilation: $1", 
-    "list of conditional symbols changed for: $1", 
-    "list of options changed for: $1", 
-    "an include file edited: $1", 
+const
+  reasonToFrmt*: array[TReasonForRecompile, string] = ["",
+    "no need to recompile: $1", "symbol file for $1 does not exist",
+    "symbol file for $1 has the wrong version",
+    "file edited since last compilation: $1",
+    "list of conditional symbols changed for: $1",
+    "list of options changed for: $1",
+    "an include file edited: $1",
     "a module $1 depends on has changed"]
 
 type
@@ -120,7 +120,7 @@ type
     tab*: TIITable
     r*: string                # writers use this
     offset*: int              # readers use this
-  
+
   TRodReader* = object of RootObj
     pos: int                 # position; used for parsing
     s: cstring               # mmap'ed file contents
@@ -136,13 +136,13 @@ type
     readerIndex: int
     line: int            # only used for debugging, but is always in the code
     moduleID: int
-    syms: TIdTable       # already processed symbols
+    syms: Table[int, PSym]       # already processed symbols
     memfile: MemFile     # unfortunately there is no point in time where we
                          # can close this! XXX
     methods*: TSymSeq
     origFile: string
     inViewMode: bool
-  
+
   PRodReader* = ref TRodReader
 
 var rodCompilerprocs*: TStrTable
@@ -161,16 +161,16 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
   # `info` is only used for debugging purposes
 proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
 
-proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = 
-  if r.s[r.pos] == '?': 
+proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
+  if r.s[r.pos] == '?':
     inc(r.pos)
     if r.s[r.pos] == ',': info.col = -1'i16
     else: info.col = int16(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] == ',': 
+    if r.s[r.pos] == ',':
       inc(r.pos)
       if r.s[r.pos] == ',': info.line = -1'i16
       else: info.line = int16(decodeVInt(r.s, r.pos))
-      if r.s[r.pos] == ',': 
+      if r.s[r.pos] == ',':
         inc(r.pos)
         info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
 
@@ -188,56 +188,56 @@ proc skipNode(r: PRodReader) =
     inc pos
   r.pos = pos+1 # skip ')'
 
-proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, 
-                        belongsTo: PSym): PNode = 
+proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
+                        belongsTo: PSym): PNode =
   result = nil
-  if r.s[r.pos] == '(': 
+  if r.s[r.pos] == '(':
     inc(r.pos)
-    if r.s[r.pos] == ')': 
+    if r.s[r.pos] == ')':
       inc(r.pos)
       return                  # nil node
     result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
     decodeLineInfo(r, result.info)
-    if r.s[r.pos] == '$': 
+    if r.s[r.pos] == '$':
       inc(r.pos)
       result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
-    if r.s[r.pos] == '^': 
+    if r.s[r.pos] == '^':
       inc(r.pos)
       var id = decodeVInt(r.s, r.pos)
       result.typ = rrGetType(r, id, result.info)
     case result.kind
-    of nkCharLit..nkInt64Lit: 
-      if r.s[r.pos] == '!': 
+    of nkCharLit..nkInt64Lit:
+      if r.s[r.pos] == '!':
         inc(r.pos)
         result.intVal = decodeVBiggestInt(r.s, r.pos)
-    of nkFloatLit..nkFloat64Lit: 
-      if r.s[r.pos] == '!': 
+    of nkFloatLit..nkFloat64Lit:
+      if r.s[r.pos] == '!':
         inc(r.pos)
         var fl = decodeStr(r.s, r.pos)
         result.floatVal = parseFloat(fl)
-    of nkStrLit..nkTripleStrLit: 
-      if r.s[r.pos] == '!': 
+    of nkStrLit..nkTripleStrLit:
+      if r.s[r.pos] == '!':
         inc(r.pos)
         result.strVal = decodeStr(r.s, r.pos)
-      else: 
+      else:
         result.strVal = ""    # BUGFIX
-    of nkIdent: 
-      if r.s[r.pos] == '!': 
+    of nkIdent:
+      if r.s[r.pos] == '!':
         inc(r.pos)
         var fl = decodeStr(r.s, r.pos)
         result.ident = getIdent(fl)
-      else: 
+      else:
         internalError(result.info, "decodeNode: nkIdent")
-    of nkSym: 
-      if r.s[r.pos] == '!': 
+    of nkSym:
+      if r.s[r.pos] == '!':
         inc(r.pos)
         var id = decodeVInt(r.s, r.pos)
         result.sym = rrGetSym(r, id, result.info)
-      else: 
+      else:
         internalError(result.info, "decodeNode: nkSym")
     else:
       var i = 0
-      while r.s[r.pos] != ')': 
+      while r.s[r.pos] != ')':
         if belongsTo != nil and i == bodyPos:
           addSonNilAllowed(result, nil)
           belongsTo.offset = r.pos
@@ -252,93 +252,93 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
 
 proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
   result = decodeNodeLazyBody(r, fInfo, nil)
-  
-proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = 
-  if r.s[r.pos] == '<': 
+
+proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
+  if r.s[r.pos] == '<':
     inc(r.pos)
-    if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: 
+    if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
       loc.k = TLocKind(decodeVInt(r.s, r.pos))
-    else: 
+    else:
       loc.k = low(loc.k)
-    if r.s[r.pos] == '*': 
+    if r.s[r.pos] == '*':
       inc(r.pos)
       loc.s = TStorageLoc(decodeVInt(r.s, r.pos))
-    else: 
+    else:
       loc.s = low(loc.s)
-    if r.s[r.pos] == '$': 
+    if r.s[r.pos] == '$':
       inc(r.pos)
       loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
-    else: 
+    else:
       loc.flags = {}
-    if r.s[r.pos] == '^': 
+    if r.s[r.pos] == '^':
       inc(r.pos)
       loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info)
-    else: 
+    else:
       loc.t = nil
-    if r.s[r.pos] == '!': 
+    if r.s[r.pos] == '!':
       inc(r.pos)
       loc.r = rope(decodeStr(r.s, r.pos))
-    else: 
+    else:
       loc.r = nil
     if r.s[r.pos] == '>': inc(r.pos)
     else: internalError(info, "decodeLoc " & r.s[r.pos])
-  
-proc decodeType(r: PRodReader, info: TLineInfo): PType = 
+
+proc decodeType(r: PRodReader, info: TLineInfo): PType =
   result = nil
-  if r.s[r.pos] == '[': 
+  if r.s[r.pos] == '[':
     inc(r.pos)
-    if r.s[r.pos] == ']': 
+    if r.s[r.pos] == ']':
       inc(r.pos)
       return                  # nil type
   new(result)
   result.kind = TTypeKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+': 
+  if r.s[r.pos] == '+':
     inc(r.pos)
     result.id = decodeVInt(r.s, r.pos)
     setId(result.id)
     if debugIds: registerID(result)
-  else: 
+  else:
     internalError(info, "decodeType: no id")
   # here this also avoids endless recursion for recursive type
-  idTablePut(gTypeTable, result, result) 
+  idTablePut(gTypeTable, result, result)
   if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
-  if r.s[r.pos] == '$': 
+  if r.s[r.pos] == '$':
     inc(r.pos)
     result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '?': 
+  if r.s[r.pos] == '?':
     inc(r.pos)
     result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '*': 
+  if r.s[r.pos] == '*':
     inc(r.pos)
     result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '&': 
+  if r.s[r.pos] == '&':
     inc(r.pos)
     result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '/': 
+  if r.s[r.pos] == '/':
     inc(r.pos)
     result.size = decodeVInt(r.s, r.pos)
-  else: 
+  else:
     result.size = - 1
-  if r.s[r.pos] == '=': 
+  if r.s[r.pos] == '=':
     inc(r.pos)
     result.align = decodeVInt(r.s, r.pos).int16
-  else: 
+  else:
     result.align = 2
   decodeLoc(r, result.loc, info)
-  while r.s[r.pos] == '^': 
+  while r.s[r.pos] == '^':
     inc(r.pos)
-    if r.s[r.pos] == '(': 
+    if r.s[r.pos] == '(':
       inc(r.pos)
       if r.s[r.pos] == ')': inc(r.pos)
       else: internalError(info, "decodeType ^(" & r.s[r.pos])
       rawAddSon(result, nil)
-    else: 
+    else:
       var d = decodeVInt(r.s, r.pos)
       rawAddSon(result, rrGetType(r, d, info))
 
-proc decodeLib(r: PRodReader, info: TLineInfo): PLib = 
+proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
   result = nil
-  if r.s[r.pos] == '|': 
+  if r.s[r.pos] == '|':
     new(result)
     inc(r.pos)
     result.kind = TLibKind(decodeVInt(r.s, r.pos))
@@ -349,34 +349,34 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
     inc(r.pos)
     result.path = decodeNode(r, info)
 
-proc decodeSym(r: PRodReader, info: TLineInfo): PSym = 
-  var 
+proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
+  var
     id: int
     ident: PIdent
   result = nil
-  if r.s[r.pos] == '{': 
+  if r.s[r.pos] == '{':
     inc(r.pos)
-    if r.s[r.pos] == '}': 
+    if r.s[r.pos] == '}':
       inc(r.pos)
       return                  # nil sym
   var k = TSymKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+': 
+  if r.s[r.pos] == '+':
     inc(r.pos)
     id = decodeVInt(r.s, r.pos)
     setId(id)
   else:
     internalError(info, "decodeSym: no id")
-  if r.s[r.pos] == '&': 
+  if r.s[r.pos] == '&':
     inc(r.pos)
     ident = getIdent(decodeStr(r.s, r.pos))
   else:
     internalError(info, "decodeSym: no ident")
   #echo "decoding: {", ident.s
-  result = PSym(idTableGet(r.syms, id))
-  if result == nil: 
+  result = r.syms.getOrDefault(id)
+  if result == nil:
     new(result)
     result.id = id
-    idTablePut(r.syms, result, result)
+    r.syms[result.id] = result
     if debugIds: registerID(result)
   elif result.id != id:
     internalError(info, "decodeSym: wrong id")
@@ -388,35 +388,35 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
     result.id = id
   result.kind = k
   result.name = ident         # read the rest of the symbol description:
-  if r.s[r.pos] == '^': 
+  if r.s[r.pos] == '^':
     inc(r.pos)
     result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
   decodeLineInfo(r, result.info)
-  if r.s[r.pos] == '*': 
+  if r.s[r.pos] == '*':
     inc(r.pos)
     result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-  if r.s[r.pos] == '$': 
+  if r.s[r.pos] == '$':
     inc(r.pos)
     result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '@': 
+  if r.s[r.pos] == '@':
     inc(r.pos)
     result.magic = TMagic(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '!': 
+  if r.s[r.pos] == '!':
     inc(r.pos)
     result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-  else: 
+  else:
     result.options = r.options
-  if r.s[r.pos] == '%': 
+  if r.s[r.pos] == '%':
     inc(r.pos)
     result.position = decodeVInt(r.s, r.pos)
   elif result.kind notin routineKinds + {skModule}:
     result.position = 0
     # this may have been misused as reader index! But we still
     # need it for routines as the body is loaded lazily.
-  if r.s[r.pos] == '`': 
+  if r.s[r.pos] == '`':
     inc(r.pos)
     result.offset = decodeVInt(r.s, r.pos)
-  else: 
+  else:
     result.offset = - 1
   decodeLoc(r, result.loc, result.info)
   result.annex = decodeLib(r, info)
@@ -433,35 +433,35 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
       result.ast = decodeNode(r, result.info)
   #echo "decoded: ", ident.s, "}"
 
-proc skipSection(r: PRodReader) = 
-  if r.s[r.pos] == ':': 
+proc skipSection(r: PRodReader) =
+  if r.s[r.pos] == ':':
     while r.s[r.pos] > '\x0A': inc(r.pos)
-  elif r.s[r.pos] == '(': 
+  elif r.s[r.pos] == '(':
     var c = 0                 # count () pairs
     inc(r.pos)
-    while true: 
+    while true:
       case r.s[r.pos]
       of '\x0A': inc(r.line)
       of '(': inc(c)
-      of ')': 
-        if c == 0: 
+      of ')':
+        if c == 0:
           inc(r.pos)
-          break 
-        elif c > 0: 
+          break
+        elif c > 0:
           dec(c)
       of '\0': break          # end of file
       else: discard
       inc(r.pos)
-  else: 
+  else:
     internalError("skipSection " & $r.line)
-  
-proc rdWord(r: PRodReader): string = 
+
+proc rdWord(r: PRodReader): string =
   result = ""
-  while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}: 
+  while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
     add(result, r.s[r.pos])
     inc(r.pos)
 
-proc newStub(r: PRodReader, name: string, id: int): PSym = 
+proc newStub(r: PRodReader, name: string, id: int): PSym =
   new(result)
   result.kind = skStub
   result.id = id
@@ -469,11 +469,11 @@ proc newStub(r: PRodReader, name: string, id: int): PSym =
   result.position = r.readerIndex
   setId(id)                   #MessageOut(result.name.s);
   if debugIds: registerID(result)
-  
-proc processInterf(r: PRodReader, module: PSym) = 
+
+proc processInterf(r: PRodReader, module: PSym) =
   if r.interfIdx == 0: internalError("processInterf")
   r.pos = r.interfIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): 
+  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
     var w = decodeStr(r.s, r.pos)
     inc(r.pos)
     var key = decodeVInt(r.s, r.pos)
@@ -481,30 +481,30 @@ proc processInterf(r: PRodReader, module: PSym) =
     var s = newStub(r, w, key)
     s.owner = module
     strTableAdd(module.tab, s)
-    idTablePut(r.syms, s, s)
+    r.syms[s.id] = s
 
-proc processCompilerProcs(r: PRodReader, module: PSym) = 
+proc processCompilerProcs(r: PRodReader, module: PSym) =
   if r.compilerProcsIdx == 0: internalError("processCompilerProcs")
   r.pos = r.compilerProcsIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): 
+  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
     var w = decodeStr(r.s, r.pos)
     inc(r.pos)
     var key = decodeVInt(r.s, r.pos)
     inc(r.pos)                # #10
-    var s = PSym(idTableGet(r.syms, key))
-    if s == nil: 
+    var s = r.syms.getOrDefault(key)
+    if s == nil:
       s = newStub(r, w, key)
       s.owner = module
-      idTablePut(r.syms, s, s)
+      r.syms[s.id] = s
     strTableAdd(rodCompilerprocs, s)
 
-proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = 
+proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
   var key, val, tmp: int
   inc(r.pos, 2)               # skip "(\10"
   inc(r.line)
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): 
+  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
     tmp = decodeVInt(r.s, r.pos)
-    if r.s[r.pos] == ' ': 
+    if r.s[r.pos] == ' ':
       inc(r.pos)
       key = idx.lastIdxKey + tmp
       val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
@@ -516,11 +516,11 @@ proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
     idx.lastIdxKey = key
     idx.lastIdxVal = val
     setId(key)                # ensure that this id will not be used
-    if r.s[r.pos] == '\x0A': 
+    if r.s[r.pos] == '\x0A':
       inc(r.pos)
       inc(r.line)
   if r.s[r.pos] == ')': inc(r.pos)
-  
+
 proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
   if old == new: return false
   # we use a 'case' statement without 'else' so that addition of a
@@ -532,32 +532,34 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
                cmdInteractive}:
       return false
   of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
-      cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, 
+      cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
       cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun:
     discard
   # else: trigger recompilation:
   result = true
-  
-proc processRodFile(r: PRodReader, crc: TCrc32) = 
-  var 
+
+proc processRodFile(r: PRodReader, hash: SecureHash) =
+  var
     w: string
-    d, inclCrc: int
-  while r.s[r.pos] != '\0': 
+    d: int
+  var inclHash: SecureHash
+  while r.s[r.pos] != '\0':
     var section = rdWord(r)
-    if r.reason != rrNone: 
+    if r.reason != rrNone:
       break                   # no need to process this file further
-    case section 
-    of "CRC": 
+    case section
+    of "HASH":
       inc(r.pos)              # skip ':'
-      if int(crc) != decodeVInt(r.s, r.pos): r.reason = rrCrcChange
-    of "ID": 
+      if hash != parseSecureHash(decodeStr(r.s, r.pos)):
+        r.reason = rrHashChange
+    of "ID":
       inc(r.pos)              # skip ':'
       r.moduleID = decodeVInt(r.s, r.pos)
       setId(r.moduleID)
     of "ORIGFILE":
       inc(r.pos)
       r.origFile = decodeStr(r.s, r.pos)
-    of "OPTIONS": 
+    of "OPTIONS":
       inc(r.pos)              # skip ':'
       r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
       if options.gOptions != r.options: r.reason = rrOptions
@@ -572,14 +574,14 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
     of "DEFINES":
       inc(r.pos)              # skip ':'
       d = 0
-      while r.s[r.pos] > '\x0A': 
+      while r.s[r.pos] > '\x0A':
         w = decodeStr(r.s, r.pos)
         inc(d)
-        if not condsyms.isDefined(getIdent(w)): 
+        if not condsyms.isDefined(getIdent(w)):
           r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
         if r.s[r.pos] == ' ': inc(r.pos)
       if (d != countDefinedSymbols()): r.reason = rrDefines
-    of "FILES": 
+    of "FILES":
       inc(r.pos, 2)           # skip "(\10"
       inc(r.line)
       while r.s[r.pos] != ')':
@@ -590,17 +592,17 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
         inc(r.pos)            # skip #10
         inc(r.line)
       if r.s[r.pos] == ')': inc(r.pos)
-    of "INCLUDES": 
+    of "INCLUDES":
       inc(r.pos, 2)           # skip "(\10"
       inc(r.line)
-      while r.s[r.pos] != ')': 
+      while r.s[r.pos] != ')':
         w = r.files[decodeVInt(r.s, r.pos)].toFullPath
         inc(r.pos)            # skip ' '
-        inclCrc = decodeVInt(r.s, r.pos)
-        if r.reason == rrNone: 
-          if not existsFile(w) or (inclCrc != int(crcFromFile(w))): 
+        inclHash = parseSecureHash(decodeStr(r.s, r.pos))
+        if r.reason == rrNone:
+          if not existsFile(w) or (inclHash != secureHashFile(w)):
             r.reason = rrInclDeps
-        if r.s[r.pos] == '\x0A': 
+        if r.s[r.pos] == '\x0A':
           inc(r.pos)
           inc(r.line)
       if r.s[r.pos] == ')': inc(r.pos)
@@ -609,28 +611,28 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
       while r.s[r.pos] > '\x0A':
         r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))])
         if r.s[r.pos] == ' ': inc(r.pos)
-    of "INTERF": 
+    of "INTERF":
       r.interfIdx = r.pos + 2
       skipSection(r)
-    of "COMPILERPROCS": 
+    of "COMPILERPROCS":
       r.compilerProcsIdx = r.pos + 2
       skipSection(r)
-    of "INDEX": 
+    of "INDEX":
       processIndex(r, r.index)
-    of "IMPORTS": 
+    of "IMPORTS":
       processIndex(r, r.imports)
-    of "CONVERTERS": 
+    of "CONVERTERS":
       r.convertersIdx = r.pos + 1
       skipSection(r)
     of "METHODS":
       r.methodsIdx = r.pos + 1
       skipSection(r)
-    of "DATA": 
+    of "DATA":
       r.dataIdx = r.pos + 2 # "(\10"
       # We do not read the DATA section here! We read the needed objects on
       # demand. And the DATA section comes last in the file, so we stop here:
       break
-    of "INIT": 
+    of "INIT":
       r.initIdx = r.pos + 2   # "(\10"
       skipSection(r)
     else:
@@ -639,7 +641,7 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
       #MsgWriteln("skipping section: " & section &
       #           " at " & $r.line & " in " & r.filename)
       skipSection(r)
-    if r.s[r.pos] == '\x0A': 
+    if r.s[r.pos] == '\x0A':
       inc(r.pos)
       inc(r.line)
 
@@ -649,8 +651,8 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool =
   while s < token.len and buf[pos+s] == token[s]: inc s
   result = s == token.len
 
-proc newRodReader(modfilename: string, crc: TCrc32, 
-                  readerIndex: int): PRodReader = 
+proc newRodReader(modfilename: string, hash: SecureHash,
+                  readerIndex: int): PRodReader =
   new(result)
   try:
     result.memfile = memfiles.open(modfilename)
@@ -665,11 +667,11 @@ proc newRodReader(modfilename: string, crc: TCrc32,
   r.line = 1
   r.readerIndex = readerIndex
   r.filename = modfilename
-  initIdTable(r.syms)
+  r.syms = initTable[int, PSym]()
   # we terminate the file explicitly with ``\0``, so the cast to `cstring`
   # is safe:
   r.s = cast[cstring](r.memfile.mem)
-  if startsWith(r.s, "NIM:"): 
+  if startsWith(r.s, "NIM:"):
     initIiTable(r.index.tab)
     initIiTable(r.imports.tab) # looks like a ROD file
     inc(r.pos, 4)
@@ -678,16 +680,16 @@ proc newRodReader(modfilename: string, crc: TCrc32,
       add(version, r.s[r.pos])
       inc(r.pos)
     if r.s[r.pos] == '\x0A': inc(r.pos)
-    if version != RodFileVersion: 
+    if version != RodFileVersion:
       # since ROD files are only for caching, no backwards compatibility is
       # needed
       result = nil
   else:
     result = nil
-  
-proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = 
+
+proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
   result = PType(idTableGet(gTypeTable, id))
-  if result == nil: 
+  if result == nil:
     # load the type:
     var oldPos = r.pos
     var d = iiTableGet(r.index.tab, id)
@@ -696,19 +698,19 @@ proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
     result = decodeType(r, info)
     r.pos = oldPos
 
-type 
-  TFileModuleRec{.final.} = object 
+type
+  TFileModuleRec{.final.} = object
     filename*: string
     reason*: TReasonForRecompile
     rd*: PRodReader
-    crc*: TCrc32
-    crcDone*: bool
+    hash*: SecureHash
+    hashDone*: bool
 
   TFileModuleMap = seq[TFileModuleRec]
 
 var gMods*: TFileModuleMap = @[]
 
-proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = 
+proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
   # all compiled modules
   if rd.dataIdx == 0: internalError(info, "dataIdx == 0")
   var oldPos = rd.pos
@@ -717,9 +719,9 @@ proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
   rd.pos = oldPos
 
 proc findSomeWhere(id: int) =
-  for i in countup(0, high(gMods)): 
+  for i in countup(0, high(gMods)):
     var rd = gMods[i].rd
-    if rd != nil: 
+    if rd != nil:
       var d = iiTableGet(rd.index.tab, id)
       if d != InvalidKey:
         echo "found id ", id, " in ", gMods[i].filename
@@ -734,12 +736,12 @@ proc getReader(moduleId: int): PRodReader =
     if result != nil and result.moduleID == moduleId: return result
   return nil
 
-proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = 
-  result = PSym(idTableGet(r.syms, id))
-  if result == nil: 
+proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
+  result = r.syms.getOrDefault(id)
+  if result == nil:
     # load the symbol:
     var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey: 
+    if d == InvalidKey:
       # import from other module:
       var moduleID = iiTableGet(r.imports.tab, id)
       if moduleID < 0:
@@ -748,24 +750,24 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
         internalError(info, "missing from both indexes: +" & x)
       var rd = getReader(moduleID)
       d = iiTableGet(rd.index.tab, id)
-      if d != InvalidKey: 
+      if d != InvalidKey:
         result = decodeSymSafePos(rd, d, info)
       else:
         var x = ""
         encodeVInt(id, x)
         when false: findSomeWhere(id)
         internalError(info, "rrGetSym: no reader found: +" & x)
-    else: 
+    else:
       # own symbol:
       result = decodeSymSafePos(r, d, info)
   if result != nil and result.kind == skStub: rawLoadStub(result)
-  
-proc loadInitSection(r: PRodReader): PNode = 
+
+proc loadInitSection(r: PRodReader): PNode =
   if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection")
   var oldPos = r.pos
   r.pos = r.initIdx
   result = newNode(nkStmtList)
-  while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': 
+  while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
     var d = decodeVInt(r.s, r.pos)
     inc(r.pos)                # #10
     var p = r.pos
@@ -774,13 +776,13 @@ proc loadInitSection(r: PRodReader): PNode =
     r.pos = p
   r.pos = oldPos
 
-proc loadConverters(r: PRodReader) = 
+proc loadConverters(r: PRodReader) =
   # We have to ensure that no exported converter is a stub anymore, and the
   # import mechanism takes care of the rest.
-  if r.convertersIdx == 0 or r.dataIdx == 0: 
+  if r.convertersIdx == 0 or r.dataIdx == 0:
     internalError("importConverters")
   r.pos = r.convertersIdx
-  while r.s[r.pos] > '\x0A': 
+  while r.s[r.pos] > '\x0A':
     var d = decodeVInt(r.s, r.pos)
     discard rrGetSym(r, d, unknownLineInfo())
     if r.s[r.pos] == ' ': inc(r.pos)
@@ -794,14 +796,14 @@ proc loadMethods(r: PRodReader) =
     r.methods.add(rrGetSym(r, d, unknownLineInfo()))
     if r.s[r.pos] == ' ': inc(r.pos)
 
-proc getCRC*(fileIdx: int32): TCrc32 =
+proc getHash*(fileIdx: int32): SecureHash =
   internalAssert fileIdx >= 0 and fileIdx < gMods.len
 
-  if gMods[fileIdx].crcDone:
-    return gMods[fileIdx].crc
-  
-  result = crcFromFile(fileIdx.toFilename)
-  gMods[fileIdx].crc = result
+  if gMods[fileIdx].hashDone:
+    return gMods[fileIdx].hash
+
+  result = secureHashFile(fileIdx.toFullPath)
+  gMods[fileIdx].hash = result
 
 template growCache*(cache, pos) =
   if cache.len <= pos: cache.setLen(pos+1)
@@ -809,22 +811,22 @@ template growCache*(cache, pos) =
 proc checkDep(fileIdx: int32): TReasonForRecompile =
   assert fileIdx != InvalidFileIDX
   growCache gMods, fileIdx
-  if gMods[fileIdx].reason != rrEmpty: 
+  if gMods[fileIdx].reason != rrEmpty:
     # reason has already been computed for this module:
     return gMods[fileIdx].reason
   let filename = fileIdx.toFilename
-  var crc = getCRC(fileIdx)
+  var hash = getHash(fileIdx)
   gMods[fileIdx].reason = rrNone  # we need to set it here to avoid cycles
   result = rrNone
   var r: PRodReader = nil
   var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
-  r = newRodReader(rodfile, crc, fileIdx)
-  if r == nil: 
+  r = newRodReader(rodfile, hash, fileIdx)
+  if r == nil:
     result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
   else:
-    processRodFile(r, crc)
+    processRodFile(r, hash)
     result = r.reason
-    if result == rrNone: 
+    if result == rrNone:
       # check modules it depends on
       # NOTE: we need to process the entire module graph so that no ID will
       # be used twice! However, compilation speed does not suffer much from
@@ -836,7 +838,7 @@ proc checkDep(fileIdx: int32): TReasonForRecompile =
         if res != rrNone:
           result = rrModDeps
           # we cannot break here, because of side-effects of `checkDep`
-  if result != rrNone and gVerbosity > 0:
+  if result != rrNone:
     rawMessage(hintProcessing, reasonToFrmt[result] % filename)
   if result != rrNone or optForceFullMake in gGlobalOptions:
     # recompilation is necessary:
@@ -844,10 +846,10 @@ proc checkDep(fileIdx: int32): TReasonForRecompile =
     r = nil
   gMods[fileIdx].rd = r
   gMods[fileIdx].reason = result  # now we know better
-  
-proc handleSymbolFile(module: PSym): PRodReader = 
+
+proc handleSymbolFile(module: PSym): PRodReader =
   let fileIdx = module.fileIdx
-  if optSymbolFiles notin gGlobalOptions: 
+  if optSymbolFiles notin gGlobalOptions:
     module.id = getID()
     return nil
   idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
@@ -855,9 +857,9 @@ proc handleSymbolFile(module: PSym): PRodReader =
   discard checkDep(fileIdx)
   if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
   result = gMods[fileIdx].rd
-  if result != nil: 
+  if result != nil:
     module.id = result.moduleID
-    idTablePut(result.syms, module, module)
+    result.syms[module.id] = module
     processInterf(result, module)
     processCompilerProcs(result, module)
     loadConverters(result)
@@ -876,13 +878,13 @@ proc rawLoadStub(s: PSym) =
     #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
     #     "\ns:  ", toHex(cast[int](s.position), int.sizeof * 2)
     internalError(rs.info, "loadStub: wrong symbol")
-  elif rs.id != theId: 
-    internalError(rs.info, "loadStub: wrong ID") 
+  elif rs.id != theId:
+    internalError(rs.info, "loadStub: wrong ID")
   #MessageOut('loaded stub: ' + s.name.s);
-  
+
 proc loadStub*(s: PSym) =
   ## loads the stub symbol `s`.
-  
+
   # deactivate the GC here because we do a deep recursion and generate no
   # garbage when restoring parts of the object graph anyway.
   # Since we die with internal errors if this fails, no try-finally is
@@ -890,12 +892,14 @@ proc loadStub*(s: PSym) =
   GC_disable()
   rawLoadStub(s)
   GC_enable()
-  
+
 proc getBody*(s: PSym): PNode =
   ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file
   ## it may perform an expensive reload operation. Otherwise it's a simple
   ## accessor.
   assert s.kind in routineKinds
+  # prevent crashes due to incorrect macro transformations (bug #2377)
+  if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
   result = s.ast.sons[bodyPos]
   if result == nil:
     assert s.offset != 0
@@ -906,7 +910,7 @@ proc getBody*(s: PSym): PNode =
     r.pos = oldPos
     s.ast.sons[bodyPos] = result
     s.offset = 0
-  
+
 initIdTable(gTypeTable)
 initStrTable(rodCompilerprocs)
 
@@ -919,16 +923,16 @@ proc writeNode(f: File; n: PNode) =
       f.write('^')
       f.write(n.typ.id)
     case n.kind
-    of nkCharLit..nkInt64Lit: 
+    of nkCharLit..nkInt64Lit:
       if n.intVal != 0:
         f.write('!')
         f.write(n.intVal)
-    of nkFloatLit..nkFloat64Lit: 
-      if n.floatVal != 0.0: 
+    of nkFloatLit..nkFloat64Lit:
+      if n.floatVal != 0.0:
         f.write('!')
         f.write($n.floatVal)
     of nkStrLit..nkTripleStrLit:
-      if n.strVal != "": 
+      if n.strVal != "":
         f.write('!')
         f.write(n.strVal.escape)
     of nkIdent:
@@ -938,7 +942,7 @@ proc writeNode(f: File; n: PNode) =
       f.write('!')
       f.write(n.sym.id)
     else:
-      for i in countup(0, sonsLen(n) - 1): 
+      for i in countup(0, sonsLen(n) - 1):
         writeNode(f, n.sons[i])
   f.write(")")
 
@@ -964,10 +968,10 @@ proc writeSym(f: File; s: PSym) =
   if s.magic != mNone:
     f.write('@')
     f.write($s.magic)
-  if s.options != gOptions: 
+  if s.options != gOptions:
     f.write('!')
     f.write($s.options)
-  if s.position != 0: 
+  if s.position != 0:
     f.write('%')
     f.write($s.position)
   if s.offset != -1:
@@ -988,12 +992,12 @@ proc writeType(f: File; t: PType) =
   f.write($t.kind)
   f.write('+')
   f.write($t.id)
-  if t.n != nil: 
+  if t.n != nil:
     f.writeNode(t.n)
   if t.flags != {}:
     f.write('$')
     f.write($t.flags)
-  if t.callConv != low(t.callConv): 
+  if t.callConv != low(t.callConv):
     f.write('?')
     f.write($t.callConv)
   if t.owner != nil:
@@ -1008,16 +1012,16 @@ proc writeType(f: File; t: PType) =
   if t.align != 2:
     f.write('=')
     f.write($t.align)
-  for i in countup(0, sonsLen(t) - 1): 
-    if t.sons[i] == nil: 
+  for i in countup(0, sonsLen(t) - 1):
+    if t.sons[i] == nil:
       f.write("^()")
     else:
-      f.write('^') 
+      f.write('^')
       f.write($t.sons[i].id)
   f.write("]\n")
 
 proc viewFile(rodfile: string) =
-  var r = newRodReader(rodfile, 0, 0)
+  var r = newRodReader(rodfile, secureHash(""), 0)
   if r == nil:
     rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
        rodfile)
@@ -1027,30 +1031,30 @@ proc viewFile(rodfile: string) =
   while r.s[r.pos] != '\0':
     let section = rdWord(r)
     case section
-    of "CRC":
+    of "HASH":
       inc(r.pos)              # skip ':'
-      outf.writeln("CRC:", $decodeVInt(r.s, r.pos))
-    of "ID": 
+      outf.writeLine("HASH:", $decodeVInt(r.s, r.pos))
+    of "ID":
       inc(r.pos)              # skip ':'
       r.moduleID = decodeVInt(r.s, r.pos)
       setId(r.moduleID)
-      outf.writeln("ID:", $r.moduleID)
+      outf.writeLine("ID:", $r.moduleID)
     of "ORIGFILE":
       inc(r.pos)
       r.origFile = decodeStr(r.s, r.pos)
-      outf.writeln("ORIGFILE:", r.origFile)
+      outf.writeLine("ORIGFILE:", r.origFile)
     of "OPTIONS":
       inc(r.pos)              # skip ':'
       r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeln("OPTIONS:", $r.options)
+      outf.writeLine("OPTIONS:", $r.options)
     of "GOPTIONS":
       inc(r.pos)              # skip ':'
       let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeln("GOPTIONS:", $dep)
+      outf.writeLine("GOPTIONS:", $dep)
     of "CMD":
       inc(r.pos)              # skip ':'
       let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
-      outf.writeln("CMD:", $dep)
+      outf.writeLine("CMD:", $dep)
     of "DEFINES":
       inc(r.pos)              # skip ':'
       var d = 0
@@ -1072,27 +1076,27 @@ proc viewFile(rodfile: string) =
         r.files.add(finalPath.fileInfoIdx)
         inc(r.pos)            # skip #10
         inc(r.line)
-        outf.writeln finalPath
+        outf.writeLine finalPath
       if r.s[r.pos] == ')': inc(r.pos)
       outf.write(")\n")
-    of "INCLUDES": 
+    of "INCLUDES":
       inc(r.pos, 2)           # skip "(\10"
       inc(r.line)
       outf.write("INCLUDES(\n")
-      while r.s[r.pos] != ')': 
+      while r.s[r.pos] != ')':
         let w = r.files[decodeVInt(r.s, r.pos)]
         inc(r.pos)            # skip ' '
-        let inclCrc = decodeVInt(r.s, r.pos)
-        if r.s[r.pos] == '\x0A': 
+        let inclHash = decodeVInt(r.s, r.pos)
+        if r.s[r.pos] == '\x0A':
           inc(r.pos)
           inc(r.line)
-        outf.write(w, " ", inclCrc, "\n")
+        outf.write(w, " ", inclHash, "\n")
       if r.s[r.pos] == ')': inc(r.pos)
       outf.write(")\n")
     of "DEPS":
       inc(r.pos)              # skip ':'
       outf.write("DEPS:")
-      while r.s[r.pos] > '\x0A': 
+      while r.s[r.pos] > '\x0A':
         let v = int32(decodeVInt(r.s, r.pos))
         r.modDeps.add(r.files[v])
         if r.s[r.pos] == ' ': inc(r.pos)
@@ -1124,7 +1128,7 @@ proc viewFile(rodfile: string) =
       if section == "METHODS": r.methodsIdx = r.pos
       else: r.convertersIdx = r.pos
       outf.write(section, ":")
-      while r.s[r.pos] > '\x0A': 
+      while r.s[r.pos] > '\x0A':
         let d = decodeVInt(r.s, r.pos)
         outf.write(" ", $d)
         if r.s[r.pos] == ' ': inc(r.pos)
@@ -1150,7 +1154,7 @@ proc viewFile(rodfile: string) =
       outf.write("INIT(\n")
       inc r.pos, 2
       r.initIdx = r.pos
-      while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': 
+      while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
         let d = decodeVInt(r.s, r.pos)
         inc(r.pos)                # #10
         #let p = r.pos
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index e0ef3c397..91f93d279 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -12,7 +12,7 @@ import strutils
 
 proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
 
-proc toStrMaxPrecision*(f: BiggestFloat): string = 
+proc toStrMaxPrecision*(f: BiggestFloat): string =
   if f != f:
     result = "NAN"
   elif f == 0.0:
@@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
     if f > 0.0: result = "INF"
     else: result = "-INF"
   else:
-    var buf: array [0..80, char]    
-    c_sprintf(buf, "%#.16e", f) 
+    var buf: array [0..80, char]
+    c_sprintf(buf, "%#.16e", f)
     result = $buf
 
 proc encodeStr*(s: string, result: var string) =
-  for i in countup(0, len(s) - 1): 
+  for i in countup(0, len(s) - 1):
     case s[i]
     of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
     else: add(result, '\\' & toHex(ord(s[i]), 2))
 
-proc hexChar(c: char, xi: var int) = 
+proc hexChar(c: char, xi: var int) =
   case c
   of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
   of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
@@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) =
 proc decodeStr*(s: cstring, pos: var int): string =
   var i = pos
   result = ""
-  while true: 
+  while true:
     case s[i]
-    of '\\': 
+    of '\\':
       inc(i, 3)
       var xi = 0
       hexChar(s[i-2], xi)
       hexChar(s[i-1], xi)
       add(result, chr(xi))
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_': 
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_':
       add(result, s[i])
       inc(i)
-    else: break 
+    else: break
   pos = i
 
 const
@@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) =
   var d: char
   var v = x
   var rem = v mod 190
-  if rem < 0: 
+  if rem < 0:
     add(result, '-')
     v = - (v div 190)
     rem = - rem
-  else: 
+  else:
     v = v div 190
   var idx = int(rem)
   if idx < 62: d = chars[idx]
@@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
   encodeVBiggestIntAux(x +% vintDelta, result)
   #  encodeIntImpl(encodeVBiggestInt)
 
-proc encodeVIntAux(x: int, result: var string) = 
+proc encodeVIntAux(x: int, result: var string) =
   ## encode an int as a variable length base 190 int.
   encodeIntImpl(encodeVIntAux)
-  
-proc encodeVInt*(x: int, result: var string) = 
+
+proc encodeVInt*(x: int, result: var string) =
   ## encode an int as a variable length base 190 int.
   encodeVIntAux(x +% vintDelta, result)
 
@@ -101,11 +101,11 @@ template decodeIntImpl() =
   var i = pos
   var sign = - 1
   assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
-  if s[i] == '-': 
+  if s[i] == '-':
     inc(i)
     sign = 1
   result = 0
-  while true: 
+  while true:
     case s[i]
     of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
     of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
@@ -116,7 +116,7 @@ template decodeIntImpl() =
   result = result * sign -% vintDelta
   pos = i
 
-proc decodeVInt*(s: cstring, pos: var int): int = 
+proc decodeVInt*(s: cstring, pos: var int): int =
   decodeIntImpl()
 
 proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index e178b7ce6..d48a9ba40 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -11,16 +11,17 @@
 # rod files is a pass, reading of rod files is not! This is why reading and
 # writing of rod files is split into two different modules.
 
-import 
+import
   intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
-  condsyms, ropes, idents, crc, rodread, passes, importer, idgen, rodutils
+  condsyms, ropes, idents, securehash, rodread, passes, importer, idgen,
+  rodutils
 
 # implementation
 
-type 
+type
   TRodWriter = object of TPassContext
     module: PSym
-    crc: TCrc32
+    hash: SecureHash
     options: TOptions
     defines: string
     inclDeps: string
@@ -38,22 +39,22 @@ type
 
   PRodWriter = ref TRodWriter
 
-proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter
+proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter
 proc addModDep(w: PRodWriter, dep: string)
 proc addInclDep(w: PRodWriter, dep: string)
 proc addInterfaceSym(w: PRodWriter, s: PSym)
 proc addStmt(w: PRodWriter, n: PNode)
 proc writeRod(w: PRodWriter)
 
-proc getDefines(): string = 
+proc getDefines(): string =
   result = ""
   for d in definedSymbolNames():
     if result.len != 0: add(result, " ")
     add(result, d)
 
-proc fileIdx(w: PRodWriter, filename: string): int = 
-  for i in countup(0, high(w.files)): 
-    if w.files[i] == filename: 
+proc fileIdx(w: PRodWriter, filename: string): int =
+  for i in countup(0, high(w.files)):
+    if w.files[i] == filename:
       return i
   result = len(w.files)
   setLen(w.files, result + 1)
@@ -62,7 +63,7 @@ proc fileIdx(w: PRodWriter, filename: string): int =
 template filename*(w: PRodWriter): string =
   w.module.filename
 
-proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter = 
+proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter =
   new(result)
   result.sstack = @[]
   result.tstack = @[]
@@ -70,7 +71,7 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter =
   initIiTable(result.imports.tab)
   result.index.r = ""
   result.imports.r = ""
-  result.crc = crc
+  result.hash = hash
   result.module = module
   result.defines = getDefines()
   result.options = options.gOptions
@@ -84,19 +85,19 @@ proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter =
   result.init = ""
   result.origFile = module.info.toFilename
   result.data = newStringOfCap(12_000)
-  
+
 proc addModDep(w: PRodWriter, dep: string) =
   if w.modDeps.len != 0: add(w.modDeps, ' ')
   encodeVInt(fileIdx(w, dep), w.modDeps)
 
-const 
+const
   rodNL = "\x0A"
 
 proc addInclDep(w: PRodWriter, dep: string) =
   var resolved = dep.findModule(w.module.info.toFullPath)
   encodeVInt(fileIdx(w, dep), w.inclDeps)
   add(w.inclDeps, " ")
-  encodeVInt(crcFromFile(resolved), w.inclDeps)
+  encodeStr($secureHashFile(resolved), w.inclDeps)
   add(w.inclDeps, rodNL)
 
 proc pushType(w: PRodWriter, t: PType) =
@@ -109,18 +110,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
   if iiTableGet(w.index.tab, s.id) == InvalidKey:
     w.sstack.add(s)
 
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, 
-                result: var string) = 
-  if n == nil: 
+proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
+                result: var string) =
+  if n == nil:
     # nil nodes have to be stored too:
     result.add("()")
     return
   result.add('(')
-  encodeVInt(ord(n.kind), result) 
+  encodeVInt(ord(n.kind), result)
   # we do not write comments for now
   # Line information takes easily 20% or more of the filesize! Therefore we
   # omit line information if it is the same as the father's line information:
-  if fInfo.fileIndex != n.info.fileIndex: 
+  if fInfo.fileIndex != n.info.fileIndex:
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
@@ -138,7 +139,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
   # No need to output the file index, as this is the serialization of one
   # file.
   var f = n.flags * PersistentNodeFlags
-  if f != {}: 
+  if f != {}:
     result.add('$')
     encodeVInt(cast[int32](f), result)
   if n.typ != nil:
@@ -146,16 +147,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     encodeVInt(n.typ.id, result)
     pushType(w, n.typ)
   case n.kind
-  of nkCharLit..nkInt64Lit: 
+  of nkCharLit..nkInt64Lit:
     if n.intVal != 0:
       result.add('!')
       encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit: 
-    if n.floatVal != 0.0: 
+  of nkFloatLit..nkFloat64Lit:
+    if n.floatVal != 0.0:
       result.add('!')
       encodeStr($n.floatVal, result)
   of nkStrLit..nkTripleStrLit:
-    if n.strVal != "": 
+    if n.strVal != "":
       result.add('!')
       encodeStr(n.strVal, result)
   of nkIdent:
@@ -166,25 +167,25 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     encodeVInt(n.sym.id, result)
     pushSym(w, n.sym)
   else:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       encodeNode(w, n.info, n.sons[i], result)
   add(result, ')')
 
-proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = 
+proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
   var oldLen = result.len
   result.add('<')
   if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
-  if loc.s != low(loc.s): 
+  if loc.s != low(loc.s):
     add(result, '*')
     encodeVInt(ord(loc.s), result)
-  if loc.flags != {}: 
+  if loc.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](loc.flags), result)
   if loc.t != nil:
     add(result, '^')
     encodeVInt(cast[int32](loc.t.id), result)
     pushType(w, loc.t)
-  if loc.r != nil: 
+  if loc.r != nil:
     add(result, '!')
     encodeStr($loc.r, result)
   if oldLen + 1 == result.len:
@@ -192,9 +193,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
     setLen(result, oldLen)
   else:
     add(result, '>')
-  
-proc encodeType(w: PRodWriter, t: PType, result: var string) = 
-  if t == nil: 
+
+proc encodeType(w: PRodWriter, t: PType, result: var string) =
+  if t == nil:
     # nil nodes have to be stored too:
     result.add("[]")
     return
@@ -206,38 +207,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   encodeVInt(ord(t.kind), result)
   add(result, '+')
   encodeVInt(t.id, result)
-  if t.n != nil: 
+  if t.n != nil:
     encodeNode(w, unknownLineInfo(), t.n, result)
-  if t.flags != {}: 
+  if t.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv): 
+  if t.callConv != low(t.callConv):
     add(result, '?')
     encodeVInt(ord(t.callConv), result)
-  if t.owner != nil: 
+  if t.owner != nil:
     add(result, '*')
     encodeVInt(t.owner.id, result)
     pushSym(w, t.owner)
-  if t.sym != nil: 
+  if t.sym != nil:
     add(result, '&')
     encodeVInt(t.sym.id, result)
     pushSym(w, t.sym)
-  if t.size != - 1: 
+  if t.size != - 1:
     add(result, '/')
     encodeVBiggestInt(t.size, result)
-  if t.align != 2: 
+  if t.align != 2:
     add(result, '=')
     encodeVInt(t.align, result)
   encodeLoc(w, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1): 
-    if t.sons[i] == nil: 
+  for i in countup(0, sonsLen(t) - 1):
+    if t.sons[i] == nil:
       add(result, "^()")
-    else: 
-      add(result, '^') 
+    else:
+      add(result, '^')
       encodeVInt(t.sons[i].id, result)
       pushType(w, t.sons[i])
 
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = 
+proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
   add(result, '|')
   encodeVInt(ord(lib.kind), result)
   add(result, '|')
@@ -276,10 +277,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   if s.magic != mNone:
     result.add('@')
     encodeVInt(ord(s.magic), result)
-  if s.options != w.options: 
+  if s.options != w.options:
     result.add('!')
     encodeVInt(cast[int32](s.options), result)
-  if s.position != 0: 
+  if s.position != 0:
     result.add('%')
     encodeVInt(s.position, result)
   if s.offset != - 1:
@@ -307,7 +308,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
       if codeAst != nil:
         # resore the AST:
         s.ast.sons[codePos] = codeAst
-  
+
 proc addToIndex(w: var TIndex, key, val: int) =
   if key - w.lastIdxKey == 1:
     # we do not store a key-diff of 1 to safe space
@@ -328,24 +329,25 @@ when debugWrittenIds:
 
 proc symStack(w: PRodWriter): int =
   var i = 0
-  while i < len(w.sstack): 
+  while i < len(w.sstack):
     var s = w.sstack[i]
     if sfForward in s.flags:
       w.sstack[result] = s
       inc result
     elif iiTableGet(w.index.tab, s.id) == InvalidKey:
       var m = getModule(s)
-      if m == nil: internalError("symStack: module nil: " & s.name.s)
-      if (m.id == w.module.id) or (sfFromGeneric in s.flags): 
+      if m == nil and s.kind != skPackage:
+        internalError("symStack: module nil: " & s.name.s)
+      if s.kind == skPackage or m.id == w.module.id or sfFromGeneric in s.flags:
         # put definition in here
         var L = w.data.len
-        addToIndex(w.index, s.id, L) 
+        addToIndex(w.index, s.id, L)
         when debugWrittenIds: incl(debugWritten, s.id)
         encodeSym(w, s, w.data)
         add(w.data, rodNL)
         # put into interface section if appropriate:
-        if {sfExported, sfFromGeneric} * s.flags == {sfExported} and 
-            s.kind in ExportableSymKinds: 
+        if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
+            s.kind in ExportableSymKinds:
           encodeStr(s.name.s, w.interf)
           add(w.interf, ' ')
           encodeVInt(s.id, w.interf)
@@ -361,26 +363,26 @@ proc symStack(w: PRodWriter): int =
         if s.kind == skMethod and sfDispatcher notin s.flags:
           if w.methods.len != 0: add(w.methods, ' ')
           encodeVInt(s.id, w.methods)
-      elif iiTableGet(w.imports.tab, s.id) == InvalidKey: 
+      elif iiTableGet(w.imports.tab, s.id) == InvalidKey:
         addToIndex(w.imports, s.id, m.id)
         when debugWrittenIds:
-          if not Contains(debugWritten, s.id):
+          if not contains(debugWritten, s.id):
             echo(w.filename)
             debug(s)
             debug(s.owner)
             debug(m)
-            InternalError("Symbol referred to but never written")
+            internalError("Symbol referred to but never written")
     inc(i)
   setLen(w.sstack, result)
 
-proc typeStack(w: PRodWriter): int = 
+proc typeStack(w: PRodWriter): int =
   var i = 0
-  while i < len(w.tstack): 
+  while i < len(w.tstack):
     var t = w.tstack[i]
     if t.kind == tyForward:
       w.tstack[result] = t
       inc result
-    elif iiTableGet(w.index.tab, t.id) == InvalidKey: 
+    elif iiTableGet(w.index.tab, t.id) == InvalidKey:
       var L = w.data.len
       addToIndex(w.index, t.id, L)
       encodeType(w, t, w.data)
@@ -400,24 +402,24 @@ proc processStacks(w: PRodWriter, finalPass: bool) =
   if finalPass and (oldS != 0 or oldT != 0):
     internalError("could not serialize some forwarded symbols/types")
 
-proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = 
+proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
   pushSym(w, s)
   processStacks(w, false)
 
-proc addInterfaceSym(w: PRodWriter, s: PSym) = 
-  if w == nil: return 
-  if s.kind in ExportableSymKinds and 
-      {sfExported, sfCompilerProc} * s.flags != {}: 
+proc addInterfaceSym(w: PRodWriter, s: PSym) =
+  if w == nil: return
+  if s.kind in ExportableSymKinds and
+      {sfExported, sfCompilerProc} * s.flags != {}:
     rawAddInterfaceSym(w, s)
 
-proc addStmt(w: PRodWriter, n: PNode) = 
+proc addStmt(w: PRodWriter, n: PNode) =
   encodeVInt(w.data.len, w.init)
   add(w.init, rodNL)
   encodeNode(w, unknownLineInfo(), n, w.data)
   add(w.data, rodNL)
   processStacks(w, false)
 
-proc writeRod(w: PRodWriter) = 
+proc writeRod(w: PRodWriter) =
   processStacks(w, true)
   var f: File
   if not open(f, completeGeneratedFilePath(changeFileExt(
@@ -438,12 +440,12 @@ proc writeRod(w: PRodWriter) =
   encodeStr(w.origFile, orig)
   f.write(orig)
   f.write(rodNL)
-  
-  var crc = "CRC:"
-  encodeVInt(w.crc, crc)
-  f.write(crc)
+
+  var hash = "HASH:"
+  encodeStr($w.hash, hash)
+  f.write(hash)
   f.write(rodNL)
-  
+
   var options = "OPTIONS:"
   encodeVInt(cast[int32](w.options), options)
   f.write(options)
@@ -457,31 +459,31 @@ proc writeRod(w: PRodWriter) =
   var cmd = "CMD:"
   encodeVInt(cast[int32](gCmd), cmd)
   f.write(cmd)
-  f.write(rodNL)  
-  
+  f.write(rodNL)
+
   f.write("DEFINES:")
   f.write(w.defines)
   f.write(rodNL)
-  
+
   var files = "FILES(" & rodNL
-  for i in countup(0, high(w.files)): 
+  for i in countup(0, high(w.files)):
     encodeStr(w.files[i], files)
     files.add(rodNL)
   f.write(files)
   f.write(')' & rodNL)
-  
+
   f.write("INCLUDES(" & rodNL)
   f.write(w.inclDeps)
   f.write(')' & rodNL)
-  
+
   f.write("DEPS:")
   f.write(w.modDeps)
   f.write(rodNL)
-  
+
   f.write("INTERF(" & rodNL)
   f.write(w.interf)
   f.write(')' & rodNL)
-  
+
   f.write("COMPILERPROCS(" & rodNL)
   f.write(w.compilerProcs)
   f.write(')' & rodNL)
@@ -489,11 +491,11 @@ proc writeRod(w: PRodWriter) =
   f.write("INDEX(" & rodNL)
   f.write(w.index.r)
   f.write(')' & rodNL)
-  
+
   f.write("IMPORTS(" & rodNL)
   f.write(w.imports.r)
   f.write(')' & rodNL)
-  
+
   f.write("CONVERTERS:")
   f.write(w.converters)
   f.write(rodNL)
@@ -501,11 +503,11 @@ proc writeRod(w: PRodWriter) =
   f.write("METHODS:")
   f.write(w.methods)
   f.write(rodNL)
-  
+
   f.write("INIT(" & rodNL)
   f.write(w.init)
   f.write(')' & rodNL)
-  
+
   f.write("DATA(" & rodNL)
   f.write(w.data)
   f.write(')' & rodNL)
@@ -513,23 +515,23 @@ proc writeRod(w: PRodWriter) =
   # for reading:
   f.write("\0")
   f.close()
-  
+
   #echo "interf: ", w.interf.len
   #echo "index:  ", w.index.r.len
   #echo "init:   ", w.init.len
   #echo "data:   ", w.data.len
 
-proc process(c: PPassContext, n: PNode): PNode = 
+proc process(c: PPassContext, n: PNode): PNode =
   result = n
-  if c == nil: return 
+  if c == nil: return
   var w = PRodWriter(c)
   case n.kind
   of nkStmtList:
     for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
     #var s = n.sons[namePos].sym
     #addInterfaceSym(w, s)
-  of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef, 
-      nkTemplateDef, nkMacroDef: 
+  of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef,
+      nkTemplateDef, nkMacroDef:
     var s = n.sons[namePos].sym
     if s == nil: internalError(n.info, "rodwrite.process")
     if n.sons[bodyPos] == nil:
@@ -538,17 +540,17 @@ proc process(c: PPassContext, n: PNode): PNode =
         sfForward notin s.flags:
       addInterfaceSym(w, s)
   of nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue
       addInterfaceSym(w, a.sons[0].sym)
-  of nkTypeSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkTypeSection:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process")
       var s = a.sons[0].sym
-      addInterfaceSym(w, s) 
+      addInterfaceSym(w, s)
       # this takes care of enum fields too
       # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
       # type aliasing! Otherwise the same enum symbol would be included
@@ -556,29 +558,29 @@ proc process(c: PPassContext, n: PNode): PNode =
       #
       #        if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
       #          a := s.typ.n;
-      #          for j := 0 to sonsLen(a)-1 do 
-      #            addInterfaceSym(w, a.sons[j].sym);        
-      #        end 
-  of nkImportStmt: 
+      #          for j := 0 to sonsLen(a)-1 do
+      #            addInterfaceSym(w, a.sons[j].sym);
+      #        end
+  of nkImportStmt:
     for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i]))
     addStmt(w, n)
-  of nkFromStmt: 
+  of nkFromStmt:
     addModDep(w, getModuleName(n.sons[0]))
     addStmt(w, n)
-  of nkIncludeStmt: 
+  of nkIncludeStmt:
     for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i]))
-  of nkPragma: 
+  of nkPragma:
     addStmt(w, n)
-  else: 
+  else:
     discard
 
 proc myOpen(module: PSym): PPassContext =
   if module.id < 0: internalError("rodwrite: module ID not set")
-  var w = newRodWriter(module.fileIdx.getCRC, module)
+  var w = newRodWriter(module.fileIdx.getHash, module)
   rawAddInterfaceSym(w, module)
   result = w
 
-proc myClose(c: PPassContext, n: PNode): PNode = 
+proc myClose(c: PPassContext, n: PNode): PNode =
   result = process(c, n)
   var w = PRodWriter(c)
   writeRod(w)
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index edac8e9d0..bfae7aaa4 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -306,7 +306,7 @@ const
 
 proc equalsFile*(r: Rope, f: File): bool =
   ## returns true if the contents of the file `f` equal `r`.
-  var 
+  var
     buf: array[bufSize, char]
     bpos = buf.len
     blen = buf.len
diff --git a/compiler/saturate.nim b/compiler/saturate.nim
index f4fe29a20..065cb5128 100644
--- a/compiler/saturate.nim
+++ b/compiler/saturate.nim
@@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
   #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
   if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
     return result
-  
+
   if floatProd >= 0.0:
     result = high(result)
   else:
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
new file mode 100644
index 000000000..22cd282fd
--- /dev/null
+++ b/compiler/scriptconfig.nim
@@ -0,0 +1,146 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements the new configuration system for Nim. Uses Nim as a scripting
+## language.
+
+import
+  ast, modules, passes, passaux, condsyms,
+  options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
+  os, times, osproc
+
+# we support 'cmpIgnoreStyle' natively for efficiency:
+from strutils import cmpIgnoreStyle
+
+proc listDirs(a: VmArgs, filter: set[PathComponent]) =
+  let dir = getString(a, 0)
+  var result: seq[string] = @[]
+  for kind, path in walkDir(dir):
+    if kind in filter: result.add path
+  setResult(a, result)
+
+proc setupVM*(module: PSym; scriptName: string): PEvalContext =
+  # For Nimble we need to export 'setupVM'.
+  result = newCtx(module)
+  result.mode = emRepl
+  registerAdditionalOps(result)
+
+  # captured vars:
+  var errorMsg: string
+  var vthisDir = scriptName.splitFile.dir
+
+  template cbconf(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        body
+
+  template cbos(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        try:
+          body
+        except OSError:
+          errorMsg = getCurrentExceptionMsg()
+
+  # Idea: Treat link to file as a file, but ignore link to directory to prevent
+  # endless recursions out of the box.
+  cbos listFiles:
+    listDirs(a, {pcFile, pcLinkToFile})
+  cbos listDirs:
+    listDirs(a, {pcDir})
+  cbos removeDir:
+    os.removeDir getString(a, 0)
+  cbos removeFile:
+    os.removeFile getString(a, 0)
+  cbos createDir:
+    os.createDir getString(a, 0)
+  cbos getOsError:
+    setResult(a, errorMsg)
+  cbos setCurrentDir:
+    os.setCurrentDir getString(a, 0)
+  cbos getCurrentDir:
+    setResult(a, os.getCurrentDir())
+  cbos moveFile:
+    os.moveFile(getString(a, 0), getString(a, 1))
+  cbos copyFile:
+    os.copyFile(getString(a, 0), getString(a, 1))
+  cbos getLastModificationTime:
+    setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
+
+  cbos rawExec:
+    setResult(a, osproc.execCmd getString(a, 0))
+
+  cbconf getEnv:
+    setResult(a, os.getEnv(a.getString 0))
+  cbconf existsEnv:
+    setResult(a, os.existsEnv(a.getString 0))
+  cbconf dirExists:
+    setResult(a, os.dirExists(a.getString 0))
+  cbconf fileExists:
+    setResult(a, os.fileExists(a.getString 0))
+
+  cbconf thisDir:
+    setResult(a, vthisDir)
+  cbconf put:
+    options.setConfigVar(getString(a, 0), getString(a, 1))
+  cbconf get:
+    setResult(a, options.getConfigVar(a.getString 0))
+  cbconf exists:
+    setResult(a, options.existsConfigVar(a.getString 0))
+  cbconf nimcacheDir:
+    setResult(a, options.getNimcacheDir())
+  cbconf paramStr:
+    setResult(a, os.paramStr(int a.getInt 0))
+  cbconf paramCount:
+    setResult(a, os.paramCount())
+  cbconf cmpIgnoreStyle:
+    setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
+  cbconf cmpIgnoreCase:
+    setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
+  cbconf setCommand:
+    options.command = a.getString 0
+    let arg = a.getString 1
+    if arg.len > 0:
+      gProjectName = arg
+      try:
+        gProjectFull = canonicalizePath(gProjectPath / gProjectName)
+      except OSError:
+        gProjectFull = gProjectName
+  cbconf getCommand:
+    setResult(a, options.command)
+  cbconf switch:
+    processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
+
+
+proc runNimScript*(scriptName: string) =
+  passes.gIncludeFile = includeModule
+  passes.gImportModule = importModule
+  initDefines()
+
+  defineSymbol("nimscript")
+  defineSymbol("nimconfig")
+  registerPass(semPass)
+  registerPass(evalPass)
+
+  appendStr(searchPaths, options.libpath)
+
+  var m = makeModule(scriptName)
+  incl(m.flags, sfMainModule)
+  vm.globalCtx = setupVM(m, scriptName)
+
+  compileSystemModule()
+  processModule(m, llStreamOpen(scriptName, fmRead), nil)
+
+  # ensure we load 'system.nim' again for the real non-config stuff!
+  resetAllModulesHard()
+  vm.globalCtx = nil
+  # do not remove the defined symbols
+  #initDefines()
+  undefSymbol("nimscript")
+  undefSymbol("nimconfig")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 346a17df1..041524f84 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
   result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
+  proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower
+
   # like newSymS, but considers gensym'ed symbols
   if n.kind == nkSym:
     # and sfGenSym in n.sym.flags:
     result = n.sym
-    internalAssert result.kind == kind
+    if result.kind != kind:
+      localError(n.info, "cannot use symbol of kind '" &
+                 $result.kind & "' as a '" & $kind & "'")
     # when there is a nested proc inside a template, semtmpl
     # will assign a wrong owner during the first pass over the
     # template; we must fix it here: see #909
@@ -421,7 +425,11 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
   for m in items(rd.methods): methodDef(m, true)
 
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
-  result = semStmt(c, n)
+  if sfNoForward in c.module.flags:
+    result = semAllTypeSections(c, n)
+  else:
+    result = n
+  result = semStmt(c, result)
   # BUGFIX: process newly generated generics here, not at the end!
   if c.lastGenericIdx < c.generics.len:
     var a = newNodeI(nkStmtList, n.info)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index c48e761e3..d8838e347 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -95,7 +95,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
   # as semOverlodedCall is already pretty slow (and we need this information
   # only in case of an error).
-  if c.inCompilesContext > 0:
+  if c.compilesContextId > 0 and optReportConceptFailures notin gGlobalOptions:
     # fail fast:
     globalError(n.info, errTypeMismatch, "")
   if errors.isNil or errors.len == 0:
@@ -133,7 +133,10 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
     add(candidates, "\n")
   if candidates != "":
     add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
-  localError(n.info, errGenerated, result)
+  if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions:
+    globalError(n.info, errGenerated, result)
+  else:
+    localError(n.info, errGenerated, result)
 
 proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
   for scope in walkScopes(c.currentScope):
@@ -209,7 +212,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       pickBest(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
-      localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
+      if nfDotField in n.flags and nfExplicitCall notin n.flags:
+        localError(n.info, errUndeclaredField, considerQuotedIdent(f).s)
+      else:
+        localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s)
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -232,7 +238,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     internalAssert result.state == csMatch
     #writeMatches(result)
     #writeMatches(alt)
-    if c.inCompilesContext > 0:
+    if c.compilesContextId > 0:
       # quick error message for performance of 'compiles' built-in:
       globalError(n.info, errGenerated, "ambiguous call")
     elif gErrorCounter == 0:
@@ -302,8 +308,25 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
     if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
       result.typ = newTypeS(x.fauxMatch, c)
     return
-  if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
-    finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
+  let gp = finalCallee.ast.sons[genericParamsPos]
+  if gp.kind != nkEmpty:
+    if x.calleeSym.kind notin {skMacro, skTemplate}:
+      if x.calleeSym.magic in {mArrGet, mArrPut}:
+        finalCallee = x.calleeSym
+      else:
+        finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
+    else:
+      # For macros and templates, the resolved generic params
+      # are added as normal params.
+      for s in instantiateGenericParamList(c, gp, x.bindings):
+        case s.kind
+          of skConst:
+            x.call.add s.ast
+          of skType:
+            x.call.add newSymNode(s, n.info)
+          else:
+            internalAssert false
+
   result = x.call
   instGenericConvertersSons(c, result, x)
   result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 345a8c0d1..9b2f2e2ce 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -45,7 +45,8 @@ type
   TExprFlag* = enum
     efLValue, efWantIterator, efInTypeof,
     efWantStmt, efAllowStmt, efDetermineType,
-    efAllowDestructor, efWantValue, efOperand, efNoSemCheck
+    efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
+    efNoProcvarCheck
   TExprFlags* = set[TExprFlag]
 
   TTypeAttachedOp* = enum
@@ -70,7 +71,8 @@ type
     inTypeClass*: int          # > 0 if we are in a user-defined type class
     inGenericContext*: int     # > 0 if we are in a generic type
     inUnrolledContext*: int    # > 0 if we are unrolling a loop
-    inCompilesContext*: int    # > 0 if we are in a ``compiles`` magic
+    compilesContextId*: int    # > 0 if we are in a ``compiles`` magic
+    compilesContextIdGenerator*: int
     inGenericInst*: int        # > 0 if we are instantiating a generic
     converters*: TSymSeq       # sequence of converters
     patterns*: TSymSeq         # sequence of pattern matchers
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index aaab49a10..1261dd460 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -24,7 +24,6 @@ var
   destructorName = getIdent"destroy_"
   destructorParam = getIdent"this_"
   destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo())
-  rangeDestructorProc*: PSym
 
 proc instantiateDestructor(c: PContext, typ: PType): PType
 
@@ -141,9 +140,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
   case t.kind
   of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
     if instantiateDestructor(c, t.sons[0]) != nil:
-      if rangeDestructorProc == nil:
-        rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
-      t.destructor = rangeDestructorProc
+      t.destructor = getCompilerProc"nimDestroyRange"
       return t
     else:
       return nil
@@ -177,6 +174,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
   else:
     return nil
 
+proc createDestructorCall(c: PContext, s: PSym): PNode =
+  let varTyp = s.typ
+  if varTyp == nil or sfGlobal in s.flags: return
+  let destructableT = instantiateDestructor(c, varTyp)
+  if destructableT != nil:
+    let call = semStmt(c, newNode(nkCall, s.info, @[
+      useSym(destructableT.destructor), useSym(s)]))
+    result = newNode(nkDefer, s.info, @[call])
+
 proc insertDestructors(c: PContext,
                        varSection: PNode): tuple[outer, inner: PNode] =
   # Accepts a var or let section.
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index cd6ba3753..f1016595a 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -33,6 +33,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     if result.typ.kind == tyVar: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
     result.typ = newTypeS(tyEmpty, c)
+    result.typ.flags.incl tfVoid
   else:
     localError(n.info, errExprXHasNoType,
                renderTree(result, {renderNoComments}))
@@ -51,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result.typ = errorType(c)
   else:
     # XXX tyGenericInst here?
-    semProcvarCheck(c, result)
+    if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind == tyVar: result = newDeref(result)
     semDestructorCheck(c, result, flags)
 
@@ -123,6 +124,9 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         return newSymNode(u, n.info)
     result = newSymNode(s, n.info)
   of skVar, skLet, skResult, skForVar:
+    if s.magic == mNimvm:
+      localError(n.info, "illegal context for 'nimvm' magic")
+
     markUsed(n.info, s)
     styleCheckUse(n.info, s)
     # if a proc accesses a global variable, it is not side effect free:
@@ -288,8 +292,6 @@ proc semConv(c: PContext, n: PNode): PNode =
 
 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)
   result = newNodeI(nkCast, n.info)
   result.typ = semTypeNode(c, n.sons[0], nil)
@@ -448,18 +450,18 @@ proc changeType(n: PNode, newType: PType, check: bool) =
     let tup = newType.skipTypes({tyGenericInst})
     if tup.kind != tyTuple:
       if tup.kind == tyObject: return
-      internalError(n.info, "changeType: no tuple type for constructor")
+      globalError(n.info, "no tuple type for constructor")
     elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
       # named tuple?
       for i in countup(0, sonsLen(n) - 1):
         var m = n.sons[i].sons[0]
         if m.kind != nkSym:
-          internalError(m.info, "changeType(): invalid tuple constr")
+          globalError(m.info, "invalid tuple constructor")
           return
         if tup.n != nil:
           var f = getSymFromList(tup.n, m.sym.name)
           if f == nil:
-            internalError(m.info, "changeType(): invalid identifier")
+            globalError(m.info, "unknown identifier: " & m.sym.name.s)
             return
           changeType(n.sons[i].sons[1], f.typ, check)
         else:
@@ -474,7 +476,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
           addSon(a, m)
           changeType(m, tup.sons[i], check)
   of nkCharLit..nkUInt64Lit:
-    if check:
+    if check and n.kind != nkUInt64Lit:
       let value = n.intVal
       if value < firstOrd(newType) or value > lastOrd(newType):
         localError(n.info, errGenerated, "cannot convert " & $value &
@@ -596,8 +598,8 @@ proc skipObjConv(n: PNode): PNode =
   of nkObjUpConv, nkObjDownConv: result = n.sons[0]
   else: result = n
 
-proc isAssignable(c: PContext, n: PNode): TAssignableResult =
-  result = parampatterns.isAssignable(c.p.owner, n)
+proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
+  result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
 
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
   if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
@@ -805,6 +807,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       return semExpr(c, result, flags)
   else:
     n.sons[0] = semExpr(c, n.sons[0])
+    let t = n.sons[0].typ
+    if t != nil and t.kind == tyVar:
+      n.sons[0] = newDeref(n.sons[0])
   let nOrig = n.copyTree
   semOpAux(c, n)
   var t: PType = nil
@@ -814,7 +819,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # This is a proc variable, apply normal overload resolution
     let m = resolveIndirectCall(c, n, nOrig, t)
     if m.state != csMatch:
-      if c.inCompilesContext > 0:
+      if c.compilesContextId > 0:
         # speed up error generation:
         globalError(n.info, errTypeMismatch, "")
         return emptyNode
@@ -1152,7 +1157,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result.add(x[0])
     return
   checkMinSonsLen(n, 2)
-  n.sons[0] = semExprWithType(c, n.sons[0])
+  n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck})
   let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
   case arr.kind
   of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
@@ -1192,7 +1197,17 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
       localError(n.info, errIndexTypesDoNotMatch)
     result = n
   else:
-    c.p.bracketExpr = n.sons[0]
+    let s = if n.sons[0].kind == nkSym: n.sons[0].sym
+            elif n[0].kind in nkSymChoices: n.sons[0][0].sym
+            else: nil
+    if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators:
+      # type parameters: partial generic specialization
+      n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
+      result = explicitGenericInstantiation(c, n, s)
+    elif s != nil and s.kind == skType:
+      result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+    else:
+      c.p.bracketExpr = n.sons[0]
 
 proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   let oldBracketExpr = c.p.bracketExpr
@@ -1246,7 +1261,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
 template resultTypeIsInferrable(typ: PType): expr =
   typ.isMetaType and typ.kind != tyTypeDesc
 
-proc semAsgn(c: PContext, n: PNode): PNode =
+proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
   checkSonsLen(n, 2)
   var a = n.sons[0]
   case a.kind
@@ -1269,18 +1284,29 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     # --> `[]=`(a, i, x)
     let oldBracketExpr = c.p.bracketExpr
     a = semSubscript(c, a, {efLValue})
-    if a == nil:
+    if a == nil and mode != noOverloadedSubscript:
       result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
       add(result, n[1])
       result = semExprNoType(c, result)
       c.p.bracketExpr = oldBracketExpr
       return result
+    elif a == nil:
+      localError(n.info, "could not resolve: " & $n[0])
+      return n
     c.p.bracketExpr = oldBracketExpr
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
     result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
     add(result, n[1])
     return semExprNoType(c, result)
+  of nkPar:
+    if a.len >= 2:
+      # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
+      # that overloading of the assignment operator still works. Usually we
+      # prefer to do these rewritings in transf.nim:
+      return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner))
+    else:
+      a = semExprWithType(c, a, {efLValue})
   else:
     a = semExprWithType(c, a, {efLValue})
   n.sons[0] = a
@@ -1311,7 +1337,8 @@ proc semAsgn(c: PContext, n: PNode): PNode =
           typeMismatch(n, lhs.typ, rhs.typ)
 
     n.sons[1] = fitNode(c, le, rhs)
-    if tfHasAsgn in lhs.typ.flags and not lhsIsResult:
+    if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
+        mode != noOverloadedAsgn:
       return overloadedAsgn(c, lhs, n.sons[1])
 
     fixAbstractType(c, n)
@@ -1369,7 +1396,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
      c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
     if isEmptyType(result.typ):
       # we inferred a 'void' return type:
-      c.p.resultSym.typ = nil
+      c.p.resultSym.typ = errorType(c)
       c.p.owner.typ.sons[0] = nil
     else:
       localError(c.p.resultSym.info, errCannotInferReturnType)
@@ -1610,7 +1637,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
   let oldErrorCount = msgs.gErrorCounter
   let oldErrorMax = msgs.gErrorMax
-  inc c.inCompilesContext
+  let oldCompilesId = c.compilesContextId
+  inc c.compilesContextIdGenerator
+  c.compilesContextId = c.compilesContextIdGenerator
   # do not halt after first error:
   msgs.gErrorMax = high(int)
 
@@ -1628,12 +1657,15 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   let oldInGenericInst = c.inGenericInst
   let oldProcCon = c.p
   c.generics = @[]
+  var err: string
   try:
     result = semExpr(c, n, flags)
     if msgs.gErrorCounter != oldErrorCount: result = nil
   except ERecoverableError:
-    discard
+    if optReportConceptFailures in gGlobalOptions:
+      err = getCurrentExceptionMsg()
   # undo symbol table changes (as far as it's possible):
+  c.compilesContextId = oldCompilesId
   c.generics = oldGenerics
   c.inGenericContext = oldInGenericContext
   c.inUnrolledContext = oldInUnrolledContext
@@ -1642,10 +1674,11 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   msgs.setInfoContextLen(oldContextLen)
   setLen(gOwners, oldOwnerLen)
   c.currentScope = oldScope
-  dec c.inCompilesContext
   errorOutputs = oldErrorOutputs
   msgs.gErrorCounter = oldErrorCount
   msgs.gErrorMax = oldErrorMax
+  if optReportConceptFailures in gGlobalOptions and not err.isNil:
+    localError(n.info, err)
 
 proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # we replace this node by a 'true' or 'false' node:
@@ -1699,10 +1732,13 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   case s.magic # magics that need special treatment
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
+  #of mArrGet: result = semArrGet(c, n, flags)
+  #of mArrPut: result = semArrPut(c, n, flags)
+  #of mAsgn: result = semAsgnOpr(c, n)
   of mDefined: result = semDefined(c, setMs(n, s), false)
   of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
   of mCompiles: result = semCompiles(c, setMs(n, s), flags)
@@ -1719,6 +1755,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
     result.typ = getSysType(tyString)
   of mParallel:
+    if not experimentalMode(c):
+      localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
     result = setMs(n, s)
     var x = n.lastSon
     if x.kind == nkDo: x = x.sons[bodyPos]
@@ -1727,13 +1765,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     dec c.inParallelStmt
   of mSpawn:
     result = setMs(n, s)
-    result.sons[1] = semExpr(c, n.sons[1])
-    if not result[1].typ.isEmptyType:
-      if spawnResult(result[1].typ, c.inParallelStmt > 0) == srFlowVar:
-        result.typ = createFlowVar(c, result[1].typ, n.info)
+    for i in 1 .. <n.len:
+      result.sons[i] = semExpr(c, n.sons[i])
+    let typ = result[^1].typ
+    if not typ.isEmptyType:
+      if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
+        result.typ = createFlowVar(c, typ, n.info)
       else:
-        result.typ = result[1].typ
-      result.add instantiateCreateFlowVarCall(c, result[1].typ, n.info).newSymNode
+        result.typ = typ
+      result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
+    else:
+      result.add emptyNode
   of mProcCall:
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
@@ -1749,22 +1791,44 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
     if semCheck: result = semStmt(c, e) # do not open a new scope!
     else: result = e
 
+  # Check if the node is "when nimvm"
+  # when nimvm:
+  #   ...
+  # else:
+  #   ...
+  var whenNimvm = false
+  if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and
+      n.sons[1].kind == nkElse:
+    let exprNode = n.sons[0].sons[0]
+    if exprNode.kind == nkIdent:
+      whenNimvm = lookUp(c, exprNode).magic == mNimvm
+    elif exprNode.kind == nkSym:
+      whenNimvm = exprNode.sym.magic == mNimvm
+
   for i in countup(0, sonsLen(n) - 1):
     var it = n.sons[i]
     case it.kind
     of nkElifBranch, nkElifExpr:
       checkSonsLen(it, 2)
-      var e = semConstExpr(c, it.sons[0])
-      if e.kind != nkIntLit:
-        # can happen for cascading errors, assume false
-        # InternalError(n.info, "semWhen")
-        discard
-      elif e.intVal != 0 and result == nil:
-        setResult(it.sons[1])
+      if whenNimvm:
+        if semCheck:
+          it.sons[1] = semStmt(c, it.sons[1])
+        result = n # when nimvm is not elimited until codegen
+      else:
+        var e = semConstExpr(c, it.sons[0])
+        if e.kind != nkIntLit:
+          # can happen for cascading errors, assume false
+          # InternalError(n.info, "semWhen")
+          discard
+        elif e.intVal != 0 and result == nil:
+          setResult(it.sons[1])
     of nkElse, nkElseExpr:
       checkSonsLen(it, 1)
-      if result == nil:
-        setResult(it.sons[0])
+      if result == nil or whenNimvm:
+        if semCheck:
+          it.sons[0] = semStmt(c, it.sons[0])
+        if result == nil:
+          result = it.sons[0]
     else: illFormedAst(n)
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
@@ -2026,6 +2090,19 @@ proc semExport(c: PContext, n: PNode): PNode =
     c.module.ast.add x
   result = n
 
+proc shouldBeBracketExpr(n: PNode): bool =
+  assert n.kind in nkCallKinds
+  let a = n.sons[0]
+  if a.kind in nkCallKinds:
+    let b = a[0]
+    if b.kind in nkSymChoices:
+      for i in 0..<b.len:
+        if b[i].sym.magic == mArrGet:
+          let be = newNodeI(nkBracketExpr, n.info)
+          for i in 1..<a.len: be.add(a[i])
+          n.sons[0] = be
+          return true
+
 proc setGenericParams(c: PContext, n: PNode) =
   for i in 1 .. <n.len:
     n[i].typ = semTypeNode(c, n[i], nil)
@@ -2133,7 +2210,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       else:
         #liMessage(n.info, warnUser, renderTree(n));
         result = semIndirectOp(c, n, flags)
-    elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]):
+    elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
+        isSymChoice(n[0][0]):
       # indirectOp can deal with explicit instantiations; the fixes
       # the 'newSeq[T](x)' bug
       setGenericParams(c, n.sons[0])
@@ -2147,19 +2225,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       result = semWhen(c, n, true)
     else:
       result = semWhen(c, n, false)
-      result = semExpr(c, result, flags)
+      if result == n:
+        # This is a "when nimvm" stmt.
+        result = semWhen(c, n, true)
+      else:
+        result = semExpr(c, result, flags)
   of nkBracketExpr:
     checkMinSonsLen(n, 1)
-    var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
-    if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or
-        n[0].kind in nkSymChoices:
-      # type parameters: partial generic specialization
-      n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
-      result = explicitGenericInstantiation(c, n, s)
-    elif s != nil and s.kind in {skType}:
-      result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
-    else:
-      result = semArrayAccess(c, n, flags)
+    result = semArrayAccess(c, n, flags)
   of nkCurlyExpr:
     result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
   of nkPragmaExpr:
@@ -2254,7 +2327,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkStaticStmt:
     result = semStaticStmt(c, n)
   of nkDefer:
-    localError(n.info, errGenerated, "'defer' not allowed in this context")
+    n.sons[0] = semExpr(c, n.sons[0])
+    if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
+      localError(n.info, errGenerated, "'defer' takes a 'void' expression")
+    #localError(n.info, errGenerated, "'defer' not allowed in this context")
   else:
     localError(n.info, errInvalidExpressionX,
                renderTree(n, {renderNoComments}))
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index e086e73f8..2e6c6c3ea 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
     let L = forLoop.len
     let call = forLoop.sons[L-2]
     if call.len > 2:
-      localError(forLoop.info, errGenerated, 
+      localError(forLoop.info, errGenerated,
                  "parallel 'fields' iterator does not work for 'case' objects")
       return
     # iterate over the selector:
@@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # a 'while true: stmt; break' loop ...
   result = newNodeI(nkWhileStmt, n.info, 2)
   var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
-  if trueSymbol == nil: 
+  if trueSymbol == nil:
     localError(n.info, errSystemNeeds, "true")
     trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
     trueSymbol.typ = getSysType(tyBool)
@@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   result.sons[0] = newSymNode(trueSymbol, n.info)
   var stmts = newNodeI(nkStmtList, n.info)
   result.sons[1] = stmts
-  
+
   var length = sonsLen(n)
   var call = n.sons[length-2]
   if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
     localError(n.info, errWrongNumberOfVariables)
     return result
-  
+
   var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
   if tupleTypeA.kind notin {tyTuple, tyObject}:
     localError(n.info, errGenerated, "no object or tuple type")
@@ -129,7 +129,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
     var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
     if not sameType(tupleTypeA, tupleTypeB):
       typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
-  
+
   inc(c.p.nestedLoopCounter)
   if tupleTypeA.kind == tyTuple:
     var loopBody = n.sons[length-1]
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index da24005c2..5fe4e3299 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -179,7 +179,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
       else:
         result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
                                abs(getFloat(a.n.sons[0])))
-  of mAbsI, mAbsI64:
+  of mAbsI:
     let a = n.sons[1].typ
     if isIntRange(a):
       if a.n[0].intVal <= 0:
@@ -200,13 +200,13 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
     if isIntRange(a) and isIntLit(b):
       result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
                             pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
-  of mAddI, mAddI64, mAddU:
+  of mAddI, mAddU:
     commutativeOp(`|+|`)
-  of mMulI, mMulI64, mMulU:
+  of mMulI, mMulU:
     commutativeOp(`|*|`)
-  of mSubI, mSubI64, mSubU:
+  of mSubI, mSubU:
     binaryOp(`|-|`)
-  of mBitandI, mBitandI64:
+  of mBitandI:
     # since uint64 is still not even valid for 'range' (since it's no ordinal
     # yet), we exclude it from the list (see bug #1638) for now:
     var a = n.sons[1]
@@ -225,7 +225,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
         result = makeRange(a.typ, 0, b.intVal-1)
       else:
         result = makeRange(a.typ, b.intVal+1, 0)
-  of mModI, mModI64:
+  of mModI:
     # so ... if you ever wondered about modulo's signedness; this defines it:
     let a = n.sons[1]
     let b = n.sons[2]
@@ -234,7 +234,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
         result = makeRange(a.typ, -(b.intVal-1), b.intVal-1)
       else:
         result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
-  of mDivI, mDivI64, mDivU:
+  of mDivI, mDivU:
     binaryOp(`|div|`)
   of mMinI:
     commutativeOp(min)
@@ -243,8 +243,8 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
   else: discard
 
 discard """
-  mShlI, mShlI64,
-  mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
+  mShlI,
+  mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
 """
 
 proc evalIs(n, a: PNode): PNode =
@@ -285,7 +285,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n)
   of mNot: result = newIntNodeT(1 - getInt(a), n)
   of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
-  of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
+  of mBitnotI: result = newIntNodeT(not getInt(a), n)
   of mLengthStr, mXLenStr:
     if a.kind == nkNilLit: result = newIntNodeT(0, n)
     else: result = newIntNodeT(len(getStr(a)), n)
@@ -298,7 +298,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     result = newFloatNodeT(toFloat(int(getInt(a))), n)
   of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
   of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
-  of mAbsI, mAbsI64:
+  of mAbsI:
     if getInt(a) >= 0: result = a
     else: result = newIntNodeT(- getInt(a), n)
   of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
@@ -307,19 +307,19 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
   of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
   of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
-  of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n)
-  of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n)
-  of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n)
-  of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
-  of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
-  of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
+  of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
+  of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
+  of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
+  of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
+  of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
+  of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
   of mMinI:
     if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
     else: result = newIntNodeT(getInt(a), n)
   of mMaxI:
     if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
     else: result = newIntNodeT(getInt(b), n)
-  of mShlI, mShlI64:
+  of mShlI:
     case skipTypes(n.typ, abstractRange).kind
     of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
     of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
@@ -327,7 +327,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     of tyInt64, tyInt, tyUInt..tyUInt64:
       result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
     else: internalError(n.info, "constant folding for shl")
-  of mShrI, mShrI64:
+  of mShrI:
     case skipTypes(n.typ, abstractRange).kind
     of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
     of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
@@ -335,14 +335,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     of tyInt64, tyInt, tyUInt..tyUInt64:
       result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
     else: internalError(n.info, "constant folding for shr")
-  of mDivI, mDivI64:
+  of mDivI:
     let y = getInt(b)
     if y != 0:
-      result = newIntNodeT(getInt(a) div y, n)
-  of mModI, mModI64:
+      result = newIntNodeT(`|div|`(getInt(a), y), n)
+  of mModI:
     let y = getInt(b)
     if y != 0:
-      result = newIntNodeT(getInt(a) mod y, n)
+      result = newIntNodeT(`|mod|`(getInt(a), y), n)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
   of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
   of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@@ -359,11 +359,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
     else: result = newFloatNodeT(getFloat(a), n)
   of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
-  of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
+  of mLtI, mLtB, mLtEnum, mLtCh:
     result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
-  of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
+  of mLeI, mLeB, mLeEnum, mLeCh:
     result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
-  of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
+  of mEqI, mEqB, mEqEnum, mEqCh:
     result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
   of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
   of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
@@ -375,9 +375,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
   of mLeU, mLeU64:
     result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
-  of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
-  of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
-  of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
+  of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
+  of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
+  of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
   of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
   of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
   of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
@@ -430,14 +430,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mCompileOptionArg:
     result = newIntNodeT(ord(
       testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
-  of mNewString, mNewStringOfCap,
-     mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh,
-     mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq,
-     mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot,
-     mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn,
-     mParallel, mPlugin:
-    discard
-  else: internalError(a.info, "evalOp(" & $m & ')')
+  of mEqProc:
+    result = newIntNodeT(ord(
+        exprStructuralEquivalent(a, b, strictSymEquality=true)), n)
+  else: discard
 
 proc getConstIfExpr(c: PSym, n: PNode): PNode =
   result = nil
@@ -540,7 +536,7 @@ proc foldConv*(n, a: PNode; check = false): PNode =
   of tyFloat..tyFloat64:
     case skipTypes(a.typ, abstractRange).kind
     of tyInt..tyInt64, tyEnum, tyBool, tyChar:
-      result = newFloatNodeT(toFloat(int(getOrdValue(a))), n)
+      result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n)
     else:
       result = a
       result.typ = n.typ
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index db910600b..620453277 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -30,6 +30,7 @@ type
   GenericCtx = object
     toMixin: IntSet
     cursorInBody: bool # only for nimsuggest
+    bracketExpr: PNode
 
 type
   TSemGenericFlag = enum
@@ -39,9 +40,9 @@ type
 proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode
 
-proc semGenericStmtScope(c: PContext, n: PNode, 
+proc semGenericStmtScope(c: PContext, n: PNode,
                          flags: TSemGenericFlags,
-                         ctx: var GenericCtx): PNode = 
+                         ctx: var GenericCtx): PNode =
   openScope(c)
   result = semGenericStmt(c, n, flags, ctx)
   closeScope(c)
@@ -57,7 +58,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of skProc, skMethod, skIterators, skConverter:
+  of skProc, skMethod, skIterators, skConverter, skModule:
     result = symChoice(c, n, s, scOpen)
   of skTemplate:
     if macroToExpand(s):
@@ -73,7 +74,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
       result = semGenericStmt(c, result, {}, ctx)
     else:
       result = symChoice(c, n, s, scOpen)
-  of skGenericParam: 
+  of skGenericParam:
     if s.typ != nil and s.typ.kind == tyStatic:
       if s.typ.n != nil:
         result = s.typ.n
@@ -85,18 +86,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skParam:
     result = n
     styleCheckUse(n.info, s)
-  of skType: 
+  of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
       result = newSymNodeTypeDesc(s, n.info)
-    else: 
+    else:
       result = n
     styleCheckUse(n.info, s)
   else:
     result = newSymNode(s, n.info)
     styleCheckUse(n.info, s)
 
-proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
+proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var GenericCtx): PNode =
   result = n
   let ident = considerQuotedIdent(n)
@@ -118,13 +119,13 @@ proc newDot(n, b: PNode): PNode =
   result.add(n.sons[0])
   result.add(b)
 
-proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
+proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
                  ctx: var GenericCtx; isMacro: var bool): PNode =
   assert n.kind == nkDotExpr
   semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared} else: {}
-  
+
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
     result = semGenericStmtSymbol(c, n, s, ctx)
@@ -141,18 +142,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
       elif s.name.id in ctx.toMixin:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
-        let sym = semGenericStmtSymbol(c, n, s, ctx)
-        if sym.kind == nkSym:
-          result = newDot(result, symChoice(c, n, s, scForceOpen))
+        let syms = semGenericStmtSymbol(c, n, s, ctx)
+        if syms.kind == nkSym:
+          let choice = symChoice(c, n, s, scForceOpen)
+          choice.kind = nkClosedSymChoice
+          result = newDot(result, choice)
         else:
-          result = newDot(result, sym)
+          result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   let s = newSymS(skUnknown, getIdentNode(n), c)
   addPrelimDecl(c, s)
   styleCheckDef(n.info, s, kind)
 
-proc semGenericStmt(c: PContext, n: PNode, 
+proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
   result = n
   #if gCmd == cmdIdeTools: suggestStmt(c, n)
@@ -181,16 +184,16 @@ proc semGenericStmt(c: PContext, n: PNode,
     result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
   of nkMixinStmt:
     result = semMixinStmt(c, n, ctx.toMixin)
-  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: 
+  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
     let fn = n.sons[0]
     var s = qualifiedLookUp(c, fn, {})
     if s == nil and withinMixin notin flags and
-        fn.kind in {nkIdent, nkAccQuoted} and 
+        fn.kind in {nkIdent, nkAccQuoted} and
         considerQuotedIdent(fn).id notin ctx.toMixin:
       localError(n.info, errUndeclaredIdentifier, fn.renderTree)
-    
+
     var first = 0
     var mixinContext = false
     if s != nil:
@@ -220,19 +223,23 @@ proc semGenericStmt(c: PContext, n: PNode,
         # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent
         # the famous "undeclared identifier: it" bug:
         mixinContext = true
-      of skUnknown, skParam: 
+      of skUnknown, skParam:
         # Leave it as an identifier.
         discard
-      of skProc, skMethod, skIterators, skConverter:
+      of skProc, skMethod, skIterators, skConverter, skModule:
         result.sons[0] = symChoice(c, fn, s, scOption)
+        # do not check of 's.magic==mRoof' here because it might be some
+        # other '^' but after overload resolution the proper one:
+        if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^":
+          result.add ctx.bracketExpr
         first = 1
       of skGenericParam:
         result.sons[0] = newSymNodeTypeDesc(s, fn.info)
         styleCheckUse(fn.info, s)
         first = 1
-      of skType: 
+      of skType:
         # bad hack for generics:
-        if (s.typ != nil) and (s.typ.kind != tyGenericParam): 
+        if (s.typ != nil) and (s.typ.kind != tyGenericParam):
           result.sons[0] = newSymNodeTypeDesc(s, fn.info)
           styleCheckUse(fn.info, s)
           first = 1
@@ -244,34 +251,68 @@ proc semGenericStmt(c: PContext, n: PNode,
       result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
       first = 1
     # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
-    # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which 
+    # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
     # is not exported and yet the generic 'threadProcWrapper' works correctly.
     let flags = if mixinContext: flags+{withinMixin} else: flags
     for i in countup(first, sonsLen(result) - 1):
       result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
-  of nkIfStmt: 
-    for i in countup(0, sonsLen(n)-1): 
+  of nkCurlyExpr:
+    result = newNodeI(nkCall, n.info)
+    result.add newIdentNode(getIdent("{}"), n.info)
+    for i in 0 ..< n.len: result.add(n[i])
+    result = semGenericStmt(c, result, flags, ctx)
+  of nkBracketExpr:
+    result = newNodeI(nkCall, n.info)
+    result.add newIdentNode(getIdent("[]"), n.info)
+    for i in 0 ..< n.len: result.add(n[i])
+    withBracketExpr ctx, n.sons[0]:
+      result = semGenericStmt(c, result, flags, ctx)
+  of nkAsgn, nkFastAsgn:
+    checkSonsLen(n, 2)
+    let a = n.sons[0]
+    let b = n.sons[1]
+
+    let k = a.kind
+    case k
+    of nkCurlyExpr:
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent("{}="), n.info)
+      for i in 0 ..< a.len: result.add(a[i])
+      result.add(b)
+      result = semGenericStmt(c, result, flags, ctx)
+    of nkBracketExpr:
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent("[]="), n.info)
+      for i in 0 ..< a.len: result.add(a[i])
+      result.add(b)
+      withBracketExpr ctx, a.sons[0]:
+        result = semGenericStmt(c, result, flags, ctx)
+    else:
+      for i in countup(0, sonsLen(n) - 1):
+        result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
+  of nkIfStmt:
+    for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx)
   of nkWhenStmt:
     for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx)
-  of nkWhileStmt: 
+  of nkWhileStmt:
     openScope(c)
-    for i in countup(0, sonsLen(n)-1): 
+    for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
     closeScope(c)
-  of nkCaseStmt: 
+  of nkCaseStmt:
     openScope(c)
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1): 
+    for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
       checkMinSonsLen(a, 1)
       var L = sonsLen(a)
-      for j in countup(0, L-2): 
+      for j in countup(0, L-2):
         a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
       a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
     closeScope(c)
-  of nkForStmt, nkParForStmt: 
+  of nkForStmt, nkParForStmt:
     var L = sonsLen(n)
     openScope(c)
     n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
@@ -279,27 +320,27 @@ proc semGenericStmt(c: PContext, n: PNode,
       addTempDecl(c, n.sons[i], skForVar)
     n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
     closeScope(c)
-  of nkBlockStmt, nkBlockExpr, nkBlockType: 
+  of nkBlockStmt, nkBlockExpr, nkBlockType:
     checkSonsLen(n, 2)
     openScope(c)
-    if n.sons[0].kind != nkEmpty: 
+    if n.sons[0].kind != nkEmpty:
       addTempDecl(c, n.sons[0], skLabel)
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
     closeScope(c)
-  of nkTryStmt: 
+  of nkTryStmt:
     checkMinSonsLen(n, 2)
     n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1): 
+    for i in countup(1, sonsLen(n)-1):
       var a = n.sons[i]
       checkMinSonsLen(a, 1)
       var L = sonsLen(a)
-      for j in countup(0, L-2): 
+      for j in countup(0, L-2):
         a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
-  of nkVarSection, nkLetSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkVarSection, nkLetSection:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
@@ -307,79 +348,79 @@ proc semGenericStmt(c: PContext, n: PNode,
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
       for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skVar)
-  of nkGenericParams: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkGenericParams:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if (a.kind != nkIdentDefs): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
-      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) 
-      # do not perform symbol lookup for default expressions 
-      for j in countup(0, L-3): 
+      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
+      # do not perform symbol lookup for default expressions
+      for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skType)
-  of nkConstSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkConstSection:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkConstDef): illFormedAst(a)
       checkSonsLen(a, 3)
       addTempDecl(c, getIdentNode(a.sons[0]), skConst)
       a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
       a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
   of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a)
       checkSonsLen(a, 3)
       addTempDecl(c, getIdentNode(a.sons[0]), skType)
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a)
       checkSonsLen(a, 3)
-      if a.sons[1].kind != nkEmpty: 
+      if a.sons[1].kind != nkEmpty:
         openScope(c)
         a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
         closeScope(c)
-      else: 
+      else:
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
-  of nkEnumTy: 
+  of nkEnumTy:
     if n.sonsLen > 0:
-      if n.sons[0].kind != nkEmpty: 
+      if n.sons[0].kind != nkEmpty:
         n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-      for i in countup(1, sonsLen(n) - 1): 
+      for i in countup(1, sonsLen(n) - 1):
         var a: PNode
         case n.sons[i].kind
         of nkEnumFieldDef: a = n.sons[i].sons[0]
         of nkIdent: a = n.sons[i]
         else: illFormedAst(n)
-        addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
+        addDecl(c, newSymS(skUnknown, getIdentNode(a), c))
   of nkObjectTy, nkTupleTy, nkTupleClassTy:
     discard
   of nkFormalParams:
     checkMinSonsLen(n, 1)
-    if n.sons[0].kind != nkEmpty: 
+    if n.sons[0].kind != nkEmpty:
       n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-    for i in countup(1, sonsLen(n) - 1): 
+    for i in countup(1, sonsLen(n) - 1):
       var a = n.sons[i]
       if (a.kind != nkIdentDefs): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
       a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
-      for j in countup(0, L-3): 
+      for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skParam)
-  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, 
-     nkIteratorDef, nkLambdaKinds: 
+  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
+     nkIteratorDef, nkLambdaKinds:
     checkSonsLen(n, bodyPos + 1)
     if n.sons[namePos].kind != nkEmpty:
       addTempDecl(c, getIdentNode(n.sons[0]), skProc)
     openScope(c)
-    n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], 
+    n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
                                               flags, ctx)
-    if n.sons[paramsPos].kind != nkEmpty: 
+    if n.sons[paramsPos].kind != nkEmpty:
       if n.sons[paramsPos].sons[0].kind != nkEmpty:
         addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
@@ -394,7 +435,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     checkMinSonsLen(n, 2)
     result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
   else:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
 
 proc semGenericStmt(c: PContext, n: PNode): PNode =
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index b2aef63a8..2c767ffc6 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -10,14 +10,10 @@
 # This module implements the instantiation of generic procs.
 # included from sem.nim
 
-proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
-                                 entry: var TInstantiation) =
-  if n.kind != nkGenericParams:
-    internalError(n.info, "instantiateGenericParamList; no generic params")
-  newSeq(entry.concreteTypes, n.len)
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+  internalAssert n.kind == nkGenericParams
   for i, a in n.pairs:
-    if a.kind != nkSym:
-      internalError(a.info, "instantiateGenericParamList; no symbol")
+    internalAssert a.kind == nkSym
     var q = a.sym
     if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
       continue
@@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
       #t = ReplaceTypeVarsT(cl, t)
     s.typ = t
     if t.kind == tyStatic: s.ast = t.n
-    addDecl(c, s)
-    entry.concreteTypes[i] = t
+    yield s
 
 proc sameInstantiation(a, b: TInstantiation): bool =
   if a.concreteTypes.len == b.concreteTypes.len:
@@ -52,10 +47,11 @@ proc sameInstantiation(a, b: TInstantiation): bool =
                           flags = {ExactTypeDescValues}): return
     result = true
 
-proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym =
+proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
+                     id: CompilesId): PSym =
   if genericSym.procInstCache != nil:
     for inst in genericSym.procInstCache:
-      if sameInstantiation(entry, inst[]):
+      if inst.compilesId == id and sameInstantiation(entry, inst[]):
         return inst.sym
 
 proc removeDefaultParamValues(n: PNode) =
@@ -169,7 +165,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   addDecl(c, prc)
 
   pushInfoContext(info)
-  var cl = initTypeVars(c, pt, info)
+  var cl = initTypeVars(c, pt, info, nil)
   var result = instCopyType(cl, prc.typ)
   let originalParams = result.n
   result.n = originalParams.shallowCopy
@@ -217,7 +213,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   ## 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
+  internalAssert fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
   if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
   inc(c.instCounter)
@@ -226,30 +222,49 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
   c.friendModules.add(getModule(fn))
-  #let oldScope = c.currentScope
-  #c.currentScope = fn.scope
+  let oldInTypeClass = c.inTypeClass
+  c.inTypeClass = 0
+  let oldScope = c.currentScope
+  while not isTopLevel(c): c.currentScope = c.currentScope.parent
   result = copySym(fn, false)
   incl(result.flags, sfFromGeneric)
   result.owner = fn
   result.ast = n
   pushOwner(result)
+
   openScope(c)
-  internalAssert n.sons[genericParamsPos].kind != nkEmpty
+  let gp = n.sons[genericParamsPos]
+  internalAssert gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
   pushInfoContext(info)
   var entry = TInstantiation.new
   entry.sym = result
-  instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
+  # we need to compare both the generic types and the concrete types:
+  # generic[void](), generic[int]()
+  # see ttypeor.nim test.
+  var i = 0
+  newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
+  for s in instantiateGenericParamList(c, gp, pt):
+    addDecl(c, s)
+    entry.concreteTypes[i] = s.typ
+    inc i
   pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
+  for j in 1 .. result.typ.len-1:
+    entry.concreteTypes[i] = result.typ.sons[j]
+    inc i
+  if tfTriggersCompileTime in result.typ.flags:
+    incl(result.flags, sfCompileTime)
   n.sons[genericParamsPos] = ast.emptyNode
-  var oldPrc = genericCacheGet(fn, entry[])
+  var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # we MUST not add potentially wrong instantiations to the caching mechanism.
     # This means recursive instantiations behave differently when in
     # a ``compiles`` context but this is the lesser evil. See
     # bug #1055 (tevilcompiles).
-    if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry)
+    #if c.compilesContextId == 0:
+    entry.compilesId = c.compilesContextId
+    fn.procInstCache.safeAdd(entry)
     c.generics.add(makeInstPair(fn, entry))
     if n.sons[pragmasPos].kind != nkEmpty:
       pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
@@ -264,7 +279,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popInfoContext()
   closeScope(c)           # close scope for parameters
   popOwner()
-  #c.currentScope = oldScope
+  c.currentScope = oldScope
   discard c.friendModules.pop()
   dec(c.instCounter)
+  c.inTypeClass = oldInTypeClass
   if result.kind == skMethod: finishMethod(c, result)
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index bb9814a16..150680af7 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -38,14 +38,16 @@ proc annotateType*(n: PNode, t: PType) =
   # Note: x can be unequal to t and we need to be careful to use 't'
   # to not to skip tyGenericInst
   case n.kind
+  of nkObjConstr:
+    n.typ = t
+    for i in 1 .. <n.len:
+      let field = x.n.ithField(i - 1)
+      if field.isNil: globalError n.info, "invalid field at index " & $i
+      else:
+        internalAssert(n.sons[i].kind == nkExprColonExpr)
+        annotateType(n.sons[i].sons[1], field.typ)
   of nkPar:
-    if x.kind == tyObject:
-      n.typ = t
-      for i in 0 .. <n.len:
-        let field = x.n.ithField(i)
-        if field.isNil: globalError n.info, "invalid field at index " & $i
-        else: annotateType(n.sons[i], field.typ)
-    elif x.kind == tyTuple:
+    if x.kind == tyTuple:
       n.typ = t
       for i in 0 .. <n.len:
         if i >= x.len: globalError n.info, "invalid field at index " & $i
@@ -53,7 +55,7 @@ proc annotateType*(n: PNode, t: PType) =
     elif x.kind == tyProc and x.callConv == ccClosure:
       n.typ = t
     else:
-      globalError(n.info, "() must have an object or tuple type")
+      globalError(n.info, "() must have a tuple type")
   of nkBracket:
     if x.kind in {tyArrayConstr, tyArray, tySequence, tyOpenArray}:
       n.typ = t
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 0a7846f1d..deef38ae3 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -10,10 +10,12 @@
 # This include file implements the semantic checking for magics.
 # included from sem.nim
 
-proc semAddr(c: PContext; n: PNode): PNode =
+proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
   result = newNodeI(nkAddr, n.info)
   let x = semExprWithType(c, n)
-  if isAssignable(c, x) notin {arLValue, arLocalLValue}:
+  if x.kind == nkSym:
+    x.sym.flags.incl(sfAddrTaken)
+  if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
     localError(n.info, errExprHasNoAddress)
   result.add x
   result.typ = makePtrType(c, x.typ)
@@ -24,6 +26,41 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
   result.add typExpr
   result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter}))
 
+type
+  SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
+
+proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
+proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
+
+proc skipAddr(n: PNode): PNode {.inline.} =
+  (if n.kind == nkHiddenAddr: n.sons[0] else: n)
+
+proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
+  result = newNodeI(nkBracketExpr, n.info)
+  for i in 1..<n.len: result.add(n[i])
+  let oldBracketExpr = c.p.bracketExpr
+  result = semSubscript(c, result, flags)
+  c.p.bracketExpr = oldBracketExpr
+  if result.isNil:
+    localError(n.info, "could not resolve: " & $n)
+    result = n
+
+proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
+  # rewrite `[]=`(a, i, x)  back to ``a[i] = x``.
+  let b = newNodeI(nkBracketExpr, n.info)
+  b.add(n[1].skipAddr)
+  for i in 2..n.len-2: b.add(n[i])
+  result = newNodeI(nkAsgn, n.info, 2)
+  result.sons[0] = b
+  result.sons[1] = n.lastSon
+  result = semAsgn(c, result, noOverloadedSubscript)
+
+proc semAsgnOpr(c: PContext; n: PNode): PNode =
+  result = newNodeI(nkAsgn, n.info, 2)
+  result.sons[0] = n[1]
+  result.sons[1] = n[2]
+  result = semAsgn(c, result, noOverloadedAsgn)
+
 proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var r = isPartOf(n[1], n[2])
   result = newIntNodeT(ord(r), n)
@@ -119,10 +156,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
+  of mArrGet: result = semArrGet(c, n, flags)
+  of mArrPut: result = semArrPut(c, n, flags)
+  of mAsgn: result = semAsgnOpr(c, n)
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
@@ -143,25 +183,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
       if isNegative(n.sons[1]) or (n.len > 2 and isNegative(n.sons[2])):
         localError(n.info, "use '^' instead of '-'; negative indexing is obsolete")
   of mRoof:
-    # error correction:
-    result = n.sons[1]
-    if c.p.bracketExpr.isNil:
+    let bracketExpr = if n.len == 3: n.sons[2] else: c.p.bracketExpr
+    if bracketExpr.isNil:
       localError(n.info, "no surrounding array access context for '^'")
-    elif c.p.bracketExpr.checkForSideEffects != seNoSideEffect:
+      result = n.sons[1]
+    elif bracketExpr.checkForSideEffects != seNoSideEffect:
       localError(n.info, "invalid context for '^' as '$#' has side effects" %
-        renderTree(c.p.bracketExpr))
-    elif c.p.bracketExpr.typ.isStrangeArray:
+        renderTree(bracketExpr))
+      result = n.sons[1]
+    elif bracketExpr.typ.isStrangeArray:
       localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
-        renderTree(c.p.bracketExpr))
+        renderTree(bracketExpr))
+      result = n.sons[1]
     else:
       # ^x  is rewritten to: len(a)-x
       let lenExpr = newNodeI(nkCall, n.info)
       lenExpr.add newIdentNode(getIdent"len", n.info)
-      lenExpr.add c.p.bracketExpr
+      lenExpr.add bracketExpr
       let lenExprB = semExprWithType(c, lenExpr)
       if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
         localError(n.info, "'$#' has to be of an ordinal type for '^'" %
           renderTree(lenExpr))
+        result = n.sons[1]
       else:
         result = newNodeIT(nkCall, n.info, getSysType(tyInt))
         result.add newSymNode(createMagic("-", mSubI), n.info)
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index fbcd6b6da..b04ba4657 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -128,10 +128,10 @@ 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)
+    localError(a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)")
   of impYes: discard
   of impNo:
-    localError(a.info, "can prove: " & ?a & " > " & ?b)
+    localError(a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
 
 proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
   checkLe(c, arr.lowBound, idx)
@@ -156,19 +156,23 @@ proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
 
 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)
+  case proveLe(m, c, y)
   of impUnknown:
-    localError(x.info,
-      "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
-        [?x, ?d, ?x, ?y, ?c, ?d])
+    case proveLe(m, x, d)
+    of impNo: discard
+    of impUnknown, impYes:
+      localError(x.info,
+        "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
+            [?c, ?y, ?x, ?y, ?c, ?d])
   of impYes:
-    case proveLe(m, c, y)
+    case proveLe(m, x, d)
     of impUnknown:
       localError(x.info,
         "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
-          [?c, ?y, ?x, ?y, ?c, ?d])
+          [?x, ?d, ?x, ?y, ?c, ?d])
     of impYes:
-      localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" % [?x, ?y, ?c, ?d])
+      localError(x.info, "($#)..($#) not disjoint from ($#)..($#)" %
+                [?c, ?y, ?x, ?y, ?c, ?d])
     of impNo: discard
   of impNo: discard
 
@@ -278,10 +282,12 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
         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])
+    let slice = n[2].skipStmtList
+    c.addSlice(n, n[1], slice[1], slice[2])
     analyseSons(c, n)
   elif op.name.s == "[]=" and op.fromSystem:
-    c.addSlice(n, n[1], n[2][1], n[2][2])
+    let slice = n[2].skipStmtList
+    c.addSlice(n, n[1], slice[1], slice[2])
     analyseSons(c, n)
   else:
     analyseSons(c, n)
@@ -363,7 +369,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
             else: internalError(it.info, "slot already has a lower bound")
         if not isSpawned: analyse(c, value)
   of nkCaseStmt: analyseCase(c, n)
-  of nkIfStmt, nkIfExpr: analyseIf(c, n)
+  of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n)
   of nkWhileStmt:
     analyse(c, n.sons[0])
     # 'while true' loop?
@@ -395,8 +401,9 @@ proc transformSlices(n: PNode): PNode =
       result = copyNode(n)
       result.add opSlice.newSymNode
       result.add n[1]
-      result.add n[2][1]
-      result.add n[2][2]
+      let slice = n[2].skipStmtList
+      result.add slice[1]
+      result.add slice[2]
       return result
   if n.safeLen > 0:
     result = shallowCopy(n)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index adf03be64..ef014963c 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards
+  wordrecg, strutils, options, guards, writetracking
 
 # Second semantic checking pass over the AST. Necessary because the old
 # way had some inherent problems. Performs:
@@ -17,7 +17,7 @@ import
 # * effect+exception tracking
 # * "usage before definition" checking
 # * checks for invalid usages of compiletime magics (not implemented)
-# * checks for invalid usages of PNimNode (not implemented)
+# * checks for invalid usages of NimNode (not implemented)
 # * later: will do an escape analysis for closures at least
 
 # Predefined effects:
@@ -29,21 +29,6 @@ import
 #   --> a TR macro can annotate the proc with user defined annotations
 #   --> the effect system can access these
 
-# Load&Store analysis is performed on *paths*. A path is an access like
-# obj.x.y[i].z; splitting paths up causes some problems:
-#
-# var x = obj.x
-# var z = x.y[i].z
-#
-# Alias analysis is affected by this too! A good solution is *type splitting*:
-# T becomes T1 and T2 if it's known that T1 and T2 can't alias.
-#
-# An aliasing problem and a race condition are effectively the same problem.
-# Type based alias analysis is nice but not sufficient; especially splitting
-# an array and filling it in parallel should be supported but is not easily
-# done: It essentially requires a built-in 'indexSplit' operation and dependent
-# typing.
-
 # ------------------------ exception and tag tracking -------------------------
 
 discard """
@@ -207,9 +192,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
                                         a.owner, reason.info)
 
-proc listGcUnsafety(s: PSym; onlyWarning: bool) =
+proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
   let u = s.gcUnsafetyReason
-  if u != nil:
+  if u != nil and not cycleCheck.containsOrIncl(u.id):
     let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
     if u.kind in {skLet, skVar}:
       message(s.info, msgKind,
@@ -218,7 +203,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
     elif u.kind in routineKinds:
       # recursive call *always* produces only a warning so the full error
       # message is printed:
-      listGcUnsafety(u, true)
+      listGcUnsafety(u, true, cycleCheck)
       message(s.info, msgKind,
         "'$#' is not GC-safe as it calls '$#'" %
         [s.name.s, u.name.s])
@@ -227,6 +212,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
       message(u.info, msgKind,
         "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
 
+proc listGcUnsafety(s: PSym; onlyWarning: bool) =
+  var cycleCheck = initIntSet()
+  listGcUnsafety(s, onlyWarning, cycleCheck)
+
 proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
@@ -237,9 +226,10 @@ proc useVar(a: PEffects, n: PNode) =
         message(n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
-  if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
+  if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet}:
     if s.guard != nil: guardGlobal(a, n, s.guard)
-    if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
+    if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
+        (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
       #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
       markGcUnsafe(a, s)
 
@@ -433,17 +423,41 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
     result = newNode(nkExprColonExpr, n.info, @[
       newIdentNode(getIdent(specialWords[effectType]), n.info), effects])
 
+proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
+  let s = n.sons[namePos].sym
+  let params = s.typ.n
+
+  var effects = newNodeI(nkBracket, n.info)
+  for i in 1 ..< params.len:
+    if params[i].kind == nkSym and flag in params[i].sym.flags:
+      effects.add params[i]
+
+  if effects.len > 0:
+    result = newNode(nkExprColonExpr, n.info, @[
+      newIdentNode(getIdent(pragmaName), n.info), effects])
+
+proc documentNewEffect(n: PNode): PNode =
+  let s = n.sons[namePos].sym
+  if tfReturnsNew in s.typ.flags:
+    result = newIdentNode(getIdent("new"), n.info)
+
 proc documentRaises*(n: PNode) =
   if n.sons[namePos].kind != nkSym: return
   let pragmas = n.sons[pragmasPos]
   let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects)
   let p2 = documentEffect(n, pragmas, wTags, tagEffects)
+  let p3 = documentWriteEffect(n, sfWrittenTo, "writes")
+  let p4 = documentNewEffect(n)
+  let p5 = documentWriteEffect(n, sfEscapes, "escapes")
 
-  if p1 != nil or p2 != nil:
+  if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
     if pragmas.kind == nkEmpty:
       n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
     if p1 != nil: n.sons[pragmasPos].add p1
     if p2 != nil: n.sons[pragmasPos].add p2
+    if p3 != nil: n.sons[pragmasPos].add p3
+    if p4 != nil: n.sons[pragmasPos].add p4
+    if p5 != nil: n.sons[pragmasPos].add p5
 
 template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
@@ -721,7 +735,7 @@ proc track(tracked: PEffects, n: PNode) =
       # since 'var (a, b): T = ()' is not even allowed, there is always type
       # inference for (a, b) and thus no nil checking is necessary.
   of nkCaseStmt: trackCase(tracked, n)
-  of nkIfStmt, nkIfExpr: trackIf(tracked, n)
+  of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
   of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
   of nkWhileStmt:
     track(tracked, n.sons[0])
@@ -748,7 +762,7 @@ proc track(tracked: PEffects, n: PNode) =
     for i in 1 .. <len(n):
       let x = n.sons[i]
       track(tracked, x)
-      if sfDiscriminant in x.sons[0].sym.flags:
+      if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
         addDiscriminantFact(tracked.guards, x)
     setLen(tracked.guards, oldFacts)
   of nkPragmaBlock:
@@ -841,6 +855,8 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
+  effects.sons[usesEffects] = ast.emptyNode
+  effects.sons[writeEffects] = ast.emptyNode
 
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
@@ -895,6 +911,7 @@ proc trackProc*(s: PSym, body: PNode) =
     message(s.info, warnLockLevel,
       "declared lock level is $1, but real lock level is $2" %
         [$s.typ.lockLevel, $t.maxLockLevel])
+  when useWriteTracking: trackWrites(s, body)
 
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index c355a5bf1..adb1c81c1 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
   else:
     result.add identDefs
 
+proc addDefer(c: PContext; result: var PNode; s: PSym) =
+  let deferDestructorCall = createDestructorCall(c, s)
+  if deferDestructorCall != nil:
+    if result.kind != nkStmtList:
+      let oldResult = result
+      result = newNodeI(nkStmtList, result.info)
+      result.add oldResult
+    result.add deferDestructorCall
+
 proc isDiscardUnderscore(v: PSym): bool =
   if v.name.s == "_":
     v.flags.incl(sfGenSym)
@@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         if def.kind == nkPar: v.ast = def[j]
         v.typ = tup.sons[j]
         b.sons[j] = newSymNode(v)
+      addDefer(c, result, v)
       checkNilable(v)
       if sfCompileTime in v.flags: hasCompileTime = true
   if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
@@ -744,13 +754,62 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
       if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
         checkForMetaFields(s.typ.n)
 
+proc semAllTypeSections(c: PContext; n: PNode): PNode =
+  proc gatherStmts(c: PContext; n: PNode; result: PNode) {.nimcall.} =
+    case n.kind
+    of nkIncludeStmt:
+      for i in 0..<n.len:
+        var f = checkModuleName(n.sons[i])
+        if f != InvalidFileIDX:
+          if containsOrIncl(c.includedFiles, f):
+            localError(n.info, errRecursiveDependencyX, f.toFilename)
+          else:
+            let code = gIncludeFile(c.module, f)
+            gatherStmts c, code, result
+            excl(c.includedFiles, f)
+    of nkStmtList:
+      for i in 0 ..< n.len:
+        gatherStmts(c, n.sons[i], result)
+    of nkTypeSection:
+      incl n.flags, nfSem
+      typeSectionLeftSidePass(c, n)
+      result.add n
+    else:
+      result.add n
+
+  result = newNodeI(nkStmtList, n.info)
+  gatherStmts(c, n, result)
+
+  template rec(name) =
+    for i in 0 ..< result.len:
+      if result[i].kind == nkTypeSection:
+        name(c, result[i])
+
+  rec typeSectionRightSidePass
+  rec typeSectionFinalPass
+  when false:
+    # too beautiful to delete:
+    template rec(name; setbit=false) =
+      proc `name rec`(c: PContext; n: PNode) {.nimcall.} =
+        if n.kind == nkTypeSection:
+          when setbit: incl n.flags, nfSem
+          name(c, n)
+        elif n.kind == nkStmtList:
+          for i in 0 ..< n.len:
+            `name rec`(c, n.sons[i])
+      `name rec`(c, n)
+    rec typeSectionLeftSidePass, true
+    rec typeSectionRightSidePass
+    rec typeSectionFinalPass
+
 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)
+  if sfNoForward notin c.module.flags or nfSem notin n.flags:
+    typeSectionLeftSidePass(c, n)
+    typeSectionRightSidePass(c, n)
+    typeSectionFinalPass(c, n)
   result = n
 
 proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
@@ -898,27 +957,35 @@ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
 proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   var n = n
 
-  n = replaceTypesInBody(c, pt, n)
-  result = n
+  let original = n.sons[namePos].sym
+  let s = copySym(original, false)
+  incl(s.flags, sfFromGeneric)
 
+  n = replaceTypesInBody(c, pt, n, original)
+  result = n
+  s.ast = result
+  n.sons[namePos].sym = s
   n.sons[genericParamsPos] = emptyNode
-  n.sons[paramsPos] = n.typ.n
-
+  let params = n.typ.n
+  n.sons[paramsPos] = params
+  s.typ = n.typ
+  for i in 1..<params.len:
+    if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
+                              tyFromExpr, tyFieldAccessor}+tyTypeClasses:
+      localError(params[i].info, "cannot infer type of parameter: " &
+                 params[i].sym.name.s)
   openScope(c)
-  var s = n.sons[namePos].sym
   pushOwner(s)
-  addParams(c, n.typ.n, skProc)
+  addParams(c, params, skProc)
   pushProcCon(c, s)
   addResult(c, n.typ.sons[0], n.info, skProc)
   addResultNode(c, n)
   let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-  n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
+  n.sons[bodyPos] = transformBody(c.module, semBody, s)
   popProcCon(c)
   popOwner()
   closeScope(c)
 
-  s.ast = result
-
   # alternative variant (not quite working):
   # var prc = arg[0].sym
   # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
@@ -974,6 +1041,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
     incl(s.flags, sfUsed)
   of "=":
+    if s.magic == mAsgn: return
     incl(s.flags, sfUsed)
     let t = s.typ
     if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
@@ -1033,12 +1101,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     n.sons[namePos] = newSymNode(s)
     s.ast = n
     #s.scope = c.currentScope
-
-    if sfNoForward in c.module.flags and
-       sfSystemModule notin c.module.flags:
-      addInterfaceOverloadableSymAt(c, c.currentScope, s)
-      s.flags.incl sfForward
-      return
+    when false:
+      # disable for now
+      if sfNoForward in c.module.flags and
+         sfSystemModule notin c.module.flags:
+        addInterfaceOverloadableSymAt(c, c.currentScope, s)
+        s.flags.incl sfForward
+        return
   else:
     s = n[namePos].sym
     s.owner = getCurrOwner()
@@ -1071,6 +1140,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # semParamList(c, n.sons[ParamsPos], nil, s)
   else:
     s.typ = newProcType(c, n.info)
+  if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
   if n.sons[patternPos].kind != nkEmpty:
     n.sons[patternPos] = semPattern(c, n.sons[patternPos])
   if s.kind in skIterators:
@@ -1160,6 +1230,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   if n.sons[patternPos].kind != nkEmpty:
     c.patterns.add(s)
   if isAnon: result.typ = s.typ
+  if isTopLevel(c) and s.kind != skClosureIterator and
+      s.typ.callConv == ccClosure:
+    message(s.info, warnDeprecated, "top level '.closure' calling convention")
 
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
@@ -1268,6 +1341,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
     of wLocks:
       result = n
       result.typ = n.sons[1].typ
+    of wNoRewrite:
+      incl(result.flags, nfNoRewrite)
     else: discard
 
 proc semStaticStmt(c: PContext, n: PNode): PNode =
@@ -1319,7 +1394,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
   for i in countup(0, length - 1):
     let k = n.sons[i].kind
     case k
-    of nkFinally, nkExceptBranch, nkDefer:
+    of nkFinally, nkExceptBranch:
       # stand-alone finally and except blocks are
       # transformed into regular try blocks:
       #
@@ -1360,7 +1435,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
             localError(result.info, "type class predicate failed")
         of tyUnknown: continue
         else: discard
-      if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
+      if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
         voidContext = true
         n.typ = enforceVoidContext
       if i == last and (length == 1 or efWantValue in flags):
@@ -1372,21 +1447,13 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
         n.typ = n.sons[i].typ
         if not isEmptyType(n.typ): n.kind = nkStmtListExpr
       case n.sons[i].kind
-      of nkVarSection, nkLetSection:
-        let (outer, inner) = insertDestructors(c, n.sons[i])
-        if outer != nil:
-          n.sons[i] = outer
-          var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
-          inner.addSon(semStmtList(c, rest, flags))
-          n.sons.setLen(i+1)
-          return
       of LastBlockStmts:
         for j in countup(i + 1, length - 1):
           case n.sons[j].kind
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: discard
-  if result.len == 1:
+  if result.len == 1 and result.sons[0].kind != nkDefer:
     result = result.sons[0]
   when defined(nimfix):
     if result.kind == nkCommentStmt and not result.comment.isNil and
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 161d22fc1..2dda8276d 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -110,6 +110,13 @@ type
     toBind, toMixin, toInject: IntSet
     owner: PSym
     cursorInBody: bool # only for nimsuggest
+    bracketExpr: PNode
+
+template withBracketExpr(ctx, x, body: untyped) =
+  let old = ctx.bracketExpr
+  ctx.bracketExpr = x
+  body
+  ctx.bracketExpr = old
 
 proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
   case n.kind
@@ -184,10 +191,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
   else:
     let ident = getIdentNode(c, n)
     if not isTemplParam(c, ident):
-      let local = newGenSym(k, ident, c)
-      addPrelimDecl(c.c, local)
-      styleCheckDef(n.info, local)
-      replaceIdentBySym(n, newSymNode(local, n.info))
+      # fix #2670, consider:
+      #
+      # when b:
+      #    var a = "hi"
+      # else:
+      #    var a = 5
+      # echo a
+      #
+      # We need to ensure that both 'a' produce the same gensym'ed symbol.
+      # So we need only check the *current* scope.
+      let s = localSearchInScope(c.c, considerQuotedIdent ident)
+      if s != nil and s.owner == c.owner and sfGenSym in s.flags:
+        styleCheckUse(n.info, s)
+        replaceIdentBySym(n, newSymNode(s, n.info))
+      else:
+        let local = newGenSym(k, ident, c)
+        addPrelimDecl(c.c, local)
+        styleCheckDef(n.info, local)
+        replaceIdentBySym(n, newSymNode(local, n.info))
     else:
       replaceIdentBySym(n, ident)
 
@@ -260,6 +282,53 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
       addLocalDecl(c, a.sons[j], symKind)
 
 proc semPattern(c: PContext, n: PNode): PNode
+
+proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
+  result = n
+  for i in 0.. < n.len:
+    result.sons[i] = semTemplBody(c, n.sons[i])
+
+proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode =
+  let ident = getIdent(opr)
+  if ident.id in c.toInject: return n
+
+  let s = searchInScopes(c.c, ident)
+  if s != nil:
+    var callee: PNode
+    if contains(c.toBind, s.id):
+      callee = symChoice(c.c, n, s, scClosed)
+    elif contains(c.toMixin, s.name.id):
+      callee = symChoice(c.c, n, s, scForceOpen)
+    elif s.owner == c.owner and sfGenSym in s.flags:
+      # template tmp[T](x: var seq[T]) =
+      # var yz: T
+      incl(s.flags, sfUsed)
+      callee = newSymNode(s, n.info)
+      styleCheckUse(n.info, s)
+    else:
+      callee = semTemplSymbol(c.c, n, s)
+
+    let call = newNodeI(nkCall, n.info)
+    call.add(callee)
+    for i in 0 .. n.len-1: call.add(n[i])
+    result = newNodeI(nkBind, n.info, 2)
+    result.sons[0] = n
+    result.sons[1] = call
+  else:
+    result = n
+
+proc oprIsRoof(n: PNode): bool =
+  const roof = "^"
+  case n.kind
+  of nkIdent: result = n.ident.s == roof
+  of nkSym: result = n.sym.name.s == roof
+  of nkAccQuoted:
+    if n.len == 1:
+      result = oprIsRoof(n.sons[0])
+  of nkOpenSymChoice, nkClosedSymChoice:
+    result = oprIsRoof(n.sons[0])
+  else: discard
+
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   result = n
   semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
@@ -402,27 +471,65 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     result.sons[1] = semTemplBody(c, n.sons[1])
   of nkPragma:
     result = onlyReplaceParams(c, n)
-  else:
+  of nkBracketExpr:
+    result = newNodeI(nkCall, n.info)
+    result.add newIdentNode(getIdent("[]"), n.info)
+    for i in 0 ..< n.len: result.add(n[i])
+    let n0 = semTemplBody(c, n.sons[0])
+    withBracketExpr c, n0:
+      result = semTemplBodySons(c, result)
+  of nkCurlyExpr:
+    result = newNodeI(nkCall, n.info)
+    result.add newIdentNode(getIdent("{}"), n.info)
+    for i in 0 ..< n.len: result.add(n[i])
+    result = semTemplBodySons(c, result)
+  of nkAsgn, nkFastAsgn:
+    checkSonsLen(n, 2)
+    let a = n.sons[0]
+    let b = n.sons[1]
+
+    let k = a.kind
+    case k
+    of nkBracketExpr:
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent("[]="), n.info)
+      for i in 0 ..< a.len: result.add(a[i])
+      result.add(b)
+      let a0 = semTemplBody(c, a.sons[0])
+      withBracketExpr c, a0:
+        result = semTemplBodySons(c, result)
+    of nkCurlyExpr:
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent("{}="), n.info)
+      for i in 0 ..< a.len: result.add(a[i])
+      result.add(b)
+      result = semTemplBodySons(c, result)
+    else:
+      result = semTemplBodySons(c, n)
+  of nkCallKinds-{nkPostfix}:
+    result = semTemplBodySons(c, n)
+    if c.bracketExpr != nil and n.len == 2 and oprIsRoof(n.sons[0]):
+      result.add c.bracketExpr
+  of nkDotExpr, nkAccQuoted:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
-    if n.kind == nkDotExpr or n.kind == nkAccQuoted:
-      let s = qualifiedLookUp(c.c, n, {})
-      if s != nil:
-        # do not symchoice a quoted template parameter (bug #2390):
-        if s.owner == c.owner and s.kind == skParam and
-            n.kind == nkAccQuoted and n.len == 1:
-          incl(s.flags, sfUsed)
-          styleCheckUse(n.info, s)
-          return newSymNode(s, n.info)
-        elif contains(c.toBind, s.id):
-          return symChoice(c.c, n, s, scClosed)
-        elif contains(c.toMixin, s.name.id):
-          return symChoice(c.c, n, s, scForceOpen)
-        else:
-          return symChoice(c.c, n, s, scOpen)
-    result = n
-    for i in countup(0, sonsLen(n) - 1):
-      result.sons[i] = semTemplBody(c, n.sons[i])
+    let s = qualifiedLookUp(c.c, n, {})
+    if s != nil:
+      # do not symchoice a quoted template parameter (bug #2390):
+      if s.owner == c.owner and s.kind == skParam and
+          n.kind == nkAccQuoted and n.len == 1:
+        incl(s.flags, sfUsed)
+        styleCheckUse(n.info, s)
+        return newSymNode(s, n.info)
+      elif contains(c.toBind, s.id):
+        return symChoice(c.c, n, s, scClosed)
+      elif contains(c.toMixin, s.name.id):
+        return symChoice(c.c, n, s, scForceOpen)
+      else:
+        return symChoice(c.c, n, s, scOpen)
+    result = semTemplBodySons(c, n)
+  else:
+    result = semTemplBodySons(c, n)
 
 proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
   result = n
@@ -452,30 +559,9 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
     for i in countup(0, sonsLen(n) - 1):
       result.sons[i] = semTemplBodyDirty(c, n.sons[i])
 
-proc transformToExpr(n: PNode): PNode =
-  var realStmt: int
-  result = n
-  case n.kind
-  of nkStmtList:
-    realStmt = - 1
-    for i in countup(0, sonsLen(n) - 1):
-      case n.sons[i].kind
-      of nkCommentStmt, nkEmpty, nkNilLit:
-        discard
-      else:
-        if realStmt == - 1: realStmt = i
-        else: realStmt = - 2
-    if realStmt >= 0: result = transformToExpr(n.sons[realStmt])
-    else: n.kind = nkStmtListExpr
-  of nkBlockStmt:
-    n.kind = nkBlockExpr
-    #nkIfStmt: n.kind = nkIfExpr // this is not correct!
-  else:
-    discard
-
 proc semTemplateDef(c: PContext, n: PNode): PNode =
   var s: PSym
-  if c.p.owner.kind == skModule:
+  if isTopLevel(c):
     s = semIdentVis(c, skTemplate, n.sons[0], {sfExported})
     incl(s.flags, sfGlobal)
   else:
@@ -530,9 +616,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
   else:
     n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
-  if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
-    n.sons[bodyPos] = transformToExpr(n.sons[bodyPos])
-    # only parameters are resolved, no type checking is performed
+  # only parameters are resolved, no type checking is performed
   semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
   closeScope(c)
   popOwner()
@@ -585,6 +669,11 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       localError(n.info, errInvalidExpression)
       result = n
 
+  proc stupidStmtListExpr(n: PNode): bool =
+    for i in 0 .. n.len-2:
+      if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
+    result = true
+
   result = n
   case n.kind
   of nkIdent:
@@ -610,6 +699,12 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
         localError(n.info, errInvalidExpression)
     else:
       localError(n.info, errInvalidExpression)
+  of nkStmtList, nkStmtListExpr:
+    if stupidStmtListExpr(n):
+      result = semPatternBody(c, n.lastSon)
+    else:
+      for i in countup(0, sonsLen(n) - 1):
+        result.sons[i] = semPatternBody(c, n.sons[i])
   of nkCallKinds:
     let s = qualifiedLookUp(c.c, n.sons[0], {})
     if s != nil:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 8c7bd7243..65cb9421b 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if n.sonsLen == 0: return newConstraint(c, tyEnum)
+  elif n.sonsLen == 1:
+    # don't create an empty tyEnum; fixes #3052
+    return errorType(c)
   var
     counter, x: BiggestInt
     e: PSym
@@ -130,6 +133,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
     let isCall = ord(n.kind in nkCallKinds)
     let n = if n[0].kind == nkBracket: n[0] else: n
     checkMinSonsLen(n, 1)
+    var base = semTypeNode(c, n.lastSon, nil)
     result = newOrPrevType(kind, prev, c)
     # check every except the last is an object:
     for i in isCall .. n.len-2:
@@ -137,7 +141,6 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
       if region.skipTypes({tyGenericInst}).kind notin {tyError, tyObject}:
         message n[i].info, errGenerated, "region needs to be an object type"
       addSonSkipIntLit(result, region)
-    var base = semTypeNode(c, n.lastSon, nil)
     addSonSkipIntLit(result, base)
 
 proc semVarType(c: PContext, n: PNode, prev: PType): PType =
@@ -251,19 +254,21 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
 
 proc semArray(c: PContext, n: PNode, prev: PType): PType =
   var base: PType
-  result = newOrPrevType(tyArray, prev, c)
   if sonsLen(n) == 3:
     # 3 = length(array indx base)
-    var indx = semArrayIndex(c, n[1])
-    addSonSkipIntLit(result, indx)
-    if indx.kind == tyGenericInst: indx = lastSon(indx)
-    if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
-      if not isOrdinalType(indx):
+    let indx = semArrayIndex(c, n[1])
+    var indxB = indx
+    if indxB.kind == tyGenericInst: indxB = lastSon(indxB)
+    if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
+      if not isOrdinalType(indxB):
         localError(n.sons[1].info, errOrdinalTypeExpected)
-      elif enumHasHoles(indx):
+      elif enumHasHoles(indxB):
         localError(n.sons[1].info, errEnumXHasHoles,
-                   typeToString(indx.skipTypes({tyRange})))
+                   typeToString(indxB.skipTypes({tyRange})))
     base = semTypeNode(c, n.sons[2], nil)
+    # ensure we only construct a tyArray when there was no error (bug #3048):
+    result = newOrPrevType(tyArray, prev, c)
+    addSonSkipIntLit(result, indx)
     addSonSkipIntLit(result, base)
   else:
     localError(n.info, errArrayExpectsTwoTypeParams)
@@ -503,8 +508,9 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
   if not isOrdinalType(typ):
     localError(n.info, errSelectorMustBeOrdinal)
-  elif firstOrd(typ) < 0:
-    localError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s)
+  elif firstOrd(typ) != 0:
+    localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s &
+                                     ") must be 0 for discriminant")
   elif lengthOrd(typ) > 0x00007FFF:
     localError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
   var chckCovered = true
@@ -629,7 +635,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
   result = t
   if result.kind == tyGenericInvocation:
     result = result.sons[0]
-  while result.kind in {tyGenericInst, tyGenericBody}:
+  while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr}:
     result = lastSon(result)
 
 proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
@@ -651,7 +657,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
     if base.isNil:
       localError(n.info, errIllegalRecursionInTypeX, "object")
     else:
-      var concreteBase = skipGenericInvocation(base).skipTypes(skipPtrs)
+      var concreteBase = skipGenericInvocation(base)
       if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
         addInheritedFields(c, check, pos, concreteBase)
       else:
@@ -712,12 +718,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   if paramType == nil: return # (e.g. proc return type)
 
   proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
-    let finalTypId = if typId != nil: typId
-                     else: getIdent(paramName & ":type")
     if genericParams == nil:
       # This happens with anonymous proc types appearing in signatures
       # XXX: we need to lift these earlier
       return
+    let finalTypId = if typId != nil: typId
+                     else: getIdent(paramName & ":type")
     # is this a bindOnce type class already present in the param list?
     for i in countup(0, genericParams.len - 1):
       if genericParams.sons[i].sym.name.id == finalTypId.id:
@@ -751,7 +757,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
 
   case paramType.kind:
   of tyAnything:
-    result = addImplicitGeneric(newTypeS(tyGenericParam, c))
+    result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
 
   of tyStatic:
     # proc(a: expr{string}, b: expr{nkLambda})
@@ -862,6 +868,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   of tyExpr:
     if procKind notin {skMacro, skTemplate}:
       result = addImplicitGeneric(newTypeS(tyAnything, c))
+      #result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
 
   of tyGenericParam:
     markUsed(info, paramType.sym)
@@ -964,18 +971,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   elif kind == skIterator:
     # XXX This is special magic we should likely get rid of
     r = newTypeS(tyExpr, c)
+    message(n.info, warnDeprecated, "implicit return type for 'iterator'")
 
   if r != nil:
     # turn explicit 'void' return type into 'nil' because the rest of the
     # compiler only checks for 'nil':
     if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
       # 'auto' as a return type does not imply a generic:
-      if r.kind != tyExpr:
+      if r.kind == tyAnything:
+        # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
+        # compiler is hardly aware of 'auto':
+        r = newTypeS(tyExpr, c)
+      elif r.kind != tyExpr:
         if r.sym == nil or sfAnon notin r.sym.flags:
           let lifted = liftParamType(c, kind, genericParams, r, "result",
                                      n.sons[0].info)
-          if lifted != nil: r = lifted
-          r.flags.incl tfRetType
+          if lifted != nil:
+            r = lifted
+            #if r.kind != tyGenericParam:
+            #echo "came here for ", typeToString(r)
+            r.flags.incl tfRetType
         r = skipIntLit(r)
         if kind == skIterator:
           # see tchainediterators
@@ -1049,6 +1064,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     return newOrPrevType(tyError, prev, c)
   else:
     var m = newCandidate(c, t)
+    m.isNoCall = true
     matches(c, n, copyTree(n), m)
 
     if m.state != csMatch and not m.typedescMatched:
@@ -1085,6 +1101,7 @@ proc semTypeExpr(c: PContext, n: PNode): PType =
     result = n.typ.base
   else:
     localError(n.info, errTypeExpected, n.renderTree)
+    result = errorType(c)
 
 proc freshType(res, prev: PType): PType {.inline.} =
   if prev.isNil:
@@ -1140,7 +1157,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     else:
       result = semAnonTuple(c, n, prev)
   of nkCallKinds:
-    if isRange(n):
+    let x = n[0]
+    let ident = case x.kind
+                of nkIdent: x.ident
+                of nkSym: x.sym.name
+                of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
+                else: nil
+    if ident != nil and ident.s == "[]":
+      let b = newNodeI(nkBracketExpr, n.info)
+      for i in 1..<n.len: b.add(n[i])
+      result = semTypeNode(c, b, prev)
+    elif ident != nil and ident.id == ord(wDotDot):
       result = semRangeAux(c, n, prev)
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n)
@@ -1326,12 +1353,23 @@ proc processMagicType(c: PContext, m: PSym) =
     rawAddSon(m.typ, newTypeS(tyEmpty, c))
   of mIntSetBaseType: setMagicType(m, tyRange, intSize)
   of mNil: setMagicType(m, tyNil, ptrSize)
-  of mExpr: setMagicType(m, tyExpr, 0)
-  of mStmt: setMagicType(m, tyStmt, 0)
+  of mExpr:
+    if m.name.s == "auto":
+      setMagicType(m, tyAnything, 0)
+    else:
+      setMagicType(m, tyExpr, 0)
+      if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt
+  of mStmt:
+    setMagicType(m, tyStmt, 0)
+    if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
   of mTypeDesc:
     setMagicType(m, tyTypeDesc, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
-  of mVoidType: setMagicType(m, tyEmpty, 0)
+  of mVoidType:
+    setMagicType(m, tyEmpty, 0)
+    # for historical reasons we conflate 'void' with 'empty' so that '@[]'
+    # has the type 'seq[void]'.
+    m.typ.flags.incl tfVoid
   of mArray:
     setMagicType(m, tyArray, 0)
   of mOpenArray:
@@ -1348,7 +1386,8 @@ proc processMagicType(c: PContext, m: PSym) =
   of mOrdinal:
     setMagicType(m, tyOrdinal, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
-  of mPNimrodNode: discard
+  of mPNimrodNode:
+    incl m.typ.flags, tfTriggersCompileTime
   of mShared:
     setMagicType(m, tyObject, 0)
     m.typ.n = newNodeI(nkRecList, m.info)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index c5caf8b92..7957ac50a 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -75,8 +75,12 @@ proc searchInstTypes*(key: PType): PType =
 proc cacheTypeInst*(inst: PType) =
   # XXX: add to module's generics
   #      update the refcount
-  let genericTyp = inst.sons[0]
-  genericTyp.sym.typeInstCache.safeAdd(inst)
+  let gt = inst.sons[0]
+  let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
+  if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
+    return
+  gt.sym.typeInstCache.safeAdd(inst)
+
 
 type
   TReplTypeVars* {.final.} = object
@@ -90,6 +94,8 @@ type
     allowMetaTypes*: bool     # allow types such as seq[Number]
                               # i.e. the result contains unresolved generics
     skipTypedesc*: bool       # wether we should skip typeDescs
+    owner*: PSym              # where this instantiation comes from
+    recursionLimit: int
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
 proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
@@ -207,6 +213,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
 
 proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
   if s == nil: return nil
+  # symbol is not our business:
+  if cl.owner != nil and s.owner != cl.owner:
+    return s
   result = PSym(idTableGet(cl.symMap, s))
   if result == nil:
     result = copySym(s, false)
@@ -365,6 +374,19 @@ proc propagateFieldFlags(t: PType, n: PNode) =
   else: discard
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
+  template bailout =
+    if cl.recursionLimit > 100:
+      # bail out, see bug #2509. But note this caching is in general wrong,
+      # look at this example where TwoVectors should not share the generic
+      # instantiations (bug #3112):
+
+      # type
+      #   Vector[N: static[int]] = array[N, float64]
+      #   TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb])
+      result = PType(idTableGet(cl.localCache, t))
+      if result != nil: return result
+    inc cl.recursionLimit
+
   result = t
   if t == nil: return
 
@@ -420,8 +442,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     result = t
 
   of tyGenericInst:
-    result = PType(idTableGet(cl.localCache, t))
-    if result != nil: return result
+    bailout()
     result = instCopyType(cl, t)
     idTablePut(cl.localCache, t, result)
     for i in 1 .. <result.sonsLen:
@@ -431,8 +452,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
   else:
     if containsGenericType(t):
       #if not cl.allowMetaTypes:
-      result = PType(idTableGet(cl.localCache, t))
-      if result != nil: return result
+      bailout()
       result = instCopyType(cl, t)
       result.size = -1 # needs to be recomputed
       #if not cl.allowMetaTypes:
@@ -440,8 +460,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
       for i in countup(0, sonsLen(result) - 1):
         if result.sons[i] != nil:
-          result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
-          propagateToOwner(result, result.sons[i])
+          var r = replaceTypeVarsT(cl, result.sons[i])
+          if result.kind == tyObject:
+            # carefully coded to not skip the precious tyGenericInst:
+            let r2 = r.skipTypes({tyGenericInst})
+            if r2.kind in {tyPtr, tyRef}:
+              r = skipTypes(r2, {tyPtr, tyRef})
+          result.sons[i] = r
+          propagateToOwner(result, r)
 
       result.n = replaceTypeVarsN(cl, result.n)
 
@@ -459,22 +485,33 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
       else: discard
 
-proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
+proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo;
+                   owner: PSym): TReplTypeVars =
   initIdTable(result.symMap)
   copyIdTable(result.typeMap, pt)
   initIdTable(result.localCache)
   result.info = info
   result.c = p
+  result.owner = owner
+
+proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
+                         owner: PSym): PNode =
+  var cl = initTypeVars(p, pt, n.info, owner)
+  pushInfoContext(n.info)
+  result = replaceTypeVarsN(cl, n)
+  popInfoContext()
 
-proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
-  var cl = initTypeVars(p, pt, n.info)
+proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
+                            original, new: PSym): PNode =
+  var cl = initTypeVars(p, pt, n.info, original)
+  idTablePut(cl.symMap, original, new)
   pushInfoContext(n.info)
   result = replaceTypeVarsN(cl, n)
   popInfoContext()
 
 proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
                            t: PType): PType =
-  var cl = initTypeVars(p, pt, info)
+  var cl = initTypeVars(p, pt, info, nil)
   pushInfoContext(info)
   result = replaceTypeVarsT(cl, t)
   popInfoContext()
diff --git a/compiler/service.nim b/compiler/service.nim
index 7cb9d7d29..640dd2010 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -18,7 +18,7 @@ when useCaas:
 
 # We cache modules and the dependency graph. However, we don't check for
 # file changes but expect the client to tell us about them, otherwise the
-# repeated CRC calculations may turn out to be too slow.
+# repeated hash calculations may turn out to be too slow.
 
 var
   curCaasCmd* = ""
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 2a9d15b5a..9fda8c860 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -47,6 +47,7 @@ type
     coerceDistincts*: bool   # this is an explicit coercion that can strip away
                              # a distrinct type
     typedescMatched*: bool
+    isNoCall*: bool          # misused for generic type instantiations C[T]
     inheritancePenalty: int  # to prefer closest father object type
     errors*: CandidateErrors # additional clarifications to be displayed to the
                              # user if overload resolution fails
@@ -57,6 +58,10 @@ type
     isSubtype,
     isSubrange,              # subrange of the wanted type; no type conversion
                              # but apart from that counts as ``isSubtype``
+    isBothMetaConvertible    # generic proc parameter was matched against
+                             # generic type, e.g., map(mySeq, x=>x+1),
+                             # maybe recoverable by rerun if the parameter is
+                             # the proc's return value
     isInferred,              # generic proc was matched against a concrete type
     isInferredConvertible,   # same as above, but requiring proc CC conversion
     isGeneric,
@@ -158,11 +163,15 @@ proc sumGeneric(t: PType): int =
       t = t.sons[0]
       inc result
       inc isvar
+    of tyTypeDesc:
+      t = t.lastSon
+      if t.kind == tyEmpty: break
+      inc result
     of tyGenericInvocation, tyTuple:
       result += ord(t.kind == tyGenericInvocation)
       for i in 0 .. <t.len: result += t.sons[i].sumGeneric
       break
-    of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
+    of tyGenericParam, tyExpr, tyStatic, tyStmt: break
     of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
         tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
         tyUInt..tyUInt64:
@@ -218,12 +227,12 @@ proc cmpCandidates*(a, b: TCandidate): int =
   result = complexDisambiguation(a.callee, b.callee)
 
 proc writeMatches*(c: TCandidate) =
-  writeln(stdout, "exact matches: " & $c.exactMatches)
-  writeln(stdout, "generic matches: " & $c.genericMatches)
-  writeln(stdout, "subtype matches: " & $c.subtypeMatches)
-  writeln(stdout, "intconv matches: " & $c.intConvMatches)
-  writeln(stdout, "conv matches: " & $c.convMatches)
-  writeln(stdout, "inheritance: " & $c.inheritancePenalty)
+  writeLine(stdout, "exact matches: " & $c.exactMatches)
+  writeLine(stdout, "generic matches: " & $c.genericMatches)
+  writeLine(stdout, "subtype matches: " & $c.subtypeMatches)
+  writeLine(stdout, "intconv matches: " & $c.intConvMatches)
+  writeLine(stdout, "conv matches: " & $c.convMatches)
+  writeLine(stdout, "inheritance: " & $c.inheritancePenalty)
 
 proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   if arg.kind in nkSymChoices:
@@ -264,6 +273,9 @@ proc concreteType(c: TCandidate, t: PType): PType =
     addSonSkipIntLit(result, t.sons[1]) # XXX: semantic checking for the type?
   of tyNil:
     result = nil              # what should it be?
+  of tyTypeDesc:
+    if c.isNoCall: result = t
+    else: result = nil
   of tySequence, tySet:
     if t.sons[0].kind == tyEmpty: result = nil
     else: result = t
@@ -381,9 +393,30 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
   result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
 
 proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
-  var f = f
+  ## For example we have:
+  ## .. code-block:: nim
+  ##   proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
+  ##   proc innerProc[Q,W](q: Q): W = ...
+  ## And we want to match: myMap(@[1,2,3], innerProc)
+  ## This proc (procParamTypeRel) will do the following steps in
+  ## three different calls:
+  ## - matches f=T to a=Q. Since f is metatype, we resolve it
+  ##    to int (which is already known at this point). So in this case
+  ##    Q=int mapping will be saved to c.bindings.
+  ## - matches f=S to a=W. Both of these metatypes are unknown, so we
+  ##    return with isBothMetaConvertible to ask for rerun.
+  ## - matches f=S to a=W. At this point the return type of innerProc
+  ##    is known (we get it from c.bindings). We can use that value
+  ##    to match with f, and save back to c.bindings.
+  var
+    f = f
+    a = a
 
   if a.isMetaType:
+    let aResolved = PType(idTableGet(c.bindings, a))
+    if aResolved != nil:
+      a = aResolved
+  if a.isMetaType:
     if f.isMetaType:
       # We are matching a generic proc (as proc param)
       # to another generic type appearing in the proc
@@ -393,12 +426,15 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
       if f == nil or f.isMetaType:
         # no luck resolving the type, so the inference fails
-        return isNone
+        return isBothMetaConvertible
+    # Note that this typeRel call will save a's resolved type into c.bindings
     let reverseRel = typeRel(c, a, f)
     if reverseRel >= isGeneric:
       result = isInferred
       #inc c.genericMatches
   else:
+    # Note that this typeRel call will save f's resolved type into c.bindings
+    # if f is metatype.
     result = typeRel(c, f, a)
 
   if result <= isSubtype or inconsistentVarTypes(f, a):
@@ -442,8 +478,9 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     elif f.callConv != a.callConv:
       # valid to pass a 'nimcall' thingie to 'closure':
       if f.callConv == ccClosure and a.callConv == ccDefault:
-        result = if result != isInferred: isConvertible
-                 else: isInferredConvertible
+        result = if result == isInferred: isInferredConvertible
+                 elif result == isBothMetaConvertible: isBothMetaConvertible
+                 else: isConvertible
       else:
         return isNone
     when useEffectSystem:
@@ -474,6 +511,9 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
 proc matchUserTypeClass*(c: PContext, m: var TCandidate,
                          ff, a: PType): TTypeRelation =
   var body = ff.skipTypes({tyUserTypeClassInst})
+  if c.inTypeClass > 20:
+    localError(body.n[3].info, $body.n[3] & " too nested for type matching")
+    return isNone
 
   openScope(c)
   inc c.inTypeClass
@@ -553,7 +593,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
   # Here, N-1 will be initially nkStaticExpr that can be evaluated only after
   # N is bound to a concrete value during the matching of the first param.
   # This proc is used to evaluate such static expressions.
-  let instantiated = replaceTypesInBody(c.c, c.bindings, n)
+  let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil)
   result = c.c.semExpr(c.c, instantiated)
 
 proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
@@ -644,6 +684,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyAnything:
     return if f.kind == tyAnything: isGeneric
            else: isNone
+
+  of tyUserTypeClass, tyUserTypeClassInst:
+    # consider this: 'var g: Node' *within* a concept where 'Node'
+    # is a concept too (tgraph)
+    let x = typeRel(c, a, f, false)
+    if x >= isGeneric:
+      return isGeneric
   else: discard
 
   case f.kind
@@ -727,8 +774,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isNone
     else: discard
   of tyOpenArray, tyVarargs:
-    # varargs[expr] is special
-    if f.kind == tyVarargs and f.sons[0].kind == tyExpr: return
+    # varargs[expr] is special too but handled earlier. So we only need to
+    # handle varargs[stmt] which is the same as varargs[typed]:
+    if f.kind == tyVarargs:
+      if tfOldSchoolExprStmt in f.sons[0].flags:
+        if f.sons[0].kind == tyExpr: return
+      elif f.sons[0].kind == tyStmt: return
     case a.kind
     of tyOpenArray, tyVarargs:
       result = typeRel(c, base(f), base(a))
@@ -748,6 +799,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isConvertible
       elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
         result = isConvertible
+    of tyString:
+      if f.kind == tyOpenArray:
+        if f.sons[0].kind == tyChar:
+          result = isConvertible
+        elif f.sons[0].kind == tyGenericParam and a.len > 0 and
+            typeRel(c, base(f), base(a)) >= isGeneric:
+          result = isConvertible
     else: discard
   of tySequence:
     case a.kind
@@ -1091,6 +1149,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       result = isNone
 
   of tyStmt:
+    if aOrig != nil and tfOldSchoolExprStmt notin f.flags:
+      put(c.bindings, f, aOrig)
     result = isGeneric
 
   of tyProxy:
@@ -1204,7 +1264,7 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   case r
   of isConvertible, isIntConv: inc(m.convMatches, convMatch)
   of isSubtype, isSubrange: inc(m.subtypeMatches)
-  of isGeneric, isInferred: inc(m.genericMatches)
+  of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches)
   of isFromIntLit: inc(m.intConvMatches, 256)
   of isInferredConvertible:
     inc(m.convMatches)
@@ -1270,6 +1330,29 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     put(m.bindings, f, inlined)
     return argSemantized
 
+  # If r == isBothMetaConvertible then we rerun typeRel.
+  # bothMetaCounter is for safety to avoid any infinite loop,
+  #  I don't have any example when it is needed.
+  # lastBindingsLenth is used to check whether m.bindings remains the same,
+  #  because in that case there is no point in continuing.
+  var bothMetaCounter = 0
+  var lastBindingsLength = -1
+  while r == isBothMetaConvertible and
+      lastBindingsLength != m.bindings.counter and
+      bothMetaCounter < 100:
+    lastBindingsLength = m.bindings.counter
+    inc(bothMetaCounter)
+    if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
+      result = c.semInferredLambda(c, m.bindings, arg)
+    elif arg.kind != nkSym:
+      return nil
+    else:
+      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
+      result = newSymNode(inferred, arg.info)
+    inc(m.convMatches)
+    arg = result
+    r = typeRel(m, f, arg.typ)
+
   case r
   of isConvertible:
     inc(m.convMatches)
@@ -1281,7 +1364,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isSubtype:
     inc(m.subtypeMatches)
-    result = implicitConv(nkHiddenSubConv, f, arg, m, c)
+    if f.kind == tyTypeDesc:
+      result = arg
+    else:
+      result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isSubrange:
     inc(m.subtypeMatches)
     if f.kind == tyVar:
@@ -1291,6 +1377,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
   of isInferred, isInferredConvertible:
     if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
       result = c.semInferredLambda(c, m.bindings, arg)
+    elif arg.kind != nkSym:
+      return nil
     else:
       let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
       result = newSymNode(inferred, arg.info)
@@ -1310,6 +1398,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
       result.typ = getInstantiatedType(c, arg, m, f)
     else:
       result = arg
+  of isBothMetaConvertible:
+    # This is the result for the 101th time.
+    result = nil
   of isFromIntLit:
     # too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
@@ -1454,6 +1545,10 @@ proc incrIndexType(t: PType) =
   assert t.kind == tyArrayConstr
   inc t.sons[0].n.sons[1].intVal
 
+template isVarargsUntyped(x): expr =
+  x.kind == tyVarargs and x.sons[0].kind == tyExpr and
+    tfOldSchoolExprStmt notin x.sons[0].flags
+
 proc matchesAux(c: PContext, n, nOrig: PNode,
                 m: var TCandidate, marker: var IntSet) =
   template checkConstraint(n: expr) {.immediate, dirty.} =
@@ -1482,10 +1577,17 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
   var formalLen = m.callee.n.len
   addSon(m.call, copyTree(n.sons[0]))
   var container: PNode = nil # constructed container
-  var formal: PSym = nil
+  var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
 
   while a < n.len:
-    if n.sons[a].kind == nkExprEqExpr:
+    if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
+      if container.isNil:
+        container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
+        setSon(m.call, formal.position + 1, container)
+      else:
+        incrIndexType(container.typ)
+      addSon(container, n.sons[a])
+    elif n.sons[a].kind == nkExprEqExpr:
       # named param
       # check if m.callee has such a param:
       prepareNamedParam(n.sons[a])
@@ -1507,7 +1609,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
       n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
       n.sons[a].typ = n.sons[a].sons[1].typ
       var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
-                                n.sons[a].sons[1], nOrig.sons[a].sons[1])
+                                n.sons[a].sons[1], n.sons[a].sons[1])
       if arg == nil:
         m.state = csNoMatch
         return
@@ -1536,7 +1638,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
             addSon(m.call, copyTree(n.sons[a]))
         elif formal != nil and formal.typ.kind == tyVarargs:
           # beware of the side-effects in 'prepareOperand'! So only do it for
-          # varags matching. See tests/metatype/tstatic_overloading.
+          # varargs matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
           n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
           var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@@ -1599,6 +1701,10 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   matchesAux(c, n, nOrig, m, marker)
 
 proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
+  if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}:
+    m.state = csMatch
+    m.call = n
+    return
   var marker = initIntSet()
   matchesAux(c, n, nOrig, m, marker)
   if m.state == csNoMatch: return
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 6b168670c..18d723315 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -15,57 +15,79 @@ import algorithm, sequtils
 
 const
   sep = '\t'
-  sectionSuggest = "sug"
-  sectionDef = "def"
-  sectionContext = "con"
-  sectionUsage = "use"
+
+type
+  Suggest* = object
+    section*: IdeCmd
+    qualifiedPath*: seq[string]
+    filePath*: string
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    doc*: string           # Not escaped (yet)
+    symkind*: TSymKind
+    forth*: string               # XXX TODO object on symkind
+
+var
+  suggestionResultHook*: proc (result: Suggest) {.closure.}
 
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
 template origModuleName(m: PSym): string = m.name.s
 
-proc symToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = 
-  result = section
-  result.add(sep)
+proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
+  result.section = parseIdeCmd(section)
   if optIdeTerse in gGlobalOptions:
-    if s.kind in routineKinds:
-      result.add renderTree(s.ast, {renderNoBody, renderNoComments,
-                                    renderDocComments, renderNoPragmas})
-    else:
-      result.add s.name.s
-    result.add(sep)
-    result.add(toFullPath(li))
-    result.add(sep)
-    result.add($toLinenumber(li))
-    result.add(sep)
-    result.add($toColumn(li))
+    result.symkind = s.kind
+    result.filePath = toFullPath(li)
+    result.line = toLinenumber(li)
+    result.column = toColumn(li)
   else:
-    result.add($s.kind)
-    result.add(sep)
+    result.symkind = s.kind
+    result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
       if ow.kind != skModule and ow.owner != nil:
         let ow2 = ow.owner
-        result.add(ow2.origModuleName)
-        result.add('.')
-      result.add(ow.origModuleName)
-      result.add('.')
-    result.add(s.name.s)
-    result.add(sep)
-    if s.typ != nil: 
-      result.add(typeToString(s.typ))
-    result.add(sep)
-    result.add(toFullPath(li))
-    result.add(sep)
-    result.add($toLinenumber(li))
-    result.add(sep)
-    result.add($toColumn(li))
-    result.add(sep)
+        result.qualifiedPath.add(ow2.origModuleName)
+      result.qualifiedPath.add(ow.origModuleName)
+    result.qualifiedPath.add(s.name.s)
+
+    if s.typ != nil:
+      result.forth = typeToString(s.typ)
+    else:
+      result.forth = ""
+    result.filePath = toFullPath(li)
+    result.line = toLinenumber(li)
+    result.column = toColumn(li)
     when not defined(noDocgen):
-      result.add(s.extractDocComment.escape)
+      result.doc = s.extractDocComment
 
-proc symToStr(s: PSym, isLocal: bool, section: string): string = 
-  result = symToStr(s, isLocal, section, s.info)
+proc `$`(suggest: Suggest): string =
+  result = $suggest.section
+  result.add(sep)
+  result.add($suggest.symkind)
+  result.add(sep)
+  result.add(suggest.qualifiedPath.join("."))
+  result.add(sep)
+  result.add(suggest.forth)
+  result.add(sep)
+  result.add(suggest.filePath)
+  result.add(sep)
+  result.add($suggest.line)
+  result.add(sep)
+  result.add($suggest.column)
+  result.add(sep)
+  when not defined(noDocgen):
+    result.add(suggest.doc.escape)
+
+proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
+  result = symToSuggest(s, isLocal, section, s.info)
+
+proc suggestResult(s: Suggest) =
+  if not isNil(suggestionResultHook):
+    suggestionResultHook(s)
+  else:
+    suggestWriteln($(s))
 
 proc filterSym(s: PSym): bool {.inline.} =
   result = s.kind != skModule
@@ -82,9 +104,9 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
       result = true
       break
 
-proc suggestField(c: PContext, s: PSym, outputs: var int) = 
+proc suggestField(c: PContext, s: PSym, outputs: var int) =
   if filterSym(s) and fieldVisible(c, s):
-    suggestWriteln(symToStr(s, isLocal=true, sectionSuggest))
+    suggestResult(symToSuggest(s, isLocal=true, $ideSug))
     inc outputs
 
 template wholeSymTab(cond, section: expr) {.immediate.} =
@@ -97,20 +119,20 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
     for item in entries:
       let it {.inject.} = item
       if cond:
-        suggestWriteln(symToStr(it, isLocal = isLocal, section))
+        suggestResult(symToSuggest(it, isLocal = isLocal, section))
         inc outputs
 
-proc suggestSymList(c: PContext, list: PNode, outputs: var int) = 
-  for i in countup(0, sonsLen(list) - 1): 
+proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
+  for i in countup(0, sonsLen(list) - 1):
     if list.sons[i].kind == nkSym:
       suggestField(c, list.sons[i].sym, outputs)
     #else: InternalError(list.info, "getSymFromList")
 
-proc suggestObject(c: PContext, n: PNode, outputs: var int) = 
+proc suggestObject(c: PContext, n: PNode, outputs: var int) =
   case n.kind
-  of nkRecList: 
+  of nkRecList:
     for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
-  of nkRecCase: 
+  of nkRecCase:
     var L = sonsLen(n)
     if L > 0:
       suggestObject(c, n.sons[0], outputs)
@@ -118,7 +140,7 @@ proc suggestObject(c: PContext, n: PNode, outputs: var int) =
   of nkSym: suggestField(c, n.sym, outputs)
   else: discard
 
-proc nameFits(c: PContext, s: PSym, n: PNode): bool = 
+proc nameFits(c: PContext, s: PSym, n: PNode): bool =
   var op = n.sons[0]
   if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
   var opr: PIdent
@@ -128,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
   else: return false
   result = opr.id == s.name.id
 
-proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = 
-  case candidate.kind 
+proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
+  case candidate.kind
   of OverloadableSyms:
     var m: TCandidate
     initCandidate(c, m, candidate, nil)
@@ -138,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
   else:
     result = false
 
-proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = 
+proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
   wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
-              sectionContext)
+              $ideCon)
 
-proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = 
+proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
   if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
     # special rule: if system and some weird generic match via 'tyExpr'
     # or 'tyGenericParam' we won't list it either to reduce the noise (nobody
@@ -157,7 +179,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
 
 proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
   assert typ != nil
-  wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), sectionSuggest)
+  wholeSymTab(filterSymNoOpr(it) and typeFits(c, it, typ), $ideSug)
 
 proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
   # do not produce too many symbols:
@@ -166,7 +188,7 @@ proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
     if scope == c.topLevelScope: isLocal = false
     for it in items(scope.symbols):
       if filterSym(it):
-        suggestWriteln(symToStr(it, isLocal = isLocal, sectionSuggest))
+        suggestResult(symToSuggest(it, isLocal = isLocal, $ideSug))
         inc outputs
     if scope == c.topLevelScope: break
 
@@ -176,48 +198,48 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
   var typ = n.typ
   if typ == nil:
     # a module symbol has no type for example:
-    if n.kind == nkSym and n.sym.kind == skModule: 
-      if n.sym == c.module: 
+    if n.kind == nkSym and n.sym.kind == skModule:
+      if n.sym == c.module:
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
-          if filterSym(it): 
-            suggestWriteln(symToStr(it, isLocal=false, sectionSuggest))
+          if filterSym(it):
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
             inc outputs
-      else: 
-        for it in items(n.sym.tab): 
-          if filterSym(it): 
-            suggestWriteln(symToStr(it, isLocal=false, sectionSuggest))
+      else:
+        for it in items(n.sym.tab):
+          if filterSym(it):
+            suggestResult(symToSuggest(it, isLocal=false, $ideSug))
             inc outputs
     else:
       # fallback:
       suggestEverything(c, n, outputs)
-  elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: 
+  elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
     # look up if the identifier belongs to the enum:
     var t = typ
-    while t != nil: 
+    while t != nil:
       suggestSymList(c, t.n, outputs)
       t = t.sons[0]
     suggestOperations(c, n, typ, outputs)
   else:
     typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
-    if typ.kind == tyObject: 
+    if typ.kind == tyObject:
       var t = typ
-      while true: 
+      while true:
         suggestObject(c, t.n, outputs)
-        if t.sons[0] == nil: break 
+        if t.sons[0] == nil: break
         t = skipTypes(t.sons[0], {tyGenericInst})
       suggestOperations(c, n, typ, outputs)
-    elif typ.kind == tyTuple and typ.n != nil: 
+    elif typ.kind == tyTuple and typ.n != nil:
       suggestSymList(c, typ.n, outputs)
       suggestOperations(c, n, typ, outputs)
     else:
       suggestOperations(c, n, typ, outputs)
 
 type
-  TCheckPointResult = enum 
+  TCheckPointResult = enum
     cpNone, cpFuzzy, cpExact
 
-proc inCheckpoint(current: TLineInfo): TCheckPointResult = 
+proc inCheckpoint(current: TLineInfo): TCheckPointResult =
   if current.fileIndex == gTrackPos.fileIndex:
     if current.line == gTrackPos.line and
         abs(current.col-gTrackPos.col) < 4:
@@ -233,8 +255,8 @@ proc findClosestDot(n: PNode): PNode =
       result = findClosestDot(n.sons[i])
       if result != nil: return
 
-proc findClosestCall(n: PNode): PNode = 
-  if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact: 
+proc findClosestCall(n: PNode): PNode =
+  if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
     result = n
   else:
     for i in 0.. <safeLen(n):
@@ -248,8 +270,8 @@ proc isTracked(current: TLineInfo, tokenLen: int): bool =
       if col >= current.col and col <= current.col+tokenLen-1:
         return true
 
-proc findClosestSym(n: PNode): PNode = 
-  if n.kind == nkSym and inCheckpoint(n.info) == cpExact: 
+proc findClosestSym(n: PNode): PNode =
+  if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
     result = n
   elif n.kind notin {nkNone..nkNilLit}:
     for i in 0.. <sonsLen(n):
@@ -263,16 +285,16 @@ var
 proc findUsages(info: TLineInfo; s: PSym) =
   if usageSym == nil and isTracked(info, s.name.s.len):
     usageSym = s
-    suggestWriteln(symToStr(s, isLocal=false, sectionUsage))
+    suggestResult(symToSuggest(s, isLocal=false, $ideUse))
   elif s == usageSym:
     if lastLineInfo != info:
-      suggestWriteln(symToStr(s, isLocal=false, sectionUsage, info))
+      suggestResult(symToSuggest(s, isLocal=false, $ideUse, info))
     lastLineInfo = info
 
 proc findDefinition(info: TLineInfo; s: PSym) =
   if s.isNil: return
   if isTracked(info, s.name.s.len):
-    suggestWriteln(symToStr(s, isLocal=false, sectionDef))
+    suggestResult(symToSuggest(s, isLocal=false, $ideDef))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -287,6 +309,10 @@ proc suggestSym*(info: TLineInfo; s: PSym) {.inline.} =
     findUsages(info, s)
   elif gIdeCmd == ideDef:
     findDefinition(info, s)
+  elif gIdeCmd == ideDus and s != nil:
+    if isTracked(info, s.name.s.len):
+      suggestResult(symToSuggest(s, isLocal=false, $ideDef))
+    findUsages(info, s)
 
 proc markUsed(info: TLineInfo; s: PSym) =
   incl(s.flags, sfUsed)
@@ -306,15 +332,15 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
   except ERecoverableError:
     result = ast.emptyNode
 
-proc suggestExpr*(c: PContext, node: PNode) = 
+proc suggestExpr*(c: PContext, node: PNode) =
   if nfIsCursor notin node.flags:
     if gTrackPos.line < 0: return
     var cp = inCheckpoint(node.info)
     if cp == cpNone: return
   var outputs = 0
   # This keeps semExpr() from coming here recursively:
-  if c.inCompilesContext > 0: return
-  inc(c.inCompilesContext)
+  if c.compilesContextId > 0: return
+  inc(c.compilesContextId)
 
   if gIdeCmd == ideSug:
     var n = if nfIsCursor in node.flags: node else: findClosestDot(node)
@@ -327,7 +353,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
       #writeStackTrace()
     else:
       suggestEverything(c, n, outputs)
-  
+
   elif gIdeCmd == ideCon:
     var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
     if n == nil: n = node
@@ -342,9 +368,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
         if x.kind == nkEmpty or x.typ == nil: break
         addSon(a, x)
       suggestCall(c, a, n, outputs)
-          
-  dec(c.inCompilesContext)
-  if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
 
-proc suggestStmt*(c: PContext, n: PNode) = 
+  dec(c.compilesContextId)
+  if outputs > 0 and gIdeCmd notin {ideUse, ideDus}: suggestQuit()
+
+proc suggestStmt*(c: PContext, n: PNode) =
   suggestExpr(c, n)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 7f9e25f82..021910544 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -9,24 +9,24 @@
 
 ## Implements the dispatcher for the different parsers.
 
-import 
-  strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, 
+import
+  strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
   pbraces, filters, filter_tmpl, renderer
 
-type 
-  TFilterKind* = enum 
+type
+  TFilterKind* = enum
     filtNone, filtTemplate, filtReplace, filtStrip
-  TParserKind* = enum 
+  TParserKind* = enum
     skinStandard, skinStrongSpaces, skinBraces, skinEndX
 
-const 
+const
   parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
                                               "braces", "endx"]
   filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
                                               "strip"]
 
 type
-  TParsers*{.final.} = object 
+  TParsers*{.final.} = object
     skin*: TParserKind
     parser*: TParser
 
@@ -42,60 +42,65 @@ proc parseTopLevelStmt*(p: var TParsers): PNode
 # implementation
 
 proc parseFile(fileIdx: int32): PNode =
-  var 
+  var
     p: TParsers
     f: File
   let filename = fileIdx.toFullPathConsiderDirty
   if not open(f, filename):
     rawMessage(errCannotOpenFile, filename)
-    return 
+    return
   openParsers(p, fileIdx, llStreamOpen(f))
   result = parseAll(p)
   closeParsers(p)
 
-proc parseAll(p: var TParsers): PNode = 
+proc parseAll(p: var TParsers): PNode =
   case p.skin
   of skinStandard, skinStrongSpaces:
     result = parser.parseAll(p.parser)
-  of skinBraces: 
+  of skinBraces:
     result = pbraces.parseAll(p.parser)
-  of skinEndX: 
-    internalError("parser to implement") 
+  of skinEndX:
+    internalError("parser to implement")
     result = ast.emptyNode
-  
-proc parseTopLevelStmt(p: var TParsers): PNode = 
+
+proc parseTopLevelStmt(p: var TParsers): PNode =
   case p.skin
   of skinStandard, skinStrongSpaces:
     result = parser.parseTopLevelStmt(p.parser)
-  of skinBraces: 
+  of skinBraces:
     result = pbraces.parseTopLevelStmt(p.parser)
-  of skinEndX: 
-    internalError("parser to implement") 
+  of skinEndX:
+    internalError("parser to implement")
     result = ast.emptyNode
-  
-proc utf8Bom(s: string): int = 
-  if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): 
+
+proc utf8Bom(s: string): int =
+  if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
     result = 3
-  else: 
+  else:
     result = 0
-  
-proc containsShebang(s: string, i: int): bool = 
-  if (s[i] == '#') and (s[i + 1] == '!'): 
+
+proc containsShebang(s: string, i: int): bool =
+  if (s[i] == '#') and (s[i + 1] == '!'):
     var j = i + 2
     while s[j] in Whitespace: inc(j)
     result = s[j] == '/'
 
-proc parsePipe(filename: string, inputStream: PLLStream): PNode = 
+proc parsePipe(filename: string, inputStream: PLLStream): PNode =
   result = ast.emptyNode
   var s = llStreamOpen(filename, fmRead)
-  if s != nil: 
+  if s != nil:
     var line = newStringOfCap(80)
     discard llStreamReadLine(s, line)
     var i = utf8Bom(line)
+    var linenumber = 1
     if containsShebang(line, i):
       discard llStreamReadLine(s, line)
       i = 0
-    if line[i] == '#' and line[i+1] == '!':
+      inc linenumber
+    if line[i] == '#' and line[i+1] in {'?', '!'}:
+      if line[i+1] == '!':
+        message(newLineInfo(filename, linenumber, 1),
+                warnDeprecated, "use '#?' instead; '#!'")
       inc(i, 2)
       while line[i] in Whitespace: inc(i)
       var q: TParser
@@ -104,50 +109,50 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode =
       closeParser(q)
     llStreamClose(s)
 
-proc getFilter(ident: PIdent): TFilterKind = 
-  for i in countup(low(TFilterKind), high(TFilterKind)): 
-    if identEq(ident, filterNames[i]): 
+proc getFilter(ident: PIdent): TFilterKind =
+  for i in countup(low(TFilterKind), high(TFilterKind)):
+    if identEq(ident, filterNames[i]):
       return i
   result = filtNone
 
-proc getParser(ident: PIdent): TParserKind = 
-  for i in countup(low(TParserKind), high(TParserKind)): 
-    if identEq(ident, parserNames[i]): 
+proc getParser(ident: PIdent): TParserKind =
+  for i in countup(low(TParserKind), high(TParserKind)):
+    if identEq(ident, parserNames[i]):
       return i
   rawMessage(errInvalidDirectiveX, ident.s)
 
-proc getCallee(n: PNode): PIdent = 
-  if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: 
+proc getCallee(n: PNode): PIdent =
+  if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
     result = n.sons[0].ident
-  elif n.kind == nkIdent: 
+  elif n.kind == nkIdent:
     result = n.ident
-  else: 
+  else:
     rawMessage(errXNotAllowedHere, renderTree(n))
-  
-proc applyFilter(p: var TParsers, n: PNode, filename: string, 
-                 stdin: PLLStream): PLLStream = 
+
+proc applyFilter(p: var TParsers, n: PNode, filename: string,
+                 stdin: PLLStream): PLLStream =
   var ident = getCallee(n)
   var f = getFilter(ident)
   case f
-  of filtNone: 
+  of filtNone:
     p.skin = getParser(ident)
     result = stdin
-  of filtTemplate: 
+  of filtTemplate:
     result = filterTmpl(stdin, filename, n)
-  of filtStrip: 
+  of filtStrip:
     result = filterStrip(stdin, filename, n)
-  of filtReplace: 
+  of filtReplace:
     result = filterReplace(stdin, filename, n)
-  if f != filtNone: 
-    if gVerbosity >= 2: 
+  if f != filtNone:
+    if hintCodeBegin in gNotes:
       rawMessage(hintCodeBegin, [])
       msgWriteln(result.s)
       rawMessage(hintCodeEnd, [])
 
-proc evalPipe(p: var TParsers, n: PNode, filename: string, 
-              start: PLLStream): PLLStream = 
+proc evalPipe(p: var TParsers, n: PNode, filename: string,
+              start: PLLStream): PLLStream =
   result = start
-  if n.kind == nkEmpty: return 
+  if n.kind == nkEmpty: return
   if n.kind == nkInfix and n.sons[0].kind == nkIdent and
       identEq(n.sons[0].ident, "|"):
     for i in countup(1, 2):
@@ -159,8 +164,8 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
     result = evalPipe(p, n.sons[0], filename, result)
   else:
     result = applyFilter(p, n, filename, result)
-  
-proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = 
+
+proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
   var s: PLLStream
   p.skin = skinStandard
   let filename = fileIdx.toFullPathConsiderDirty
@@ -172,6 +177,6 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
     parser.openParser(p.parser, fileIdx, s, false)
   of skinStrongSpaces:
     parser.openParser(p.parser, fileIdx, s, true)
-  
+
 proc closeParsers(p: var TParsers) =
   parser.closeParser(p.parser)
diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim
index 0082dfcbb..7616641fc 100644
--- a/compiler/tccgen.nim
+++ b/compiler/tccgen.nim
@@ -12,10 +12,10 @@ import
 
 {.compile: "../tinyc/libtcc.c".}
 
-proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} = 
+proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
   rawMessage(errGenerated, $msg)
-  
-proc initTinyCState: PccState = 
+
+proc initTinyCState: PccState =
   result = openCCState()
   setErrorFunc(result, nil, tinyCErrorHandler)
 
@@ -23,25 +23,25 @@ var
   gTinyC = initTinyCState()
   libIncluded = false
 
-proc addFile(filename: string) =  
+proc addFile(filename: string) =
   if addFile(gTinyC, filename) != 0'i32:
     rawMessage(errCannotOpenFile, filename)
 
-proc setupEnvironment = 
+proc setupEnvironment =
   when defined(amd64):
     defineSymbol(gTinyC, "__x86_64__", nil)
   elif defined(i386):
-    defineSymbol(gTinyC, "__i386__", nil)  
+    defineSymbol(gTinyC, "__i386__", nil)
   when defined(linux):
     defineSymbol(gTinyC, "__linux__", nil)
     defineSymbol(gTinyC, "__linux", nil)
   var nimrodDir = getPrefixDir()
 
   addIncludePath(gTinyC, libpath)
-  when defined(windows): 
+  when defined(windows):
     addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
   addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
-  when defined(windows): 
+  when defined(windows):
     defineSymbol(gTinyC, "_WIN32", nil)
     # we need Mingw's headers too:
     var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
@@ -54,7 +54,7 @@ proc setupEnvironment =
     #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
     #addFile(nimrodDir / r"tinyc\win32\dllmain.o")
     addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
-    
+
     #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
     #addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
   else:
@@ -62,12 +62,12 @@ proc setupEnvironment =
     when defined(amd64):
       addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
 
-proc compileCCode*(ccode: string) = 
+proc compileCCode*(ccode: string) =
   if not libIncluded:
     libIncluded = true
     setupEnvironment()
   discard compileString(gTinyC, ccode)
-  
+
 proc run*(args: string) =
   var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
   var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 57547b682..3d78a6b92 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -16,6 +16,7 @@
 # * converts "continue" to "break"; disambiguates "break"
 # * introduces method dispatchers
 # * performs lambda lifting for closure support
+# * transforms 'defer' into a 'try finally' statement
 
 import
   intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
@@ -44,6 +45,7 @@ type
     inlining: int            # > 0 if we are in inlining context (copy vars)
     nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
+    deferDetected: bool
   PTransf = ref TTransfContext
 
 proc newTransNode(a: PNode): PTransNode {.inline.} =
@@ -113,7 +115,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   #  return liftIterSym(n)
   var b: PNode
   var tc = c.transCon
-  if sfBorrow in n.sym.flags:
+  if sfBorrow in n.sym.flags and n.sym.kind in routineKinds:
     # simply exchange the symbol:
     b = n.sym.getBody
     if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
@@ -137,23 +139,26 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
     if it.kind == nkCommentStmt:
       result[i] = PTransNode(it)
     elif it.kind == nkIdentDefs:
-      if it.sons[0].kind != nkSym: internalError(it.info, "transformVarSection")
-      internalAssert(it.len == 3)
-      var newVar = copySym(it.sons[0].sym)
-      incl(newVar.flags, sfFromGeneric)
-      # fixes a strange bug for rodgen:
-      #include(it.sons[0].sym.flags, sfFromGeneric);
-      newVar.owner = getCurrOwner(c)
-      idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
-      var defs = newTransNode(nkIdentDefs, it.info, 3)
-      if importantComments():
-        # keep documentation information:
-        PNode(defs).comment = it.comment
-      defs[0] = newSymNode(newVar).PTransNode
-      defs[1] = it.sons[1].PTransNode
-      defs[2] = transform(c, it.sons[2])
-      newVar.ast = defs[2].PNode
-      result[i] = defs
+      if it.sons[0].kind == nkSym:
+        internalAssert(it.len == 3)
+        var newVar = copySym(it.sons[0].sym)
+        incl(newVar.flags, sfFromGeneric)
+        # fixes a strange bug for rodgen:
+        #include(it.sons[0].sym.flags, sfFromGeneric);
+        newVar.owner = getCurrOwner(c)
+        idNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar))
+        var defs = newTransNode(nkIdentDefs, it.info, 3)
+        if importantComments():
+          # keep documentation information:
+          PNode(defs).comment = it.comment
+        defs[0] = newSymNode(newVar).PTransNode
+        defs[1] = it.sons[1].PTransNode
+        defs[2] = transform(c, it.sons[2])
+        newVar.ast = defs[2].PNode
+        result[i] = defs
+      else:
+        # has been transformed into 'param.x' for closure iterators, so keep it:
+        result[i] = PTransNode(it)
     else:
       if it.kind != nkVarTuple:
         internalError(it.info, "transformVarSection: not nkVarTuple")
@@ -299,6 +304,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
   var e = n.sons[0]
   # c.transCon.forStmt.len == 3 means that there is one for loop variable
   # and thus no tuple unpacking:
+  if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons
   if skipTypes(e.typ, {tyGenericInst}).kind == tyTuple and
       c.transCon.forStmt.len != 3:
     e = skipConv(e)
@@ -465,10 +471,13 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   var call = n.sons[length - 2]
 
   let labl = newLabel(c, n)
-  c.breakSyms.add(labl)
   result = newTransNode(nkBlockStmt, n.info, 2)
   result[0] = newSymNode(labl).PTransNode
-
+  if call.typ.isNil:
+    # see bug #3051
+    result[1] = newNode(nkEmpty).PTransNode
+    return result
+  c.breakSyms.add(labl)
   if call.typ.kind != tyIter and
     (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
       call.sons[0].sym.kind != skIterator):
@@ -479,10 +488,10 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
 
   #echo "transforming: ", renderTree(n)
   var stmtList = newTransNode(nkStmtList, n.info, 0)
+  result[1] = stmtList
 
   var loopBody = transformLoopBody(c, n.sons[length-1])
 
-  result[1] = stmtList
   discard c.breakSyms.pop
 
   var v = newNodeI(nkVarSection, n.info)
@@ -677,6 +686,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
       result = n
 
 proc transform(c: PTransf, n: PNode): PTransNode =
+  when false:
+    var oldDeferAnchor: PNode
+    if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
+                  nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally,
+                  nkBlockStmt, nkBlockExpr}:
+      oldDeferAnchor = c.deferAnchor
+      c.deferAnchor = n
+
   case n.kind
   of nkSym:
     result = transformSym(c, n)
@@ -709,13 +726,36 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     result = transformFor(c, n)
   of nkParForStmt:
     result = transformSons(c, n)
-  of nkCaseStmt: result = transformCase(c, n)
+  of nkCaseStmt:
+    result = transformCase(c, n)
+  of nkWhileStmt: result = transformWhile(c, n)
+  of nkBlockStmt, nkBlockExpr:
+    result = transformBlock(c, n)
+  of nkDefer:
+    c.deferDetected = true
+    result = transformSons(c, n)
+    when false:
+      let deferPart = newNodeI(nkFinally, n.info)
+      deferPart.add n.sons[0]
+      let tryStmt = newNodeI(nkTryStmt, n.info)
+      if c.deferAnchor.isNil:
+        tryStmt.add c.root
+        c.root = tryStmt
+        result = PTransNode(tryStmt)
+      else:
+        # modify the corresponding *action*, don't rely on nkStmtList:
+        let L = c.deferAnchor.len-1
+        tryStmt.add c.deferAnchor.sons[L]
+        c.deferAnchor.sons[L] = tryStmt
+        result = newTransNode(nkCommentStmt, n.info, 0)
+      tryStmt.addSon(deferPart)
+      # disable the original 'defer' statement:
+      n.kind = nkCommentStmt
   of nkContinueStmt:
     result = PTransNode(newNodeI(nkBreakStmt, n.info))
     var labl = c.contSyms[c.contSyms.high]
     add(result, PTransNode(newSymNode(labl)))
   of nkBreakStmt: result = transformBreak(c, n)
-  of nkWhileStmt: result = transformWhile(c, n)
   of nkCallKinds:
     result = transformCall(c, n)
   of nkAddr, nkHiddenAddr:
@@ -751,8 +791,6 @@ proc transform(c: PTransf, n: PNode): PTransNode =
       result = transformYield(c, n)
     else:
       result = transformSons(c, n)
-  of nkBlockStmt, nkBlockExpr:
-    result = transformBlock(c, n)
   of nkIdentDefs, nkConstDef:
     result = transformSons(c, n)
     # XXX comment handling really sucks:
@@ -761,6 +799,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   of nkClosure: return PTransNode(n)
   else:
     result = transformSons(c, n)
+  when false:
+    if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
   var cnst = getConstExpr(c.module, PNode(result))
   # we inline constants if they are not complex constants:
   if cnst != nil and not dontInlineConstant(n, cnst):
@@ -782,12 +822,54 @@ proc openTransf(module: PSym, filename: string): PTransf =
   result.breakSyms = @[]
   result.module = module
 
+proc flattenStmts(n: PNode) =
+  var goOn = true
+  while goOn:
+    goOn = false
+    var i = 0
+    while i < n.len:
+      let it = n[i]
+      if it.kind in {nkStmtList, nkStmtListExpr}:
+        n.sons[i..i] = it.sons[0..<it.len]
+        goOn = true
+      inc i
+
+proc liftDeferAux(n: PNode) =
+  if n.kind in {nkStmtList, nkStmtListExpr}:
+    flattenStmts(n)
+    var goOn = true
+    while goOn:
+      goOn = false
+      let last = n.len-1
+      for i in 0..last:
+        if n.sons[i].kind == nkDefer:
+          let deferPart = newNodeI(nkFinally, n.sons[i].info)
+          deferPart.add n.sons[i].sons[0]
+          var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
+          var body = newNodeI(n.kind, n.sons[i].info)
+          if i < last:
+            body.sons = n.sons[(i+1)..last]
+          tryStmt.addSon(body)
+          tryStmt.addSon(deferPart)
+          n.sons[i] = tryStmt
+          n.sons.setLen(i+1)
+          n.typ = n.sons[i].typ
+          goOn = true
+          break
+  for i in 0..n.safeLen-1:
+    liftDeferAux(n.sons[i])
+
+template liftDefer(c, root) =
+  if c.deferDetected:
+    liftDeferAux(root)
+
 proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
   if nfTransf in n.flags or prc.kind in {skTemplate}:
     result = n
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, prc)
+    liftDefer(c, result)
     result = liftLambdas(prc, result)
     #if prc.kind == skClosureIterator:
     #  result = lambdalifting.liftIterator(prc, result)
@@ -802,6 +884,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, module)
+    liftDefer(c, result)
     result = liftLambdasForTopLevel(module, result)
     incl(result.flags, nfTransf)
     when useEffectSystem: trackTopLevelStmt(module, result)
@@ -812,4 +895,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, module)
+    liftDefer(c, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 2c631af99..659df334b 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -36,15 +36,18 @@ proc cyclicTree*(n: PNode): bool =
   var s = newNodeI(nkEmpty, n.info)
   result = cyclicTreeAux(n, s)
 
-proc exprStructuralEquivalent*(a, b: PNode): bool =
+proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
   result = false
   if a == b:
     result = true
   elif (a != nil) and (b != nil) and (a.kind == b.kind):
     case a.kind
     of nkSym:
-      # don't go nuts here: same symbol as string is enough:
-      result = a.sym.name.id == b.sym.name.id
+      if strictSymEquality:
+        result = a.sym == b.sym
+      else:
+        # don't go nuts here: same symbol as string is enough:
+        result = a.sym.name.id == b.sym.name.id
     of nkIdent: result = a.ident.id == b.ident.id
     of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
@@ -53,7 +56,8 @@ proc exprStructuralEquivalent*(a, b: PNode): bool =
     else:
       if sonsLen(a) == sonsLen(b):
         for i in countup(0, sonsLen(a) - 1):
-          if not exprStructuralEquivalent(a.sons[i], b.sons[i]): return
+          if not exprStructuralEquivalent(a.sons[i], b.sons[i],
+                                          strictSymEquality): return
         result = true
 
 proc sameTree*(a, b: PNode): bool =
diff --git a/compiler/treetab.nim b/compiler/treetab.nim
index 8d66d56c7..e6eb8c666 100644
--- a/compiler/treetab.nim
+++ b/compiler/treetab.nim
@@ -9,36 +9,36 @@
 
 # Implements a table from trees to trees. Does structural equivalence checking.
 
-import 
+import
   hashes, ast, astalgo, types
 
-proc hashTree(n: PNode): THash = 
-  if n == nil: return 
+proc hashTree(n: PNode): Hash =
+  if n == nil: return
   result = ord(n.kind)
   case n.kind
-  of nkEmpty, nkNilLit, nkType: 
+  of nkEmpty, nkNilLit, nkType:
     discard
-  of nkIdent: 
+  of nkIdent:
     result = result !& n.ident.h
   of nkSym:
     result = result !& n.sym.name.h
-  of nkCharLit..nkUInt64Lit: 
-    if (n.intVal >= low(int)) and (n.intVal <= high(int)): 
+  of nkCharLit..nkUInt64Lit:
+    if (n.intVal >= low(int)) and (n.intVal <= high(int)):
       result = result !& int(n.intVal)
   of nkFloatLit..nkFloat64Lit:
-    if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): 
+    if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
       result = result !& toInt(n.floatVal)
   of nkStrLit..nkTripleStrLit:
     if not n.strVal.isNil:
       result = result !& hash(n.strVal)
-  else: 
-    for i in countup(0, sonsLen(n) - 1): 
+  else:
+    for i in countup(0, sonsLen(n) - 1):
       result = result !& hashTree(n.sons[i])
-  
-proc treesEquivalent(a, b: PNode): bool = 
-  if a == b: 
+
+proc treesEquivalent(a, b: PNode): bool =
+  if a == b:
     result = true
-  elif (a != nil) and (b != nil) and (a.kind == b.kind): 
+  elif (a != nil) and (b != nil) and (a.kind == b.kind):
     case a.kind
     of nkEmpty, nkNilLit, nkType: result = true
     of nkSym: result = a.sym.id == b.sym.id
@@ -46,64 +46,64 @@ proc treesEquivalent(a, b: PNode): bool =
     of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
-    else: 
-      if sonsLen(a) == sonsLen(b): 
-        for i in countup(0, sonsLen(a) - 1): 
-          if not treesEquivalent(a.sons[i], b.sons[i]): return 
+    else:
+      if sonsLen(a) == sonsLen(b):
+        for i in countup(0, sonsLen(a) - 1):
+          if not treesEquivalent(a.sons[i], b.sons[i]): return
         result = true
     if result: result = sameTypeOrNil(a.typ, b.typ)
-  
-proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int = 
-  var h: THash = k and high(t.data)
-  while t.data[h].key != nil: 
-    if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key): 
+
+proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
+  var h: Hash = k and high(t.data)
+  while t.data[h].key != nil:
+    if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
       return h
     h = nextTry(h, high(t.data))
   result = -1
 
-proc nodeTableGet*(t: TNodeTable, key: PNode): int = 
+proc nodeTableGet*(t: TNodeTable, key: PNode): int =
   var index = nodeTableRawGet(t, hashTree(key), key)
   if index >= 0: result = t.data[index].val
   else: result = low(int)
-  
-proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode, 
-                        val: int) = 
-  var h: THash = k and high(data)
+
+proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
+                        val: int) =
+  var h: Hash = k and high(data)
   while data[h].key != nil: h = nextTry(h, high(data))
   assert(data[h].key == nil)
   data[h].h = k
   data[h].key = key
   data[h].val = val
 
-proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) = 
+proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
   var n: TNodePairSeq
-  var k: THash = hashTree(key)
+  var k: Hash = hashTree(key)
   var index = nodeTableRawGet(t, k, key)
-  if index >= 0: 
+  if index >= 0:
     assert(t.data[index].key != nil)
     t.data[index].val = val
-  else: 
-    if mustRehash(len(t.data), t.counter): 
+  else:
+    if mustRehash(len(t.data), t.counter):
       newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)): 
-        if t.data[i].key != nil: 
+      for i in countup(0, high(t.data)):
+        if t.data[i].key != nil:
           nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
       swap(t.data, n)
     nodeTableRawInsert(t.data, k, key, val)
     inc(t.counter)
 
-proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = 
+proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
   var n: TNodePairSeq
-  var k: THash = hashTree(key)
+  var k: Hash = hashTree(key)
   var index = nodeTableRawGet(t, k, key)
-  if index >= 0: 
+  if index >= 0:
     assert(t.data[index].key != nil)
     result = t.data[index].val
-  else: 
-    if mustRehash(len(t.data), t.counter): 
+  else:
+    if mustRehash(len(t.data), t.counter):
       newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)): 
-        if t.data[i].key != nil: 
+      for i in countup(0, high(t.data)):
+        if t.data[i].key != nil:
           nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
       swap(t.data, n)
     nodeTableRawInsert(t.data, k, key, val)
diff --git a/compiler/types.nim b/compiler/types.nim
index e205f5722..3846be8a0 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -395,7 +395,7 @@ proc rangeToStr(n: PNode): string =
 
 const
   typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
-    "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc",
+    "Array Constructor [$1]", "nil", "untyped", "typed", "typeDesc",
     "GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
     "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
     "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
@@ -411,6 +411,10 @@ const
 
 const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
 
+proc addTypeFlags(name: var string, typ: PType) {.inline.} =
+  if tfShared in typ.flags: name = "shared " & name
+  if tfNotNil in typ.flags: name.add(" not nil")
+
 proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   var t = typ
   result = ""
@@ -418,11 +422,13 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   if prefer in preferToResolveSymbols and t.sym != nil and
        sfAnon notin t.sym.flags:
     if t.kind == tyInt and isIntLit(t):
-      return t.sym.name.s & " literal(" & $t.n.intVal & ")"
-    if prefer == preferName or t.sym.owner.isNil:
-      return t.sym.name.s
+      result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
+    elif prefer == preferName or t.sym.owner.isNil:
+      result = t.sym.name.s
     else:
-      return t.sym.owner.name.s & '.' & t.sym.name.s
+      result = t.sym.owner.name.s & '.' & t.sym.name.s
+    result.addTypeFlags(t)
+    return
   case t.kind
   of tyInt:
     if not isIntLit(t) or prefer == preferExported:
@@ -481,7 +487,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     result = "not " & typeToString(t.sons[0])
   of tyExpr:
     internalAssert t.len == 0
-    result = "expr"
+    result = "untyped"
   of tyFromExpr, tyFieldAccessor:
     result = renderTree(t.n)
   of tyArray:
@@ -563,8 +569,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     result = typeToStr[t.kind] % typeToString(t.sons[0])
   else:
     result = typeToStr[t.kind]
-  if tfShared in t.flags: result = "shared " & result
-  if tfNotNil in t.flags: result.add(" not nil")
+  result.addTypeFlags(t)
 
 proc resultType(t: PType): PType =
   assert(t.kind == tyProc)
@@ -986,7 +991,9 @@ proc compareTypes*(x, y: PType,
   var c = initSameTypeClosure()
   c.cmp = cmp
   c.flags = flags
-  result = sameTypeAux(x, y, c)
+  if x == y: result = true
+  elif x.isNil or y.isNil: result = false
+  else: result = sameTypeAux(x, y, c)
 
 proc inheritanceDiff*(a, b: PType): int =
   # | returns: 0 iff `a` == `b`
@@ -1260,13 +1267,16 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
       else: result = 8
     a = result
   of tySet:
-    length = lengthOrd(typ.sons[0])
-    if length <= 8: result = 1
-    elif length <= 16: result = 2
-    elif length <= 32: result = 4
-    elif length <= 64: result = 8
-    elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8
-    else: result = align(length, 8) div 8 + 1
+    if typ.sons[0].kind == tyGenericParam:
+      result = szUnknownSize
+    else:
+      length = lengthOrd(typ.sons[0])
+      if length <= 8: result = 1
+      elif length <= 16: result = 2
+      elif length <= 32: result = 4
+      elif length <= 64: result = 8
+      elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8
+      else: result = align(length, 8) div 8 + 1
     a = result
   of tyRange:
     result = computeSizeAux(typ.sons[0], a)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 1c6c9a30b..495b0c747 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -83,7 +83,7 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
 
 proc bailOut(c: PCtx; tos: PStackFrame) =
   stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX,
-             c.currentExceptionA.sons[2].strVal)
+             c.currentExceptionA.sons[3].skipColon.strVal)
 
 when not defined(nimComputedGoto):
   {.pragma: computedGoto.}
@@ -120,10 +120,10 @@ template decodeBx(k: expr) {.immediate, dirty.} =
 template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b)
 # XXX fix minor 'shallowCopy' overloading bug in compiler
 
-proc createStrKeepNode(x: var TFullReg) =
+proc createStrKeepNode(x: var TFullReg; keepNode=true) =
   if x.node.isNil:
     x.node = newNode(nkStrLit)
-  elif x.node.kind == nkNilLit:
+  elif x.node.kind == nkNilLit and keepNode:
     when defined(useNodeIds):
       let id = x.node.id
     system.reset(x.node[])
@@ -311,7 +311,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
           if f.position == x:
             dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
             return
-        internalError("opConv for enum")
+        dest.node.strVal = styp.sym.name.s & " " & $x
     of tyInt..tyInt64:
       dest.node.strVal = $src.intVal
     of tyUInt..tyUInt64:
@@ -320,8 +320,19 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
       dest.node.strVal = if src.intVal == 0: "false" else: "true"
     of tyFloat..tyFloat128:
       dest.node.strVal = $src.floatVal
-    of tyString, tyCString:
+    of tyString:
       dest.node.strVal = src.node.strVal
+    of tyCString:
+      if src.node.kind == nkBracket:
+        # Array of chars
+        var strVal = ""
+        for son in src.node.sons:
+          let c = char(son.intVal)
+          if c == '\0': break
+          strVal.add(c)
+        dest.node.strVal = strVal
+      else:
+        dest.node.strVal = src.node.strVal
     of tyChar:
       dest.node.strVal = $chr(src.intVal)
     else:
@@ -351,7 +362,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
         myreset(dest); dest.kind = rkFloat
       case skipTypes(srctyp, abstractRange).kind
       of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
-        dest.floatVal = toFloat(src.intVal.int)
+        dest.floatVal = toBiggestFloat(src.intVal)
       else:
         dest.floatVal = src.floatVal
     else:
@@ -383,8 +394,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     let instr = c.code[pc]
     let ra = instr.regA
     #if c.traceActive:
-    #  echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
+    #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
     #  message(c.debug[pc], warnUser, "Trace")
+
     case instr.opcode
     of opcEof: return regs[ra]
     of opcRet:
@@ -407,8 +419,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkInt)
       regs[ra].intVal = regs[rb].intVal
     of opcAsgnStr:
-      decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      decodeBC(rkNode)
+      createStrKeepNode regs[ra], rc != 0
       regs[ra].node.strVal = regs[rb].node.strVal
     of opcAsgnFloat:
       decodeB(rkFloat)
@@ -431,7 +443,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         assert regs[rb].kind == rkNode
         let nb = regs[rb].node
         case nb.kind
-        of nkCharLit..nkInt64Lit:
+        of nkCharLit..nkUInt64Lit:
           ensureKind(rkInt)
           regs[ra].intVal = nb.intVal
         of nkFloatLit..nkFloat64Lit:
@@ -474,14 +486,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeBC(rkNode)
       let src = regs[rb].node
       if src.kind notin {nkEmpty..nkNilLit}:
-        let n = src.sons[rc].skipColon
+        let n = src.sons[rc + ord(src.kind == nkObjConstr)].skipColon
         regs[ra].node = n
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrObj:
       # a.b = c
       decodeBC(rkNode)
-      putIntoNode(regs[ra].node.sons[rb], regs[rc])
+      let shiftedRb = rb + ord(regs[ra].node.kind == nkObjConstr)
+      let dest = regs[ra].node
+      if dest.sons[shiftedRb].kind == nkExprColonExpr:
+        putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc])
+      else:
+        putIntoNode(dest.sons[shiftedRb], regs[rc])
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
@@ -494,7 +511,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].regAddr = addr(regs[rb])
     of opcAddrNode:
       decodeB(rkNodeAddr)
-      regs[ra].nodeAddr = addr(regs[rb].node)
+      if regs[rb].kind == rkNode:
+        regs[ra].nodeAddr = addr(regs[rb].node)
+      else:
+        stackTrace(c, tos, pc, errGenerated, "limited VM support for 'addr'")
     of opcLdDeref:
       # a = b[]
       let ra = instr.regA
@@ -509,8 +529,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       of rkNode:
         if regs[rb].node.kind == nkNilLit:
           stackTrace(c, tos, pc, errNilAccess)
-        assert regs[rb].node.kind == nkRefTy
-        regs[ra].node = regs[rb].node.sons[0]
+        if regs[rb].node.kind == nkRefTy:
+          regs[ra].node = regs[rb].node.sons[0]
+        else:
+          stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'")
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
@@ -682,11 +704,19 @@ 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, opcEqNimrodNode:
+    of opcEqRef:
       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(exprStructuralEquivalent(regs[rb].node, regs[rc].node,
+                                     strictSymEquality=true))
+    of opcSameNodeType:
+      decodeBC(rkInt)
+      regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil regs[rc].node.typ)
     of opcXor:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
@@ -770,16 +800,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node.add(copyTree(regs[rb].regToNode))
       else:
         stackTrace(c, tos, pc, errNilAccess)
+    of opcGetImpl:
+      decodeB(rkNode)
+      let a = regs[rb].node
+      if a.kind == nkSym:
+        regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
+                        else: copyTree(a.sym.ast)
+      else:
+        stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
     of opcEcho:
       let rb = instr.regB
       if rb == 1:
-        msgWriteln(regs[ra].node.strVal)
+        msgWriteln(regs[ra].node.strVal, {msgStdout})
       else:
         var outp = ""
         for i in ra..ra+rb-1:
           #if regs[i].kind != rkNode: debug regs[i]
           outp.add(regs[i].node.strVal)
-        msgWriteln(outp)
+        msgWriteln(outp, {msgStdout})
     of opcContainsSet:
       decodeBC(rkInt)
       regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
@@ -1047,18 +1085,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           # set to default value:
           for i in oldLen .. <newLen:
             regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
-    of opcSwap:
-      let rb = instr.regB
-      if regs[ra].kind == regs[rb].kind:
-        case regs[ra].kind
-        of rkNone: discard
-        of rkInt: swap regs[ra].intVal, regs[rb].intVal
-        of rkFloat: swap regs[ra].floatVal, regs[rb].floatVal
-        of rkNode: swap regs[ra].node, regs[rb].node
-        of rkRegisterAddr: swap regs[ra].regAddr, regs[rb].regAddr
-        of rkNodeAddr: swap regs[ra].nodeAddr, regs[rb].nodeAddr
-      else:
-        internalError(c.debug[pc], "cannot swap operands")
     of opcReset:
       internalError(c.debug[pc], "too implement")
     of opcNarrowS:
@@ -1121,7 +1147,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkInt)
       let a = regs[rb].node
       case a.kind
-      of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal
+      of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal
       else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
     of opcNFloatVal:
       decodeB(rkFloat)
@@ -1172,9 +1198,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                                      c.module)
     of opcGorge:
       decodeBC(rkNode)
+      inc pc
+      let rd = c.code[pc].regA
+
       createStr regs[ra]
       regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
-                                     regs[rc].node.strVal)
+                                     regs[rc].node.strVal, regs[rd].node.strVal)
     of opcNError:
       stackTrace(c, tos, pc, errUser, regs[ra].node.strVal)
     of opcNWarning:
@@ -1276,7 +1305,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNSetIntVal:
       decodeB(rkNode)
       var dest = regs[ra].node
-      if dest.kind in {nkCharLit..nkInt64Lit} and
+      if dest.kind in {nkCharLit..nkUInt64Lit} and
          regs[rb].kind in {rkInt}:
         dest.intVal = regs[rb].intVal
       else:
@@ -1385,6 +1414,31 @@ proc execute(c: PCtx, start: int): PNode =
   newSeq(tos.slots, c.prc.maxSlots)
   result = rawExecute(c, start, tos).regToNode
 
+proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
+  if sym.kind in routineKinds:
+    if sym.typ.len-1 != args.len:
+      localError(sym.info,
+        "NimScript: expected $# arguments, but got $#" % [
+        $(sym.typ.len-1), $args.len])
+    else:
+      let start = genProc(c, sym)
+
+      var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
+      let maxSlots = sym.offset
+      newSeq(tos.slots, maxSlots)
+
+      # setup parameters:
+      if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro:
+        putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info))
+      # XXX We could perform some type checking here.
+      for i in 1.. <sym.typ.len:
+        putIntoReg(tos.slots[i], args[i-1])
+
+      result = rawExecute(c, start, tos).regToNode
+  else:
+    localError(sym.info,
+      "NimScript: attempt to call non-routine: " & sym.name.s)
+
 proc evalStmt*(c: PCtx, n: PNode) =
   let n = transformExpr(c.module, n)
   let start = genStmt(c, n)
@@ -1399,13 +1453,17 @@ proc evalExpr*(c: PCtx, n: PNode): PNode =
   assert c.code[start].opcode != opcEof
   result = execute(c, start)
 
+proc getGlobalValue*(c: PCtx; s: PSym): PNode =
+  internalAssert s.kind in {skLet, skVar} and sfGlobal in s.flags
+  result = c.globals.sons[s.position-1]
+
 include vmops
 
 # for now we share the 'globals' environment. XXX Coming soon: An API for
 # storing&loading the 'globals' environment to get what a component system
 # requires.
 var
-  globalCtx: PCtx
+  globalCtx*: PCtx
 
 proc setupGlobalCtx(module: PSym) =
   if globalCtx.isNil:
@@ -1442,6 +1500,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
   let n = transformExpr(module, n)
   setupGlobalCtx(module)
   var c = globalCtx
+  let oldMode = c.mode
+  defer: c.mode = oldMode
   c.mode = mode
   let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
   if c.code[start].opcode == opcEof: return emptyNode
@@ -1465,12 +1525,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
 proc setupCompileTimeVar*(module: PSym, n: PNode) =
   discard evalConstExprAux(module, nil, n, emStaticStmt)
 
-proc setupMacroParam(x: PNode): PNode =
-  result = x
-  if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
-  result = canonValue(result)
-  result.flags.incl nfIsRef
-  result.typ = x.typ
+proc setupMacroParam(x: PNode, typ: PType): TFullReg =
+  case typ.kind
+  of tyStatic:
+    putIntoReg(result, x)
+  of tyTypeDesc:
+    putIntoReg(result, x)
+  else:
+    result.kind = rkNode
+    var n = x
+    if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
+    n = n.canonValue
+    n.flags.incl nfIsRef
+    n.typ = x.typ
+    result.node = n
 
 var evalMacroCounter: int
 
@@ -1506,10 +1574,17 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   # return value:
   tos.slots[0].kind = rkNode
   tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
+
   # setup parameters:
-  for i in 1 .. < min(tos.slots.len, L):
-    tos.slots[i].kind = rkNode
-    tos.slots[i].node = setupMacroParam(n.sons[i])
+  for i in 1.. <sym.typ.len:
+    tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
+
+  if sfImmediate notin sym.flags:
+    let gp = sym.ast[genericParamsPos]
+    for i in 0 .. <gp.len:
+      let idx = sym.typ.len + i
+      tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+
   # temporary storage:
   #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 047009f01..337e4ec8f 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -60,12 +60,13 @@ type
     opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
     opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
     opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
-    opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimrodNode, opcXor,
-    opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
+    opcLeFloat, opcLtFloat, opcLeu, opcLtu,
+    opcEqRef, opcEqNimrodNode, opcSameNodeType,
+    opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
     opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
     opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
     opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
-    opcSwap, opcIsNil, opcOf, opcIs,
+    opcIsNil, opcOf, opcIs,
     opcSubStr, opcParseFloat, opcConv, opcCast,
     opcQuit, opcReset,
     opcNarrowS, opcNarrowU,
@@ -101,6 +102,7 @@ type
     opcEqIdent,
     opcStrToIdent,
     opcIdentToStr,
+    opcGetImpl,
 
     opcEcho,
     opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 21ee4967b..2cc4a107b 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, osproc, streams, options, idents
+import ast, types, msgs, osproc, streams, options, idents, securehash
 
 proc readOutput(p: Process): string =
   result = ""
@@ -15,18 +15,39 @@ proc readOutput(p: Process): string =
   while not output.atEnd:
     result.add(output.readLine)
     result.add("\n")
-  result.setLen(result.len - "\n".len)
+  if result.len > 0:
+    result.setLen(result.len - "\n".len)
   discard p.waitForExit
 
-proc opGorge*(cmd, input: string): string =
-  try:
-    var p = startProcess(cmd, options={poEvalCommand})
-    if input.len != 0:
-      p.inputStream.write(input)
-      p.inputStream.close()
-    result = p.readOutput
-  except IOError, OSError:
-    result = ""
+proc opGorge*(cmd, input, cache: string): string =
+  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
+    let h = secureHash(cmd & "\t" & input & "\t" & cache)
+    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
+    var f: File
+    if open(f, filename):
+      result = f.readAll
+      f.close
+      return
+    var readSuccessful = false
+    try:
+      var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+      readSuccessful = true
+      writeFile(filename, result)
+    except IOError, OSError:
+      if not readSuccessful: result = ""
+  else:
+    try:
+      var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+    except IOError, OSError:
+      result = ""
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
   try:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 0743a4502..97c6a5580 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -37,7 +37,7 @@ when hasFFI:
   import evalffi
 
 type
-  TGenFlag = enum gfNone, gfAddrOf
+  TGenFlag = enum gfAddrOf, gfFieldAccess
   TGenFlags = set[TGenFlag]
 
 proc debugInfo(info: TLineInfo): string =
@@ -74,8 +74,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
       result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
                   i+x.regBx-wordExcess)
     elif opc in {opcLdConst, opcAsgnConst}:
-      result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
-        c.constants[x.regBx-wordExcess].renderTree)
+      let idx = x.regBx-wordExcess
+      result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
+        c.constants[idx].renderTree, $idx)
     elif opc in {opcMarshalLoad, opcMarshalStore}:
       let y = c.code[i+1]
       result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
@@ -169,8 +170,12 @@ proc getSlotKind(t: PType): TSlotKind =
 const
   HighRegisterPressure = 40
 
-proc getTemp(c: PCtx; typ: PType): TRegister =
-  let c = c.prc
+proc bestEffort(c: PCtx): TLineInfo =
+  (if c.prc == nil: c.module.info else: c.prc.sym.info)
+
+proc getTemp(cc: PCtx; tt: PType): TRegister =
+  let typ = tt.skipTypesOrNil({tyStatic})
+  let c = cc.prc
   # we prefer the same slot kind here for efficiency. Unfortunately for
   # discardable return types we may not know the desired type. This can happen
   # for e.g. mNAdd[Multiple]:
@@ -181,13 +186,13 @@ proc getTemp(c: PCtx; typ: PType): TRegister =
       return TRegister(i)
 
   # if register pressure is high, we re-use more aggressively:
-  if c.maxSlots >= HighRegisterPressure:
+  if c.maxSlots >= HighRegisterPressure and false:
     for i in 0 .. c.maxSlots-1:
       if not c.slots[i].inUse:
         c.slots[i] = (inUse: true, kind: k)
         return TRegister(i)
   if c.maxSlots >= high(TRegister):
-    internalError("cannot generate code; too many registers required")
+    globalError(cc.bestEffort, "VM problem: too many registers required")
   result = TRegister(c.maxSlots)
   c.slots[c.maxSlots] = (inUse: true, kind: k)
   inc c.maxSlots
@@ -196,9 +201,9 @@ proc freeTemp(c: PCtx; r: TRegister) =
   let c = c.prc
   if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false
 
-proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister =
+proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
   # if register pressure is high, we re-use more aggressively:
-  let c = c.prc
+  let c = cc.prc
   if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister):
     for i in 0 .. c.maxSlots-n:
       if not c.slots[i].inUse:
@@ -209,7 +214,7 @@ proc getTempRange(c: PCtx; n: int; kind: TSlotKind): TRegister =
           for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
           return
   if c.maxSlots+n >= high(TRegister):
-    internalError("cannot generate code; too many registers required")
+    globalError(cc.bestEffort, "VM problem: too many registers required")
   result = TRegister(c.maxSlots)
   inc c.maxSlots, n
   for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
@@ -305,7 +310,7 @@ proc genBreak(c: PCtx; n: PNode) =
       if c.prc.blocks[i].label == n.sons[0].sym:
         c.prc.blocks[i].fixups.add L1
         return
-    internalError(n.info, "cannot find 'break' target")
+    globalError(n.info, errGenerated, "VM problem: cannot find 'break' target")
   else:
     c.prc.blocks[c.prc.blocks.high].fixups.add L1
 
@@ -393,7 +398,7 @@ proc genLiteral(c: PCtx; n: PNode): int =
 proc unused(n: PNode; x: TDest) {.inline.} =
   if x >= 0:
     #debug(n)
-    internalError(n.info, "not unused")
+    globalError(n.info, "not unused")
 
 proc genCase(c: PCtx; n: PNode; dest: var TDest) =
   #  if (!expr1) goto L1;
@@ -509,10 +514,10 @@ proc needsAsgnPatch(n: PNode): bool =
 
 proc genField(n: PNode): TRegister =
   if n.kind != nkSym or n.sym.kind != skField:
-    internalError(n.info, "no field symbol")
+    globalError(n.info, "no field symbol")
   let s = n.sym
   if s.position > high(result):
-      internalError(n.info,
+    globalError(n.info,
         "too large offset! cannot generate code for: " & s.name.s)
   result = s.position
 
@@ -530,7 +535,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
 proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
     let idx = c.genIndex(le.sons[1], le.sons[0].typ)
     c.gABC(le, opcWrArr, dest, idx, value)
     c.freeTemp(dest)
@@ -538,7 +543,7 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   of nkDotExpr, nkCheckedFieldExpr:
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf})
+    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
     let idx = genField(left.sons[1])
     c.gABC(left, opcWrObj, dest, idx, value)
     c.freeTemp(dest)
@@ -596,6 +601,18 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   c.freeTemp(tmp)
   c.freeTemp(tmp2)
 
+proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
+  let
+    tmp = c.genx(n.sons[1])
+    tmp2 = c.genx(n.sons[2])
+    tmp3 = c.genx(n.sons[3])
+  if dest < 0: dest = c.getTemp(n.typ)
+  c.gABC(n, opc, dest, tmp, tmp2)
+  c.gABC(n, opc, tmp3)
+  c.freeTemp(tmp)
+  c.freeTemp(tmp2)
+  c.freeTemp(tmp3)
+
 proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
   let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
   # uint is uint64 in the VM, we we only need to mask the result for
@@ -691,7 +708,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
   c.gABx(n, opc, 0, genType(c, n.typ))
-  c.gABx(n, opc, 0, genType(c, arg.typ))
+  c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
   c.freeTemp(tmp)
 
 proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@@ -710,9 +727,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABI(n, opcSubImmInt, dest, tmp, 1)
     c.freeTemp(tmp)
-  of mPred, mSubI, mSubI64:
+  of mPred, mSubI:
     c.genAddSubInt(n, dest, opcSubInt)
-  of mSucc, mAddI, mAddI64:
+  of mSucc, mAddI:
     c.genAddSubInt(n, dest, opcAddInt)
   of mInc, mDec:
     unused(n, dest)
@@ -759,28 +776,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(d)
     c.freeTemp(tmp)
   of mCard: genCard(c, n, dest)
-  of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt)
-  of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt)
-  of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt)
+  of mMulI: genBinaryABCnarrow(c, n, dest, opcMulInt)
+  of mDivI: genBinaryABCnarrow(c, n, dest, opcDivInt)
+  of mModI: genBinaryABCnarrow(c, n, dest, opcModInt)
   of mAddF64: genBinaryABC(c, n, dest, opcAddFloat)
   of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
   of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
   of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
-  of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt)
-  of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt)
-  of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
-  of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
-  of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
+  of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt)
+  of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
+  of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
+  of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
+  of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
   of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu)
   of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu)
   of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu)
   of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu)
   of mModU: genBinaryABCnarrowU(c, n, dest, opcModu)
-  of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
+  of mEqI, mEqB, mEqEnum, mEqCh:
     genBinaryABC(c, n, dest, opcEqInt)
-  of mLeI, mLeI64, mLeEnum, mLeCh, mLeB:
+  of mLeI, mLeEnum, mLeCh, mLeB:
     genBinaryABC(c, n, dest, opcLeInt)
-  of mLtI, mLtI64, mLtEnum, mLtCh, mLtB:
+  of mLtI, mLtEnum, mLtCh, mLtB:
     genBinaryABC(c, n, dest, opcLtInt)
   of mEqF64: genBinaryABC(c, n, dest, opcEqFloat)
   of mLeF64: genBinaryABC(c, n, dest, opcLeFloat)
@@ -796,7 +813,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     genNarrow(c, n, dest)
   of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
   of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
-  of mBitnotI, mBitnotI64:
+  of mBitnotI:
     genUnaryABC(c, n, dest, opcBitnotInt)
     genNarrowU(c, n, dest)
   of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
@@ -831,12 +848,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp)
   of mSwap:
     unused(n, dest)
-    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)
+    c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mCopyStr:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -934,8 +946,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.gABC(n, opcTypeTrait, dest, tmp)
     c.freeTemp(tmp)
   of mSlurp: genUnaryABC(c, n, dest, opcSlurp)
-  of mStaticExec: genBinaryABC(c, n, dest, opcGorge)
+  of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)
   of mNLen: genUnaryABI(c, n, dest, opcLenSeq)
+  of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
   of mNSetChild, mNDel:
     unused(n, dest)
@@ -988,11 +1001,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       if dest < 0: dest = c.getTemp(n.typ)
       c.gABx(n, opcNBindSym, dest, idx)
     else:
-      internalError(n.info, "invalid bindSym usage")
+      localError(n.info, "invalid bindSym usage")
   of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
   of mIdentToStr: genUnaryABC(c, n, dest, opcIdentToStr)
   of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
   of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
+  of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
   of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo)
   of mNHint:
     unused(n, dest)
@@ -1013,7 +1027,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.gABC(n, opcCallSite, dest)
   of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
   of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
-     mAbsI64, mDotDot:
+     mDotDot:
     c.genCall(n, dest)
   of mExpandToAst:
     if n.len != 2:
@@ -1030,7 +1044,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       globalError(n.info, "expandToAst requires a call expression")
   else:
     # mGCref, mGCunref,
-    internalError(n.info, "cannot generate code for: " & $m)
+    globalError(n.info, "cannot generate code for: " & $m)
 
 proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
   ## Signature: proc to*[T](data: string): T
@@ -1062,6 +1076,7 @@ const
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
 
 proc fitsRegister*(t: PType): bool =
+  assert t != nil
   t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
     tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
 
@@ -1076,10 +1091,32 @@ proc requiresCopy(n: PNode): bool =
 proc unneededIndirection(n: PNode): bool =
   n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
 
+proc canElimAddr(n: PNode): PNode =
+  case n.sons[0].kind
+  of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
+    var m = n.sons[0].sons[0]
+    if m.kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
+      result = copyNode(n.sons[0])
+      result.add m.sons[0]
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    var m = n.sons[0].sons[1]
+    if m.kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
+      result = copyNode(n.sons[0])
+      result.add m.sons[0]
+  else:
+    if n.sons[0].kind in {nkDerefExpr, nkHiddenDeref}:
+      # addr ( deref ( x )) --> x
+      result = n.sons[0].sons[0]
+
 proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
                   flags: TGenFlags) =
   # a nop for certain types
   let isAddr = opc in {opcAddrNode, opcAddrReg}
+  if isAddr and (let m = canElimAddr(n); m != nil):
+    gen(c, m, dest, flags)
+    return
   let newflags = if isAddr: flags+{gfAddrOf} else: flags
   # consider:
   # proc foo(f: var ref int) =
@@ -1092,6 +1129,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
   # nkAddr we must not use 'unneededIndirection', but for deref we use it.
   if not isAddr and unneededIndirection(n.sons[0]):
     gen(c, n.sons[0], dest, newflags)
+    if gfAddrOf notin flags and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
   elif isAddr and isGlobal(n.sons[0]):
     gen(c, n.sons[0], dest, flags+{gfAddrOf})
   else:
@@ -1099,6 +1138,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
     if dest < 0: dest = c.getTemp(n.typ)
     if not isAddr:
       gABC(c, n, opc, dest, tmp)
+      assert n.typ != nil
       if gfAddrOf notin flags and fitsRegister(n.typ):
         c.gABC(n, opcNodeToReg, dest, dest)
     elif c.prc.slots[tmp].kind >= slotTempUnknown:
@@ -1132,7 +1172,7 @@ proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
 proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
   let tmp = c.genx(ri)
   assert dest >= 0
-  gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
+  gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy))
   c.freeTemp(tmp)
 
 proc setSlot(c: PCtx; v: PSym) =
@@ -1140,7 +1180,7 @@ proc setSlot(c: PCtx; v: PSym) =
   if v.position == 0:
     if c.prc.maxSlots == 0: c.prc.maxSlots = 1
     if c.prc.maxSlots >= high(TRegister):
-      internalError(v.info, "cannot generate code; too many registers required")
+      globalError(v.info, "cannot generate code; too many registers required")
     v.position = c.prc.maxSlots
     c.prc.slots[v.position] = (inUse: true,
         kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
@@ -1166,7 +1206,10 @@ proc checkCanEval(c: PCtx; n: PNode) =
   let s = n.sym
   if {sfCompileTime, sfGlobal} <= s.flags: return
   if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
-      not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
+      not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
+    cannotEval(n)
+  elif s.kind in {skProc, skConverter, skMethod,
+                  skIterator, skClosureIterator} and sfForward in s.flags:
     cannotEval(n)
 
 proc isTemp(c: PCtx; dest: TDest): bool =
@@ -1183,9 +1226,10 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
   # 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!
+  assert n.typ != nil
   if needsAdditionalCopy(n):
     var cc = c.getTemp(n.typ)
-    c.gABC(n, whichAsgnOpc(n), cc, value)
+    c.gABC(n, whichAsgnOpc(n), cc, value, 0)
     c.gABC(n, opc, dest, idx, cc)
     c.freeTemp(cc)
   else:
@@ -1194,7 +1238,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
 proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
     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 {
@@ -1206,7 +1250,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   of nkDotExpr, nkCheckedFieldExpr:
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf})
+    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
     let idx = genField(left.sons[1])
     let tmp = c.genx(ri)
     c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
@@ -1230,10 +1274,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
       internalAssert s.position > 0 or (s.position == 0 and
                                         s.kind in {skParam,skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
+      assert le.typ != nil
       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.gABC(le, whichAsgnOpc(le), dest, cc, 1)
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
@@ -1292,12 +1337,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       if sfImportc in s.flags: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
+    assert s.typ != nil
     if gfAddrOf notin flags and fitsRegister(s.typ):
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
       c.gABC(n, opcNodeToReg, dest, cc)
       c.freeTemp(cc)
-    elif gfAddrOf in flags:
+    elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}:
       c.gABx(n, opcLdGlobalAddr, dest, s.position)
     else:
       c.gABx(n, opcLdGlobal, dest, s.position)
@@ -1353,9 +1399,11 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   genObjAccess(c, n.sons[0], dest, flags)
 
 proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
-  if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
-      tyString, tyCString}:
+  let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
+  if arrayType in {tyString, tyCString}:
     genArrAccess2(c, n, dest, opcLdStrIdx, {})
+  elif arrayType == tyTypeDesc:
+    c.genTypeLit(n.typ, dest)
   else:
     genArrAccess2(c, n, dest, opcLdArr, flags)
 
@@ -1368,8 +1416,11 @@ proc getNullValueAux(obj: PNode, result: PNode) =
     for i in countup(1, sonsLen(obj) - 1):
       getNullValueAux(lastSon(obj.sons[i]), result)
   of nkSym:
-    addSon(result, getNullValue(obj.sym.typ, result.info))
-  else: internalError(result.info, "getNullValueAux")
+    let field = newNodeI(nkExprColonExpr, result.info)
+    field.add(obj)
+    field.add(getNullValue(obj.sym.typ, result.info))
+    addSon(result, field)
+  else: globalError(result.info, "cannot create null element for: " & $obj)
 
 proc getNullValue(typ: PType, info: TLineInfo): PNode =
   var t = skipTypes(typ, abstractRange-{tyTypeDesc})
@@ -1394,7 +1445,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
       result.add(newNodeIT(nkNilLit, info, t))
       result.add(newNodeIT(nkNilLit, info, t))
   of tyObject:
-    result = newNodeIT(nkPar, info, t)
+    result = newNodeIT(nkObjConstr, info, t)
+    result.add(newNodeIT(nkEmpty, info, t))
     getNullValueAux(t.n, result)
     # initialize inherited fields:
     var base = t.sons[0]
@@ -1411,9 +1463,11 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
       addSon(result, getNullValue(t.sons[i], info))
   of tySet:
     result = newNodeIT(nkCurly, info, t)
-  else: internalError(info, "getNullValue: " & $t.kind)
+  else:
+    globalError(info, "cannot create null element for: " & $t.kind)
 
 proc ldNullOpcode(t: PType): TOpcode =
+  assert t != nil
   if fitsRegister(t): opcLdNullReg else: opcLdNull
 
 proc genVarSection(c: PCtx; n: PNode) =
@@ -1441,7 +1495,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.preventFalseAlias(a, opcWrDeref, tmp, 0, val)
+          c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else:
@@ -1449,13 +1503,15 @@ 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:
+          assert s.typ != nil
           if not fitsRegister(s.typ):
             c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
           let le = a.sons[0]
+          assert le.typ != nil
           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.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1)
             c.freeTemp(cc)
           else:
             gen(c, a.sons[2], s.position.TRegister)
@@ -1522,7 +1578,7 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
                           dest, idx, tmp)
       c.freeTemp(tmp)
     else:
-      internalError(n.info, "invalid object constructor")
+      globalError(n.info, "invalid object constructor")
 
 proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
   if dest < 0: dest = c.getTemp(n.typ)
@@ -1596,8 +1652,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
         c.gABx(n, opcLdConst, dest, lit)
     of skType:
       genTypeLit(c, s.typ, dest)
+    of skGenericParam:
+      if c.prc.sym.kind == skMacro:
+        genRdVar(c, n, dest, flags)
+      else:
+        internalError(n.info, "cannot generate code for: " & s.name.s)
     else:
-      internalError(n.info, "cannot generate code for: " & s.name.s)
+      globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
     if n.sons[0].kind == nkSym:
       let s = n.sons[0].sym
@@ -1631,7 +1692,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkBracketExpr: genArrAccess(c, n, dest, flags)
   of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
   of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
-  of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
+  of nkIfStmt, nkIfExpr: genIf(c, n, dest)
+  of nkWhenStmt:
+      # This is "when nimvm" node. Chose the first branch.
+      gen(c, n.sons[0].sons[1], dest)
   of nkCaseStmt: genCase(c, n, dest)
   of nkWhileStmt:
     unused(n, dest)
@@ -1682,7 +1746,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     c.freeTemp(tmp1)
     c.freeTemp(tmp2)
     if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+      gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1)
       c.freeTemp(tmp0)
     else:
       dest = tmp0
@@ -1701,7 +1765,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     else:
       globalError(n.info, errGenerated, "VM is not allowed to 'cast'")
   else:
-    internalError n.info, "cannot generate VM code for " & n.renderTree
+    globalError(n.info, errGenerated, "cannot generate VM code for " & $n)
 
 proc removeLastEof(c: PCtx) =
   let last = c.code.len-1
@@ -1717,7 +1781,8 @@ proc genStmt*(c: PCtx; n: PNode): int =
   var d: TDest = -1
   c.gen(n, d)
   c.gABC(n, opcEof)
-  if d >= 0: internalError(n.info, "some destination set")
+  if d >= 0:
+    globalError(n.info, errGenerated, "VM problem: dest register is set")
 
 proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
   c.removeLastEof
@@ -1725,10 +1790,14 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
   var d: TDest = -1
   c.gen(n, d)
   if d < 0:
-    if requiresValue: internalError(n.info, "no destination set")
+    if requiresValue:
+      globalError(n.info, errGenerated, "VM problem: dest register is not set")
     d = 0
   c.gABC(n, opcEof, d)
 
+  #echo renderTree(n)
+  #c.echoCode(result)
+
 proc genParams(c: PCtx; params: PNode) =
   # res.sym.position is already 0
   c.prc.slots[0] = (inUse: true, kind: slotFixedVar)
@@ -1744,6 +1813,14 @@ proc finalJumpTarget(c: PCtx; pc, diff: int) =
   c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
                 uint32(diff+wordExcess) shl 16'u32).TInstr
 
+proc genGenericParams(c: PCtx; gp: PNode) =
+  var base = c.prc.maxSlots
+  for i in 0.. <gp.len:
+    var param = gp.sons[i].sym
+    param.position = base + i # XXX: fix this earlier; make it consistent with templates
+    c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
+  c.prc.maxSlots = base + gp.len
+
 proc optimizeJumps(c: PCtx; start: int) =
   const maxIterations = 10
   for i in start .. <c.code.len:
@@ -1808,6 +1885,13 @@ proc genProc(c: PCtx; s: PSym): int =
     c.prc = p
     # iterate over the parameters and allocate space for them:
     genParams(c, s.typ.n)
+
+    # allocate additional space for any generically bound parameters
+    if s.kind == skMacro and
+       sfImmediate notin s.flags and
+       s.ast[genericParamsPos].kind != nkEmpty:
+      genGenericParams(c, s.ast[genericParamsPos])
+
     if tfCapturesEnv in s.typ.flags:
       #let env = s.ast.sons[paramsPos].lastSon.sym
       #assert env.position == 2
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 6ec5f6044..576b0565f 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -17,7 +17,7 @@ template setX(k, field) {.immediate, dirty.} =
 
 proc setResult*(a: VmArgs; v: BiggestInt) = setX(rkInt, intVal)
 proc setResult*(a: VmArgs; v: BiggestFloat) = setX(rkFloat, floatVal)
-proc setResult*(a: VmArgs; v: bool) = 
+proc setResult*(a: VmArgs; v: bool) =
   let v = v.ord
   setX(rkInt, intVal)
 
@@ -30,6 +30,24 @@ proc setResult*(a: VmArgs; v: string) =
   s[a.ra].node = newNode(nkStrLit)
   s[a.ra].node.strVal = v
 
+proc setResult*(a: VmArgs; n: PNode) =
+  var s: seq[TFullReg]
+  move(s, cast[seq[TFullReg]](a.slots))
+  if s[a.ra].kind != rkNode:
+    myreset(s[a.ra])
+    s[a.ra].kind = rkNode
+  s[a.ra].node = n
+
+proc setResult*(a: VmArgs; v: seq[string]) =
+  var s: seq[TFullReg]
+  move(s, cast[seq[TFullReg]](a.slots))
+  if s[a.ra].kind != rkNode:
+    myreset(s[a.ra])
+    s[a.ra].kind = rkNode
+  var n = newNode(nkBracket)
+  for x in v: n.add newStrNode(nkStrLit, x)
+  s[a.ra].node = n
+
 template getX(k, field) {.immediate, dirty.} =
   doAssert i < a.rc-1
   let s = cast[seq[TFullReg]](a.slots)
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index 293d0d949..c08c5d249 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -36,8 +36,8 @@ proc getField(n: PNode; position: int): PSym =
 proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet)
 
 proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet) =
-  internalAssert x.kind in {nkObjConstr, nkPar}
-  let start = ord(x.kind == nkObjConstr)
+  internalAssert x.kind == nkObjConstr
+  let start = 1
   for i in countup(start, sonsLen(x) - 1):
     if i > start: s.add(", ")
     var it = x.sons[i]
@@ -205,18 +205,23 @@ proc loadAny(p: var JsonParser, t: PType,
   of tyObject:
     if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
     next(p)
-    result = newNode(nkPar)
-    result.sons = @[]
+    result = newNode(nkObjConstr)
+    result.sons = @[newNode(nkEmpty)]
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
-      let field = lookupInRecord(t.n, getIdent(p.str))
+      let ident = getIdent(p.str)
+      let field = lookupInRecord(t.n, ident)
       if field.isNil:
         raiseParseErr(p, "unknown field for object of type " & typeToString(t))
       next(p)
-      if field.position >= result.sons.len:
-        setLen(result.sons, field.position+1)
-      result.sons[field.position] = loadAny(p, field.typ, tab)
+      let pos = field.position + 1
+      if pos >= result.sons.len:
+        setLen(result.sons, pos + 1)
+      let fieldNode = newNode(nkExprColonExpr)
+      fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo())))
+      fieldNode.addSon(loadAny(p, field.typ, tab))
+      result.sons[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
   of tySet:
@@ -234,7 +239,7 @@ proc loadAny(p: var JsonParser, t: PType,
       result = newNode(nkNilLit)
       next(p)
     of jsonInt:
-      result = tab[p.getInt]
+      result = tab.getOrDefault(p.getInt)
       if result.isNil:
         raiseParseErr(p, "cannot load object with address " & $p.getInt)
       next(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 1023d4783..e1a0dfef8 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -46,7 +46,7 @@ template wrap2svoid(op) {.immediate, dirty.} =
 
 proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
   setResult(a, if a.currentException.isNil: ""
-               else: a.currentException.sons[2].strVal)
+               else: a.currentException.sons[3].skipColon.strVal)
 
 proc registerAdditionalOps*(c: PCtx) =
   wrap1f(sqrt)
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 63fd995c4..0a0534118 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -42,10 +42,10 @@ type
     wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
     wImportCpp, wImportObjC,
     wImportCompilerProc,
-    wImportc, wExportc, wIncompleteStruct, wRequiresInit,
+    wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit,
     wAlign, wNodecl, wPure, wSideeffect, wHeader,
     wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
-    wCompilerproc, wProcVar,
+    wCompilerproc, wProcVar, wBase,
     wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
     wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
     wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
@@ -55,7 +55,7 @@ type
     wFloatchecks, wNanChecks, wInfChecks,
     wAssertions, wPatterns, wWarnings,
     wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
-    wDeadCodeElim, wSafecode, wNoForward,
+    wDeadCodeElim, wSafecode, wNoForward, wNoRewrite,
     wPragma,
     wCompileTime, wNoInit,
     wPassc, wPassl, wBorrow, wDiscardable,
@@ -82,6 +82,7 @@ type
     wStdIn, wStdOut, wStdErr,
 
     wInOut, wByCopy, wByRef, wOneWay,
+    wBitsize,
 
   TSpecialWords* = set[TSpecialWord]
 
@@ -125,10 +126,12 @@ const
 
     "immediate", "constructor", "destructor", "delegator", "override",
     "importcpp", "importobjc",
-    "importcompilerproc", "importc", "exportc", "incompletestruct",
+    "importcompilerproc", "importc", "exportc", "exportnims",
+    "incompletestruct",
     "requiresinit", "align", "nodecl", "pure", "sideeffect",
     "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
-    "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
+    "compilerproc", "procvar", "base",
+    "fatal", "error", "warning", "hint", "line",
     "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
     "link", "compile", "linksys", "deprecated", "varargs",
     "callconv", "breakpoint", "debugger", "nimcall", "stdcall",
@@ -139,7 +142,7 @@ const
 
     "assertions", "patterns", "warnings", "hints",
     "optimization", "raises", "writes", "reads", "size", "effects", "tags",
-    "deadcodeelim", "safecode", "noforward",
+    "deadcodeelim", "safecode", "noforward", "norewrite",
     "pragma",
     "compiletime", "noinit",
     "passc", "passl", "borrow", "discardable", "fieldchecks",
@@ -167,6 +170,7 @@ const
     "stdin", "stdout", "stderr",
 
     "inout", "bycopy", "byref", "oneway",
+    "bitsize",
     ]
 
 proc findStr*(a: openArray[string], s: string): int =
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
new file mode 100644
index 000000000..141b496c1
--- /dev/null
+++ b/compiler/writetracking.nim
@@ -0,0 +1,272 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the write tracking analysis. Read my block post for
+## a basic description of the algorithm and ideas.
+## The algorithm operates in 2 phases:
+##
+##   * Collecting information about assignments (and pass-by-var calls).
+##   * Computing an aliasing relation based on the assignments. This relation
+##     is then used to compute the 'writes' and 'escapes' effects.
+
+import intsets, idents, ast, astalgo, trees, renderer, msgs, types
+
+const
+  debug = false
+
+type
+  AssignToResult = enum
+    asgnNil,   # 'nil' is fine
+    asgnNew,   # 'new(result)'
+    asgnOther  # result = fooBar # not a 'new' --> 'result' might not 'new'
+  NewLocation = enum
+    newNone,
+    newLit,
+    newCall
+  RootInfo = enum
+    rootIsResultOrParam,
+    rootIsHeapAccess,
+    rootIsSym,
+    markAsWrittenTo,
+    markAsEscaping
+
+  Assignment = object # \
+    # Note that the transitive closures MUST be computed in
+    # phase 2 of the algorithm.
+    dest, src: seq[ptr TSym] # we use 'ptr' here to save RC ops and GC cycles
+    destNoTc, srcNoTc: int # length of 'dest', 'src' without the
+                           # transitive closure
+    destInfo: set[RootInfo]
+    info: TLineInfo
+
+  W = object # WriteTrackContext
+    owner: PSym
+    returnsNew: AssignToResult # assignments to 'result'
+    assignments: seq[Assignment] # list of all assignments in this proc
+
+proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) =
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}:
+      if n.sym.kind in {skResult, skParam}: incl(info, rootIsResultOrParam)
+      result.add(cast[ptr TSym](n.sym))
+  of nkHiddenDeref, nkDerefExpr:
+    incl(info, rootIsHeapAccess)
+    allRoots(n.sons[0], result, info)
+  of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr,
+      nkHiddenAddr, nkObjUpConv, nkObjDownConv:
+    allRoots(n.sons[0], result, info)
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv,
+      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
+      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
+    allRoots(n.lastSon, result, info)
+  of nkCallKinds:
+    if getMagic(n) == mSlice:
+      allRoots(n.sons[1], result, info)
+    else:
+      # we do significantly better here by using the available escape
+      # information:
+      if n.sons[0].typ.isNil: return
+      var typ = n.sons[0].typ
+      if typ != nil:
+        typ = skipTypes(typ, abstractInst)
+        if typ.kind != tyProc: typ = nil
+        else: assert(sonsLen(typ) == sonsLen(typ.n))
+
+      for i in 1 ..< n.len:
+        let it = n.sons[i]
+        if typ != nil and i < sonsLen(typ):
+          assert(typ.n.sons[i].kind == nkSym)
+          let paramType = typ.n.sons[i]
+          if paramType.typ.isCompileTimeOnly: continue
+          if sfEscapes in paramType.sym.flags or paramType.typ.kind == tyVar:
+            allRoots(it, result, info)
+        else:
+          allRoots(it, result, info)
+  else:
+    for i in 0..<n.safeLen:
+      allRoots(n.sons[i], result, info)
+
+proc addAsgn(a: var Assignment; dest, src: PNode; destInfo: set[RootInfo]) =
+  a.dest = @[]
+  a.src = @[]
+  a.destInfo = destInfo
+  allRoots(dest, a.dest, a.destInfo)
+  if dest.kind == nkSym: incl(a.destInfo, rootIsSym)
+  if src != nil:
+    var dummy: set[RootInfo]
+    allRoots(src, a.src, dummy)
+  a.destNoTc = a.dest.len
+  a.srcNoTc = a.src.len
+  a.info = dest.info
+  #echo "ADDING ", dest.info, " ", a.destInfo
+
+proc srcHasSym(a: Assignment; x: ptr TSym): bool =
+  for i in 0 ..< a.srcNoTc:
+    if a.src[i] == x: return true
+
+proc returnsNewExpr*(n: PNode): NewLocation =
+  case n.kind
+  of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
+      nkFloatLit..nkFloat64Lit, nkNilLit:
+    result = newLit
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv,
+      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
+      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
+    result = returnsNewExpr(n.lastSon)
+  of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure,
+      nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt:
+    result = newLit
+    for i in ord(n.kind == nkObjConstr) .. <n.len:
+      let x = returnsNewExpr(n.sons[i])
+      case x
+      of newNone: return newNone
+      of newLit: discard
+      of newCall: result = newCall
+  of nkCallKinds:
+    if n.sons[0].typ != nil and tfReturnsNew in n.sons[0].typ.flags:
+      result = newCall
+  else:
+    result = newNone
+
+proc deps(w: var W; dest, src: PNode; destInfo: set[RootInfo]) =
+  # let x = (localA, localB)
+  # compute 'returnsNew' property:
+  let retNew = if src.isNil: newNone else: returnsNewExpr(src)
+  if dest.kind == nkSym and dest.sym.kind == skResult:
+    if retNew != newNone:
+      if w.returnsNew != asgnOther: w.returnsNew = asgnNew
+    else:
+      w.returnsNew = asgnOther
+  # mark the dependency, but
+  # rule out obviously innocent assignments like 'somebool = true'
+  if dest.kind == nkSym and retNew == newLit: discard
+  else:
+    let L = w.assignments.len
+    w.assignments.setLen(L+1)
+    addAsgn(w.assignments[L], dest, src, destInfo)
+
+proc depsArgs(w: var W; n: PNode) =
+  if n.sons[0].typ.isNil: return
+  var typ = skipTypes(n.sons[0].typ, abstractInst)
+  if typ.kind != tyProc: return
+  # echo n.info, " ", n, " ", w.owner.name.s, " ", typeToString(typ)
+  assert(sonsLen(typ) == sonsLen(typ.n))
+  for i in 1 ..< n.len:
+    let it = n.sons[i]
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      let paramType = typ.n.sons[i]
+      if paramType.typ.isCompileTimeOnly: continue
+      var destInfo: set[RootInfo] = {}
+      if sfWrittenTo in paramType.sym.flags or paramType.typ.kind == tyVar:
+        # p(f(x, y), X, g(h, z))
+        destInfo.incl markAsWrittenTo
+      if sfEscapes in paramType.sym.flags:
+        destInfo.incl markAsEscaping
+      if destInfo != {}:
+        deps(w, it, nil, destInfo)
+
+proc deps(w: var W; n: PNode) =
+  case n.kind
+  of nkLetSection, nkVarSection:
+    for child in n:
+      let last = lastSon(child)
+      if last.kind == nkEmpty: continue
+      if child.kind == nkVarTuple and last.kind == nkPar:
+        internalAssert child.len-2 == last.len
+        for i in 0 .. child.len-3:
+          deps(w, child.sons[i], last.sons[i], {})
+      else:
+        for i in 0 .. child.len-3:
+          deps(w, child.sons[i], last, {})
+  of nkAsgn, nkFastAsgn:
+    deps(w, n.sons[0], n.sons[1], {})
+  else:
+    for i in 0 ..< n.safeLen:
+      deps(w, n.sons[i])
+    if n.kind in nkCallKinds:
+      if getMagic(n) in {mNew, mNewFinalize, mNewSeq}:
+        # may not look like an assignment, but it is:
+        deps(w, n.sons[1], newNodeIT(nkObjConstr, n.info, n.sons[1].typ), {})
+      else:
+        depsArgs(w, n)
+
+proc possibleAliases(w: var W; result: var seq[ptr TSym]) =
+  # this is an expensive fixpoint iteration. We could speed up this analysis
+  # by a smarter data-structure but we wait until profiling shows us it's
+  # expensive. Usually 'w.assignments' is small enough.
+  var alreadySeen = initIntSet()
+  template addNoDup(x) =
+    if not alreadySeen.containsOrIncl(x.id): result.add x
+  for x in result: alreadySeen.incl x.id
+
+  var todo = 0
+  while todo < result.len:
+    let x = result[todo]
+    inc todo
+    for a in mitems(w.assignments):
+      #if a.srcHasSym(x):
+      #  # y = f(..., x, ...)
+      #  for i in 0 ..< a.destNoTc: addNoDup a.dest[i]
+      if a.destNoTc > 0 and a.dest[0] == x and rootIsSym in a.destInfo:
+        # x = f(..., y, ....)
+        for i in 0 ..< a.srcNoTc: addNoDup a.src[i]
+
+proc markWriteOrEscape(w: var W) =
+  ## Both 'writes' and 'escapes' effects ultimately only care
+  ## about *parameters*.
+  ## However, due to aliasing, even locals that might not look as parameters
+  ## have to count as parameters if they can alias a parameter:
+  ##
+  ## .. code-block:: nim
+  ##   proc modifies(n: Node) {.writes: [n].} =
+  ##     let x = n
+  ##     x.data = "abc"
+  ##
+  ## We call a symbol *parameter-like* if it is a parameter or can alias a
+  ## parameter.
+  ## Let ``p``, ``q`` be *parameter-like* and ``x``, ``y`` be general
+  ## expressions.
+  ##
+  ## A write then looks like ``p[] = x``.
+  ## An escape looks like ``p[] = q`` or more generally
+  ## like ``p[] = f(q)`` where ``f`` can forward ``q``.
+  for a in mitems(w.assignments):
+    if a.destInfo != {}:
+      possibleAliases(w, a.dest)
+
+    if {rootIsHeapAccess, markAsWrittenTo} * a.destInfo != {}:
+      for p in a.dest:
+        if p.kind == skParam and p.owner == w.owner:
+          incl(p.flags, sfWrittenTo)
+
+    if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
+      var destIsParam = false
+      for p in a.dest:
+        if p.kind in {skResult, skParam} and p.owner == w.owner:
+          destIsParam = true
+          break
+      if destIsParam:
+        possibleAliases(w, a.src)
+        for p in a.src:
+          if p.kind == skParam and p.owner == w.owner:
+            incl(p.flags, sfEscapes)
+
+proc trackWrites*(owner: PSym; body: PNode) =
+  var w: W
+  w.owner = owner
+  w.assignments = @[]
+  # Phase 1: Collect and preprocess any assignments in the proc body:
+  deps(w, body)
+  # Phase 2: Compute the 'writes' and 'escapes' effects:
+  markWriteOrEscape(w)
+  if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and
+      containsGarbageCollectedRef(owner.typ.sons[0]):
+    incl(owner.typ.flags, tfReturnsNew)