summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim32
-rw-r--r--compiler/ccgcalls.nim8
-rw-r--r--compiler/ccgexprs.nim139
-rw-r--r--compiler/ccgmerge.nim11
-rw-r--r--compiler/ccgstmts.nim14
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtrav.nim6
-rw-r--r--compiler/ccgtypes.nim246
-rw-r--r--compiler/ccgutils.nim4
-rw-r--r--compiler/cgen.nim53
-rw-r--r--compiler/cgendata.nim30
-rw-r--r--compiler/cgmeth.nim14
-rw-r--r--compiler/commands.nim60
-rw-r--r--compiler/condsyms.nim10
-rw-r--r--compiler/debuginfo.nim81
-rw-r--r--compiler/docgen.nim147
-rw-r--r--compiler/docgen2.nim23
-rw-r--r--compiler/evaltempl.nim10
-rw-r--r--compiler/extccomp.nim8
-rw-r--r--compiler/guards.nim16
-rw-r--r--compiler/hlo.nim2
-rw-r--r--compiler/idgen.nim8
-rw-r--r--compiler/importer.nim12
-rw-r--r--compiler/installer.ini182
-rw-r--r--compiler/jsgen.nim155
-rw-r--r--compiler/jstypes.nim3
-rw-r--r--compiler/lambdalifting.nim5
-rw-r--r--compiler/lexer.nim46
-rw-r--r--compiler/lookups.nim51
-rw-r--r--compiler/lowerings.nim3
-rw-r--r--compiler/magicsys.nim14
-rw-r--r--compiler/main.nim18
-rw-r--r--compiler/modules.nim53
-rw-r--r--compiler/msgs.nim26
-rw-r--r--compiler/nim.cfg2
-rw-r--r--compiler/nim.nim4
-rw-r--r--compiler/nimfix/nimfix.nim.cfg2
-rw-r--r--compiler/nimfix/pretty.nim8
-rw-r--r--compiler/nimsets.nim2
-rw-r--r--compiler/options.nim97
-rw-r--r--compiler/packagehandling.nim56
-rw-r--r--compiler/parser.nim26
-rw-r--r--compiler/passaux.nim2
-rw-r--r--compiler/patterns.nim4
-rw-r--r--compiler/platform.nim5
-rw-r--r--compiler/pragmas.nim103
-rw-r--r--compiler/renderer.nim16
-rw-r--r--compiler/rodutils.nim4
-rw-r--r--compiler/scriptconfig.nim27
-rw-r--r--compiler/sem.nim55
-rw-r--r--compiler/semasgn.nim2
-rw-r--r--compiler/semcall.nim75
-rw-r--r--compiler/semdata.nim5
-rw-r--r--compiler/semdestruct.nim3
-rw-r--r--compiler/semexprs.nim192
-rw-r--r--compiler/semfold.nim40
-rw-r--r--compiler/semgnrc.nim37
-rw-r--r--compiler/seminst.nim22
-rw-r--r--compiler/semmacrosanity.nim16
-rw-r--r--compiler/semmagic.nim19
-rw-r--r--compiler/semparallel.nim10
-rw-r--r--compiler/sempass2.nim8
-rw-r--r--compiler/semstmts.nim96
-rw-r--r--compiler/semtempl.nim22
-rw-r--r--compiler/semtypes.nim54
-rw-r--r--compiler/semtypinst.nim23
-rw-r--r--compiler/sigmatch.nim214
-rw-r--r--compiler/suggest.nim4
-rw-r--r--compiler/transf.nim28
-rw-r--r--compiler/trees.nim19
-rw-r--r--compiler/types.nim58
-rw-r--r--compiler/vm.nim54
-rw-r--r--compiler/vmdef.nim12
-rw-r--r--compiler/vmdeps.nim284
-rw-r--r--compiler/vmgen.nim126
-rw-r--r--compiler/vmhooks.nim4
-rw-r--r--compiler/vmops.nim74
-rw-r--r--compiler/wordrecg.nim3
78 files changed, 2211 insertions, 1200 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index fecbefd7e..277a21ba5 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -271,13 +271,14 @@ type
   TSymFlags* = set[TSymFlag]
 
 const
-  sfFakeConst* = sfDeadCodeElim  # const cannot be put into a data section
   sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
   sfImmediate* = sfDeadCodeElim
     # macro or template is immediately expanded
     # without considering any possible overloads
+  sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
+    # in a generic context
 
   sfDirty* = sfPure
     # template is not hygienic (old styled template)
@@ -395,6 +396,9 @@ type
       # sons[1]: field type
       # .n: nkDotExpr storing the field name
 
+    tyVoid #\
+      # now different from tyEmpty, hurray!
+
 static:
   # remind us when TTypeKind stops to fit in a single 64-bit word
   assert TTypeKind.high.ord <= 63
@@ -528,8 +532,6 @@ const
   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
 
@@ -544,7 +546,7 @@ type
     mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
     mUnaryLt, mInc, mDec, mOrd,
-    mNew, mNewFinalize, mNewSeq,
+    mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
     mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
     mXLenStr, mXLenSeq,
     mIncl, mExcl, mCard, mChr,
@@ -563,8 +565,8 @@ type
     mEqEnum, mLeEnum, mLtEnum,
     mEqCh, mLeCh, mLtCh,
     mEqB, mLeB, mLtB,
-    mEqRef, mEqUntracedRef, mLePtr, mLtPtr, mEqCString,
-    mXor, mEqProc,
+    mEqRef, mEqUntracedRef, mLePtr, mLtPtr,
+    mXor, mEqCString, mEqProc,
     mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
     mUnaryPlusI, mBitnotI,
     mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
@@ -590,6 +592,7 @@ type
     mNewString, mNewStringOfCap, mParseBiggestFloat,
     mReset,
     mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
+    mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal,
     mInt, mInt8, mInt16, mInt32, mInt64,
     mUInt, mUInt8, mUInt16, mUInt32, mUInt64,
@@ -609,7 +612,7 @@ type
     mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
     mNHint, mNWarning, mNError,
     mInstantiationInfo, mGetTypeInfo, mNGenSym,
-    mNimvm
+    mNimvm, mIntDefine, mStrDefine
 
 # things that we can evaluate safely at compile time, even if not asked for it:
 const
@@ -769,7 +772,7 @@ type
       procInstCache*: seq[PInstantiation]
       gcUnsafetyReason*: PSym  # for better error messages wrt gcsafe
       #scope*: PScope          # the scope where the proc was defined
-    of skModule:
+    of skModule, skPackage:
       # modules keep track of the generic symbols they use from other modules.
       # this is because in incremental compilation, when a module is about to
       # be replaced with a newer version, we must decrement the usage count
@@ -848,6 +851,7 @@ type
                               # see instantiateDestructor in semdestruct.nim
     deepCopy*: PSym           # overriden 'deepCopy' operation
     assignment*: PSym         # overriden '=' operator
+    methods*: seq[(int,PSym)] # attached methods
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
     align*: int16             # the type's alignment requirements
@@ -938,7 +942,7 @@ const
   genericParamsPos* = 2
   paramsPos* = 3
   pragmasPos* = 4
-  optimizedCodePos* = 5  # will be used for exception tracking
+  miscPos* = 5  # used for undocumented and hacky stuff
   bodyPos* = 6       # position of body; use rodread.getBody() instead!
   resultPos* = 7
   dispatcherPos* = 8 # caution: if method has no 'result' it can be position 7!
@@ -961,6 +965,8 @@ const
                   skMethod, skConverter}
 
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
+var
+  gMainPackageId*: int
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
@@ -984,12 +990,12 @@ proc add*(father, son: PNode) =
 proc `[]`*(n: PNode, i: int): PNode {.inline.} =
   result = n.sons[i]
 
-template `-|`*(b, s: expr): expr =
+template `-|`*(b, s: untyped): untyped =
   (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 =
+template `{}`*(n: PNode, i: int): untyped = n[i -| n]
+template `{}=`*(n: PNode, i: int, s: PNode) =
   n.sons[i -| n] = s
 
 when defined(useNodeIds):
@@ -1584,7 +1590,7 @@ proc isAtom*(n: PNode): bool {.inline.} =
 
 proc isEmptyType*(t: PType): bool {.inline.} =
   ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
-  result = t == nil or t.kind in {tyEmpty, tyStmt}
+  result = t == nil or t.kind in {tyVoid, tyStmt}
 
 proc makeStmtList*(n: PNode): PNode =
   if n.kind == nkStmtList:
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index bd17f85e4..dffb8a9a5 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -124,7 +124,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
         result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
       of tyArray, tyArrayConstr:
         result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
-      else: 
+      else:
         internalError("openArrayLoc: " & typeToString(a.t))
     else: internalError("openArrayLoc: " & typeToString(a.t))
 
@@ -260,7 +260,11 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
     else:
       result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
   else:
-    result = genArgNoParam(p, ri.sons[i])
+    if tfVarargs notin typ.flags:
+      localError(ri.info, "wrong argument count")
+      result = nil
+    else:
+      result = genArgNoParam(p, ri.sons[i])
 
 discard """
 Dot call syntax in C++
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 4fcbeeec2..6c96f209e 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -45,7 +45,7 @@ proc genHexLiteral(v: PNode): Rope =
 
 proc getStrLit(m: BModule, s: string): Rope =
   discard cgsym(m, "TGenericSeq")
-  result = "TMP" & rope(backendId())
+  result = getTempName(m)
   addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
        [result, makeCString(s), rope(len(s))])
 
@@ -67,11 +67,11 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
   of nkNilLit:
     let t = skipTypes(ty, abstractVarRange)
     if t.kind == tyProc and t.callConv == ccClosure:
-      var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
-      result = "TMP" & rope(id)
-      if id == gBackendId:
+      let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+      result = p.module.tmpBase & rope(id)
+      if id == p.module.labels:
         # not found in cache:
-        inc(gBackendId)
+        inc(p.module.labels)
         addf(p.module.s[cfsData],
              "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n",
              [getTypeDesc(p.module, t), result])
@@ -81,13 +81,14 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     if n.strVal.isNil:
       result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", [])
     elif skipTypes(ty, abstractVarRange).kind == tyString:
-      var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
-      if id == gBackendId:
+      let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+      if id == p.module.labels:
         # string literal not found in the cache:
         result = ropecg(p.module, "((#NimStringDesc*) &$1)",
                         [getStrLit(p.module, n.strVal)])
       else:
-        result = ropecg(p.module, "((#NimStringDesc*) &TMP$1)", [rope(id)])
+        result = ropecg(p.module, "((#NimStringDesc*) &$1$2)",
+                        [p.module.tmpBase, rope(id)])
     else:
       result = makeCString(n.strVal)
   of nkFloatLit..nkFloat64Lit:
@@ -134,11 +135,11 @@ proc genSetNode(p: BProc, n: PNode): Rope =
   var size = int(getSize(n.typ))
   toBitSet(n, cs)
   if size > 8:
-    var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
-    result = "TMP" & rope(id)
-    if id == gBackendId:
+    let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+    result = p.module.tmpBase & rope(id)
+    if id == p.module.labels:
       # not found in cache:
-      inc(gBackendId)
+      inc(p.module.labels)
       addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
            [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)])
   else:
@@ -490,7 +491,7 @@ proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
   var size = getSize(t)
   let storage = if size < platform.intSize: rope("NI")
                 else: getTypeDesc(p.module, t)
-  result = getTempName()
+  result = getTempName(p.module)
   linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
   lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
   if size < platform.intSize or t.kind in {tyRange, tyEnum}:
@@ -591,7 +592,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "($1 == $2)",           # EqPtr
       "($1 <= $2)",           # LePtr
       "($1 < $2)",            # LtPtr
-      "($1 == $2)",           # EqCString
       "($1 != $2)"]           # Xor
   var
     a, b: TLoc
@@ -612,7 +612,7 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
   assert(e.sons[2].typ != nil)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  if a.t.callConv == ccClosure:
+  if a.t.skipTypes(abstractInst).callConv == ccClosure:
     putIntoDest(p, d, e.typ,
       "($1.ClPrc == $2.ClPrc && $1.ClEnv == $2.ClEnv)" % [rdLoc(a), rdLoc(b)])
   else:
@@ -670,6 +670,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
     #if e[0].kind != nkBracketExpr:
     #  message(e.info, warnUser, "CAME HERE " & renderTree(e))
     expr(p, e.sons[0], d)
+    if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
+      d.s = OnHeap
   else:
     var a: TLoc
     let typ = skipTypes(e.sons[0].typ, abstractInst)
@@ -802,9 +804,9 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
     v.r.add(d.loc.r)
     genInExprAux(p, it, u, v, test)
     let id = nodeTableTestOrSet(p.module.dataCache,
-                               newStrNode(nkStrLit, field.name.s), gBackendId)
-    let strLit = if id == gBackendId: getStrLit(p.module, field.name.s)
-                 else: "TMP" & rope(id)
+                               newStrNode(nkStrLit, field.name.s), p.module.labels)
+    let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s)
+                 else: p.module.tmpBase & rope(id)
     if op.magic == mNot:
       linefmt(p, cpsStmts,
               "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n",
@@ -1141,7 +1143,35 @@ proc genNewSeq(p: BProc, e: PNode) =
   genNewSeqAux(p, a, b.rdLoc)
   gcUsage(e)
 
+proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
+  let seqtype = skipTypes(e.typ, abstractVarRange)
+  var a: TLoc
+  initLocExpr(p, e.sons[1], a)
+  putIntoDest(p, d, e.typ, ropecg(p.module,
+              "($1)#nimNewSeqOfCap($2, $3)", [
+              getTypeDesc(p.module, seqtype),
+              genTypeInfo(p.module, seqtype), a.rdLoc]))
+  gcUsage(e)
+
+proc genConstExpr(p: BProc, n: PNode): Rope
+proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
+  if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
+    var t = getUniqueType(n.typ)
+    discard getTypeDesc(p.module, t) # so that any fields are initialized
+    let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+    fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic)
+    if id == p.module.labels:
+      # expression not found in the cache:
+      inc(p.module.labels)
+      addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
+           [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)])
+    result = true
+  else:
+    result = false
+
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
+  if handleConstExpr(p, e, d): return
+  #echo rendertree e, " ", e.isDeepConstExpr
   var tmp: TLoc
   var t = e.typ.skipTypes(abstractInst)
   getTemp(p, t, tmp)
@@ -1235,7 +1265,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope =
   # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
   # have to call it here first:
   let ti = genTypeInfo(p.module, dest)
-  if tfFinal in dest.flags or (p.module.objHasKidsValid and
+  if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
                                tfObjHasKids notin dest.flags):
     result = "$1.m_type == $2" % [a, ti]
   else:
@@ -1293,7 +1323,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     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)", [
+                ropecg(p.module, "#reprEnum((NI)$1, $2)", [
                 rdLoc(a), genTypeInfo(p.module, t)]), a.s)
   of tyString:
     putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
@@ -1320,7 +1350,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e.typ,
                 ropecg(p.module, "#reprAny($1, $2)", [
                 rdLoc(a), genTypeInfo(p.module, t)]), a.s)
-  of tyEmpty:
+  of tyEmpty, tyVoid:
     localError(e.info, "'repr' doesn't support 'void' type")
   else:
     putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
@@ -1558,11 +1588,12 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
         [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
 
 proc genCast(p: BProc, e: PNode, d: var TLoc) =
-  const floatTypes = {tyFloat..tyFloat128}
+  const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject,
+                      tyArray, tyArrayConstr}
   let
     destt = skipTypes(e.typ, abstractRange)
     srct = skipTypes(e.sons[1].typ, abstractRange)
-  if destt.kind in floatTypes or srct.kind in floatTypes:
+  if destt.kind in ValueTypes or srct.kind in ValueTypes:
     # 'cast' and some float type involved? --> use a union.
     inc(p.labels)
     var lbl = p.labels.rope
@@ -1668,10 +1699,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
     else: unaryExpr(p, e, d, "#subInt($1, 1)")
   of mInc, mDec:
-    const opr: array [mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"]
-    const fun64: array [mInc..mDec, string] = ["$# = #addInt64($#, $#);$n",
+    const opr: array[mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"]
+    const fun64: array[mInc..mDec, string] = ["$# = #addInt64($#, $#);$n",
                                                "$# = #subInt64($#, $#);$n"]
-    const fun: array [mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
+    const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
                                              "$# = #subInt($#, $#);$n"]
     let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyVar, tyRange})
     if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
@@ -1712,6 +1743,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mNew: genNew(p, e)
   of mNewFinalize: genNewFinalize(p, e)
   of mNewSeq: genNewSeq(p, e)
+  of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
   of mSizeOf:
     let t = e.sons[1].typ.skipTypes({tyTypeDesc})
     putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
@@ -1754,25 +1786,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     initLocExpr(p, x, a)
     initLocExpr(p, e.sons[2], b)
     genDeepCopy(p, a, b)
-  of mDotDot: genCall(p, e, d)
+  of mDotDot, mEqCString: genCall(p, e, d)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
-proc genConstExpr(p: BProc, n: PNode): Rope
-proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
-  if nfAllConst in n.flags and d.k == locNone and n.len > 0 and n.isDeepConstExpr:
-    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), OnStatic)
-    if id == gBackendId:
-      # expression not found in the cache:
-      inc(gBackendId)
-      addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
-           [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)])
-    result = true
-  else:
-    result = false
-
 proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
   # example: { a..b, c, d, e, f..g }
   # we have to emit an expression of the form:
@@ -1856,10 +1872,16 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
     initLocExpr(p, n.sons[1], b)
     if n.sons[0].skipConv.kind == nkClosure:
       internalError(n.info, "closure to closure created")
-    getTemp(p, n.typ, tmp)
-    linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
-            tmp.rdLoc, a.rdLoc, b.rdLoc)
-    putLocIntoDest(p, d, tmp)
+    # tasyncawait.nim breaks with this optimization:
+    when false:
+      if d.k != locNone:
+        linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
+                d.rdLoc, a.rdLoc, b.rdLoc)
+    else:
+      getTemp(p, n.typ, tmp)
+      linefmt(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
+              tmp.rdLoc, a.rdLoc, b.rdLoc)
+      putLocIntoDest(p, d, tmp)
 
 proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
   var arr: TLoc
@@ -1947,12 +1969,12 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
 proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   var t = getUniqueType(n.typ)
   discard getTypeDesc(p.module, t) # so that any fields are initialized
-  var id = nodeTableTestOrSet(p.module.dataCache, n, gBackendId)
-  var tmp = "TMP" & rope(id)
+  let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+  let tmp = p.module.tmpBase & rope(id)
 
-  if id == gBackendId:
+  if id == p.module.labels:
     # expression not found in the cache:
-    inc(gBackendId)
+    inc(p.module.labels)
     addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
          [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
 
@@ -1960,6 +1982,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
     fillLoc(d, locData, t, tmp, OnStatic)
   else:
     putDataIntoDest(p, d, t, tmp)
+    # This fixes bug #4551, but we really need better dataflow
+    # analysis to make this 100% safe.
+    if t.kind notin {tySequence, tyString}:
+      d.s = OnStatic
 
 proc expr(p: BProc, n: PNode, d: var TLoc) =
   case n.kind
@@ -1985,10 +2011,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         internalError(n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
-      if sfFakeConst in sym.flags:
-        if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
-        putLocIntoDest(p, d, sym.loc)
-      elif isSimpleConst(sym.typ):
+      if isSimpleConst(sym.typ):
         putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
       else:
         genComplexConst(p, sym, d)
@@ -2160,7 +2183,8 @@ proc genConstSimpleList(p: BProc, n: PNode): Rope =
   result = rope("{")
   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]))
+  if length > ord(n.kind == nkObjConstr):
+    add(result, genNamedConstExpr(p, n.sons[length - 1]))
   addf(result, "}$n", [])
 
 proc genConstSeq(p: BProc, n: PNode, t: PType): Rope =
@@ -2174,8 +2198,7 @@ proc genConstSeq(p: BProc, n: PNode, t: PType): Rope =
     data.add("}")
   data.add("}")
 
-  inc(gBackendId)
-  result = "CNSTSEQ" & gBackendId.rope
+  result = getTempName(p.module)
 
   appcg(p.module, cfsData,
         "NIM_CONST struct {$n" &
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 2a37257b6..92b6aa9dc 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -107,13 +107,13 @@ proc genMergeInfo*(m: BModule): Rope =
   writeIntSet(m.typeInfoMarker, s)
   s.add("labels:")
   encodeVInt(m.labels, s)
-  s.add(" hasframe:")
-  encodeVInt(ord(m.frameDeclared), s)
+  s.add(" flags:")
+  encodeVInt(cast[int](m.flags), s)
   s.add(tnl)
   s.add("*/")
   result = s.rope
 
-template `^`(pos: int): expr = L.buf[pos]
+template `^`(pos: int): untyped = L.buf[pos]
 
 proc skipWhite(L: var TBaseLexer) =
   var pos = L.bufpos
@@ -222,13 +222,14 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
     of "declared":  readIntSet(L, m.declaredThings)
     of "typeInfo":  readIntSet(L, m.typeInfoMarker)
     of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
-    of "hasframe":  m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
+    of "flags":
+      m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
     else: internalError("ccgmerge: unknown key: " & k)
 
 when not defined(nimhygiene):
   {.pragma: inject.}
 
-template withCFile(cfilename: string, body: stmt) {.immediate.} =
+template withCFile(cfilename: string, body: untyped) =
   var s = llStreamOpen(cfilename, fmRead)
   if s == nil: return
   var L {.inject.}: TBaseLexer
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index f2ceadcce..a5ce147c3 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -16,7 +16,7 @@ const
     # above X strings a hash-switch for strings is generated
 
 proc registerGcRoot(p: BProc, v: PSym) =
-  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2} and
+  if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
       containsGarbageCollectedRef(v.loc.t):
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
@@ -136,7 +136,7 @@ proc exprBlock(p: BProc, n: PNode, d: var TLoc) =
   expr(p, n, d)
   endBlock(p)
 
-template preserveBreakIdx(body: stmt): stmt {.immediate.} =
+template preserveBreakIdx(body: untyped): untyped =
   var oldBreakIdx = p.breakIdx
   body
   p.breakIdx = oldBreakIdx
@@ -269,8 +269,6 @@ proc genConstStmt(p: BProc, t: PNode) =
     if it.kind != nkConstDef: internalError(t.info, "genConstStmt")
     var c = it.sons[0].sym
     if c.typ.containsCompileTimeOnly: continue
-    if sfFakeConst in c.flags:
-      genSingleVar(p, it)
     elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
         c.ast.len != 0:
       if not emitLazily(c): requestConstImpl(p, c)
@@ -570,7 +568,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
   else:
     genLineDir(p, t)
     # reraise the last exception:
-    if p.module.compileToCpp:
+    if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
       line(p, cpsStmts, ~"throw;$n")
     else:
       linefmt(p, cpsStmts, "#reraiseException();$n")
@@ -793,7 +791,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   if not isEmptyType(t.typ) and d.k == locNone:
     getTemp(p, t.typ, d)
   genLineDir(p, t)
-  let exc = getTempName()
+  let exc = getTempName(p.module)
   if getCompilerProc("Exception") != nil:
     discard cgsym(p.module, "Exception")
   else:
@@ -886,7 +884,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
     getTemp(p, t.typ, d)
   discard lists.includeStr(p.module.headerFiles, "<setjmp.h>")
   genLineDir(p, t)
-  var safePoint = getTempName()
+  var safePoint = getTempName(p.module)
   if getCompilerProc("Exception") != nil:
     discard cgsym(p.module, "Exception")
   else:
@@ -961,6 +959,8 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
         var a: TLoc
         initLocExpr(p, t.sons[i], a)
         res.add($rdLoc(a))
+      elif sym.kind == skType:
+        res.add($getTypeDesc(p.module, sym.typ))
       else:
         var r = sym.loc.r
         if r == nil:
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index ab771d240..81af89249 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -18,7 +18,7 @@ proc emulatedThreadVars(): bool =
 proc accessThreadLocalVar(p: BProc, s: PSym) =
   if emulatedThreadVars() and not p.threadVarAccessed:
     p.threadVarAccessed = true
-    p.module.usesThreadVars = true
+    incl p.module.flags, usesThreadVars
     addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
     add(p.procSec(cpsInit),
       ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
@@ -51,7 +51,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     addf(m.s[cfsVars], " $1;$n", [s.loc.r])
 
 proc generateThreadLocalStorage(m: BModule) =
-  if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
+  if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
     for t in items(nimtvDeps): discard getTypeDesc(m, t)
     addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
 
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 0da6396ea..c4d47f148 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -100,7 +100,7 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
 proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
   var c: TTraversalClosure
   var p = newProc(nil, m)
-  result = getGlobalTempName()
+  result = getTempName(m)
 
   case reason
   of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
@@ -135,7 +135,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
   var c: TTraversalClosure
   var p = newProc(nil, m)
   var sLoc = s.loc.r
-  result = getGlobalTempName()
+  result = getTempName(m)
 
   if sfThread in s.flags and emulatedThreadVars():
     accessThreadLocalVar(p, s)
@@ -143,7 +143,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
 
   c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
   c.p = p
-  let header = "N_NIMCALL(void, $1)()" % [result]
+  let header = "N_NIMCALL(void, $1)(void)" % [result]
   genTraverseProc(c, sLoc, s.loc.t)
 
   let generatedProc = "$1 {$n$2$3$4}$n" %
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 39f16ff0d..3d1c2affc 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2016 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -11,6 +11,8 @@
 
 # ------------------------- Name Mangling --------------------------------
 
+import debuginfo
+
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
   # it's more efficient to test the whole Nim keywords range
@@ -23,70 +25,70 @@ proc isKeyword(w: PIdent): bool =
 proc mangleField(name: PIdent): string =
   result = mangle(name.s)
   if isKeyword(name):
-    result[0] = result[0].toUpper # Mangling makes everything lowercase,
-                                  # but some identifiers are C keywords
+    result[0] = result[0].toUpperAscii
+    # Mangling makes everything lowercase,
+    # but some identifiers are C keywords
+
+proc hashOwner(s: PSym): FilenameHash =
+  var m = s
+  while m.kind != skModule: m = m.owner
+  let p = m.owner
+  assert p.kind == skPackage
+  result = gDebugInfo.register(p.name.s, m.name.s)
 
 proc mangleName(s: PSym): Rope =
   result = s.loc.r
   if result == nil:
-    when oKeepVariableNames:
-      let keepOrigName = s.kind in skLocalVars - {skForVar} and
-        {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
-        not isKeyword(s.name)
-      # XXX: This is still very experimental
-      #
-      # Even with all these inefficient checks, the bootstrap
-      # time is actually improved. This is probably because so many
-      # rope concatenations are now eliminated.
-      #
-      # Future notes:
-      # sfFromGeneric seems to be needed in order to avoid multiple
-      # definitions of certain variables generated in transf with
-      # names such as:
-      # `r`, `res`
-      # I need to study where these come from.
-      #
-      # about sfShadowed:
-      # consider the following Nim code:
-      #   var x = 10
-      #   block:
-      #     var x = something(x)
-      # The generated C code will be:
-      #   NI x;
-      #   x = 10;
-      #   {
-      #     NI x;
-      #     x = something(x); // Oops, x is already shadowed here
-      #   }
-      # Right now, we work-around by not keeping the original name
-      # of the shadowed variable, but we can do better - we can
-      # create an alternative reference to it in the outer scope and
-      # use that in the inner scope.
-      #
-      # about isCKeyword:
-      # Nim variable names can be C keywords.
-      # We need to avoid such names in the generated code.
-      # XXX: Study whether mangleName is called just once per variable.
-      # Otherwise, there might be better place to do this.
-      #
-      # about sfGlobal:
-      # This seems to be harder - a top level extern variable from
-      # another modules can have the same name as a local one.
-      # Maybe we should just implement sfShadowed for them too.
-      #
-      # about skForVar:
-      # These are not properly scoped now - we need to add blocks
-      # around for loops in transf
-      if keepOrigName:
-        result = s.name.s.mangle.rope
-      else:
-        add(result, rope(mangle(s.name.s)))
-        add(result, ~"_")
-        add(result, rope(s.id))
+    let keepOrigName = s.kind in skLocalVars - {skForVar} and
+      {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
+      not isKeyword(s.name)
+    # Even with all these inefficient checks, the bootstrap
+    # time is actually improved. This is probably because so many
+    # rope concatenations are now eliminated.
+    #
+    # sfFromGeneric is needed in order to avoid multiple
+    # definitions of certain variables generated in transf with
+    # names such as:
+    # `r`, `res`
+    # I need to study where these come from.
+    #
+    # about sfShadowed:
+    # consider the following Nim code:
+    #   var x = 10
+    #   block:
+    #     var x = something(x)
+    # The generated C code will be:
+    #   NI x;
+    #   x = 10;
+    #   {
+    #     NI x;
+    #     x = something(x); // Oops, x is already shadowed here
+    #   }
+    # Right now, we work-around by not keeping the original name
+    # of the shadowed variable, but we can do better - we can
+    # create an alternative reference to it in the outer scope and
+    # use that in the inner scope.
+    #
+    # about isCKeyword:
+    # Nim variable names can be C keywords.
+    # We need to avoid such names in the generated code.
+    #
+    # about sfGlobal:
+    # This seems to be harder - a top level extern variable from
+    # another modules can have the same name as a local one.
+    # Maybe we should just implement sfShadowed for them too.
+    #
+    # about skForVar:
+    # These are not properly scoped now - we need to add blocks
+    # around for loops in transf
+    result = s.name.s.mangle.rope
+    if keepOrigName:
+      result.add "0"
     else:
-      add(result, rope(mangle(s.name.s)))
       add(result, ~"_")
       add(result, rope(s.id))
+      add(result, ~"_")
+      add(result, rope(hashOwner(s).BiggestInt))
     s.loc.r = result
 
 proc typeName(typ: PType): Rope =
@@ -137,6 +139,10 @@ proc mapType(typ: PType): TCTypeKind =
     var base = skipTypes(typ.lastSon, typedescInst)
     case base.kind
     of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
+    #of tySet:
+    #  if mapSetType(base) == ctArray: result = ctPtrToArray
+    #  else: result = ctPtr
+    # XXX for some reason this breaks the pegs module
     else: result = ctPtr
   of tyPointer: result = ctPtr
   of tySequence: result = ctNimSeq
@@ -145,11 +151,15 @@ proc mapType(typ: PType): TCTypeKind =
   of tyCString: result = ctCString
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
+  of tyStatic:
+    if typ.n != nil: result = mapType(lastSon typ)
+    else: internalError("mapType")
   else: internalError("mapType")
 
 proc mapReturnType(typ: PType): TCTypeKind =
-  if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
-  else: result = mapType(typ)
+  #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
+  #else:
+  result = mapType(typ)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -196,11 +206,9 @@ proc cacheGetType(tab: TIdTable, key: PType): Rope =
   # linear search is not necessary anymore:
   result = Rope(idTableGet(tab, key))
 
-proc getTempName(): Rope =
-  result = rfmt(nil, "TMP$1", rope(backendId()))
-
-proc getGlobalTempName(): Rope =
-  result = rfmt(nil, "TMP$1", rope(backendId()))
+proc getTempName(m: BModule): Rope =
+  result = m.tmpBase & rope(m.labels)
+  inc m.labels
 
 proc ccgIntroducedPtr(s: PSym): bool =
   var pt = skipTypes(s.typ, typedescInst)
@@ -223,7 +231,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
 proc fillResult(param: PSym) =
   fillLoc(param.loc, locParam, param.typ, ~"Result",
           OnStack)
-  if (mapReturnType(param.typ) != ctArray) and isInvalidReturnType(param.typ):
+  if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
     incl(param.loc.flags, lfIndirect)
     param.loc.s = OnUnknown
 
@@ -238,22 +246,10 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
     NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
       "NI", "NI8", "NI16", "NI32", "NI64",
       "NF", "NF32", "NF64", "NF128",
-      "NU", "NU8", "NU16", "NU32", "NU64",]
+      "NU", "NU8", "NU16", "NU32", "NU64"]
   case typ.kind
   of tyPointer:
     result = typeNameOrLiteral(typ, "void*")
-  of tyEnum:
-    if firstOrd(typ) < 0:
-      result = typeNameOrLiteral(typ, "NI32")
-    else:
-      case int(getSize(typ))
-      of 1: result = typeNameOrLiteral(typ, "NU8")
-      of 2: result = typeNameOrLiteral(typ, "NU16")
-      of 4: result = typeNameOrLiteral(typ, "NI32")
-      of 8: result = typeNameOrLiteral(typ, "NI64")
-      else:
-        internalError(typ.sym.info, "getSimpleTypeDesc: " & $(getSize(typ)))
-        result = nil
   of tyString:
     discard cgsym(m, "NimStringDesc")
     result = typeNameOrLiteral(typ, "NimStringDesc*")
@@ -264,6 +260,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
   of tyInt..tyUInt64:
     result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind])
   of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0])
+  of tyStatic:
+    if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
+    else: internalError("tyStatic for getSimpleTypeDesc")
+  of tyGenericInst:
+    result = getSimpleTypeDesc(m, lastSon typ)
   else: result = nil
 
 proc pushType(m: BModule, typ: PType) =
@@ -317,7 +318,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
     result = getTypeDescAux(m, t, check)
 
 proc paramStorageLoc(param: PSym): TStorageLoc =
-  if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}:
+  if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray, tyVarargs, tyArrayConstr}:
     result = OnStack
   else:
     result = OnUnknown
@@ -326,7 +327,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
                    check: var IntSet, declareEnvironment=true;
                    weakDep=false) =
   params = nil
-  if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
+  if t.sons[0] == nil or isInvalidReturnType(t.sons[0]):
     rettype = ~"void"
   else:
     rettype = getTypeDescAux(m, t.sons[0], check)
@@ -359,10 +360,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
       addf(params, ", NI $1Len$2", [param.loc.r, j.rope])
       inc(j)
       arr = arr.sons[0]
-  if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]):
+  if t.sons[0] != nil and isInvalidReturnType(t.sons[0]):
     var arr = t.sons[0]
     if params != nil: add(params, ", ")
-    if (mapReturnType(t.sons[0]) != ctArray):
+    if mapReturnType(t.sons[0]) != ctArray:
       add(params, getTypeDescWeak(m, arr, check))
       add(params, "*")
     else:
@@ -424,7 +425,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
       addf(result, "union{$n$1} $2;$n", [unionBody, uname])
   of nkSym:
     field = n.sym
-    if field.typ.kind == tyEmpty: return
+    if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
     sname = mangleRecFieldName(field, rectype)
     if accessExpr != nil: ae = "$1.$2" % [accessExpr, sname]
@@ -483,7 +484,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
   else:
     addf(result, " {$n", [name])
 
-  var desc = getRecordFields(m, typ, check)
+  let desc = getRecordFields(m, typ, check)
   if desc == nil and not hasField:
     addf(result, "char dummy;$n", [])
   else:
@@ -536,8 +537,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
   result = getTypePre(m, t)
   if result != nil: return
   if containsOrIncl(check, t.id):
-    if isImportedCppType(typ) or isImportedCppType(t): return
-    internalError("cannot generate C type for: " & typeToString(typ))
+    if not (isImportedCppType(typ) or isImportedCppType(t)):
+      internalError("cannot generate C type for: " & typeToString(typ))
     # XXX: this BUG is hard to fix -> we need to introduce helper structs,
     # but determining when this needs to be done is hard. We should split
     # C type generation into an analysis and a code generation phase somehow.
@@ -576,8 +577,37 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
       result = getTypeDescAux(m, et, check) & star
       idTablePut(m.typeCache, t, result)
   of tyOpenArray, tyVarargs:
-    result = getTypeDescAux(m, t.sons[0], check) & "*"
+    result = getTypeDescWeak(m, t.sons[0], check) & "*"
     idTablePut(m.typeCache, t, result)
+  of tyRange, tyEnum:
+    let t = if t.kind == tyRange: t.lastSon else: t
+    result = cacheGetType(m.typeCache, t)
+    if result == nil:
+      result = getTypeName(t)
+      if not (isImportedCppType(t) or
+          (sfImportc in t.sym.flags and t.sym.magic == mNone)):
+        idTablePut(m.typeCache, t, result)
+        var size: int
+        if firstOrd(t) < 0:
+          addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
+          size = 4
+        else:
+          size = int(getSize(t))
+          case size
+          of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
+          of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
+          of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
+          of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
+          else: internalError(t.sym.info, "getTypeDescAux: enum")
+        let owner = hashOwner(t.sym)
+        if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
+          var vals: seq[(string, int)] = @[]
+          for i in countup(0, t.n.len - 1):
+            assert(t.n.sons[i].kind == nkSym)
+            let field = t.n.sons[i].sym
+            vals.add((field.name.s, field.position.int))
+          gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id,
+            name: t.sym.name.s, values: vals))
   of tyProc:
     result = getTypeName(t)
     idTablePut(m.typeCache, t, result)
@@ -642,7 +672,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
             chunkStart = i
 
             let typeInSlot = resolveStarsInCppType(typ, idx + 1, stars)
-            if typeInSlot == nil or typeInSlot.kind == tyEmpty:
+            if typeInSlot == nil or typeInSlot.kind == tyVoid:
               result.add(~"void")
             else:
               result.add getTypeDescAux(m, typeInSlot, check)
@@ -654,7 +684,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
       else:
         result = cppName & "<"
         for i in 1 .. typ.len-2:
-          if i > 1: result.add(", ")
+          if i > 1: result.add(" COMMA ")
           result.add(getTypeDescAux(m, typ.sons[i], check))
         result.add("> ")
       # always call for sideeffects:
@@ -673,16 +703,13 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
                     else: getTupleDesc(m, t, result, check)
       if not isImportedType(t): add(m.s[cfsTypes], recdesc)
   of tySet:
-    case int(getSize(t))
-    of 1: result = rope("NU8")
-    of 2: result = rope("NU16")
-    of 4: result = rope("NU32")
-    of 8: result = rope("NU64")
-    else:
-      result = getTypeName(t)
-      idTablePut(m.typeCache, t, result)
-      if not isImportedType(t):
-        addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
+    result = getTypeName(t.lastSon) & "Set"
+    idTablePut(m.typeCache, t, result)
+    if not isImportedType(t):
+      let s = int(getSize(t))
+      case s
+      of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
+      else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
              [result, rope(getSize(t))])
   of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable,
       tyIter, tyTypeDesc:
@@ -704,7 +731,7 @@ type
 proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
   assert t.kind == tyProc
   var check = initIntSet()
-  result = getTempName()
+  result = getTempName(m)
   var rettype, desc: Rope
   genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
   if not isImportedType(t):
@@ -739,7 +766,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
   genCLineDir(result, prc.info)
   # using static is needed for inline procs
   if lfExportLib in prc.loc.flags:
-    if m.isHeaderFile:
+    if isHeaderFile in m.flags:
       result.add "N_LIB_IMPORT "
     else:
       result.add "N_LIB_EXPORT "
@@ -819,7 +846,7 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) =
     if L == 1:
       genObjectFields(m, typ, n.sons[0], expr)
     elif L > 0:
-      var tmp = getTempName()
+      var tmp = getTempName(m)
       addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)])
       for i in countup(0, L-1):
         var tmp2 = getNimNode(m)
@@ -891,7 +918,7 @@ proc genTupleInfo(m: BModule, typ: PType, name: Rope) =
   var expr = getNimNode(m)
   var length = sonsLen(typ)
   if length > 0:
-    var tmp = getTempName()
+    var tmp = getTempName(m)
     addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)])
     for i in countup(0, length - 1):
       var a = typ.sons[i]
@@ -915,7 +942,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope) =
   # anyway. We generate a cstring array and a loop over it. Exceptional
   # positions will be reset after the loop.
   genTypeInfoAux(m, typ, typ, name)
-  var nodePtrs = getTempName()
+  var nodePtrs = getTempName(m)
   var length = sonsLen(typ.n)
   addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
        [nodePtrs, rope(length)])
@@ -935,8 +962,8 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope) =
     if field.position != i or tfEnumHasHoles in typ.flags:
       addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)])
       hasHoles = true
-  var enumArray = getTempName()
-  var counter = getTempName()
+  var enumArray = getTempName(m)
+  var counter = getTempName(m)
   addf(m.s[cfsTypeInit1], "NI $1;$n", [counter])
   addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n",
        [enumArray, rope(length), enumNames])
@@ -1004,9 +1031,12 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
          [result, rope(typeToString(t))])
     return "(&".rope & result & ")".rope
   case t.kind
-  of tyEmpty: result = rope"0"
+  of tyEmpty, tyVoid: result = rope"0"
   of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
     genTypeInfoAuxBase(m, t, t, result, rope"0")
+  of tyStatic:
+    if t.n != nil: result = genTypeInfo(m, lastSon t)
+    else: internalError("genTypeInfo(" & $t.kind & ')')
   of tyProc:
     if t.callConv != ccClosure:
       genTypeInfoAuxBase(m, t, t, result, rope"0")
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 6dfd7b52c..ecd98a2bf 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -93,7 +93,7 @@ proc getUniqueType*(key: PType): PType =
     # produced instead of ``NI``.
     result = key
   of  tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
-      tyCString, tyNone, tyBigNum:
+      tyCString, tyNone, tyBigNum, tyVoid:
     result = gCanonicalTypes[k]
     if result == nil:
       gCanonicalTypes[k] = key
@@ -188,7 +188,7 @@ proc mangle*(name: string): string =
     let c = name[i]
     case c
     of 'A'..'Z':
-      add(result, c.toLower)
+      add(result, c.toLowerAscii)
     of '_':
       discard
     of 'a'..'z', '0'..'9':
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index db376821c..9851ab0e2 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -64,8 +64,8 @@ proc isSimpleConst(typ: PType): bool =
       (t.kind == tyProc and t.callConv == ccClosure)
 
 proc useStringh(m: BModule) =
-  if not m.includesStringh:
-    m.includesStringh = true
+  if includesStringh notin m.flags:
+    incl m.flags, includesStringh
     discard lists.includeStr(m.headerFiles, "<string.h>")
 
 proc useHeader(m: BModule, sym: PSym) =
@@ -129,7 +129,7 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
     if i - 1 >= start:
       add(result, substr(frmt, start, i - 1))
 
-template rfmt(m: BModule, fmt: string, args: varargs[Rope]): expr =
+template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
   ropecg(m, fmt, args)
 
 proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
@@ -215,7 +215,7 @@ proc accessThreadLocalVar(p: BProc, s: PSym)
 proc emulatedThreadVars(): bool {.inline.}
 proc genProc(m: BModule, prc: PSym)
 
-template compileToCpp(m: BModule): expr =
+template compileToCpp(m: BModule): untyped =
   gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
 
 include "ccgtypes.nim"
@@ -301,7 +301,8 @@ proc resetLoc(p: BProc, loc: var TLoc) =
 proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
   let typ = skipTypes(loc.t, abstractRange)
   if not isComplexValueType(typ):
-    linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
+    linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
+      getTypeDesc(p.module, typ))
   else:
     if not isTemp or containsGarbageCollectedRef(loc.t):
       # don't use memset for temporary values for performance if we can
@@ -327,10 +328,12 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
 proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   inc(p.labels)
   result.r = "LOC" & rope(p.labels)
-  linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
+  addf(p.blocks[0].sections[cpsLocals],
+     "$1 $2;$n", [getTypeDesc(p.module, t), result.r])
   result.k = locTemp
   #result.a = - 1
-  result.t = getUniqueType(t)
+  result.t = t
+  #result.t = getUniqueType(t)
   result.s = OnStack
   result.flags = {}
   constructLoc(p, result, not needsInit)
@@ -498,7 +501,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
   assert(lib != nil)
   if not lib.generated:
     lib.generated = true
-    var tmp = getGlobalTempName()
+    var tmp = getTempName(m)
     assert(lib.name == nil)
     lib.name = tmp # BUGFIX: cgsym has awful side-effects
     addf(m.s[cfsVars], "static void* $1;$n", [tmp])
@@ -666,7 +669,7 @@ proc genProcAux(m: BModule, prc: PSym) =
       fillResult(res)
       assignParam(p, res)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
-        incl(res.loc.flags, lfIndirect)
+        #incl(res.loc.flags, lfIndirect)
         res.loc.s = OnUnknown
 
   for i in countup(1, sonsLen(prc.typ.n) - 1):
@@ -1009,11 +1012,11 @@ proc genInitCode(m: BModule) =
   add(prc, m.postInitProc.s(cpsLocals))
   add(prc, genSectionEnd(cpsLocals))
 
-  if optStackTrace in m.initProc.options and not m.frameDeclared:
+  if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
     # BUT: the generated init code might depend on a current frame, so
     # declare it nevertheless:
-    m.frameDeclared = true
-    if not m.preventStackTrace:
+    incl m.flags, frameDeclared
+    if preventStackTrace notin m.flags:
       var procname = makeCString(m.module.name.s)
       add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
@@ -1030,7 +1033,7 @@ proc genInitCode(m: BModule) =
   add(prc, m.initProc.s(cpsStmts))
   add(prc, m.postInitProc.s(cpsStmts))
   add(prc, genSectionEnd(cpsStmts))
-  if optStackTrace in m.initProc.options and not m.preventStackTrace:
+  if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
     add(prc, deinitFrame(m.initProc))
   add(prc, deinitGCFrame(m.initProc))
   addf(prc, "}$N$N", [])
@@ -1059,9 +1062,8 @@ proc genModule(m: BModule, cfile: string): Rope =
   result = getFileHeader(cfile)
   result.add(genMergeInfo(m))
 
-  generateHeaders(m)
-
   generateThreadLocalStorage(m)
+  generateHeaders(m)
   for i in countup(cfsHeaders, cfsProcs):
     add(result, genSectionStart(i))
     add(result, m.s[i])
@@ -1083,6 +1085,7 @@ proc initProcOptions(m: BModule): TOptions =
 
 proc rawNewModule(module: PSym, filename: string): BModule =
   new(result)
+  result.tmpBase = rope("T" & $hashOwner(module) & "_")
   initLinkedList(result.headerFiles)
   result.declaredThings = initIntSet()
   result.declaredProtos = initIntSet()
@@ -1099,12 +1102,12 @@ proc rawNewModule(module: PSym, filename: string): BModule =
   initNodeTable(result.dataCache)
   result.typeStack = @[]
   result.forwardedProcs = @[]
-  result.typeNodesName = getTempName()
-  result.nimTypesName = getTempName()
+  result.typeNodesName = getTempName(result)
+  result.nimTypesName = getTempName(result)
   # no line tracing for the init sections of the system module so that we
   # don't generate a TFrame which can confuse the stack botton initialization:
   if sfSystemModule in module.flags:
-    result.preventStackTrace = true
+    incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
     excl(result.postInitProc.options, optStackTrace)
 
@@ -1125,11 +1128,13 @@ proc resetModule*(m: BModule) =
   initNodeTable(m.dataCache)
   m.typeStack = @[]
   m.forwardedProcs = @[]
-  m.typeNodesName = getTempName()
-  m.nimTypesName = getTempName()
-  m.preventStackTrace = sfSystemModule in m.module.flags
+  m.typeNodesName = getTempName(m)
+  m.nimTypesName = getTempName(m)
+  if sfSystemModule in m.module.flags:
+    incl m.flags, preventStackTrace
+  else:
+    excl m.flags, preventStackTrace
   nullify m.s
-  m.usesThreadVars = false
   m.typeNodes = 0
   m.nimTypes = 0
   nullify m.extensionLoaders
@@ -1174,7 +1179,7 @@ proc myOpen(module: PSym): PPassContext =
     let f = if headerFile.len > 0: headerFile else: gProjectFull
     generatedHeader = rawNewModule(module,
       changeFileExt(completeCFilePath(f), hExt))
-    generatedHeader.isHeaderFile = true
+    incl generatedHeader.flags, isHeaderFile
 
 proc writeHeader(m: BModule) =
   var result = getCopyright(m.filename)
@@ -1306,7 +1311,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
   registerModuleToMain(m.module)
 
   if sfMainModule in m.module.flags:
-    m.objHasKidsValid = true
+    incl m.flags, objHasKidsValid
     var disp = generateMethodDispatchers()
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 187186373..a94950029 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -15,7 +15,7 @@ import
 from msgs import TLineInfo
 
 type
-  TLabel* = Rope             # for the C generator a label is just a rope
+  TLabel* = Rope              # for the C generator a label is just a rope
   TCFileSection* = enum       # the sections a generated C file consists of
     cfsMergeInfo,             # section containing merge information
     cfsHeaders,               # section for C include file headers
@@ -89,28 +89,32 @@ type
                               # requires 'T x = T()' to become 'T x; x = T()'
                               # (yes, C++ is weird like that)
     gcFrameId*: Natural       # for the GC stack marking
-    gcFrameType*: Rope       # the struct {} we put the GC markers into
+    gcFrameType*: Rope        # the struct {} we put the GC markers into
 
   TTypeSeq* = seq[PType]
+
+  Codegenflag* = enum
+    preventStackTrace,  # true if stack traces need to be prevented
+    usesThreadVars,     # true if the module uses a thread var
+    frameDeclared,      # hack for ROD support so that we don't declare
+                        # a frame var twice in an init proc
+    isHeaderFile,       # C source file is the header file
+    includesStringh,    # C source file already includes ``<string.h>``
+    objHasKidsValid     # whether we can rely on tfObjHasKids
   TCGen = object of TPassContext # represents a C source file
+    s*: TCFileSections        # sections of the C file
+    flags*: set[Codegenflag]
     module*: PSym
     filename*: string
-    s*: TCFileSections        # sections of the C file
-    preventStackTrace*: bool  # true if stack traces need to be prevented
-    usesThreadVars*: bool     # true if the module uses a thread var
-    frameDeclared*: bool      # hack for ROD support so that we don't declare
-                              # a frame var twice in an init proc
-    isHeaderFile*: bool       # C source file is the header file
-    includesStringh*: bool    # C source file already includes ``<string.h>``
-    objHasKidsValid*: bool    # whether we can rely on tfObjHasKids
     cfilename*: string        # filename of the module (including path,
                               # without extension)
+    tmpBase*: Rope            # base for temp identifier generation
     typeCache*: TIdTable      # cache the generated types
     forwTypeCache*: TIdTable  # cache for forward declarations of types
-    declaredThings*: IntSet  # things we have declared in this .c file
-    declaredProtos*: IntSet  # prototypes we have declared in this .c file
+    declaredThings*: IntSet   # things we have declared in this .c file
+    declaredProtos*: IntSet   # prototypes we have declared in this .c file
     headerFiles*: TLinkedList # needed headers to include
-    typeInfoMarker*: IntSet  # needed for generating type information
+    typeInfoMarker*: IntSet   # needed for generating type information
     initProc*: BProc          # code for init procedure
     postInitProc*: BProc      # code to be executed after the init proc
     preInitProc*: BProc       # code executed before the init proc
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 312afec1a..624c5b183 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -63,19 +63,25 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
     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):
-      if aa.kind == tyObject and result != Invalid: result = Yes
+      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
+        if result != Invalid:
+          result = Yes
+        else:
+          return No
       elif diff != high(int):
         result = Invalid
+      else:
+        return No
     else:
       return No
 
@@ -181,7 +187,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
       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)) and d != 0:
         return d
 
 proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 86bc1c205..de1197292 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -12,7 +12,7 @@
 
 # We do this here before the 'import' statement so 'defined' does not get
 # confused with 'TGCMode.gcGenerational' etc.
-template bootSwitch(name, expr, userString: expr): expr =
+template bootSwitch(name, expr, userString) =
   # Helper to build boot constants, for debugging you can 'echo' the else part.
   const name = if expr: " " & userString else: ""
 
@@ -158,7 +158,7 @@ var
   enableNotes: TNoteKinds
   disableNotes: TNoteKinds
 
-proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
+proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
                          info: TLineInfo; orig: string) =
   var id = ""  # arg = "X]:on|off"
   var i = 0
@@ -181,10 +181,13 @@ proc processSpecificNote(arg: string, state: TSpecialWord, pass: TCmdLinePass,
   case whichKeyword(substr(arg, i))
   of wOn:
     incl(gNotes, n)
+    incl(gMainPackageNotes, n)
     incl(enableNotes, n)
   of wOff:
     excl(gNotes, n)
+    excl(gMainPackageNotes, n)
     incl(disableNotes, n)
+    excl(ForeignPackageNotes, n)
   else: localError(info, errOnOrOffExpectedButXFound, arg)
 
 proc processCompile(filename: string) =
@@ -213,6 +216,16 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
     of "size": result = contains(gOptions, optOptimizeSize)
     of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {}
     else: localError(info, errNoneSpeedOrSizeExpectedButXFound, arg)
+  of "verbosity": result = $gVerbosity == arg
+  of "app":
+    case arg.normalize
+    of "gui":       result = contains(gGlobalOptions, optGenGuiApp)
+    of "console":   result = not contains(gGlobalOptions, optGenGuiApp)
+    of "lib":       result = contains(gGlobalOptions, optGenDynLib) and
+                      not contains(gGlobalOptions, optGenGuiApp)
+    of "staticlib": result = contains(gGlobalOptions, optGenStaticLib) and
+                      not contains(gGlobalOptions, optGenGuiApp)
+    else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
   else: invalidCmdLineOption(passCmd1, switch, info)
 
 proc testCompileOption*(switch: string, info: TLineInfo): bool =
@@ -253,20 +266,18 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of "experimental": result = gExperimentalMode
   else: invalidCmdLineOption(passCmd1, switch, info)
 
-proc processPath(path: string, notRelativeToProj = false,
-                               cfginfo = unknownLineInfo()): string =
+proc processPath(path: string, info: TLineInfo,
+                 notRelativeToProj = false): string =
   let p = if notRelativeToProj or os.isAbsolute(path) or
               '$' in path or path[0] == '.':
             path
           else:
             options.gProjectPath / path
-  result = unixToNativePath(p % ["nimrod", getPrefixDir(),
-    "nim", getPrefixDir(),
-    "lib", libpath,
-    "home", removeTrailingDirSep(os.getHomeDir()),
-    "config", cfginfo.toFullPath().splitFile().dir,
-    "projectname", options.gProjectName,
-    "projectpath", options.gProjectPath])
+  try:
+    result = pathSubs(p, info.toFullPath().splitFile().dir)
+  except ValueError:
+    localError(info, "invalid path: " & p)
+    result = p
 
 proc trackDirty(arg: string, info: TLineInfo) =
   var a = arg.split(',')
@@ -307,19 +318,22 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   case switch.normalize
   of "path", "p":
     expectArg(switch, arg, pass, info)
-    addPath(processPath(arg, cfginfo=info), info)
+    addPath(processPath(arg, info), info)
   of "nimblepath", "babelpath":
     # keep the old name for compat
     if pass in {passCmd2, passPP} and not options.gNoNimblePath:
       expectArg(switch, arg, pass, info)
-      let path = processPath(arg, notRelativeToProj=true)
+      let path = processPath(arg, info, notRelativeToProj=true)
       nimblePath(path, info)
   of "nonimblepath", "nobabelpath":
     expectNoArg(switch, arg, pass, info)
     options.gNoNimblePath = true
+    options.lazyPaths.head = nil
+    options.lazyPaths.tail = nil
+    options.lazyPaths.counter = 0
   of "excludepath":
     expectArg(switch, arg, pass, info)
-    let path = processPath(arg)
+    let path = processPath(arg, info)
     lists.excludePath(options.searchPaths, path)
     lists.excludePath(options.lazyPaths, path)
     if (len(path) > 0) and (path[len(path) - 1] == DirSep):
@@ -328,7 +342,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
       lists.excludePath(options.lazyPaths, strippedPath)
   of "nimcache":
     expectArg(switch, arg, pass, info)
-    options.nimcacheDir = processPath(arg, true)
+    options.nimcacheDir = processPath(arg, info, true)
   of "out", "o":
     expectArg(switch, arg, pass, info)
     options.outFile = arg
@@ -339,7 +353,11 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     discard "allow for backwards compatibility, but don't do anything"
   of "define", "d":
     expectArg(switch, arg, pass, info)
-    defineSymbol(arg)
+    if {':', '='} in arg:
+      splitSwitch(arg, key, val, pass, info)
+      defineSymbol(key, val)
+    else:
+      defineSymbol(arg)
   of "undef", "u":
     expectArg(switch, arg, pass, info)
     undefSymbol(arg)
@@ -407,6 +425,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     if processOnOffSwitchOrList({optHints}, arg, pass, info): listHints()
   of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
   of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
+  of "excessivestacktrace": processOnOffSwitchG({optExcessiveStackTrace}, arg, pass, info)
   of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
   of "debugger":
     case arg.normalize
@@ -493,13 +512,13 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
   of "cincludes":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cIncludes.add arg.processPath
+    if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info)
   of "clibdir":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLibs.add arg.processPath
+    if pass in {passCmd2, passPP}: cLibs.add arg.processPath(info)
   of "clib":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath
+    if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
   of "header":
     headerFile = arg
     incl(gGlobalOptions, optGenIndex)
@@ -540,6 +559,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     gNotes = NotesVerbosity[gVerbosity]
     incl(gNotes, enableNotes)
     excl(gNotes, disableNotes)
+    gMainPackageNotes = gNotes
   of "parallelbuild":
     expectArg(switch, arg, pass, info)
     gNumberOfProcessors = parseInt(arg)
@@ -572,7 +592,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
   of "lib":
     expectArg(switch, arg, pass, info)
-    libpath = processPath(arg, notRelativeToProj=true)
+    libpath = processPath(arg, info, notRelativeToProj=true)
   of "putenv":
     expectArg(switch, arg, pass, info)
     splitSwitch(arg, key, val, pass, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 60e8f2826..bcd9592e6 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -19,8 +19,8 @@ var gSymbols: StringTableRef
 const
   catNone = "false"
 
-proc defineSymbol*(symbol: string) =
-  gSymbols[symbol] = "true"
+proc defineSymbol*(symbol: string, value: string = "true") =
+  gSymbols[symbol] = value
 
 proc undefSymbol*(symbol: string) =
   gSymbols[symbol] = catNone
@@ -62,6 +62,11 @@ proc isDefined*(symbol: string): bool =
 
 proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
 
+proc lookupSymbol*(symbol: string): string =
+  result = if isDefined(symbol): gSymbols[symbol] else: nil
+
+proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
+
 iterator definedSymbolNames*: string =
   for key, val in pairs(gSymbols):
     if val != catNone: yield key
@@ -93,3 +98,4 @@ proc initDefines*() =
   defineSymbol("nimtypedescfixed")
   defineSymbol("nimKnowsNimvm")
   defineSymbol("nimArrIdx")
+  defineSymbol("nimImmediateDeprecated")
diff --git a/compiler/debuginfo.nim b/compiler/debuginfo.nim
new file mode 100644
index 000000000..8589730b9
--- /dev/null
+++ b/compiler/debuginfo.nim
@@ -0,0 +1,81 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code
+## to Nim. The data structure has been designed to produce something useful with Nim's marshal
+## module.
+
+type
+  FilenameHash* = uint32
+  FilenameMapping* = object
+    package*, file*: string
+    mangled*: FilenameHash
+  EnumDesc* = object
+    size*: int
+    owner*: FilenameHash
+    id*: int
+    name*: string
+    values*: seq[(string, int)]
+  DebugInfo* = object
+    version*: int
+    files*: seq[FilenameMapping]
+    enums*: seq[EnumDesc]
+    conflicts*: bool
+
+proc sdbmHash(hash: FilenameHash, c: char): FilenameHash {.inline.} =
+  return FilenameHash(c) + (hash shl 6) + (hash shl 16) - hash
+
+proc sdbmHash(package, file: string): FilenameHash =
+  template `&=`(x, c) = x = sdbmHash(x, c)
+  result = 0
+  for i in 0..<package.len:
+    result &= package[i]
+  result &= '.'
+  for i in 0..<file.len:
+    result &= file[i]
+
+proc register*(self: var DebugInfo; package, file: string): FilenameHash =
+  result = sdbmHash(package, file)
+  for f in self.files:
+    if f.mangled == result:
+      if f.package == package and f.file == file: return
+      self.conflicts = true
+      break
+  self.files.add(FilenameMapping(package: package, file: file, mangled: result))
+
+proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: FilenameHash): bool =
+  for en in self.enums:
+    if en.owner == owner and en.name == ename and en.id == id: return true
+
+proc registerEnum*(self: var DebugInfo; ed: EnumDesc) =
+  self.enums.add ed
+
+proc init*(self: var DebugInfo) =
+  self.version = 1
+  self.files = @[]
+  self.enums = @[]
+
+var gDebugInfo*: DebugInfo
+debuginfo.init gDebugInfo
+
+import marshal, streams
+
+proc writeDebugInfo*(self: var DebugInfo; file: string) =
+  let s = newFileStream(file, fmWrite)
+  store(s, self)
+  s.close
+
+proc writeDebugInfo*(file: string) = writeDebugInfo(gDebugInfo, file)
+
+proc loadDebugInfo*(self: var DebugInfo; file: string) =
+  let s = newFileStream(file, fmRead)
+  load(s, self)
+  s.close
+
+proc loadDebugInfo*(file: string) = loadDebugInfo(gDebugInfo, file)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 8555ec4f0..bbbec081a 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -14,7 +14,7 @@
 import
   ast, strutils, strtabs, options, msgs, os, ropes, idents,
   wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
-  importer, sempass2, json, xmltree, cgi, typesrenderer
+  importer, sempass2, json, xmltree, cgi, typesrenderer, astalgo
 
 type
   TSections = array[TSymKind, Rope]
@@ -25,9 +25,32 @@ type
     indexValFilename: string
     analytics: string  # Google Analytics javascript, "" if doesn't exist
     seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
+    jArray: JsonNode
+    types: TStrTable
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
+proc whichType(d: PDoc; n: PNode): PSym =
+  if n.kind == nkSym:
+    if d.types.strTableContains(n.sym):
+      result = n.sym
+  else:
+    for i in 0..<safeLen(n):
+      let x = whichType(d, n[i])
+      if x != nil: return x
+
+proc attachToType(d: PDoc; p: PSym): PSym =
+  let params = p.ast.sons[paramsPos]
+  # first check the first parameter, then the return type,
+  # then the other parameter:
+  template check(i) =
+    result = whichType(d, params[i])
+    if result != nil: return result
+
+  if params.len > 1: check(1)
+  if params.len > 0: check(0)
+  for i in 2..<params.len: check(i)
+
 proc compilerMsgHandler(filename: string, line, col: int,
                         msgKind: rst.MsgKind, arg: string) {.procvar.} =
   # translate msg kind:
@@ -81,6 +104,8 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
 
   result.seenSymbols = newStringTable(modeCaseInsensitive)
   result.id = 100
+  result.jArray = newJArray()
+  initStrTable result.types
 
 proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
   if gCmd != cmdRst2tex: addf(dest, xml, args)
@@ -226,10 +251,27 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
     result = esc(d.target, "`")
     for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter))
     result.add esc(d.target, "`")
+  of nkOpenSymChoice, nkClosedSymChoice:
+    result = getName(d, n[0], splitAfter)
   else:
     internalError(n.info, "getName()")
     result = ""
 
+proc getNameIdent(n: PNode): PIdent =
+  case n.kind
+  of nkPostfix: result = getNameIdent(n.sons[1])
+  of nkPragmaExpr: result = getNameIdent(n.sons[0])
+  of nkSym: result = n.sym.name
+  of nkIdent: result = n.ident
+  of nkAccQuoted:
+    var r = ""
+    for i in 0.. <n.len: r.add(getNameIdent(n[i]).s)
+    result = getIdent(r)
+  of nkOpenSymChoice, nkClosedSymChoice:
+    result = getNameIdent(n[0])
+  else:
+    result = nil
+
 proc getRstName(n: PNode): PRstNode =
   case n.kind
   of nkPostfix: result = getRstName(n.sons[1])
@@ -239,6 +281,8 @@ proc getRstName(n: PNode): PRstNode =
   of nkAccQuoted:
     result = getRstName(n.sons[0])
     for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text)
+  of nkOpenSymChoice, nkClosedSymChoice:
+    result = getRstName(n[0])
   else:
     internalError(n.info, "getRstName()")
     result = nil
@@ -311,7 +355,7 @@ proc docstringSummary(rstText: string): string =
   ## Also, we hope to not break the rst, but maybe we do. If there is any
   ## trimming done, an ellipsis unicode char is added.
   const maxDocstringChars = 100
-  assert (rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#'))
+  assert(rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#'))
   result = rstText.substr(2).strip
   var pos = result.find('\L')
   if pos > 0:
@@ -380,13 +424,27 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
             "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
     of tkSpaces, tkInvalid:
       add(result, literal)
+    of tkCurlyDotLe:
+      dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
+                    "\\spanOther{$1}",
+                  [rope(esc(d.target, literal))])
+    of tkCurlyDotRi:
+      dispA(result, "</div><span class=\"Other pragmaend\">$1</span>",
+                    "\\spanOther{$1}",
+                  [rope(esc(d.target, literal))])
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
-       tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
+       tkBracketDotLe, tkBracketDotRi, tkParDotLe,
        tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
        tkAccent, tkColonColon,
        tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
       dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
             [rope(esc(d.target, literal))])
+
+  if k in routineKinds and nameNode.kind == nkSym:
+    let att = attachToType(d, nameNode.sym)
+    if att != nil:
+      dispA(result, """<span class="attachedType" style="visibility:hidden">$1</span>""", "",
+        [rope esc(d.target, att.name.s)])
   inc(d.id)
   let
     plainNameRope = rope(xmltree.escape(plainName.strip))
@@ -402,12 +460,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
   var seeSrcRope: Rope = nil
   let docItemSeeSrc = getConfigVar("doc.item.seesrc")
   if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
-    # XXX toFilename doesn't really work. We need to ensure that this keeps
-    # returning a relative path.
+    let path = n.info.toFilename.extractFilename.rope
     let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
-      ["path", "line"], [n.info.toFilename.rope, rope($n.info.line)])
+      ["path", "line"], [path, rope($n.info.line)])
     dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
-        ["path", "line", "url"], [n.info.toFilename.rope,
+        ["path", "line", "url"], [path,
         rope($n.info.line), urlRope])])
 
   add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
@@ -431,8 +488,10 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
 
   setIndexTerm(d[], symbolOrId, name, linkTitle,
     xmltree.escape(plainDocstring.docstringSummary))
+  if k == skType and nameNode.kind == nkSym:
+    d.types.strTableAdd nameNode.sym
 
-proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
+proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
   if not isVisible(nameNode): return
   var
     name = getName(d, nameNode)
@@ -441,8 +500,8 @@ proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
 
   initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
 
-  result = %{ "name": %name, "type": %($k) }
-
+  result = %{ "name": %name, "type": %($k), "line": %n.info.line,
+                 "col": %n.info.col}
   if comm != nil and comm != "":
     result["description"] = %comm
   if r.buf != nil:
@@ -492,46 +551,45 @@ proc generateDoc*(d: PDoc, n: PNode) =
   of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
   else: discard
 
-proc generateJson(d: PDoc, n: PNode, jArray: JsonNode = nil): JsonNode =
+proc add(d: PDoc; j: JsonNode) =
+  if j != nil: d.jArray.add j
+
+proc generateJson*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt:
     if n.comment != nil and startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
-      result = %{ "comment": %stripped }
+      d.add %{ "comment": %stripped, "line": %n.info.line,
+               "col": %n.info.col }
   of nkProcDef:
     when useEffectSystem: documentRaises(n)
-    result = genJSONItem(d, n, n.sons[namePos], skProc)
+    d.add genJsonItem(d, n, n.sons[namePos], skProc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(n)
-    result = genJSONItem(d, n, n.sons[namePos], skMethod)
+    d.add genJsonItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
     when useEffectSystem: documentRaises(n)
-    result = genJSONItem(d, n, n.sons[namePos], skIterator)
+    d.add genJsonItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef:
-    result = genJSONItem(d, n, n.sons[namePos], skMacro)
+    d.add genJsonItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef:
-    result = genJSONItem(d, n, n.sons[namePos], skTemplate)
+    d.add genJsonItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
     when useEffectSystem: documentRaises(n)
-    result = genJSONItem(d, n, n.sons[namePos], skConverter)
+    d.add genJsonItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
       if n.sons[i].kind != nkCommentStmt:
         # order is always 'type var let const':
-        result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
+        d.add genJsonItem(d, n.sons[i], n.sons[i].sons[0],
                 succ(skType, ord(n.kind)-ord(nkTypeSection)))
   of nkStmtList:
-    result = if jArray != nil: jArray else: newJArray()
-
     for i in countup(0, sonsLen(n) - 1):
-      var r = generateJson(d, n.sons[i], result)
-      if r != nil:
-        result.add(r)
-
+      generateJson(d, n.sons[i])
   of nkWhenStmt:
     # generate documentation for the first branch only:
-    if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
-      discard generateJson(d, lastSon(n.sons[0]), jArray)
+    if not checkForFalse(n.sons[0].sons[0]):
+      generateJson(d, lastSon(n.sons[0]))
   else: discard
 
 proc genSection(d: PDoc, kind: TSymKind) =
@@ -593,12 +651,36 @@ proc generateIndex*(d: PDoc) =
     writeIndexFile(d[], splitFile(options.outFile).dir /
                         splitFile(d.filename).name & IndexExt)
 
+proc getOutFile2(filename, ext, dir: string): string =
+  if gWholeProject:
+    let d = if options.outFile != "": options.outFile else: dir
+    createDir(d)
+    result = d / changeFileExt(filename, ext)
+  else:
+    result = getOutFile(filename, ext)
+
 proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
   if optStdout in gGlobalOptions:
     writeRope(stdout, content)
   else:
-    writeRope(content, getOutFile(filename, outExt), useWarning)
+    writeRope(content, getOutFile2(filename, outExt, "htmldocs"), useWarning)
+
+proc writeOutputJson*(d: PDoc, filename, outExt: string,
+                      useWarning = false) =
+  let content = %*{"orig": d.filename,
+    "nimble": getPackageName(d.filename),
+    "entries": d.jArray}
+  if optStdout in gGlobalOptions:
+    write(stdout, $content)
+  else:
+    var f: File
+    if open(f, getOutFile2(splitFile(filename).name,
+            outExt, "jsondocs"), fmWrite):
+      write(f, $content)
+      close(f)
+    else:
+      discard "fixme: error report"
 
 proc commandDoc*() =
   var ast = parseFile(gProjectMainIdx)
@@ -629,18 +711,19 @@ proc commandRst2TeX*() =
   splitter = "\\-"
   commandRstAux(gProjectFull, TexExt)
 
-proc commandJSON*() =
+proc commandJson*() =
   var ast = parseFile(gProjectMainIdx)
   if ast == nil: return
   var d = newDocumentor(gProjectFull, options.gConfigVars)
   d.hasToc = true
-  var json = generateJson(d, ast)
-  var content = rope(pretty(json))
+  generateJson(d, ast)
+  let json = d.jArray
+  let content = rope(pretty(json))
 
   if optStdout in gGlobalOptions:
     writeRope(stdout, content)
   else:
-    echo getOutFile(gProjectFull, JsonExt)
+    #echo getOutFile(gProjectFull, JsonExt)
     writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
 
 proc commandBuildIndex*() =
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 27de06811..d70d5406c 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -19,21 +19,36 @@ type
     module: PSym
   PGen = ref TGen
 
-proc close(p: PPassContext, n: PNode): PNode =
+template closeImpl(body: untyped) {.dirty.} =
   var g = PGen(p)
   let useWarning = sfMainModule notin g.module.flags
-  if gWholeProject or sfMainModule in g.module.flags:
-    writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
+  #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId
+  if (g.module.owner.id == gMainPackageId and gWholeProject) or
+    sfMainModule in g.module.flags:
+    body
     try:
       generateIndex(g.doc)
     except IOError:
       discard
 
+proc close(p: PPassContext, n: PNode): PNode =
+  closeImpl:
+    writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
+
+proc closeJson(p: PPassContext, n: PNode): PNode =
+  closeImpl:
+    writeOutputJson(g.doc, g.module.filename, ".json", useWarning)
+
 proc processNode(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
   generateDoc(g.doc, n)
 
+proc processNodeJson(c: PPassContext, n: PNode): PNode =
+  result = n
+  var g = PGen(c)
+  generateJson(g.doc, n)
+
 proc myOpen(module: PSym): PPassContext =
   var g: PGen
   new(g)
@@ -44,6 +59,8 @@ proc myOpen(module: PSym): PPassContext =
   result = g
 
 const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
+const docgen2JsonPass* = makePass(open = myOpen, process = processNodeJson,
+                                  close = closeJson)
 
 proc finishDoc2Pass*(project: string) =
   discard
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index a5a132005..96ede44fd 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -59,7 +59,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
       evalTemplateAux(templ.sons[i], actual, c, res)
     result.add res
 
-proc evalTemplateArgs(n: PNode, s: PSym): PNode =
+proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
   # if the template has zero arguments, it can be called without ``()``
   # `n` is then a nkSym or something similar
   var totalParams = case n.kind
@@ -75,11 +75,11 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
     # 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
+    genericParams = if sfImmediate in s.flags or fromHlo: 0
                     else: s.ast[genericParamsPos].len
     expectedRegularParams = <s.typ.len
     givenRegularParams = totalParams - genericParams
-
+  if givenRegularParams < 0: givenRegularParams = 0
   if totalParams > expectedRegularParams + genericParams:
     globalError(n.info, errWrongNumberOfArguments)
 
@@ -104,14 +104,14 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
 var evalTemplateCounter* = 0
   # to prevent endless recursion in templates instantiation
 
-proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
+proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
   inc(evalTemplateCounter)
   if evalTemplateCounter > 100:
     globalError(n.info, errTemplateInstantiationTooNested)
     result = n
 
   # replace each param by the corresponding node:
-  var args = evalTemplateArgs(n, tmpl)
+  var args = evalTemplateArgs(n, tmpl, fromHlo)
   var ctx: TemplCtx
   ctx.owner = tmpl
   ctx.genSymOwner = genSymOwner
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 16a8b8bd4..2dcfc0226 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -16,6 +16,8 @@ import
   lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
   securehash, streams
 
+from debuginfo import writeDebugInfo
+
 type
   TSystemCC* = enum
     ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
@@ -59,7 +61,7 @@ type
 # When adding new compilers, the cmake sources could be a good reference:
 # http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
 
-template compiler(name: expr, settings: stmt): stmt {.immediate.} =
+template compiler(name, settings: untyped): untyped =
   proc name: TInfoCC {.compileTime.} = settings
 
 # GNU C and C++ Compiler
@@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) =
       if not noAbsolutePaths():
         if not exefile.isAbsolute():
           exefile = joinPath(splitFile(projectfile).dir, exefile)
+      if optCDebug in gGlobalOptions:
+        writeDebugInfo(exefile.changeFileExt("ndb"))
       exefile = quoteShell(exefile)
       let linkOptions = getLinkOptions() & " " &
                         getConfigVar(cCompiler, ".options.linker")
@@ -750,7 +754,7 @@ proc callCCompiler*(projectfile: string) =
           "lib", quoteShell(libpath)])
     if optCompileOnly notin gGlobalOptions:
       execExternalProgram(linkCmd,
-                          if gVerbosity > 1: hintExecuting else: hintLinking)
+        if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
   else:
     linkCmd = ""
   if optGenScript in gGlobalOptions:
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 5ad932e48..4e887d3e3 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -728,12 +728,12 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
     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
+template isMul(x): untyped = x.getMagic in someMul
+template isDiv(x): untyped = x.getMagic in someDiv
+template isAdd(x): untyped = x.getMagic in someAdd
+template isSub(x): untyped = x.getMagic in someSub
+template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
+template isIntVal(x, y): untyped = x.intVal == y
 
 import macros
 
@@ -776,8 +776,8 @@ proc isMinusOne(n: PNode): bool =
 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
+  template `<=?`(a,b): untyped = ple(m,a,b) == impYes
+  template `>=?`(a,b): untyped = ple(m, nkIntLit.newIntNode(b), a) == impYes
 
   #   0 <= 3
   if a.isValue and b.isValue:
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 6cc9567af..de0fa6216 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -24,7 +24,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
   of skMacro:
     result = semMacroExpr(c, n, orig, s)
   of skTemplate:
-    result = semTemplateExpr(c, n, s)
+    result = semTemplateExpr(c, n, s, {efFromHlo})
   else:
     result = semDirectOp(c, n, {})
   if optHints in gOptions and hintPattern in gNotes:
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
index 906c16546..333772705 100644
--- a/compiler/idgen.nim
+++ b/compiler/idgen.nim
@@ -11,7 +11,7 @@
 
 import idents, strutils, os, options
 
-var gFrontEndId, gBackendId*: int
+var gFrontEndId*: int
 
 const
   debugIds* = false
@@ -30,10 +30,6 @@ proc getID*(): int {.inline.} =
   result = gFrontEndId
   inc(gFrontEndId)
 
-proc backendId*(): int {.inline.} =
-  result = gBackendId
-  inc(gBackendId)
-
 proc setId*(id: int) {.inline.} =
   gFrontEndId = max(gFrontEndId, id + 1)
 
@@ -49,7 +45,6 @@ proc toGid(f: string): string =
 proc saveMaxIds*(project: string) =
   var f = open(project.toGid, fmWrite)
   f.writeLine($gFrontEndId)
-  f.writeLine($gBackendId)
   f.close()
 
 proc loadMaxIds*(project: string) =
@@ -61,5 +56,4 @@ proc loadMaxIds*(project: string) =
       if f.readLine(line):
         var backEndId = parseInt(line)
         gFrontEndId = max(gFrontEndId, frontEndId)
-        gBackendId = max(gBackendId, backEndId)
     f.close()
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 86993358b..dd2c4d954 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -22,7 +22,11 @@ proc getModuleName*(n: PNode): string =
   # The proc won't perform any checks that the path is actually valid
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
-    result = unixToNativePath(n.strVal)
+    try:
+      result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
+    except ValueError:
+      localError(n.info, "invalid path: " & n.strVal)
+      result = n.strVal
   of nkIdent:
     result = n.ident.s
   of nkSym:
@@ -172,7 +176,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
     var m = myImportModule(c, n.sons[i])
     if m != nil:
       # ``addDecl`` needs to be done before ``importAllSymbols``!
-      addDecl(c, m)             # add symbol to symbol table of module
+      addDecl(c, m, n.info)             # add symbol to symbol table of module
       importAllSymbolsExcept(c, m, emptySet)
       #importForwarded(c, m.ast, emptySet)
 
@@ -182,7 +186,7 @@ proc evalFrom(c: PContext, n: PNode): PNode =
   var m = myImportModule(c, n.sons[0])
   if m != nil:
     n.sons[0] = newSymNode(m)
-    addDecl(c, m)               # add symbol to symbol table of module
+    addDecl(c, m, n.info)               # add symbol to symbol table of module
     for i in countup(1, sonsLen(n) - 1):
       if n.sons[i].kind != nkNilLit:
         importSymbol(c, n.sons[i], m)
@@ -193,7 +197,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
   var m = myImportModule(c, n.sons[0])
   if m != nil:
     n.sons[0] = newSymNode(m)
-    addDecl(c, m)               # add symbol to symbol table of module
+    addDecl(c, m, n.info)               # add symbol to symbol table of module
     var exceptSet = initIntSet()
     for i in countup(1, sonsLen(n) - 1):
       let ident = lookups.considerQuotedIdent(n.sons[i])
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 12d9baf82..b802f08f1 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -8,7 +8,7 @@ Platforms: """
   windows: i386;amd64
   linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
   macosx: i386;amd64;powerpc64
-  solaris: i386;amd64;sparc
+  solaris: i386;amd64;sparc;sparc64
   freebsd: i386;amd64
   netbsd: i386;amd64
   openbsd: i386;amd64
@@ -48,8 +48,8 @@ Start: "doc/overview.html"
 [Other]
 Files: "readme.txt;install.txt;contributors.txt;copying.txt"
 Files: "makefile"
-Files: "*.ini"
 Files: "koch.nim"
+Files: "install_nimble.nims"
 
 Files: "icons/nim.ico"
 Files: "icons/nim.rc"
@@ -60,176 +60,26 @@ Files: "icons/koch.rc"
 Files: "icons/koch.res"
 Files: "icons/koch_icon.o"
 
-Files: "compiler/readme.txt"
-Files: "compiler/installer.ini"
-Files: "compiler/*.cfg"
-Files: "compiler/*.nim"
-Files: "doc/*.txt"
-Files: "doc/manual/*.txt"
-Files: "doc/*.nim"
-Files: "doc/*.cfg"
-Files: "compiler/nimfix/*.nim"
-Files: "compiler/nimfix/*.cfg"
-Files: "compiler/nimsuggest/*.nim"
-Files: "compiler/nimsuggest/*.cfg"
-Files: "compiler/plugins/locals/*.nim"
-Files: "compiler/plugins/*.nim"
-Files: "tools/*.nim"
-Files: "tools/*.cfg"
-Files: "tools/*.tmpl"
-Files: "tools/niminst/*.nim"
-Files: "tools/niminst/*.cfg"
-Files: "tools/niminst/*.tmpl"
-Files: "tools/niminst/*.nsh"
+Files: "compiler"
+Files: "doc"
+Files: "tools"
 Files: "web/website.ini"
+Files: "web/ticker.html"
 Files: "web/*.nim"
-Files: "web/*.txt"
+Files: "web/*.rst"
+Files: "web/*.csv"
+Files: "web/news/*.rst"
 Files: "bin/nimblepkg/*.nim"
 Files: "bin/nimblepkg/*.cfg"
 
 [Lib]
-Files: "lib/nimbase.h"
-Files: "lib/*.nim"
-Files: "lib/*.cfg"
-Files: "lib/*.nimble"
-
-Files: "lib/system/*.nim"
-Files: "lib/core/*.nim"
-Files: "lib/pure/*.nim"
-Files: "lib/pure/*.cfg"
-Files: "lib/pure/collections/*.nim"
-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/arch/*.nim"
-
-Files: "lib/wrappers/readline/*.nim"
-Files: "lib/wrappers/linenoise/*.nim"
-Files: "lib/wrappers/linenoise/*.c"
-Files: "lib/wrappers/linenoise/*.h"
-
-Files: "lib/windows/*.nim"
-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"
+Files: "lib"
 
 [Other]
-Files: "examples/*.nim"
-Files: "examples/c++iface/*.nim"
-Files: "examples/objciface/*.nim"
-Files: "examples/cross_calculator/"
-
-Files: "examples/*.html"
-Files: "examples/*.txt"
-Files: "examples/*.cfg"
-Files: "examples/*.tmpl"
+Files: "examples"
+#Files: "dist/nimble"
 
-Files: "tests/actiontable/*.nim"
-Files: "tests/alias/*.nim"
-Files: "tests/ambsym/*.nim"
-Files: "tests/array/*.nim"
-Files: "tests/assign/*.nim"
-Files: "tests/astoverload/*.nim"
-Files: "tests/async/*.nim"
-Files: "tests/benchmarks/*.nim"
-Files: "tests/bind/*.nim"
-Files: "tests/borrow/*.nim"
-Files: "tests/casestmt/*.nim"
-Files: "tests/ccgbugs/*.nim"
-Files: "tests/clearmsg/*.nim"
-Files: "tests/closure/*.nim"
-Files: "tests/cnstseq/*.nim"
-Files: "tests/collections/*.nim"
-Files: "tests/compiles/*.nim"
-Files: "tests/concat/*.nim"
-Files: "tests/concepts/*.nim"
-Files: "tests/constr/*.nim"
-Files: "tests/constraints/*.nim"
-Files: "tests/controlflow/*.nim"
-Files: "tests/converter/*.nim"
-Files: "tests/cpp/*.nim"
-Files: "tests/defaultprocparam/*.nim"
-Files: "tests/deprecated/*.nim"
-Files: "tests/destructor/*.nim"
-Files: "tests/dir with space/*.nim"
-Files: "tests/discard/*.nim"
-Files: "tests/distinct/*.nim"
-Files: "tests/dll/*.nim"
-Files: "tests/effects/*.nim"
-Files: "tests/enum/*.nim"
-Files: "tests/exception/*.nim"
-Files: "tests/exprs/*.nim"
-Files: "tests/fields/*.nim"
-Files: "tests/float/*.nim"
-Files: "tests/friends/*.nim"
-Files: "tests/gc/*.nim"
-Files: "tests/generics/*.nim"
-Files: "tests/gensym/*.nim"
-Files: "tests/global/*.nim"
-Files: "tests/implicit/*.nim"
-Files: "tests/init/*.nim"
-Files: "tests/iter/*.nim"
-Files: "tests/js/*.nim"
-Files: "tests/js/*.cfg"
-Files: "tests/let/*.nim"
-Files: "tests/lexer/*.nim"
-Files: "tests/lookups/*.nim"
-Files: "tests/macros/*.nim"
-Files: "tests/magics/*.nim"
-Files: "tests/metatype/*.nim"
-Files: "tests/method/*.nim"
-Files: "tests/misc/*.nim"
-Files: "tests/modules/*.nim"
-Files: "tests/namedparams/*.nim"
-Files: "tests/notnil/*.nim"
-Files: "tests/objects/*.nim"
-Files: "tests/objvariant/*.nim"
-Files: "tests/openarray/*.nim"
-Files: "tests/osproc/*.nim"
-Files: "tests/overflw/*.nim"
-Files: "tests/overload/*.nim"
-Files: "tests/parallel/*.nim"
-Files: "tests/parallel/*.cfg"
-Files: "tests/parser/*.nim"
-Files: "tests/pragmas/*.nim"
-Files: "tests/proc/*.nim"
-Files: "tests/procvar/*.nim"
-Files: "tests/range/*.nim"
-Files: "tests/rodfiles/*.nim"
-Files: "tests/seq/*.nim"
-Files: "tests/sets/*.nim"
-Files: "tests/showoff/*.nim"
-Files: "tests/specialops/*.nim"
-Files: "tests/stdlib/*.nim"
-Files: "tests/system/*.nim"
-Files: "tests/template/*.nim"
-Files: "tests/testament/*.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"
-Files: "tests/tuples/*.nim"
-Files: "tests/typerel/*.nim"
-Files: "tests/types/*.nim"
-Files: "tests/usingstmt/*.nim"
-Files: "tests/varres/*.nim"
-Files: "tests/varstmt/*.nim"
-Files: "tests/vm/*.nim"
-Files: "tests/readme.txt"
-Files: "tests/testament/css/*.css"
-Files: "tests/testament/*.cfg"
-Files: "lib/pure/unidecode/unidecode.dat"
+Files: "tests"
 
 [Windows]
 Files: "bin/nim.exe"
@@ -249,7 +99,7 @@ BinPath: r"bin;dist\mingw\bin;dist"
 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"
+Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
 ; for now only NSIS supports optional downloads
 
 [UnixBin]
@@ -279,3 +129,7 @@ buildDepends: "gcc (>= 4:4.3.2)"
 pkgDepends: "gcc (>= 4:4.3.2)"
 shortDesc: "The Nim Compiler"
 licenses: "bin/nim,MIT;lib/*,MIT;"
+
+[nimble]
+pkgName: "compiler"
+pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt"
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 2da7db900..80bcd2b0e 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -8,7 +8,7 @@
 #
 
 # This is the JavaScript code generator.
-# Soon also a PHP code generator. ;-)
+# Also a PHP code generator. ;-)
 
 discard """
 The JS code generator contains only 2 tricks:
@@ -48,6 +48,7 @@ type
     etyNull,                  # null type
     etyProc,                  # proc type
     etyBool,                  # bool type
+    etySeq,                   # Nim seq or string type
     etyInt,                   # JavaScript's int
     etyFloat,                 # JavaScript's float
     etyString,                # JavaScript's string
@@ -71,7 +72,7 @@ type
     isLoop: bool             # whether it's a 'block' or 'while'
 
   TGlobals = object
-    typeInfo, code: Rope
+    typeInfo, constants, code: Rope
     forwarded: seq[PSym]
     generatedSyms: IntSet
     typeInfoGenerated: IntSet
@@ -94,7 +95,7 @@ type
     up: PProc     # up the call chain; required for closure support
     declaredGlobals: IntSet
 
-template `|`(a, b: expr): expr {.immediate, dirty.} =
+template `|`(a, b: untyped): untyped {.dirty.} =
   (if p.target == targetJS: a else: b)
 
 proc newGlobals(): PGlobals =
@@ -156,15 +157,18 @@ proc mapType(typ: PType): TJSTypeKind =
   of tyBool: result = etyBool
   of tyFloat..tyFloat128: result = etyFloat
   of tySet: result = etyObject # map a set to a table
-  of tyString, tySequence: result = etyInt # little hack to get right semantics
+  of tyString, tySequence: result = etySeq
   of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum,
      tyVarargs:
     result = etyObject
   of tyNil: result = etyNull
   of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
      tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
-     tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
+     tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid:
     result = etyNone
+  of tyStatic:
+    if t.n != nil: result = mapType(lastSon t)
+    else: result = etyNone
   of tyProc: result = etyProc
   of tyCString: result = etyString
 
@@ -237,7 +241,7 @@ proc useMagic(p: PProc, name: string) =
     internalAssert s.kind in {skProc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
       let code = genProc(p, s)
-      add(p.g.code, code)
+      add(p.g.constants, code)
   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
@@ -359,8 +363,8 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
     ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
     ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
     ["", "", "($1 != $2)", "($1 != $2)"], # Xor
+    ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
     ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
     ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
     ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
@@ -817,7 +821,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
     addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
     return
 
-  let xtyp = mapType(p, x.typ)
+  var xtyp = mapType(p, x.typ)
 
   if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
     gen(p, x.sons[0], a)
@@ -829,7 +833,18 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
 
   gen(p, y, b)
 
+  # we don't care if it's an etyBaseIndex (global) of a string, it's
+  # still a string that needs to be copied properly:
+  if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
+    xtyp = etySeq
   case xtyp
+  of etySeq:
+    if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+      addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+    else:
+      useMagic(p, "nimCopy")
+      addf(p.body, "$1 = nimCopy(null, $2, $3);$n",
+           [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
   of etyObject:
     if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
@@ -981,7 +996,7 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
         r.res = "nimAt($1, $2)" % [r.address, r.res]
     elif ty.kind in {tyString, tyCString}:
       # XXX this needs to be more like substr($1,$2)
-      r.res = "ord($1[$2])" % [r.address, r.res]
+      r.res = "ord(@$1[$2])" % [r.address, r.res]
     else:
       r.res = "$1[$2]" % [r.address, r.res]
   else:
@@ -1049,6 +1064,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
       else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
   of nkObjDownConv:
     gen(p, n.sons[0], r)
+  of nkHiddenDeref:
+    gen(p, n.sons[0].sons[0], r)
   else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind)
 
 proc thisParam(p: PProc; typ: PType): PType =
@@ -1215,11 +1232,13 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
     genArgNoParam(p, it, r)
   else:
     genArg(p, it, paramType.sym, r)
+  inc generated
 
 proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
                     r: var TCompRes) =
   var i = 0
   var j = 1
+  r.kind = resExpr
   while i < pat.len:
     case pat[i]
     of '@':
@@ -1233,31 +1252,43 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
       genOtherArg(p, n, j, typ, generated, r)
       inc j
       inc i
+    of '\31':
+      # unit separator
+      add(r.res, "#")
+      inc i
+    of '\29':
+      # group separator
+      add(r.res, "@")
+      inc i
     else:
       let start = i
       while i < pat.len:
-        if pat[i] notin {'@', '#'}: inc(i)
+        if pat[i] notin {'@', '#', '\31', '\29'}: inc(i)
         else: break
       if i - 1 >= start:
         add(r.res, substr(pat, start, i - 1))
 
 proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   # don't call '$' here for efficiency:
-  let pat = n.sons[0].sym.loc.r.data
-  internalAssert pat != nil
-  if pat.contains({'#', '(', '@'}):
-    var typ = skipTypes(n.sons[0].typ, abstractInst)
-    assert(typ.kind == tyProc)
-    genPatternCall(p, n, pat, typ, r)
-    return
-  gen(p, n.sons[1], r)
-  if r.typ == etyBaseIndex:
-    if r.address == nil:
-      globalError(n.info, "cannot invoke with infix syntax")
-    r.res = "$1[$2]" % [r.address, r.res]
-    r.address = nil
-    r.typ = etyNone
-  add(r.res, "." | "->")
+  let f = n[0].sym
+  if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
+  if sfInfixCall in f.flags:
+    let pat = n.sons[0].sym.loc.r.data
+    internalAssert pat != nil
+    if pat.contains({'#', '(', '@'}):
+      var typ = skipTypes(n.sons[0].typ, abstractInst)
+      assert(typ.kind == tyProc)
+      genPatternCall(p, n, pat, typ, r)
+      return
+  if n.len != 1:
+    gen(p, n.sons[1], r)
+    if r.typ == etyBaseIndex:
+      if r.address == nil:
+        globalError(n.info, "cannot invoke with infix syntax")
+      r.res = "$1[$2]" % [r.address, r.res]
+      r.address = nil
+      r.typ = etyNone
+    add(r.res, "." | "->")
   var op: TCompRes
   if p.target == targetPHP:
     op.kind = resCallee
@@ -1266,7 +1297,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   genArgs(p, n, r, 2)
 
 proc genCall(p: PProc, n: PNode, r: var TCompRes) =
-  if thisParam(p, n.sons[0].typ) != nil:
+  if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil:
     genInfixCall(p, n, r)
     return
   if p.target == targetPHP:
@@ -1400,6 +1431,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = putToSeq("null", indirect)
   of tySequence, tyString, tyCString, tyPointer, tyProc:
     result = putToSeq("null", indirect)
+  of tyStatic:
+    if t.n != nil:
+      result = createVar(p, lastSon t, indirect)
+    else:
+      internalError("createVar: " & $t.kind)
+      result = nil
   else:
     internalError("createVar: " & $t.kind)
     result = nil
@@ -1409,13 +1446,16 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     a: TCompRes
     s: Rope
   if n.kind == nkEmpty:
+    let mname = mangleName(v, p.target)
     addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n",
-         [mangleName(v, p.target), createVar(p, v.typ, isIndirect(v))])
+         [mname, createVar(p, v.typ, isIndirect(v))])
+    if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex:
+      addf(p.body, "var $1_Idx = 0;$n", [ mname ])
   else:
     discard mangleName(v, p.target)
     gen(p, n, a)
     case mapType(p, v.typ)
-    of etyObject:
+    of etyObject, etySeq:
       if needsNoCopy(p, n):
         s = a.res
       else:
@@ -1458,7 +1498,7 @@ proc genConstant(p: PProc, c: PSym) =
     p.body = nil
     #genLineDir(p, c.ast)
     genVarInit(p, c, c.ast)
-    add(p.g.code, p.body)
+    add(p.g.constants, p.body)
     p.body = oldBody
 
 proc genNew(p: PProc, n: PNode) =
@@ -1554,8 +1594,16 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
   of tyEnum, tyOrdinal:
     gen(p, n.sons[1], r)
     useMagic(p, "cstrToNimstr")
+    var offset = ""
+    if t.kind == tyEnum:
+      let firstFieldOffset = t.n.sons[0].sym.position
+      if firstFieldOffset < 0:
+        offset = "+" & $(-firstFieldOffset)
+      elif firstFieldOffset > 0:
+        offset = "-" & $firstFieldOffset
+
     r.kind = resExpr
-    r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res]
+    r.res = "cstrToNimstr($1.node.sons[$2$3].name)" % [genTypeInfo(p, t), r.res, rope(offset)]
   else:
     # XXX:
     internalError(n.info, "genRepr: Not implemented")
@@ -1645,8 +1693,11 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
   of mLengthStr:
-    unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" |
-                           "strlen($1)")
+    if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
+      unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
+    else:
+      unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" |
+                             "strlen($1)")
   of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)")
   of mLengthSeq, mLengthOpenArray, mLengthArray:
     unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" |
@@ -1672,8 +1723,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     else:
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
       else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
-  of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "")
-  of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "")
+  of mSetLengthStr:
+    binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)")
+  of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "$1 = array_slice($1, 0, $2)")
   of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
   of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
   of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
@@ -1683,8 +1735,31 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
   of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
   of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]")
-  of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)" | "isset($1[$2])")
+  of mInSet:
+    if p.target == targetJS:
+      binaryExpr(p, n, r, "", "($1[$2] != undefined)")
+    else:
+      let s = n.sons[1]
+      if s.kind == nkCurly:
+        var a, b, x: TCompRes
+        gen(p, n.sons[2], x)
+        r.res = rope("(")
+        r.kind = resExpr
+        for i in countup(0, sonsLen(s) - 1):
+          if i > 0: add(r.res, " || ")
+          var it = s.sons[i]
+          if it.kind == nkRange:
+            gen(p, it.sons[0], a)
+            gen(p, it.sons[1], b)
+            addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,])
+          else:
+            gen(p, it, a)
+            addf(r.res, "($1 == $2)", [x.res, a.res])
+        add(r.res, ")")
+      else:
+        binaryExpr(p, n, r, "", "isset($1[$2])")
   of mNewSeq: genNewSeq(p, n)
+  of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()")
   of mOf: genOf(p, n, r)
   of mReset: genReset(p, n)
   of mEcho: genEcho(p, n, r)
@@ -1877,9 +1952,13 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   let header = generateHeader(p, prc.typ)
   if prc.typ.sons[0] != nil and sfPure notin prc.flags:
     resultSym = prc.ast.sons[resultPos].sym
+    let mname = mangleName(resultSym, p.target)
     resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [
-        mangleName(resultSym, p.target),
+        mname,
         createVar(p, resultSym.typ, isIndirect(resultSym))]
+    if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex:
+      resultAsgn.add "var $#_Idx = 0;$n" % [
+        mname ];
     gen(p, prc.ast.sons[resultPos], a)
     if mapType(p, resultSym.typ) == etyBaseIndex:
       returnStmt = "return [$#, $#];$n" % [a.address, a.res]
@@ -1983,7 +2062,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
       genMagic(p, n, r)
     elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
-        n.len >= 2:
+        n.len >= 1:
       genInfixCall(p, n, r)
     else:
       genCall(p, n, r)
@@ -2131,7 +2210,7 @@ proc wholeCode*(m: BModule): Rope =
       var p = newProc(globals, m, nil, m.module.options)
       attachProc(p, prc)
 
-  result = globals.typeInfo & globals.code
+  result = globals.typeInfo & globals.constants & globals.code
 
 proc getClassName(t: PType): Rope =
   var s = t.sym
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 8d109e48a..cf1679ee4 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -165,4 +165,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
   of tyEnum: genEnumInfo(p, t, result)
   of tyObject: genObjectInfo(p, t, result)
   of tyTuple: genTupleInfo(p, t, result)
+  of tyStatic:
+    if t.n != nil: result = genTypeInfo(p, lastSon t)
+    else: internalError("genTypeInfo(" & $t.kind & ')')
   else: internalError("genTypeInfo(" & $t.kind & ')')
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 53fca4863..753602c80 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -716,11 +716,16 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #localError(n.info, "internal error: closure to closure created")
         # now we know better, so patch it:
         n.sons[0] = x.sons[0]
+        n.sons[1] = x.sons[1]
   of nkLambdaKinds, nkIteratorDef:
     if n.typ != nil and n[namePos].kind == nkSym:
       let m = newSymNode(n[namePos].sym)
       m.typ = n.typ
       result = liftCapturedVars(m, owner, d, c)
+  of nkHiddenStdConv:
+    if n.len == 2:
+      n.sons[1] = liftCapturedVars(n[1], owner, d, c)
+      if n[1].kind == nkClosure: result = n[1]
   else:
     if owner.isIterator:
       if n.kind == nkYieldStmt:
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 0032b97df..0eee5004e 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -138,6 +138,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
 proc isKeyword*(kind: TTokType): bool =
   result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
 
+template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
+
 proc isNimIdentifier*(s: string): bool =
   if s[0] in SymStartChars:
     var i = 1
@@ -589,12 +591,29 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
   of '\\':
     add(tok.literal, '\\')
     inc(L.bufpos)
-  of 'x', 'X':
+  of 'x', 'X', 'u', 'U':
+    var tp = L.buf[L.bufpos]
     inc(L.bufpos)
     var xi = 0
     handleHexChar(L, xi)
     handleHexChar(L, xi)
-    add(tok.literal, chr(xi))
+    if tp in {'u', 'U'}:
+      handleHexChar(L, xi)
+      handleHexChar(L, xi)
+      # inlined toUTF-8 to avoid unicode and strutils dependencies.
+      if xi <=% 127:
+        add(tok.literal, xi.char )
+      elif xi <=% 0x07FF:
+        add(tok.literal, ((xi shr 6) or 0b110_00000).char )
+        add(tok.literal, ((xi and ones(6)) or 0b10_0000_00).char )
+      elif xi <=% 0xFFFF:
+        add(tok.literal, (xi shr 12 or 0b1110_0000).char )
+        add(tok.literal, (xi shr 6 and ones(6) or 0b10_0000_00).char )
+        add(tok.literal, (xi and ones(6) or 0b10_0000_00).char )
+      else: # value is 0xFFFF
+        add(tok.literal, "\xef\xbf\xbf" )
+    else:
+      add(tok.literal, chr(xi))
   of '0'..'9':
     if matchTwoChars(L, '0', {'0'..'9'}):
       lexMessage(L, warnOctalEscape)
@@ -716,7 +735,8 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
       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:
+        if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars or
+            isMagicIdentSeparatorRune(buf, pos+magicIdentSeparatorRuneByteWidth) or pos == L.bufpos:
           lexMessage(L, errInvalidToken, "–")
           break
         inc(pos, magicIdentSeparatorRuneByteWidth)
@@ -728,7 +748,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
       h = h !& ord(c)
       inc(pos)
     of '_':
-      if buf[pos+1] notin SymChars:
+      if buf[pos+1] notin SymChars or isMagicIdentSeparatorRune(buf, pos+1):
         lexMessage(L, errInvalidToken, "_")
         break
       inc(pos)
@@ -811,10 +831,6 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
           break
         dec nesting
       inc pos
-    of '\t':
-      lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
-      inc(pos)
-      if isDoc: tok.literal.add '\t'
     of CR, LF:
       pos = handleCRLF(L, pos)
       buf = L.buf
@@ -877,10 +893,10 @@ proc scanComment(L: var TLexer, tok: var TToken) =
       inc(indent)
 
     when defined(nimfix):
-      template doContinue(): expr =
+      template doContinue(): untyped =
         buf[pos] == '#' and (col == indent or lastBackslash > 0)
     else:
-      template doContinue(): expr =
+      template doContinue(): untyped =
         buf[pos] == '#' and buf[pos+1] == '#'
     if doContinue():
       tok.literal.add "\n"
@@ -926,9 +942,9 @@ proc skip(L: var TLexer, tok: var TToken) =
           break
       tok.strongSpaceA = 0
       when defined(nimfix):
-        template doBreak(): expr = buf[pos] > ' '
+        template doBreak(): untyped = buf[pos] > ' '
       else:
-        template doBreak(): expr =
+        template doBreak(): untyped =
           buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#')
       if doBreak():
         tok.indent = indent
@@ -1041,7 +1057,8 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       inc(L.bufpos)
     of '_':
       inc(L.bufpos)
-      if L.buf[L.bufpos] notin SymChars:
+      if L.buf[L.bufpos] notin SymChars+{'_'} and not
+          isMagicIdentSeparatorRune(L.buf, L.bufpos):
         tok.tokType = tkSymbol
         tok.ident = getIdent("_")
       else:
@@ -1062,6 +1079,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       tok.tokType = tkCharLit
     of '0'..'9':
       getNumber(L, tok)
+      let c = L.buf[L.bufpos]
+      if c in SymChars+{'_'}:
+        lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
     else:
       if c in OpChars:
         getOperator(L, tok)
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index a337bf0f3..df19a6afb 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -99,8 +99,11 @@ proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
 
 proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
   for scope in walkScopes(c.currentScope):
-    result = strTableGet(scope.symbols, s)
-    if result != nil and result.kind in filter: return
+    var ti: TIdentIter
+    var candidate = initIdentIter(ti, scope.symbols, s)
+    while candidate != nil:
+      if candidate.kind in filter: return candidate
+      candidate = nextIdentIter(ti, scope.symbols)
   result = nil
 
 proc errorSym*(c: PContext, n: PNode): PSym =
@@ -153,7 +156,8 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
       if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
         # XXX: implicit type params are currently skTypes
         # maybe they can be made skGenericParam as well.
-        if s.typ != nil and tfImplicitTypeParam notin s.typ.flags:
+        if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
+           s.typ.kind != tyGenericParam:
           message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
     s = nextIter(it, scope.symbols)
 
@@ -161,6 +165,10 @@ proc wrongRedefinition*(info: TLineInfo, s: string) =
   if gCmd != cmdInteractive:
     localError(info, errAttemptToRedefine, s)
 
+proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
+  if not c.currentScope.addUniqueSym(sym):
+    wrongRedefinition(info, sym.name.s)
+
 proc addDecl*(c: PContext, sym: PSym) =
   if not c.currentScope.addUniqueSym(sym):
     wrongRedefinition(sym.info, sym.name.s)
@@ -212,14 +220,27 @@ when defined(nimfix):
     of 'a'..'z': result = getIdent(toLower(x.s[0]) & x.s.substr(1))
     else: result = x
 
-  template fixSpelling(n: PNode; ident: PIdent; op: expr) =
+  template fixSpelling(n: PNode; ident: PIdent; op: untyped) =
     let alt = ident.altSpelling
     result = op(c, alt).skipAlias(n)
     if result != nil:
       prettybase.replaceDeprecated(n.info, ident, alt)
       return result
 else:
-  template fixSpelling(n: PNode; ident: PIdent; op: expr) = discard
+  template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard
+
+proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
+  var err = "Error: ambiguous identifier: '" & s.name.s & "'"
+  var ti: TIdentIter
+  var candidate = initIdentIter(ti, c.importTable.symbols, s.name)
+  var i = 0
+  while candidate != nil:
+    if i == 0: err.add " --use "
+    else: err.add " or "
+    err.add candidate.owner.name.s & "." & candidate.name.s
+    candidate = nextIdentIter(ti, c.importTable.symbols)
+    inc i
+  localError(info, errGenerated, err)
 
 proc lookUp*(c: PContext, n: PNode): PSym =
   # Looks up a symbol. Generates an error in case of nil.
@@ -243,32 +264,36 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     internalError(n.info, "lookUp")
     return
   if contains(c.ambiguousSymbols, result.id):
-    localError(n.info, errUseQualifier, result.name.s)
+    errorUseQualifier(c, n.info, result)
   if result.kind == skStub: loadStub(result)
 
 type
   TLookupFlag* = enum
-    checkAmbiguity, checkUndeclared
+    checkAmbiguity, checkUndeclared, checkModule
 
-proc qualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
+proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
+  const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
   case n.kind
   of nkIdent, nkAccQuoted:
     var ident = considerQuotedIdent(n)
-    result = searchInScopes(c, ident).skipAlias(n)
+    if checkModule in flags:
+      result = searchInScopes(c, ident).skipAlias(n)
+    else:
+      result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
     if result == nil and checkUndeclared in flags:
       fixSpelling(n, ident, searchInScopes)
       localError(n.info, errUndeclaredIdentifier, ident.s)
       result = errorSym(c, n)
     elif checkAmbiguity in flags and result != nil and
         contains(c.ambiguousSymbols, result.id):
-      localError(n.info, errUseQualifier, ident.s)
+      errorUseQualifier(c, n.info, result)
   of nkSym:
     result = n.sym
     if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id):
-      localError(n.info, errUseQualifier, n.sym.name.s)
+      errorUseQualifier(c, n.info, n.sym)
   of nkDotExpr:
     result = nil
-    var m = qualifiedLookUp(c, n.sons[0], flags*{checkUndeclared})
+    var m = qualifiedLookUp(c, n.sons[0], (flags*{checkUndeclared})+{checkModule})
     if m != nil and m.kind == skModule:
       var ident: PIdent = nil
       if n.sons[1].kind == nkIdent:
@@ -313,7 +338,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     o.mode = oimDone
   of nkDotExpr:
     o.mode = oimOtherModule
-    o.m = qualifiedLookUp(c, n.sons[0])
+    o.m = qualifiedLookUp(c, n.sons[0], {checkUndeclared, checkModule})
     if o.m != nil and o.m.kind == skModule:
       var ident: PIdent = nil
       if n.sons[1].kind == nkIdent:
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 7a5c7f44b..36eb24653 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -471,6 +471,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
       let slice = newNodeI(nkCall, n.info, 4)
       slice.typ = n.typ
       slice.sons[0] = newSymNode(createMagic("slice", mSlice))
+      slice.sons[0].typ = getSysType(tyInt) # fake type
       var fieldB = newSym(skField, tmpName, objType.owner, n.info)
       fieldB.typ = getSysType(tyInt)
       objType.addField(fieldB)
@@ -577,6 +578,8 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
   var fn = n.sons[0]
   # templates and macros are in fact valid here due to the nature of
   # the transformation:
+  if fn.kind == nkClosure:
+    localError(n.info, "closure in spawn environment is not allowed")
   if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
                                                skMethod, skConverter}):
     # for indirect calls we pass the function pointer in the scratchObj
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 40e0728ae..13b365d04 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -41,11 +41,15 @@ proc getSysSym*(name: string): PSym =
 proc getSysMagic*(name: string, m: TMagic): PSym =
   var ti: TIdentIter
   let id = getIdent(name)
-  result = initIdentIter(ti, systemModule.tab, id)
-  while result != nil:
-    if result.kind == skStub: loadStub(result)
-    if result.magic == m: return result
-    result = nextIdentIter(ti, systemModule.tab)
+  var r = initIdentIter(ti, systemModule.tab, id)
+  while r != nil:
+    if r.kind == skStub: loadStub(r)
+    if r.magic == m:
+      # prefer the tyInt variant:
+      if r.typ.sons[0].kind == tyInt: return r
+      result = r
+    r = nextIdentIter(ti, systemModule.tab)
+  if result != nil: return result
   rawMessage(errSystemNeeds, name)
   result = newSym(skError, id, systemModule, systemModule.info)
   result.typ = newType(tyError, systemModule)
diff --git a/compiler/main.nim b/compiler/main.nim
index ece21a817..0db66b53e 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -41,14 +41,16 @@ proc commandGenDepend =
 
 proc commandCheck =
   msgs.gErrorMax = high(int)  # do not stop after first error
+  defineSymbol("nimcheck")
   semanticPasses()            # use an empty backend for semantic checking only
   rodPass()
   compileProject()
 
-proc commandDoc2 =
+proc commandDoc2(json: bool) =
   msgs.gErrorMax = high(int)  # do not stop after first error
   semanticPasses()
-  registerPass(docgen2Pass)
+  if json: registerPass(docgen2JsonPass)
+  else: registerPass(docgen2Pass)
   #registerPass(cleanupPass())
   compileProject()
   finishDoc2Pass(gProjectName)
@@ -241,7 +243,7 @@ proc mainCommand* =
   clearPasses()
   gLastCmdTime = epochTime()
   appendStr(searchPaths, options.libpath)
-  if gProjectFull.len != 0:
+  when false: # gProjectFull.len != 0:
     # current path is always looked first for modules
     prependStr(searchPaths, gProjectPath)
   setId(100)
@@ -280,7 +282,7 @@ proc mainCommand* =
     gCmd = cmdDoc
     loadConfigs(DocConfig)
     defineSymbol("nimdoc")
-    commandDoc2()
+    commandDoc2(false)
   of "rst2html":
     gCmd = cmdRst2html
     loadConfigs(DocConfig)
@@ -295,7 +297,13 @@ proc mainCommand* =
     loadConfigs(DocConfig)
     wantMainModule()
     defineSymbol("nimdoc")
-    commandJSON()
+    commandJson()
+  of "jsondoc2":
+    gCmd = cmdDoc
+    loadConfigs(DocConfig)
+    wantMainModule()
+    defineSymbol("nimdoc")
+    commandDoc2(true)
   of "buildindex":
     gCmd = cmdDoc
     loadConfigs(DocConfig)
diff --git a/compiler/modules.nim b/compiler/modules.nim
index ef727e200..9120bd1b6 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -29,12 +29,16 @@ var
   gMemCacheData*: seq[TModuleInMemory] = @[]
     ## XXX: we should implement recycling of file IDs
     ## if the user keeps renaming modules, the file IDs will keep growing
+  gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
+  packageSyms: TStrTable
+
+initStrTable(packageSyms)
 
 proc getModule*(fileIdx: int32): PSym =
   if fileIdx >= 0 and fileIdx < gCompiledModules.len:
     result = gCompiledModules[fileIdx]
 
-template hash(x: PSym): expr =
+template hash(x: PSym): untyped =
   gMemCacheData[x.position].hash
 
 proc hashChanged(fileIdx: int32): bool =
@@ -45,7 +49,7 @@ proc hashChanged(fileIdx: int32): bool =
                                        else: hashNotChanged
     # echo "TESTING Hash: ", fileIdx.toFilename, " ", result
 
-  case gMemCacheData[fileIdx].hashStatus:
+  case gMemCacheData[fileIdx].hashStatus
   of hashHasChanged:
     result = true
   of hashNotChanged:
@@ -90,6 +94,7 @@ proc resetAllModules* =
     if gCompiledModules[i] != nil:
       resetModule(i.int32)
   resetPackageCache()
+  initStrTable(packageSyms)
   # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
 proc resetAllModulesHard* =
@@ -97,6 +102,7 @@ proc resetAllModulesHard* =
   gCompiledModules.setLen 0
   gMemCacheData.setLen 0
   magicsys.resetSysTypes()
+  initStrTable(packageSyms)
   # XXX
   #gOwners = @[]
 
@@ -105,12 +111,16 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
     resetModule(fileIdx)
     return Yes
 
-  if gMemCacheData[fileIdx].needsRecompile != Maybe:
-    return gMemCacheData[fileIdx].needsRecompile
+  if gFuzzyGraphChecking:
+    if gMemCacheData[fileIdx].needsRecompile != Maybe:
+      return gMemCacheData[fileIdx].needsRecompile
+  else:
+    # cycle detection: We claim that a cycle does no harm.
+    if gMemCacheData[fileIdx].needsRecompile == Probing:
+      return No
 
-  if optForceFullMake in gGlobalOptions or
-     hashChanged(fileIdx):
-       markDirty
+  if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
+    markDirty()
 
   if gMemCacheData[fileIdx].deps != nil:
     gMemCacheData[fileIdx].needsRecompile = Probing
@@ -118,7 +128,7 @@ proc checkDepMem(fileIdx: int32): TNeedRecompile =
       let d = checkDepMem(dep)
       if d in {Yes, Recompiled}:
         # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
-        markDirty
+        markDirty()
 
   gMemCacheData[fileIdx].needsRecompile = No
   return No
@@ -135,8 +145,16 @@ proc newModule(fileIdx: int32): PSym =
     rawMessage(errInvalidModuleName, result.name.s)
 
   result.info = newLineInfo(fileIdx, 1, 1)
-  result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
-                        result.info)
+  let pack = getIdent(getPackageName(filename))
+  var packSym = packageSyms.strTableGet(pack)
+  if packSym == nil:
+    let pck = getPackageName(filename)
+    let pck2 = if pck.len > 0: pck else: "unknown"
+    packSym = newSym(skPackage, getIdent(pck2), nil, result.info)
+    initStrTable(packSym.tab)
+    packageSyms.strTableAdd(packSym)
+
+  result.owner = packSym
   result.position = fileIdx
 
   growCache gMemCacheData, fileIdx
@@ -146,6 +164,11 @@ proc newModule(fileIdx: int32): PSym =
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
   strTableAdd(result.tab, result) # a module knows itself
+  let existing = strTableGet(packSym.tab, result.name)
+  if existing != nil and existing.info.fileIndex != result.info.fileIndex:
+    localError(result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
+  # strTableIncl() for error corrections:
+  discard strTableIncl(packSym.tab, result)
 
 proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
   result = getModule(fileIdx)
@@ -156,6 +179,9 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
     #var rd = handleSymbolFile(result)
     var rd: PRodReader
     result.flags = result.flags + flags
+    if sfMainModule in result.flags:
+      gMainPackageId = result.owner.id
+
     if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
       rd = handleSymbolFile(result)
       if result.id < 0:
@@ -181,8 +207,11 @@ proc importModule*(s: PSym, fileIdx: int32): PSym {.procvar.} =
   # this is called by the semantic checking phase
   result = compileModule(fileIdx, {})
   if optCaasEnabled in gGlobalOptions: addDep(s, fileIdx)
-  if sfSystemModule in result.flags:
-    localError(result.info, errAttemptToRedefine, result.name.s)
+  #if sfSystemModule in result.flags:
+  #  localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
+           else: ForeignPackageNotes
 
 proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
   result = syntaxes.parseFile(fileIdx)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a556ad0c5..4a9980066 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -471,6 +471,8 @@ type
     shortName*: string         # short name of the module
     quotedName*: Rope          # cached quoted short name for codegen
                                # purposes
+    quotedFullName*: Rope      # cached quoted full name for codegen
+                               # purposes
 
     lines*: seq[Rope]          # the source code of the module
                                #   used for better error messages and
@@ -505,7 +507,7 @@ const
                                          warnProveField, warnProveIndex,
                                          warnGcUnsafe,
                                          hintSuccessX, hintPath, hintConf,
-                                         hintProcessing,
+                                         hintProcessing, hintPattern,
                                          hintDependency,
                                          hintExecuting, hintLinking,
                                          hintCodeBegin, hintCodeEnd,
@@ -514,9 +516,8 @@ const
     {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
                                          warnProveField, warnProveIndex,
                                          warnGcUnsafe,
-                                         hintPath, hintConf,
+                                         hintPath,
                                          hintDependency,
-                                         hintExecuting,
                                          hintCodeBegin, hintCodeEnd,
                                          hintSource, hintStackTrace,
                                          hintGCStats},
@@ -527,6 +528,8 @@ const
   InvalidFileIDX* = int32(-1)
 
 var
+  ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
+    hintQuitCalled, hintExecuting}
   filenameToIndexTbl = initTable[string, int32]()
   fileInfos*: seq[TFileInfo] = @[]
   systemFileIdx*: int32
@@ -561,6 +564,7 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
   let fileName = projPath.extractFilename
   result.shortName = fileName.changeFileExt("")
   result.quotedName = fileName.makeCString
+  result.quotedFullName = fullPath.makeCString
   if optEmbedOrigSrc in gGlobalOptions or true:
     result.lines = @[]
 
@@ -616,6 +620,7 @@ var
   gHintCounter*: int = 0
   gWarnCounter*: int = 0
   gErrorMax*: int = 1         # stop after gErrorMax errors
+  gMainPackageNotes*: TNoteKinds = NotesVerbosity[1]
 
 proc unknownLineInfo*(): TLineInfo =
   result.line = int16(-1)
@@ -755,7 +760,7 @@ proc msgWriteln*(s: string, flags: MsgFlags = {}) =
         flushFile(stderr)
 
 macro callIgnoringStyle(theProc: typed, first: typed,
-                        args: varargs[expr]): stmt =
+                        args: varargs[typed]): untyped =
   let typForegroundColor = bindSym"ForegroundColor".getType
   let typBackgroundColor = bindSym"BackgroundColor".getType
   let typStyle = bindSym"Style".getType
@@ -772,7 +777,7 @@ macro callIgnoringStyle(theProc: typed, first: typed,
        typ != typTerminalCmd:
       result.add(arg)
 
-macro callStyledWriteLineStderr(args: varargs[expr]): stmt =
+macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
   result = newCall(bindSym"styledWriteLine")
   result.add(bindSym"stderr")
   for arg in children(args[0][1]):
@@ -784,7 +789,7 @@ template callWritelnHook(args: varargs[string, `$`]) =
     s.add arg
   writelnHook s
 
-template styledMsgWriteln*(args: varargs[expr]) =
+template styledMsgWriteln*(args: varargs[typed]) =
   if not isNil(writelnHook):
     callIgnoringStyle(callWritelnHook, nil, args)
   elif optStdout in gGlobalOptions:
@@ -995,11 +1000,11 @@ proc internalError*(errMsg: string) =
   writeContext(unknownLineInfo())
   rawMessage(errInternal, errMsg)
 
-template assertNotNil*(e: expr): expr =
+template assertNotNil*(e): untyped =
   if e == nil: internalError($instantiationInfo())
   e
 
-template internalAssert*(e: bool): stmt =
+template internalAssert*(e: bool) =
   if not e: internalError($instantiationInfo())
 
 proc addSourceLine*(fileIdx: int32, line: string) =
@@ -1022,7 +1027,10 @@ proc sourceLine*(i: TLineInfo): Rope =
 
 proc quotedFilename*(i: TLineInfo): Rope =
   internalAssert i.fileIndex >= 0
-  result = fileInfos[i.fileIndex].quotedName
+  if optExcessiveStackTrace in gGlobalOptions:
+    result = fileInfos[i.fileIndex].quotedFullName
+  else:
+    result = fileInfos[i.fileIndex].quotedName
 
 ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
   case err
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 4f9962ea8..0ff128ba3 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -7,7 +7,7 @@ path:"$projectPath/.."
 path:"$lib/packages/docutils"
 
 define:booting
-import:testability
+#import:"$projectpath/testability"
 
 @if windows:
   cincludes: "$lib/wrappers/libffi/common"
diff --git a/compiler/nim.nim b/compiler/nim.nim
index b242052ec..a58afd593 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -56,12 +56,12 @@ proc handleCmdLine() =
     loadConfigs(DefaultConfig) # load all config files
     let scriptFile = gProjectFull.changeFileExt("nims")
     if fileExists(scriptFile):
-      runNimScript(scriptFile)
+      runNimScript(scriptFile, freshDefines=false)
       # '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")
+      runNimScript(gProjectPath / "config.nims", freshDefines=false)
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg
index 73219d6f8..0d9dbfa4b 100644
--- a/compiler/nimfix/nimfix.nim.cfg
+++ b/compiler/nimfix/nimfix.nim.cfg
@@ -5,7 +5,7 @@ hint[XDeclaredButNotUsed]:off
 path:"$projectPath/.."
 
 path:"$lib/packages/docutils"
-path:"../../compiler"
+path:"$nim"
 
 define:useStdoutAsStdmsg
 symbol:nimfix
diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim
index 1123afb9e..8ba922927 100644
--- a/compiler/nimfix/pretty.nim
+++ b/compiler/nimfix/pretty.nim
@@ -66,7 +66,7 @@ proc beautifyName(s: string, k: TSymKind): string =
              ]:
       result.add s[i]
     else:
-      result.add toUpper(s[i])
+      result.add toUpperAscii(s[i])
   of skConst, skEnumField:
     # for 'const' we keep how it's spelt; either upper case or lower case:
     result.add s[0]
@@ -74,7 +74,7 @@ proc beautifyName(s: string, k: TSymKind): string =
     # as a special rule, don't transform 'L' to 'l'
     if s.len == 1 and s[0] == 'L': result.add 'L'
     elif '_' in s: result.add(s[i])
-    else: result.add toLower(s[0])
+    else: result.add toLowerAscii(s[0])
   inc i
   while i < s.len:
     if s[i] == '_':
@@ -85,9 +85,9 @@ proc beautifyName(s: string, k: TSymKind): string =
         result.add s[i]
       else:
         inc i
-        result.add toUpper(s[i])
+        result.add toUpperAscii(s[i])
     elif allUpper:
-      result.add toLower(s[i])
+      result.add toLowerAscii(s[i])
     else:
       result.add s[i]
     inc i
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index f15ad6368..94507adf0 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -121,7 +121,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: untyped) {.dirty.} =
   var x, y: TBitSet
   toBitSet(a, x)
   toBitSet(b, y)
diff --git a/compiler/options.nim b/compiler/options.nim
index 2716a98d3..a5154cb48 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -67,6 +67,8 @@ type                          # please make sure we have under 32 options
     optIdeDebug               # idetools: debug mode
     optIdeTerse               # idetools: use terse descriptions
     optNoCppExceptions        # use C exception handling even with CPP
+    optExcessiveStackTrace    # fully qualified module filenames
+
   TGlobalOptions* = set[TGlobalOption]
   TCommands* = enum           # Nim's commands
                               # **keep binary compatible**
@@ -127,10 +129,10 @@ var
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
 
-template compilationCachePresent*: expr =
+template compilationCachePresent*: untyped =
   {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
 
-template optPreserveOrigSource*: expr =
+template optPreserveOrigSource*: untyped =
   optEmbedOrigSrc in gGlobalOptions
 
 const
@@ -149,6 +151,7 @@ const
 var
   gConfigVars* = newStringTable(modeStyleInsensitive)
   gDllOverrides = newStringTable(modeCaseInsensitive)
+  gModuleOverrides* = newStringTable(modeStyleInsensitive)
   gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
   libpath* = ""
   gProjectName* = "" # holds a name like 'nim'
@@ -209,9 +212,7 @@ proc setDefaultLibpath*() =
 
     # Special rule to support other tools (nimble) which import the compiler
     # modules and make use of them.
-    let realNimPath = # Make sure we expand the symlink
-      if symlinkExists(findExe("nim")): expandSymlink(findExe("nim"))
-      else: findExe("nim")
+    let realNimPath = findExe("nim")
     # Find out if $nim/../../lib/system.nim exists.
     let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
     if not fileExists(libpath / "system.nim") and
@@ -219,7 +220,7 @@ proc setDefaultLibpath*() =
       libpath = parentNimLibPath
 
 proc canonicalizePath*(path: string): string =
-  when not FileSystemCaseSensitive: result = path.expandFilename.toLower
+  when not FileSystemCaseSensitive: result = path.expandFilename.toLowerAscii
   else: result = path.expandFilename
 
 proc shortenDir*(dir: string): string =
@@ -238,57 +239,26 @@ proc removeTrailingDirSep*(path: string): string =
   else:
     result = path
 
+include packagehandling
+
 proc getNimcacheDir*: string =
   result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
                                                          genSubDir
 
-template newPackageCache(): expr =
-  newStringTable(when FileSystemCaseSensitive:
-                   modeCaseInsensitive
-                 else:
-                   modeCaseSensitive)
-
-var packageCache = newPackageCache()
-
-proc resetPackageCache*() = packageCache = newPackageCache()
-
-iterator myParentDirs(p: string): string =
-  # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
-  var current = p
-  while true:
-    current = current.parentDir
-    if current.len == 0: break
-    yield current
-
-proc getPackageName*(path: string): string =
-  var parents = 0
-  block packageSearch:
-    for d in myParentDirs(path):
-      if packageCache.hasKey(d):
-        #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
-        return packageCache[d]
-      inc parents
-      for file in walkFiles(d / "*.nimble"):
-        result = file.splitFile.name
-        break packageSearch
-      for file in walkFiles(d / "*.babel"):
-        result = file.splitFile.name
-        break packageSearch
-  # we also store if we didn't find anything:
-  if result.isNil: result = ""
-  for d in myParentDirs(path):
-    #echo "set cache ", d, " |", result, "|", parents
-    packageCache[d] = result
-    dec parents
-    if parents <= 0: break
-
-proc withPackageName*(path: string): string =
-  let x = path.getPackageName
-  if x.len == 0:
-    result = path
-  else:
-    let (p, file, ext) = path.splitFile
-    result = (p / (x & '_' & file)) & ext
+
+proc pathSubs*(p, config: string): string =
+  let home = removeTrailingDirSep(os.getHomeDir())
+  result = unixToNativePath(p % [
+    "nim", getPrefixDir(),
+    "lib", libpath,
+    "home", home,
+    "config", config,
+    "projectname", options.gProjectName,
+    "projectpath", options.gProjectPath,
+    "projectdir", options.gProjectPath,
+    "nimcache", getNimcacheDir()])
+  if '~' in result:
+    result = result.replace("~", home)
 
 proc toGeneratedFile*(path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
@@ -357,17 +327,25 @@ proc rawFindFile2(f: string): string =
     it = PStrEntry(it.next)
   result = ""
 
+template patchModule() {.dirty.} =
+  if result.len > 0 and gModuleOverrides.len > 0:
+    let key = getPackageName(result) & "_" & splitFile(result).name
+    if gModuleOverrides.hasKey(key):
+      let ov = gModuleOverrides[key]
+      if ov.len > 0: result = ov
+
 proc findFile*(f: string): string {.procvar.} =
   if f.isAbsolute:
     result = if f.existsFile: f else: ""
   else:
     result = f.rawFindFile
     if result.len == 0:
-      result = f.toLower.rawFindFile
+      result = f.toLowerAscii.rawFindFile
       if result.len == 0:
         result = f.rawFindFile2
         if result.len == 0:
-          result = f.toLower.rawFindFile2
+          result = f.toLowerAscii.rawFindFile2
+  patchModule()
 
 proc findModule*(modulename, currentModule: string): string =
   # returns path to module
@@ -386,6 +364,7 @@ proc findModule*(modulename, currentModule: string): string =
   result = currentPath / m
   if not existsFile(result):
     result = findFile(m)
+  patchModule()
 
 proc libCandidates*(s: string, dest: var seq[string]) =
   var le = strutils.find(s, '(')
@@ -426,10 +405,10 @@ proc binaryStrSearch*(x: openArray[string], y: string): int =
       return mid
   result = - 1
 
-template nimdbg*: expr = c.module.fileIdx == gProjectMainIdx
-template cnimdbg*: expr = p.module.module.fileIdx == gProjectMainIdx
-template pnimdbg*: expr = p.lex.fileIdx == gProjectMainIdx
-template lnimdbg*: expr = L.fileIdx == gProjectMainIdx
+template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx
+template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx
+template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx
+template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx
 
 proc parseIdeCmd*(s: string): IdeCmd =
   case s:
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
new file mode 100644
index 000000000..6a98ffe60
--- /dev/null
+++ b/compiler/packagehandling.nim
@@ -0,0 +1,56 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+iterator myParentDirs(p: string): string =
+  # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
+  var current = p
+  while true:
+    current = current.parentDir
+    if current.len == 0: break
+    yield current
+
+template newPackageCache(): untyped =
+  newStringTable(when FileSystemCaseSensitive:
+                   modeCaseInsensitive
+                 else:
+                   modeCaseSensitive)
+
+var packageCache = newPackageCache()
+
+proc resetPackageCache*() = packageCache = newPackageCache()
+
+proc getPackageName*(path: string): string =
+  var parents = 0
+  block packageSearch:
+    for d in myParentDirs(path):
+      if packageCache.hasKey(d):
+        #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
+        return packageCache[d]
+      inc parents
+      for file in walkFiles(d / "*.nimble"):
+        result = file.splitFile.name
+        break packageSearch
+      for file in walkFiles(d / "*.babel"):
+        result = file.splitFile.name
+        break packageSearch
+  # we also store if we didn't find anything:
+  if result.isNil: result = ""
+  for d in myParentDirs(path):
+    #echo "set cache ", d, " |", result, "|", parents
+    packageCache[d] = result
+    dec parents
+    if parents <= 0: break
+
+proc withPackageName*(path: string): string =
+  let x = path.getPackageName
+  if x.len == 0:
+    result = path
+  else:
+    let (p, file, ext) = path.splitFile
+    result = (p / (x & '_' & file)) & ext
diff --git a/compiler/parser.nim b/compiler/parser.nim
index f22177ac1..19ef0960a 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -98,7 +98,7 @@ proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
   ## Produce and emit a parser message to output about the token `tok`
   parMessage(p, msg, prettyTok(tok))
 
-template withInd(p: expr, body: stmt) {.immediate.} =
+template withInd(p, body: untyped) =
   let oldInd = p.currInd
   p.currInd = p.tok.indent
   body
@@ -202,7 +202,7 @@ proc isRightAssociative(tok: TToken): bool {.inline.} =
 
 proc getPrecedence(tok: TToken, strongSpaces: bool): int =
   ## Calculates the precedence of the given token.
-  template considerStrongSpaces(x): expr =
+  template considerStrongSpaces(x): untyped =
     x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
 
   case tok.tokType
@@ -214,7 +214,7 @@ proc getPrecedence(tok: TToken, strongSpaces: bool): int =
     if L > 1 and tok.ident.s[L-1] == '>' and
       tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
 
-    template considerAsgn(value: expr) =
+    template considerAsgn(value: untyped) =
       result = if tok.ident.s[L-1] == '=': 1 else: value
 
     case relevantChar
@@ -326,6 +326,7 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
         getTok(p)
       else:
         parMessage(p, errIdentifierExpected, p.tok)
+        break
     eat(p, tkAccent)
   else:
     if allowNil and p.tok.tokType == tkNil:
@@ -685,11 +686,19 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
   #|       | '{' optInd indexExprList optPar '}'
   #|       | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
   result = r
+
+  template somePar() =
+    if p.tok.strongSpaceA > 0:
+      if p.strongSpaces:
+        break
+      else:
+        parMessage(p, warnDeprecated,
+          "a [b] will be parsed as command syntax; spacing")
   while p.tok.indent < 0 or
        (p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
     case p.tok.tokType
     of tkParLe:
-      if p.strongSpaces and p.tok.strongSpaceA > 0: break
+      somePar()
       result = namedParams(p, result, nkCall, tkParRi)
       if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
         result.kind = nkObjConstr
@@ -704,10 +713,10 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
       result = dotExpr(p, result)
       result = parseGStrLit(p, result)
     of tkBracketLe:
-      if p.strongSpaces and p.tok.strongSpaceA > 0: break
+      somePar()
       result = namedParams(p, result, nkBracketExpr, tkBracketRi)
     of tkCurlyLe:
-      if p.strongSpaces and p.tok.strongSpaceA > 0: break
+      somePar()
       result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
     of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
       if p.inPragma == 0:
@@ -963,8 +972,9 @@ proc parseDoBlock(p: var TParser): PNode =
 proc parseDoBlocks(p: var TParser, call: PNode) =
   #| doBlocks = doBlock ^* IND{=}
   if p.tok.tokType == tkDo:
-    addSon(call, parseDoBlock(p))
-    while sameInd(p) and p.tok.tokType == tkDo:
+    #withInd(p):
+    #  addSon(call, parseDoBlock(p))
+    while sameOrNoInd(p) and p.tok.tokType == tkDo:
       addSon(call, parseDoBlock(p))
 
 proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 9b9fdca4e..d4361a671 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -24,7 +24,7 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode =
     # 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)
+    message(n.info, hintProcessing, $idgen.gFrontendId)
 
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
 
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 604d3521d..09b8d3305 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -75,7 +75,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
     result = matchNodeKinds(p.constraint, n)
     if not result: return
   if isNil(n.typ):
-    result = p.typ.kind in {tyEmpty, tyStmt}
+    result = p.typ.kind in {tyVoid, tyStmt}
   else:
     result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
 
@@ -129,7 +129,7 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
       result = bindOrCheck(c, p.sons[2].sym, arglist)
 
 proc matches(c: PPatternContext, p, n: PNode): bool =
-  # hidden conversions (?)
+  let n = skipHidden(n)
   if nfNoRewrite in n.flags:
     result = false
   elif isPatternParam(c, p):
diff --git a/compiler/platform.nim b/compiler/platform.nim
index dc414bfeb..36ec10477 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -159,7 +159,7 @@ type
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
     cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
-    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430
+    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64
 
 type
   TEndian* = enum
@@ -187,7 +187,8 @@ const
     (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: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
+    (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
+    (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64)]
 
 var
   targetCPU*, hostCPU*: TSystemCPU
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 2280ef712..db5f8e727 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -63,11 +63,12 @@ const
     wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
     wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
-    wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims}
+    wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
+    wIntDefine, wStrDefine}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
                       wThread, wRaises, wLocks, wTags, wGcSafe}
-  allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
+  allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
 
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
 # implementation
@@ -89,43 +90,38 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
       else:
         invalidPragma(it)
 
-proc setExternName(s: PSym, extname: string) =
-  s.loc.r = rope(extname % s.name.s)
+proc setExternName(s: PSym, extname: string, info: TLineInfo) =
+  # special cases to improve performance:
+  if extname == "$1":
+    s.loc.r = rope(s.name.s)
+  elif '$' notin extname:
+    s.loc.r = rope(extname)
+  else:
+    try:
+      s.loc.r = rope(extname % s.name.s)
+    except ValueError:
+      localError(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
   if gCmd == cmdPretty and '$' notin extname:
     # note that '{.importc.}' is transformed into '{.importc: "$1".}'
     s.loc.flags.incl(lfFullExternalName)
 
-proc makeExternImport(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc makeExternImport(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
 
-proc validateExternCName(s: PSym, info: TLineInfo) =
-  ## Validates that the symbol name in s.loc.r is a valid C identifier.
-  ##
-  ## Valid identifiers are those alphanumeric including the underscore not
-  ## starting with a number. If the check fails, a generic error will be
-  ## displayed to the user.
-  let target = $s.loc.r
-  if target.len < 1 or target[0] notin IdentStartChars or
-      not target.allCharsInSet(IdentChars):
-    localError(info, errGenerated, "invalid exported symbol")
-
 proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname)
-  # XXX to fix make it work with nimrtl.
-  #if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}:
-  #  validateExternCName(s, info)
+  setExternName(s, extname, info)
   incl(s.flags, sfExportc)
 
-proc processImportCompilerProc(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
   incl(s.loc.flags, lfImportCompilerProc)
 
-proc processImportCpp(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportCpp(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfInfixCall)
   excl(s.flags, sfForward)
@@ -133,8 +129,8 @@ proc processImportCpp(s: PSym, extname: string) =
   incl(m.flags, sfCompileToCpp)
   extccomp.gMixedMode = true
 
-proc processImportObjC(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportObjC(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfNamedParamCall)
   excl(s.flags, sfForward)
@@ -256,8 +252,9 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
 
 proc processDynLib(c: PContext, n: PNode, sym: PSym) =
   if (sym == nil) or (sym.kind == skModule):
-    POptionEntry(c.optionStack.tail).dynlib = getLib(c, libDynamic,
-        expectDynlibNode(c, n))
+    let lib = getLib(c, libDynamic, expectDynlibNode(c, n))
+    if not lib.isOverriden:
+      POptionEntry(c.optionStack.tail).dynlib = lib
   else:
     if n.kind == nkExprColonExpr:
       var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
@@ -276,6 +273,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
 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.len == 2) and
       (n.sons[0].sons[1].kind == nkIdent) and
       (n.sons[0].sons[0].kind == nkIdent):
       #and (n.sons[1].kind == nkIdent):
@@ -391,21 +389,25 @@ type
   TLinkFeature = enum
     linkNormal, linkSys
 
-proc processCompile(c: PContext, n: PNode) =
+proc relativeFile(c: PContext; n: PNode; ext=""): string =
   var s = expectStrLit(c, n)
-  var found = findFile(s)
-  if found == "": found = s
-  var trunc = changeFileExt(found, "")
-  if not isAbsolute(found):
-    found = parentDir(n.info.toFullPath) / found
+  if ext.len > 0 and splitFile(s).ext == "":
+    s = addFileExt(s, ext)
+  result = parentDir(n.info.toFullPath) / s
+  if not fileExists(result):
+    if isAbsolute(s): result = s
+    else:
+      result = findFile(s)
+      if result.len == 0: result = s
+
+proc processCompile(c: PContext, n: PNode) =
+  let found = relativeFile(c, n)
+  let trunc = found.changeFileExt("")
   extccomp.addExternalFileToCompile(found)
   extccomp.addFileToLink(completeCFilePath(trunc, false))
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
-  var f = expectStrLit(c, n)
-  if splitFile(f).ext == "": f = addFileExt(f, CC[cCompiler].objExt)
-  var found = findFile(f)
-  if found == "": found = f # use the default
+  let found = relativeFile(c, n, CC[cCompiler].objExt)
   case feature
   of linkNormal: extccomp.addFileToLink(found)
   of linkSys:
@@ -564,7 +566,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
     localError(pragma.info, "list of key:value pairs expected"); return
   for n in pragma:
     if n.kind in {nkExprColonExpr, nkExprEqExpr}:
-      let dest = qualifiedLookUp(c, n[1])
+      let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
       let src = considerQuotedIdent(n[0])
       let alias = newSym(skAlias, src, dest, n[0].info)
       incl(alias.flags, sfExported)
@@ -589,7 +591,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # and perform the lookup on demand instead.
       result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info)
   else:
-    result = qualifiedLookUp(c, n)
+    result = qualifiedLookUp(c, n, {checkUndeclared})
 
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
                   validPragmas: TSpecialWords): bool =
@@ -616,20 +618,23 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
       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 wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"), it.info)
       of wImportCompilerProc:
-        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
-      of wExtern: setExternName(sym, expectStrLit(c, it))
+        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"), it.info)
+      of wExtern: setExternName(sym, expectStrLit(c, it), it.info)
       of wImmediate:
-        if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
+        if sym.kind in {skTemplate, skMacro}:
+          incl(sym.flags, sfImmediate)
+          incl(sym.flags, sfAllUntyped)
+          message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
         else: invalidPragma(it)
       of wDirty:
         if sym.kind == skTemplate: incl(sym.flags, sfDirty)
         else: invalidPragma(it)
       of wImportCpp:
-        processImportCpp(sym, getOptionalStr(c, it, "$1"))
+        processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info)
       of wImportObjC:
-        processImportObjC(sym, getOptionalStr(c, it, "$1"))
+        processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info)
       of wAlign:
         if sym.typ == nil: invalidPragma(it)
         var align = expectIntLit(c, it)
@@ -894,6 +899,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
       of wBase:
         noVal(it)
         sym.flags.incl sfBase
+      of wIntDefine:
+        sym.magic = mIntDefine
+      of wStrDefine:
+        sym.magic = mStrDefine
       else: invalidPragma(it)
     else: invalidPragma(it)
 
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 03f6d4832..0e733d643 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -704,7 +704,10 @@ proc gcase(g: var TSrcGen, n: PNode) =
 proc gproc(g: var TSrcGen, n: PNode) =
   var c: TContext
   if n.sons[namePos].kind == nkSym:
-    put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
+    let s = n.sons[namePos].sym
+    put(g, tkSymbol, renderDefinitionName(s))
+    if sfGenSym in s.flags:
+      put(g, tkIntLit, $s.id)
   else:
     gsub(g, n.sons[namePos])
 
@@ -712,7 +715,11 @@ proc gproc(g: var TSrcGen, n: PNode) =
     gpattern(g, n.sons[patternPos])
   let oldCheckAnon = g.checkAnon
   g.checkAnon = true
-  gsub(g, n.sons[genericParamsPos])
+  if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
+      n[miscPos][1].kind != nkEmpty:
+    gsub(g, n[miscPos][1])
+  else:
+    gsub(g, n.sons[genericParamsPos])
   g.checkAnon = oldCheckAnon
   gsub(g, n.sons[paramsPos])
   gsub(g, n.sons[pragmasPos])
@@ -794,7 +801,8 @@ proc gident(g: var TSrcGen, n: PNode) =
   else:
     t = tkOpr
   put(g, t, s)
-  if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
+  if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
+    put(g, tkIntLit, $n.sym.id)
 
 proc doParamsAux(g: var TSrcGen, params: PNode) =
   if params.len > 1:
@@ -802,7 +810,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
     gsemicolon(g, params, 1)
     put(g, tkParRi, ")")
 
-  if params.sons[0].kind != nkEmpty:
+  if params.len > 0 and params.sons[0].kind != nkEmpty:
     putWithSpace(g, tkOpr, "->")
     gsub(g, params.sons[0])
 
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 91f93d279..d24e8f560 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -21,7 +21,7 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
     if f > 0.0: result = "INF"
     else: result = "-INF"
   else:
-    var buf: array [0..80, char]
+    var buf: array[0..80, char]
     c_sprintf(buf, "%#.16e", f)
     result = $buf
 
@@ -64,7 +64,7 @@ const
 const
   vintDelta = 5
 
-template encodeIntImpl(self: expr) =
+template encodeIntImpl(self) =
   var d: char
   var v = x
   var rem = v mod 190
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 22cd282fd..8b2653bc9 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -13,10 +13,10 @@
 import
   ast, modules, passes, passaux, condsyms,
   options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
-  os, times, osproc
+  os, times, osproc, wordrecg, strtabs
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
-from strutils import cmpIgnoreStyle
+from strutils import cmpIgnoreStyle, contains
 
 proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   let dir = getString(a, 0)
@@ -116,12 +116,27 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext =
     setResult(a, options.command)
   cbconf switch:
     processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
-
-
-proc runNimScript*(scriptName: string) =
+  cbconf hintImpl:
+    processSpecificNote(a.getString 0, wHint, passPP, unknownLineInfo(),
+      a.getString 1)
+  cbconf warningImpl:
+    processSpecificNote(a.getString 0, wWarning, passPP, unknownLineInfo(),
+      a.getString 1)
+  cbconf patchFile:
+    let key = a.getString(0) & "_" & a.getString(1)
+    var val = a.getString(2).addFileExt(NimExt)
+    if {'$', '~'} in val:
+      val = pathSubs(val, vthisDir)
+    elif not isAbsolute(val):
+      val = vthisDir / val
+    gModuleOverrides[key] = val
+  cbconf selfExe:
+    setResult(a, os.getAppFilename())
+
+proc runNimScript*(scriptName: string; freshDefines=true) =
   passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
-  initDefines()
+  if freshDefines: initDefines()
 
   defineSymbol("nimscript")
   defineSymbol("nimconfig")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 97a20a4da..7db4ae47e 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -59,21 +59,11 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
   # templates perform some quick check whether the cursor is actually in
   # the generic or template.
   when defined(nimsuggest):
-    assert gCmd == cmdIdeTools
-    if requiresCheck:
+    if gCmd == cmdIdeTools and requiresCheck:
       #if optIdeDebug in gGlobalOptions:
       #  echo "passing to safeSemExpr: ", renderTree(n)
       discard safeSemExpr(c, n)
 
-proc typeMismatch(n: PNode, formal, actual: PType) =
-  if formal.kind != tyError and actual.kind != tyError:
-    let named = typeToString(formal)
-    let desc = typeToString(formal, preferDesc)
-    let x = if named == desc: named else: named & " = " & desc
-    localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
-        typeToString(actual) & ") " &
-        `%`(msgKindToString(errButExpectedX), [x]))
-
 proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
   if arg.typ.isNil:
     localError(arg.info, errExprXHasNoType,
@@ -174,7 +164,7 @@ 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
+  proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii
 
   # like newSymS, but considers gensym'ed symbols
   if n.kind == nkSym:
@@ -316,6 +306,13 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
 
 include hlo, seminst, semcall
 
+when false:
+  # hopefully not required:
+  proc resetSemFlag(n: PNode) =
+    excl n.flags, nfSem
+    for i in 0 ..< n.safeLen:
+      resetSemFlag(n[i])
+
 proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
                        flags: TExprFlags): PNode =
   ## Semantically check the output of a macro.
@@ -329,6 +326,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
   c.friendModules.add(s.owner.getModule)
 
   result = n
+  excl(n.flags, nfSem)
+  #resetSemFlag n
   if s.typ.sons[0] == nil:
     result = semStmt(c, result)
   else:
@@ -363,7 +362,6 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-
   result = evalMacroCall(c.module, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, result, sym, flags)
@@ -419,17 +417,42 @@ proc myOpen(module: PSym): PPassContext =
   c.importTable.addSym(module) # a module knows itself
   if sfSystemModule in module.flags:
     magicsys.systemModule = module # set global variable!
-  else:
-    c.importTable.addSym magicsys.systemModule # import the "System" identifier
-    importAllSymbols(c, magicsys.systemModule)
   c.topLevelScope = openScope(c)
+  # don't be verbose unless the module belongs to the main package:
+  if module.owner.id == gMainPackageId:
+    gNotes = gMainPackageNotes
+  else:
+    if gMainPackageNotes == {}: gMainPackageNotes = gNotes
+    gNotes = ForeignPackageNotes
   result = c
 
 proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
   result = myOpen(module)
   for m in items(rd.methods): methodDef(m, true)
 
+proc isImportSystemStmt(n: PNode): bool =
+  if magicsys.systemModule == nil: return false
+  case n.kind
+  of nkImportStmt:
+    for x in n:
+      let f = checkModuleName(x)
+      if f == magicsys.systemModule.info.fileIndex:
+        return true
+  of nkImportExceptStmt, nkFromStmt:
+    let f = checkModuleName(n[0])
+    if f == magicsys.systemModule.info.fileIndex:
+      return true
+  else: discard
+
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
+  if c.topStmts == 0 and not isImportSystemStmt(n):
+    if sfSystemModule notin c.module.flags and
+        n.kind notin {nkEmpty, nkCommentStmt}:
+      c.importTable.addSym magicsys.systemModule # import the "System" identifier
+      importAllSymbols(c, magicsys.systemModule)
+      inc c.topStmts
+  else:
+    inc c.topStmts
   if sfNoForward in c.module.flags:
     result = semAllTypeSections(c, n)
   else:
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index a1e209263..9702128ba 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -183,7 +183,7 @@ proc newSeqCall(c: PContext; x, y: PNode): PNode =
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
-  of tyNone, tyEmpty: discard
+  of tyNone, tyEmpty, tyVoid: discard
   of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
       tyPtr, tyString, tyRef:
     defaultOp(c, t, body, x, y)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 916d897c9..97209167d 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -53,7 +53,18 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
     if symx.kind in filter:
       syms.add((symx, o.lastOverloadScope))
     symx = nextOverloadIter(o, c, headSymbol)
-  if syms.len == 0: return
+  if syms.len == 0:
+    when false:
+      if skIterator notin filter:
+        # also try iterators, but these are 2nd class:
+        symx = initOverloadIter(o, c, headSymbol)
+        while symx != nil:
+          if symx.kind == skIterator:
+            syms.add((symx, 100))
+          symx = nextOverloadIter(o, c, headSymbol)
+        if syms.len == 0: return
+    else:
+      return
 
   var z: TCandidate
   initCandidate(c, best, syms[0][0], initialBinding, symScope)
@@ -69,7 +80,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
     #  gDebug = true
     matches(c, n, orig, z)
     if errors != nil:
-      errors.safeAdd(sym)
+      errors.safeAdd((sym, int z.mutabilityProblem))
       if z.errors != nil:
         for err in z.errors:
           errors.add(err)
@@ -111,7 +122,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   let proto = describeArgs(c, n, 1, preferName)
 
   var prefer = preferName
-  for err in errors:
+  for err, mut in items(errors):
     var errProto = ""
     let n = err.typ.n
     for i in countup(1, n.len - 1):
@@ -123,14 +134,21 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
     if errProto == proto:
       prefer = preferModuleInfo
       break
+
   # now use the information stored in 'prefer' to produce a nice error message:
   var result = msgKindToString(errTypeMismatch)
   add(result, describeArgs(c, n, 1, prefer))
   add(result, ')')
   var candidates = ""
-  for err in errors:
-    add(candidates, err.getProcHeader(prefer))
+  for err, mut in items(errors):
+    if err.kind in routineKinds and err.ast != nil:
+      add(candidates, renderTree(err.ast,
+            {renderNoBody, renderNoComments,renderNoPragmas}))
+    else:
+      add(candidates, err.getProcHeader(prefer))
     add(candidates, "\n")
+    if mut != 0 and mut < n.len:
+      add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
   if candidates != "":
     add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
   if c.compilesContextId > 0 and optReportConceptFailures in gGlobalOptions:
@@ -138,6 +156,20 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   else:
     localError(n.info, errGenerated, result)
 
+proc bracketNotFoundError(c: PContext; n: PNode) =
+  var errors: CandidateErrors = @[]
+  var o: TOverloadIter
+  let headSymbol = n[0]
+  var symx = initOverloadIter(o, c, headSymbol)
+  while symx != nil:
+    if symx.kind in routineKinds:
+      errors.add((symx, 0))
+    symx = nextOverloadIter(o, c, headSymbol)
+  if errors.len == 0:
+    localError(n.info, "could not resolve: " & $n)
+  else:
+    notFoundError(c, n, errors)
+
 proc resolveOverloads(c: PContext, n, orig: PNode,
                       filter: TSymKinds;
                       errors: var CandidateErrors): TCandidate =
@@ -154,8 +186,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
   template pickBest(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
                       filter, result, alt, errors)
-
-
   pickBest(f)
 
   let overloadsState = result.state
@@ -226,7 +256,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         #notFoundError(c, n, errors)
 
       return
-
   if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
       not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
     internalAssert result.state == csMatch
@@ -295,8 +324,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   var finalCallee = x.calleeSym
   markUsed(n.sons[0].info, finalCallee)
   styleCheckUse(n.sons[0].info, finalCallee)
-  if finalCallee.ast == nil:
-    internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
+  assert finalCallee.ast != nil
   if x.hasFauxMatch:
     result = x.call
     result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
@@ -315,12 +343,12 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
       # 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
+        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)
@@ -361,7 +389,14 @@ proc explicitGenericInstError(n: PNode): PNode =
 
 proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
   var m: TCandidate
-  initCandidate(c, m, s, n)
+  # binding has to stay 'nil' for this to work!
+  initCandidate(c, m, s, nil)
+
+  for i in 1..sonsLen(n)-1:
+    let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
+    let arg = n[i].typ
+    let tm = typeRel(m, formal, arg, true)
+    if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   markUsed(n.info, s)
   styleCheckUse(n.info, s)
@@ -382,6 +417,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
          "; got " & $(n.len-1) & " type(s) but expected " & $expected)
       return n
     result = explicitGenericSym(c, n, s)
+    if result == nil: result = explicitGenericInstError(n)
   elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
     # choose the generic proc with the proper number of type parameters.
     # XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
@@ -394,11 +430,12 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
         # it suffices that the candidate has the proper number of generic
         # type parameters:
         if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
-          result.add(explicitGenericSym(c, n, candidate))
+          let x = explicitGenericSym(c, n, candidate)
+          if x != nil: result.add(x)
     # get rid of nkClosedSymChoice if not ambiguous:
     if result.len == 1 and a.kind == nkClosedSymChoice:
       result = result[0]
-    # candidateCount != 1: return explicitGenericInstError(n)
+    elif result.len == 0: result = explicitGenericInstError(n)
   else:
     result = explicitGenericInstError(n)
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index a13f2c232..30b6e261d 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -47,7 +47,7 @@ type
     efLValue, efWantIterator, efInTypeof,
     efWantStmt, efAllowStmt, efDetermineType,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
-    efNoProcvarCheck
+    efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo
   TExprFlags* = set[TExprFlag]
 
   TTypeAttachedOp* = enum
@@ -99,11 +99,12 @@ type
     unknownIdents*: IntSet     # ids of all unknown identifiers to prevent
                                # naming it multiple times
     generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
+    topStmts*: int # counts the number of encountered top level statements
     lastGenericIdx*: int      # used for the generics stack
     hloLoopDetector*: int     # used to prevent endless loops in the HLO
     inParallelStmt*: int
     instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
-                            op: TTypeAttachedOp): PSym {.nimcall.}
+                            op: TTypeAttachedOp; col: int): PSym {.nimcall.}
     selfName*: PIdent
     signatures*: TStrTable
 
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index 1261dd460..9ea581f3a 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -87,7 +87,7 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
     result = nil
 
 proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
-  template maybeAddLine(e: expr): stmt =
+  template maybeAddLine(e) =
     let stmt = e
     if stmt != nil:
       if result == nil: result = newNode(nkStmtList)
@@ -139,6 +139,7 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
   t = t.skipTypes({tyGenericInst})
   case t.kind
   of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
+    t.destructor = analyzingDestructor
     if instantiateDestructor(c, t.sons[0]) != nil:
       t.destructor = getCompilerProc"nimDestroyRange"
       return t
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 16b4ee479..fc31829ba 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -15,7 +15,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   markUsed(n.info, s)
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
-  result = evalTemplate(n, s, getCurrOwner())
+  result = evalTemplate(n, s, getCurrOwner(), efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
   popInfoContext()
 
@@ -24,16 +24,15 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # same as 'semExprWithType' but doesn't check for proc vars
   result = semExpr(c, n, flags + {efOperand})
-  if result.kind == nkEmpty and result.typ.isNil:
+  #if result.kind == nkEmpty and result.typ.isNil:
     # do not produce another redundant error message:
     #raiseRecoverableError("")
-    result = errorNode(c, n)
+  #  result = errorNode(c, n)
   if result.typ != nil:
     # XXX tyGenericInst here?
     if result.typ.kind == tyVar: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
-    result.typ = newTypeS(tyEmpty, c)
-    result.typ.flags.incl tfVoid
+    result.typ = newTypeS(tyVoid, c)
   else:
     localError(n.info, errExprXHasNoType,
                renderTree(result, {renderNoComments}))
@@ -51,7 +50,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
                renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
   else:
-    # XXX tyGenericInst here?
     if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind == tyVar: result = newDeref(result)
     semDestructorCheck(c, result, flags)
@@ -74,8 +72,12 @@ proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
 
 proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
   result = copyTree(s.ast)
-  result.typ = s.typ
-  result.info = n.info
+  if result.isNil:
+    localError(n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
+    result = newSymNode(s)
+  else:
+    result.typ = s.typ
+    result.info = n.info
 
 type
   TConvStatus = enum
@@ -83,8 +85,9 @@ type
     convNotNeedeed,
     convNotLegal
 
-proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus =
-  return if inheritanceDiff(castDest, src) == high(int):
+proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus =
+  let diff = inheritanceDiff(castDest, src)
+  return if diff == high(int) or (pointers > 1 and diff != 0):
       convNotLegal
     else:
       convOK
@@ -101,13 +104,15 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
     return
   var d = skipTypes(castDest, abstractVar)
   var s = skipTypes(src, abstractVar-{tyTypeDesc})
+  var pointers = 0
   while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind):
     d = d.lastSon
     s = s.lastSon
+    inc pointers
   if d == nil:
     result = convNotLegal
   elif d.kind == tyObject and s.kind == tyObject:
-    result = checkConversionBetweenObjects(d, s)
+    result = checkConversionBetweenObjects(d, s, pointers)
   elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and
       (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes):
     # accept conversion between integral types
@@ -186,17 +191,17 @@ proc semConv(c: PContext, n: PNode): PNode =
     case status
     of convOK:
       # handle SomeProcType(SomeGenericProc)
-      # XXX: This needs fixing. checkConvertible uses typeRel internally, but
-      # doesn't bother to perform the work done in paramTypeMatchAux/fitNode
-      # so we are redoing the typeRel work here. Why does semConv exist as a
-      # separate proc from fitNode?
       if op.kind == nkSym and op.sym.isGenericRoutine:
         result.sons[1] = fitNode(c, result.typ, result.sons[1])
+      elif op.kind == nkPar and targetType.kind == tyTuple:
+        op = fitNode(c, targetType, op)
     of convNotNeedeed:
       message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
     of convNotLegal:
-      localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)%
-        [op.typ.typeToString, result.typ.typeToString])
+      result = fitNode(c, result.typ, result.sons[1])
+      if result == nil:
+        localError(n.info, errGenerated, msgKindToString(errIllegalConvFromXtoY)%
+          [op.typ.typeToString, result.typ.typeToString])
   else:
     for i in countup(0, sonsLen(op) - 1):
       let it = op.sons[i]
@@ -206,7 +211,7 @@ proc semConv(c: PContext, n: PNode): PNode =
         styleCheckUse(n.info, it.sym)
         markIndirect(c, it.sym)
         return it
-    localError(n.info, errUseQualifier, op.sons[0].sym.name.s)
+    errorUseQualifier(c, n.info, op.sons[0].sym)
 
 proc semCast(c: PContext, n: PNode): PNode =
   ## Semantically analyze a casting ("cast[type](param)")
@@ -594,6 +599,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
   result = n
   if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
   var callee = n.sons[0].sym
+  # workaround for bug #537 (overly aggressive inlining leading to
+  # wrong NimNode semantics):
+  if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return
 
   # constant folding that is necessary for correctness of semantic pass:
   if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil:
@@ -710,25 +718,60 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
       initCandidate(c, result, t)
       matches(c, n, nOrig, result)
 
+proc bracketedMacro(n: PNode): PSym =
+  if n.len >= 1 and n[0].kind == nkSym:
+    result = n[0].sym
+    if result.kind notin {skMacro, skTemplate}:
+      result = nil
+
+proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym;
+                       flags: TExprFlags): PNode =
+  # We received untransformed bracket expression coming from macroOrTmpl[].
+  # Transform it to macro or template call, where first come normal
+  # arguments, next come generic template arguments.
+  var sons = newSeq[PNode]()
+  sons.add inner.sons[0]
+  # Normal arguments:
+  for i in 1..<outer.len:
+    sons.add outer.sons[i]
+  # Generic template arguments from bracket expression:
+  for i in 1..<inner.len:
+    sons.add inner.sons[i]
+  shallowCopy(outer.sons, sons)
+  # FIXME: Shouldn't we check sfImmediate and call semDirectOp?
+  # However passing to semDirectOp doesn't work here.
+  case s.kind
+  of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
+  of skTemplate: result = semTemplateExpr(c, outer, s, flags)
+  else: assert(false)
+  return
+
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = nil
   checkMinSonsLen(n, 1)
   var prc = n.sons[0]
   if n.sons[0].kind == nkDotExpr:
     checkSonsLen(n.sons[0], 2)
-    n.sons[0] = semFieldAccess(c, n.sons[0])
-    if n.sons[0].kind == nkDotCall:
+    let n0 = semFieldAccess(c, n.sons[0])
+    if n0.kind == nkDotCall:
       # it is a static call!
-      result = n.sons[0]
+      result = n0
       result.kind = nkCall
       result.flags.incl nfExplicitCall
       for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
       return semExpr(c, result, flags)
+    else:
+      n.sons[0] = n0
   else:
-    n.sons[0] = semExpr(c, n.sons[0])
+    n.sons[0] = semExpr(c, n.sons[0], {efInCall})
     let t = n.sons[0].typ
     if t != nil and t.kind == tyVar:
       n.sons[0] = newDeref(n.sons[0])
+    elif n.sons[0].kind == nkBracketExpr:
+      let s = bracketedMacro(n.sons[0])
+      if s != nil:
+        return semBracketedMacro(c, n, n.sons[0], s, flags)
+
   let nOrig = n.copyTree
   semOpAux(c, n)
   var t: PType = nil
@@ -958,8 +1001,20 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       else: result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
-  of skMacro: result = semMacroExpr(c, n, n, s, flags)
-  of skTemplate: result = semTemplateExpr(c, n, s, flags)
+  of skMacro:
+    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
+      markUsed(n.info, s)
+      styleCheckUse(n.info, s)
+      result = newSymNode(s, n.info)
+    else:
+      result = semMacroExpr(c, n, n, s, flags)
+  of skTemplate:
+    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
+      markUsed(n.info, s)
+      styleCheckUse(n.info, s)
+      result = newSymNode(s, n.info)
+    else:
+      result = semTemplateExpr(c, n, s, flags)
   of skParam:
     markUsed(n.info, s)
     styleCheckUse(n.info, s)
@@ -1011,8 +1066,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     result = newSymNode(s, n.info)
     result.typ = makeTypeDesc(c, s.typ)
   of skField:
-    if c.p != nil and c.p.selfSym != nil:
-      var ty = skipTypes(c.p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef})
+    var p = c.p
+    while p != nil and p.selfSym == nil:
+      p = p.next
+    if p != nil and p.selfSym != nil:
+      var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef})
       while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
       var check: PNode = nil
       if ty.kind == tyObject:
@@ -1025,7 +1083,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
             markUsed(n.info, f)
             styleCheckUse(n.info, f)
             result = newNodeIT(nkDotExpr, n.info, f.typ)
-            result.add makeDeref(newSymNode(c.p.selfSym))
+            result.add makeDeref(newSymNode(p.selfSym))
             result.add newSymNode(f) # we now have the correct field
             if check != nil:
               check.sons[0] = result
@@ -1051,7 +1109,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # here at all!
   #if isSymChoice(n.sons[1]): return
 
-  var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
+  var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
   if s != nil:
     if s.kind in OverloadableSyms:
       result = symChoice(c, n, s, scClosed)
@@ -1186,7 +1244,9 @@ 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], {efNoProcvarCheck})
+  # make sure we don't evaluate generic macros/templates
+  n.sons[0] = semExprWithType(c, n.sons[0],
+                              {efNoProcvarCheck, efNoEvaluateGeneric})
   let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
   case arr.kind
   of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
@@ -1229,12 +1289,30 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     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, skIterator}:
-      # 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)
+    if s != nil:
+      case s.kind
+      of skProc, skMethod, skConverter, skIterator:
+        # type parameters: partial generic specialization
+        n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
+        result = explicitGenericInstantiation(c, n, s)
+      of skMacro, skTemplate:
+        if efInCall in flags:
+          # We are processing macroOrTmpl[] in macroOrTmpl[](...) call.
+          # Return as is, so it can be transformed into complete macro or
+          # template call in semIndirectOp caller.
+          result = n
+        else:
+          # We are processing macroOrTmpl[] not in call. Transform it to the
+          # macro or template call with generic arguments here.
+          n.kind = nkCall
+          case s.kind
+          of skMacro: result = semMacroExpr(c, n, n, s, flags)
+          of skTemplate: result = semTemplateExpr(c, n, s, flags)
+          else: discard
+      of skType:
+        result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
+      else:
+        c.p.bracketExpr = n.sons[0]
     else:
       c.p.bracketExpr = n.sons[0]
 
@@ -1287,7 +1365,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
       n.sons[1] = takeImplicitAddr(c, ri)
       x.typ.flags.incl tfVarIsPtr
 
-template resultTypeIsInferrable(typ: PType): expr =
+template resultTypeIsInferrable(typ: PType): untyped =
   typ.isMetaType and typ.kind != tyTypeDesc
 
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
@@ -1313,15 +1391,16 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
     # --> `[]=`(a, i, x)
     let oldBracketExpr = c.p.bracketExpr
     a = semSubscript(c, a, {efLValue})
-    if a == nil and mode != noOverloadedSubscript:
+    if a == nil:
       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
+      if mode == noOverloadedSubscript:
+        bracketNotFoundError(c, result)
+        return n
+      else:
+        result = semExprNoType(c, result)
+        c.p.bracketExpr = oldBracketExpr
+        return result
     c.p.bracketExpr = oldBracketExpr
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
@@ -1557,8 +1636,8 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo,
   result.flags = {sfGenSym}
 
 proc semExpandToAst(c: PContext, n: PNode): PNode =
-  var macroCall = n[1]
-  var expandedSym = expectMacroOrTemplateCall(c, macroCall)
+  let macroCall = n[1]
+  let expandedSym = expectMacroOrTemplateCall(c, macroCall)
   if expandedSym.kind == skError: return n
 
   macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
@@ -1566,11 +1645,12 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
   styleCheckUse(n.info, expandedSym)
 
   for i in countup(1, macroCall.len-1):
+    #if macroCall.sons[0].typ.sons[i].kind != tyExpr:
     macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
 
   # Preserve the magic symbol in order to be handled in evals.nim
   internalAssert n.sons[0].sym.magic == mExpandToAst
-  #n.typ = getSysSym("PNimrodNode").typ # expandedSym.getReturnType
+  #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
   n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
           else: sysTypeFromName"PNimrodNode"
   result = n
@@ -1810,8 +1890,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # the correct branch. Otherwise the AST will be passed through semStmt.
   result = nil
 
-  template setResult(e: expr) =
-    if semCheck: result = semStmt(c, e) # do not open a new scope!
+  template setResult(e: untyped) =
+    if semCheck: result = semExpr(c, e) # do not open a new scope!
     else: result = e
 
   # Check if the node is "when nimvm"
@@ -1820,6 +1900,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # else:
   #   ...
   var whenNimvm = false
+  var typ = commonTypeBegin
   if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and
       n.sons[1].kind == nkElse:
     let exprNode = n.sons[0].sons[0]
@@ -1836,7 +1917,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       checkSonsLen(it, 2)
       if whenNimvm:
         if semCheck:
-          it.sons[1] = semStmt(c, it.sons[1])
+          it.sons[1] = semExpr(c, it.sons[1])
+          typ = commonType(typ, it.sons[1].typ)
         result = n # when nimvm is not elimited until codegen
       else:
         var e = semConstExpr(c, it.sons[0])
@@ -1850,12 +1932,14 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       checkSonsLen(it, 1)
       if result == nil or whenNimvm:
         if semCheck:
-          it.sons[0] = semStmt(c, it.sons[0])
+          it.sons[0] = semExpr(c, it.sons[0])
+          typ = commonType(typ, it.sons[0].typ)
         if result == nil:
           result = it.sons[0]
     else: illFormedAst(n)
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
+  if whenNimvm: result.typ = typ
   # The ``when`` statement implements the mechanism for platform dependent
   # code. Thus we try to ensure here consistent ID allocation after the
   # ``when`` statement.
@@ -2132,7 +2216,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   if nfSem in n.flags: return
   case n.kind
   of nkIdent, nkAccQuoted:
-    var s = lookUp(c, n)
+    let checks = if efNoEvaluateGeneric in flags: {checkUndeclared}
+                 else: {checkUndeclared, checkModule, checkAmbiguity}
+    var s = qualifiedLookUp(c, n, checks)
     if c.inTypeClass == 0: semCaptureSym(s, c.p.owner)
     result = semSym(c, n, s, flags)
     if s.kind in {skProc, skMethod, skConverter, skIterator}:
@@ -2220,7 +2306,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
         elif n.len == 1:
           result = semObjConstr(c, n, flags)
         elif contains(c.ambiguousSymbols, s.id):
-          localError(n.info, errUseQualifier, s.name.s)
+          errorUseQualifier(c, n.info, s)
         elif s.magic == mNone: result = semDirectOp(c, n, flags)
         else: result = semMagic(c, n, s, flags)
       of skProc, skMethod, skConverter, skIterator:
@@ -2336,7 +2422,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "from")
     result = evalFrom(c, n)
   of nkIncludeStmt:
-    if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include")
+    #if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "include")
     result = evalInclude(c, n)
   of nkExportStmt, nkExportExceptStmt:
     if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "export")
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 5fe4e3299..02f238ae6 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -143,7 +143,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
   const ordIntLit = {nkIntLit..nkUInt64Lit}
   result = n.typ
 
-  template commutativeOp(opr: expr) {.immediate.} =
+  template commutativeOp(opr: untyped) =
     let a = n.sons[1]
     let b = n.sons[2]
     if isIntRangeOrLit(a.typ) and isIntRangeOrLit(b.typ):
@@ -151,7 +151,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
                          opr(pickMinInt(a), pickMinInt(b)),
                          opr(pickMaxInt(a), pickMaxInt(b)))
 
-  template binaryOp(opr: expr) {.immediate.} =
+  template binaryOp(opr: untyped) =
     let a = n.sons[1]
     let b = n.sons[2]
     if isIntRange(a.typ) and b.kind in {nkIntLit..nkUInt64Lit}:
@@ -286,13 +286,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mNot: result = newIntNodeT(1 - getInt(a), n)
   of mCard: result = newIntNodeT(nimsets.cardSet(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)
   of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
-  of mLengthSeq, mLengthOpenArray, mXLenSeq:
-    if a.kind == nkNilLit: result = newIntNodeT(0, n)
-    else: result = newIntNodeT(sonsLen(a), n) # BUGFIX
+  of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
+    if a.kind == nkNilLit:
+      result = newIntNodeT(0, n)
+    elif a.kind in {nkStrLit..nkTripleStrLit}:
+      result = newIntNodeT(len a.strVal, n)
+    else:
+      result = newIntNodeT(sonsLen(a), n) # BUGFIX
   of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
   of mToFloat, mToBiggestFloat:
     result = newFloatNodeT(toFloat(int(getInt(a))), n)
@@ -419,7 +420,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
                                            int(getOrdValue(c))), n)
   of mFloatToStr: result = newStrNodeT($getFloat(a), n)
-  of mCStrToStr, mCharToStr: result = newStrNodeT(getStrOrChar(a), n)
+  of mCStrToStr, mCharToStr:
+    if a.kind == nkBracket:
+      var s = ""
+      for b in a.sons:
+        s.add b.getStrOrChar
+      result = newStrNodeT(s, n)
+    else:
+      result = newStrNodeT(getStrOrChar(a), n)
   of mStrToStr: result = a
   of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
   of mArrToSeq:
@@ -482,7 +490,7 @@ proc leValueConv(a, b: PNode): bool =
   of nkCharLit..nkUInt64Lit:
     case b.kind
     of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
-    of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal)
+    of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
     else: internalError(a.info, "leValueConv")
   of nkFloatLit..nkFloat128Lit:
     case b.kind
@@ -627,14 +635,20 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
       of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
       of mCompileTime: result = newStrNodeT(times.getClockStr(), n)
       of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
-      of mHostOS: result = newStrNodeT(toLower(platform.OS[targetOS].name), n)
-      of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLower, n)
+      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
+      of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)
       of mAppType: result = getAppType(n)
       of mNaN: result = newFloatNodeT(NaN, n)
       of mInf: result = newFloatNodeT(Inf, n)
       of mNegInf: result = newFloatNodeT(NegInf, n)
+      of mIntDefine:
+        if isDefined(s.name):
+          result = newIntNodeT(lookupSymbol(s.name).parseInt, n)
+      of mStrDefine:
+        if isDefined(s.name):
+          result = newStrNodeT(lookupSymbol(s.name), n)
       else:
-        if sfFakeConst notin s.flags: result = copyTree(s.ast)
+        result = copyTree(s.ast)
     of {skProc, skMethod}:
       result = n
     of skType:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 6651de78e..b8451865e 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -47,8 +47,11 @@ proc semGenericStmtScope(c: PContext, n: PNode,
   result = semGenericStmt(c, n, flags, ctx)
   closeScope(c)
 
-template macroToExpand(s: expr): expr =
-  s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags)
+template macroToExpand(s): untyped =
+  s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
+
+template macroToExpandSym(s): untyped =
+  s.kind in {skMacro, skTemplate} and (s.typ.len == 1)
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
                           ctx: var GenericCtx): PNode =
@@ -61,14 +64,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skProc, skMethod, skIterator, skConverter, skModule:
     result = symChoice(c, n, s, scOpen)
   of skTemplate:
-    if macroToExpand(s):
+    if macroToExpandSym(s):
       styleCheckUse(n.info, s)
       result = semTemplateExpr(c, n, s, {efNoSemCheck})
       result = semGenericStmt(c, result, {}, ctx)
     else:
       result = symChoice(c, n, s, scOpen)
   of skMacro:
-    if macroToExpand(s):
+    if macroToExpandSym(s):
       styleCheckUse(n.info, s)
       result = semMacroExpr(c, n, n, s, {efNoSemCheck})
       result = semGenericStmt(c, result, {}, ctx)
@@ -124,7 +127,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
   assert n.kind == nkDotExpr
   semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
 
-  let luf = if withinMixin notin flags: {checkUndeclared} else: {}
+  let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
@@ -199,24 +202,25 @@ proc semGenericStmt(c: PContext, n: PNode,
     if s != nil:
       incl(s.flags, sfUsed)
       mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
-      let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen
+      let sc = symChoice(c, fn, s,
+            if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
       case s.kind
       of skMacro:
-        if macroToExpand(s):
+        if macroToExpand(s) and sc.safeLen <= 1:
           styleCheckUse(fn.info, s)
           result = semMacroExpr(c, n, n, s, {efNoSemCheck})
-          result = semGenericStmt(c, result, {}, ctx)
+          result = semGenericStmt(c, result, flags, ctx)
         else:
-          n.sons[0] = symChoice(c, fn, s, scOption)
+          n.sons[0] = sc
           result = n
         mixinContext = true
       of skTemplate:
-        if macroToExpand(s):
+        if macroToExpand(s) and sc.safeLen <= 1:
           styleCheckUse(fn.info, s)
           result = semTemplateExpr(c, n, s, {efNoSemCheck})
-          result = semGenericStmt(c, result, {}, ctx)
+          result = semGenericStmt(c, result, flags, ctx)
         else:
-          n.sons[0] = symChoice(c, fn, s, scOption)
+          n.sons[0] = sc
           result = n
         # BUGFIX: we must not return here, we need to do first phase of
         # symbol lookup. Also since templates and macros can do scope injections
@@ -227,7 +231,7 @@ proc semGenericStmt(c: PContext, n: PNode,
         # Leave it as an identifier.
         discard
       of skProc, skMethod, skIterator, skConverter, skModule:
-        result.sons[0] = symChoice(c, fn, s, scOption)
+        result.sons[0] = sc
         # 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 == "^":
@@ -426,7 +430,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
     n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
     var body: PNode
-    if n.sons[namePos].kind == nkSym: body = n.sons[namePos].sym.getBody
+    if n.sons[namePos].kind == nkSym:
+      let s = n.sons[namePos].sym
+      if sfGenSym in s.flags and s.ast == nil:
+        body = n.sons[bodyPos]
+      else:
+        body = s.getBody
     else: body = n.sons[bodyPos]
     n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
     closeScope(c)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 4a45dee9d..460db4f7c 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -43,9 +43,11 @@ proc rawHandleSelf(c: PContext; owner: PSym) =
       if arg.name.id == c.selfName.id:
         c.p.selfSym = arg
         arg.flags.incl sfIsSelf
-        let t = c.p.selfSym.typ.skipTypes(abstractPtrs)
-        if t.kind == tyObject:
+        var t = c.p.selfSym.typ.skipTypes(abstractPtrs)
+        while t.kind == tyObject:
           addObjFieldsToLocalScope(c, t.n)
+          if t.sons[0] == nil: break
+          t = t.sons[0].skipTypes(abstractPtrs)
 
 proc pushProcCon*(c: PContext; owner: PSym) =
   rawPushProcCon(c, owner)
@@ -111,9 +113,9 @@ proc removeDefaultParamValues(n: PNode) =
         # not possible... XXX We don't solve this issue here.
         a.sons[L-1] = ast.emptyNode
 
-proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
+proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
   # we need to create a fresh set of gensym'ed symbols:
-  if n.kind == nkSym and sfGenSym in n.sym.flags:
+  if n.kind == nkSym and sfGenSym in n.sym.flags and n.sym.owner == orig:
     let s = n.sym
     var x = PSym(idTableGet(symMap, s))
     if x == nil:
@@ -122,7 +124,7 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
       idTablePut(symMap, s, x)
     n.sym = x
   else:
-    for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, symMap)
+    for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, orig, symMap)
 
 proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
 
@@ -137,7 +139,7 @@ proc addProcDecls(c: PContext, fn: PSym) =
 
   maybeAddResult(c, fn, fn.ast)
 
-proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
+proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
   if n.sons[bodyPos].kind != nkEmpty:
     inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
@@ -149,7 +151,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result: PSym) =
         let param = params[i].sym
         if sfGenSym in param.flags:
           idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
-    freshGenSyms(b, result, symMap)
+    freshGenSyms(b, result, orig, symMap)
     b = semProcBody(c, b)
     b = hloBody(c, b)
     n.sons[bodyPos] = transformBody(c.module, b, result)
@@ -165,7 +167,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       openScope(c)
       var n = oldPrc.ast
       n.sons[bodyPos] = copyTree(s.getBody)
-      instantiateBody(c, n, nil, oldPrc)
+      instantiateBody(c, n, nil, oldPrc, s)
       closeScope(c)
       popInfoContext()
 
@@ -203,7 +205,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   # The solution would be to move this logic into semtypinst, but
   # at this point semtypinst have to become part of sem, because it
   # will need to use openScope, addDecl, etc.
-  addDecl(c, prc)
+  #addDecl(c, prc)
 
   pushInfoContext(info)
   var cl = initTypeVars(c, pt, info, nil)
@@ -312,7 +314,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
       pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
     if isNil(n.sons[bodyPos]):
       n.sons[bodyPos] = copyTree(fn.getBody)
-    instantiateBody(c, n, fn.typ.n, result)
+    instantiateBody(c, n, fn.typ.n, result, fn)
     sideEffectsCheck(c, result)
     paramsTypeCheck(c, result.typ)
   else:
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 150680af7..6cd5c4a3c 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -12,25 +12,26 @@
 
 import ast, astalgo, msgs, types
 
-proc ithField(n: PNode, field: int): PSym =
+proc ithField(n: PNode, field: var int): PSym =
   result = nil
   case n.kind
   of nkRecList:
     for i in countup(0, sonsLen(n) - 1):
-      result = ithField(n.sons[i], field-i)
+      result = ithField(n.sons[i], field)
       if result != nil: return
   of nkRecCase:
     if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
-    result = ithField(n.sons[0], field-1)
+    result = ithField(n.sons[0], field)
     if result != nil: return
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
-        result = ithField(lastSon(n.sons[i]), field-1)
+        result = ithField(lastSon(n.sons[i]), field)
         if result != nil: return
       else: internalError(n.info, "ithField(record case branch)")
   of nkSym:
     if field == 0: result = n.sym
+    else: dec(field)
   else: discard
 
 proc annotateType*(n: PNode, t: PType) =
@@ -39,10 +40,13 @@ proc annotateType*(n: PNode, t: PType) =
   # to not to skip tyGenericInst
   case n.kind
   of nkObjConstr:
+    let x = t.skipTypes(abstractPtrs)
     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
+      var j = i-1
+      let field = x.n.ithField(j)
+      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)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 1a70e4a12..cbe9bc176 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -42,7 +42,10 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   result = semSubscript(c, result, flags)
   c.p.bracketExpr = oldBracketExpr
   if result.isNil:
-    localError(n.info, "could not resolve: " & $n)
+    let x = copyTree(n)
+    x.sons[0] = newIdentNode(getIdent"[]", n.info)
+    bracketNotFoundError(c, x)
+    #localError(n.info, "could not resolve: " & $n)
     result = n
 
 proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
@@ -111,8 +114,12 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
 
 proc semOrd(c: PContext, n: PNode): PNode =
   result = n
-  result.typ = makeRangeType(c, firstOrd(n.sons[1].typ),
-                                lastOrd(n.sons[1].typ), n.info)
+  let parType = n.sons[1].typ
+  if isOrdinalType(parType) or parType.kind == tySet:
+    result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
+  else:
+    localError(n.info, errOrdinalTypeExpected)
+    result.typ = errorType(c)
 
 proc semBindSym(c: PContext, n: PNode): PNode =
   result = copyNode(n)
@@ -130,7 +137,7 @@ proc semBindSym(c: PContext, n: PNode): PNode =
     return errorNode(c, n)
 
   let id = newIdentNode(getIdent(sl.strVal), n.info)
-  let s = qualifiedLookUp(c, id)
+  let s = qualifiedLookUp(c, id, {checkUndeclared})
   if s != nil:
     # we need to mark all symbols:
     var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
@@ -203,7 +210,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
         result = n.sons[1]
       else:
         result = newNodeIT(nkCall, n.info, getSysType(tyInt))
-        result.add newSymNode(getSysMagic("-", mSubI), n.info)
+        let subi = getSysMagic("-", mSubI)
+        #echo "got ", typeToString(subi.typ)
+        result.add newSymNode(subi, n.info)
         result.add lenExprB
         result.add n.sons[1]
   of mPlugin:
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index b04ba4657..90c1a315a 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -74,8 +74,6 @@ type
     currentSpawnId: int
     inLoop: int
 
-let opSlice = createMagic("slice", mSlice)
-
 proc initAnalysisCtx(): AnalysisCtx =
   result.locals = @[]
   result.slices = @[]
@@ -123,7 +121,7 @@ proc checkLocal(c: AnalysisCtx; n: PNode) =
   else:
     for i in 0 .. <n.safeLen: checkLocal(c, n.sons[i])
 
-template `?`(x): expr = x.renderTree
+template `?`(x): untyped = x.renderTree
 
 proc checkLe(c: AnalysisCtx; a, b: PNode) =
   case proveLe(c.guards, a, b)
@@ -262,7 +260,7 @@ proc min(a, b: PNode): PNode =
 
 proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
 
-template pushSpawnId(c: expr, body: stmt) {.immediate, dirty.} =
+template pushSpawnId(c, body) {.dirty.} =
   inc c.spawns
   let oldSpawnId = c.currentSpawnId
   c.currentSpawnId = c.spawns
@@ -399,7 +397,9 @@ proc transformSlices(n: PNode): PNode =
     let op = n[0].sym
     if op.name.s == "[]" and op.fromSystem:
       result = copyNode(n)
-      result.add opSlice.newSymNode
+      let opSlice = newSymNode(createMagic("slice", mSlice))
+      opSlice.typ = getSysType(tyInt)
+      result.add opSlice
       result.add n[1]
       let slice = n[2].skipStmtList
       result.add slice[1]
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index c3a9e01a0..b12ab5e96 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -151,7 +151,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
     guardGlobal(a, n, g)
 
 proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
-  template compileToCpp(a): expr =
+  template compileToCpp(a): untyped =
     gCmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
   if a.inTryStmt > 0 and not compileToCpp(a):
     incl(s.flags, sfVolatile)
@@ -459,7 +459,7 @@ proc documentRaises*(n: PNode) =
     if p4 != nil: n.sons[pragmasPos].add p4
     if p5 != nil: n.sons[pragmasPos].add p5
 
-template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
+template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
 proc importedFromC(n: PNode): bool =
   # when imported from C, we assume GC-safety.
@@ -505,7 +505,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
       # addr(x[]) can't be proven, but addr(x) can:
       if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
     elif (n.kind == nkSym and n.sym.kind in routineKinds) or
-         n.kind in procDefs+{nkObjConstr}:
+         n.kind in procDefs+{nkObjConstr, nkBracket}:
       # 'p' is not nil obviously:
       return
     case impliesNotNil(tracked.guards, n)
@@ -532,7 +532,7 @@ proc isOwnedProcVar(n: PNode; owner: PSym): bool =
 proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
   let a = skipConvAndClosure(n)
   let op = a.typ
-  if op != nil and op.kind == tyProc and n.kind != nkNilLit:
+  if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
     internalAssert op.n.sons[0].kind == nkEffectList
     var effectList = op.n.sons[0]
     let s = n.skipConv
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 910267e57..5f9989ea2 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -17,7 +17,8 @@ proc semDiscard(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 1)
   if n.sons[0].kind != nkEmpty:
     n.sons[0] = semExprWithType(c, n.sons[0])
-    if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard)
+    if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone:
+      localError(n.info, errInvalidDiscard)
 
 proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   result = n
@@ -137,7 +138,7 @@ proc fixNilType(n: PNode) =
 
 proc discardCheck(c: PContext, result: PNode) =
   if c.inTypeClass > 0: return
-  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
+  if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
     if result.kind == nkNilLit:
       result.typ = nil
       message(result.info, warnNilStatement)
@@ -303,8 +304,14 @@ proc semTry(c: PContext, n: PNode): PNode =
 proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
   result = fitNode(c, typ, n)
   if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
-    changeType(result.sons[1], typ, check=true)
-    result = result.sons[1]
+    let r1 = result.sons[1]
+    if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
+      result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
+      result.info = n.info
+      result.typ = typ
+    else:
+      changeType(r1, typ, check=true)
+      result = r1
   elif not sameType(result.typ, typ):
     changeType(result, typ, check=false)
 
@@ -336,7 +343,7 @@ proc checkNilable(v: PSym) =
       {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
     if v.ast.isNil:
       message(v.info, warnProveInit, v.name.s)
-    elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
+    elif tfNeedsInit in v.typ.flags and tfNotNil notin v.ast.typ.flags:
       message(v.info, warnProveInit, v.name.s)
 
 include semasgn
@@ -410,6 +417,13 @@ proc semUsing(c: PContext; n: PNode): PNode =
     if a.sons[length-1].kind != nkEmpty:
       localError(a.info, "'using' sections cannot contain assignments")
 
+proc hasEmpty(typ: PType): bool =
+  if typ.kind in {tySequence, tyArray, tySet}:
+    result = typ.lastSon.kind == tyEmpty
+  elif typ.kind == tyTuple:
+    for s in typ.sons:
+      result = result or hasEmpty(s)
+
 proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
   var b: PNode
   result = copyNode(n)
@@ -444,10 +458,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           #changeType(def.skipConv, typ, check=true)
       else:
         typ = skipIntLit(def.typ)
-        if typ.kind in {tySequence, tyArray, tySet} and
-           typ.lastSon.kind == tyEmpty:
+        if hasEmpty(typ):
           localError(def.info, errCannotInferTypeOfTheLiteral,
-                     ($typ.kind).substr(2).toLower)
+                     ($typ.kind).substr(2).toLowerAscii)
     else:
       def = ast.emptyNode
       if symkind == skLet: localError(a.info, errLetNeedsInit)
@@ -535,7 +548,7 @@ proc semConst(c: PContext, n: PNode): PNode =
       localError(a.sons[2].info, errConstExprExpected)
       continue
     if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
-      localError(a.info, errXisNoType, typeToString(typ))
+      localError(a.info, "invalid type for const: " & typeToString(typ))
       continue
     v.typ = typ
     v.ast = def               # no need to copy
@@ -668,7 +681,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     let name = a.sons[0]
     var s: PSym
     if name.kind == nkDotExpr:
-      s = qualifiedLookUp(c, name)
+      s = qualifiedLookUp(c, name, {checkUndeclared, checkModule})
       if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
         localError(name.info, "only .partial objects can be extended")
     else:
@@ -710,7 +723,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       a.sons[1] = s.typ.n
       s.typ.size = -1 # could not be computed properly
       # we fill it out later. For magic generics like 'seq', it won't be filled
-      # so we use tyEmpty instead of nil to not crash for strange conversions
+      # so we use tyNone instead of nil to not crash for strange conversions
       # like: mydata.seq
       rawAddSon(s.typ, newTypeS(tyNone, c))
       s.ast = a
@@ -778,11 +791,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
     var s = a.sons[0].sym
     # compute the type's size and check for illegal recursions:
     if a.sons[1].kind == nkEmpty:
-      if a.sons[2].kind in {nkSym, nkIdent, nkAccQuoted}:
+      var x = a[2]
+      while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
+        x = x.lastSon
+      if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
+          s.typ.kind notin {tyObject, tyEnum}:
         # type aliases are hard:
-        #MessageOut('for type ' + typeToString(s.typ));
-        var t = semTypeNode(c, a.sons[2], nil)
-        if t.kind in {tyObject, tyEnum}:
+        var t = semTypeNode(c, x, nil)
+        assert t != nil
+        if t.kind in {tyObject, tyEnum, tyDistinct}:
+          assert s.typ != nil
           assignType(s.typ, t)
           s.typ.id = t.id     # same id
       checkConstructedType(s.info, s.typ)
@@ -924,6 +942,17 @@ proc semProcAnnotation(c: PContext, prc: PNode;
       pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
     return
 
+proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
+  let orig = n.sons[genericParamsPos]
+  # we keep the original params around for better error messages, see
+  # issue https://github.com/nim-lang/Nim/issues/1713
+  result = semGenericParamList(c, orig)
+  if n.sons[miscPos].kind == nkEmpty:
+    n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig)
+  else:
+    n.sons[miscPos].sons[1] = orig
+  n.sons[genericParamsPos] = result
+
 proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # XXX semProcAux should be good enough for this now, we will eventually
   # remove semLambda
@@ -942,8 +971,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
-    n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
-    gp = n.sons[genericParamsPos]
+    gp = setGenericParamsMisc(c, n)
   else:
     gp = newNodeI(nkGenericParams, n.info)
 
@@ -1165,8 +1193,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
-    n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
-    gp = n.sons[genericParamsPos]
+    gp = setGenericParamsMisc(c, n)
   else:
     gp = newNodeI(nkGenericParams, n.info)
   # process parameters:
@@ -1231,6 +1258,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     pushOwner(s)
   s.options = gOptions
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
+  if s.name.s[0] in {'.', '('}:
+    if s.name.s in [".", ".()", ".=", "()"] and not experimentalMode(c):
+      message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
   if n.sons[bodyPos].kind != nkEmpty:
     # for DLL generation it is annoying to check for sfImportc!
     if sfBorrow in s.flags:
@@ -1245,7 +1275,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
 
       c.p.wasForwarded = proto != nil
       maybeAddResult(c, s, n)
-      if sfImportc notin s.flags:
+      if lfDynamicLib notin s.loc.flags:
         # no semantic checking for importc:
         let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
         # unfortunately we cannot skip this step when in 'system.compiles'
@@ -1285,6 +1315,7 @@ proc determineType(c: PContext, s: PSym) =
 
 proc semIterator(c: PContext, n: PNode): PNode =
   # gensym'ed iterator?
+  let isAnon = n[namePos].kind == nkEmpty
   if n[namePos].kind == nkSym:
     # gensym'ed iterators might need to become closure iterators:
     n[namePos].sym.owner = getCurrOwner()
@@ -1294,6 +1325,8 @@ proc semIterator(c: PContext, n: PNode): PNode =
   var t = s.typ
   if t.sons[0] == nil and s.typ.callConv != ccClosure:
     localError(n.info, errXNeedsReturnType, "iterator")
+  if isAnon and s.typ.callConv == ccInline:
+    localError(n.info, "inline iterators are not first-class / cannot be assigned to variables")
   # iterators are either 'inline' or 'closure'; for backwards compatibility,
   # we require first class iterators to be marked with 'closure' explicitly
   # -- at least for 0.9.2.
@@ -1325,9 +1358,24 @@ proc finishMethod(c: PContext, s: PSym) =
 proc semMethod(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
-
+  # macros can transform methods to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
-  if not isGenericRoutine(s):
+  if isGenericRoutine(s):
+    let tt = s.typ
+    var foundObj = false
+    # we start at 1 for now so that tparsecombnum continues to compile.
+    # XXX Revisit this problem later.
+    for col in countup(1, sonsLen(tt)-1):
+      let t = tt.sons[col]
+      if t != nil and t.kind == tyGenericInvocation:
+        var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, tyGenericInvocation, tyGenericBody})
+        if x.kind == tyObject and t.len-1 == result.sons[genericParamsPos].len:
+          foundObj = true
+          x.methods.safeAdd((col,s))
+    if not foundObj:
+      message(n.info, warnDeprecated, "generic method not attachable to object type")
+  else:
     # why check for the body? bug #2400 has none. Checking for sfForward makes
     # no sense either.
     # and result.sons[bodyPos].kind != nkEmpty:
@@ -1340,6 +1388,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
   checkSonsLen(n, bodyPos + 1)
   result = semProcAux(c, n, skConverter, converterPragmas)
+  # macros can transform converters to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
   var t = s.typ
   if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter")
@@ -1349,6 +1399,8 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
 proc semMacroDef(c: PContext, n: PNode): PNode =
   checkSonsLen(n, bodyPos + 1)
   result = semProcAux(c, n, skMacro, macroPragmas)
+  # macros can transform macros to nothing:
+  if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
   var t = s.typ
   if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index a4498a3ae..20b5071ac 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -51,9 +51,10 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
   var i = 0
   a = initOverloadIter(o, c, n)
   while a != nil:
+    if a.kind != skModule:
+      inc(i)
+      if i > 1: break
     a = nextOverloadIter(o, c, n)
-    inc(i)
-    if i > 1: break
   if i <= 1 and r != scForceOpen:
     # XXX this makes more sense but breaks bootstrapping for now:
     # (s.kind notin routineKinds or s.magic != mNone):
@@ -68,8 +69,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     result = newNodeIT(kind, n.info, newTypeS(tyNone, c))
     a = initOverloadIter(o, c, n)
     while a != nil:
-      incl(a.flags, sfUsed)
-      addSon(result, newSymNode(a, n.info))
+      if a.kind != skModule:
+        incl(a.flags, sfUsed)
+        addSon(result, newSymNode(a, n.info))
       a = nextOverloadIter(o, c, n)
 
 proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
@@ -80,7 +82,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
     # the same symbol!
     # This is however not true anymore for hygienic templates as semantic
     # processing for them changes the symbol table...
-    let s = qualifiedLookUp(c, a)
+    let s = qualifiedLookUp(c, a, {checkUndeclared})
     if s != nil:
       # we need to mark all symbols:
       let sc = symChoice(c, n, s, scClosed)
@@ -577,13 +579,16 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   else:
     gp = newNodeI(nkGenericParams, n.info)
   # process parameters:
+  var allUntyped = true
   if n.sons[paramsPos].kind != nkEmpty:
     semParamList(c, n.sons[paramsPos], gp, s)
     # a template's parameters are not gensym'ed even if that was originally the
     # case as we determine whether it's a template parameter in the template
     # body by the absence of the sfGenSym flag:
     for i in 1 .. s.typ.n.len-1:
-      s.typ.n.sons[i].sym.flags.excl sfGenSym
+      let param = s.typ.n.sons[i].sym
+      param.flags.excl sfGenSym
+      if param.typ.kind != tyExpr: allUntyped = false
     if sonsLen(gp) > 0:
       if n.sons[genericParamsPos].kind == nkEmpty:
         # we have a list of implicit type parameters:
@@ -599,6 +604,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     s.typ.n = newNodeI(nkFormalParams, n.info)
     rawAddSon(s.typ, newTypeS(tyStmt, c))
     addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
+  if allUntyped: incl(s.flags, sfAllUntyped)
   if n.sons[patternPos].kind != nkEmpty:
     n.sons[patternPos] = semPattern(c, n.sons[patternPos])
   var ctx: TemplCtx
@@ -628,8 +634,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     c.patterns.add(s)
 
 proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
-  template templToExpand(s: expr): expr =
-    s.kind == skTemplate and (s.typ.len == 1 or sfImmediate in s.flags)
+  template templToExpand(s: untyped): untyped =
+    s.kind == skTemplate and (s.typ.len == 1 or sfAllUntyped in s.flags)
 
   proc newParam(c: var TemplCtx, n: PNode, s: PSym): PNode =
     # the param added in the current scope is actually wrong here for
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index ba17cc307..9d00c06ca 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -130,7 +130,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
   if n.len < 1:
     result = newConstraint(c, kind)
   else:
-    let isCall = ord(n.kind in nkCallKinds)
+    let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr})
     let n = if n[0].kind == nkBracket: n[0] else: n
     checkMinSonsLen(n, 1)
     var base = semTypeNode(c, n.lastSon, nil)
@@ -370,7 +370,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   result.n = newNodeI(nkRecList, n.info)
   var check = initIntSet()
   var counter = 0
-  for i in countup(0, sonsLen(n) - 1):
+  for i in countup(ord(n.kind == nkBracketExpr), sonsLen(n) - 1):
     var a = n.sons[i]
     if (a.kind != nkIdentDefs): illFormedAst(a)
     checkMinSonsLen(a, 3)
@@ -393,20 +393,24 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
         addSon(result.n, newSymNode(field))
         addSonSkipIntLit(result, typ)
       if gCmd == cmdPretty: styleCheckDef(a.sons[j].info, field)
+  if result.n.len == 0: result.n = nil
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym =
   # identifier with visibility
   if n.kind == nkPostfix:
-    if sonsLen(n) == 2 and n.sons[0].kind == nkIdent:
+    if sonsLen(n) == 2:
       # for gensym'ed identifiers the identifier may already have been
       # transformed to a symbol and we need to use that here:
       result = newSymG(kind, n.sons[1], c)
-      var v = n.sons[0].ident
+      var v = considerQuotedIdent(n.sons[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
-        localError(n.sons[0].info, errInvalidVisibilityX, v.s)
+        if not (sfExported in allowed):
+          localError(n.sons[0].info, errXOnlyAtModuleScope, "export")
+        else:
+          localError(n.sons[0].info, errInvalidVisibilityX, renderTree(n[0]))
     else:
       illFormedAst(n)
   else:
@@ -751,18 +755,18 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     addDecl(c, s)
 
   # XXX: There are codegen errors if this is turned into a nested proc
-  template liftingWalk(typ: PType, anonFlag = false): expr =
+  template liftingWalk(typ: PType, anonFlag = false): untyped =
     liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag)
   #proc liftingWalk(paramType: PType, anon = false): PType =
 
   var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
                    else: nil
 
-  template maybeLift(typ: PType): expr =
+  template maybeLift(typ: PType): untyped =
     let lifted = liftingWalk(typ)
     (if lifted != nil: lifted else: typ)
 
-  template addImplicitGeneric(e: expr): expr =
+  template addImplicitGeneric(e): untyped =
     addImplicitGenericImpl(e, paramTypId)
 
   case paramType.kind:
@@ -942,7 +946,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       if isType: localError(a.info, "':' expected")
       if kind in {skTemplate, skMacro}:
         typ = newTypeS(tyExpr, c)
-    elif skipTypes(typ, {tyGenericInst}).kind == tyEmpty:
+    elif skipTypes(typ, {tyGenericInst}).kind == tyVoid:
       continue
     for j in countup(0, length-3):
       var arg = newSymG(skParam, a.sons[j], c)
@@ -974,7 +978,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   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:
+    if skipTypes(r, {tyGenericInst}).kind != tyVoid:
       # 'auto' as a return type does not imply a generic:
       if r.kind == tyAnything:
         # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
@@ -1035,7 +1039,7 @@ proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
 proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   if s.typ == nil:
     localError(n.info, "cannot instantiate the '$1' $2" %
-                       [s.name.s, ($s.kind).substr(2).toLower])
+                       [s.name.s, ($s.kind).substr(2).toLowerAscii])
     return newOrPrevType(tyError, prev, c)
 
   var t = s.typ
@@ -1094,10 +1098,14 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
         result = instGenericContainer(c, n.info, result,
                                       allowMetaTypes = false)
 
-proc semTypeExpr(c: PContext, n: PNode): PType =
+proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
   var n = semExprWithType(c, n, {efDetermineType})
   if n.typ.kind == tyTypeDesc:
     result = n.typ.base
+    # fix types constructed by macros:
+    if prev != nil and prev.sym != nil and result.sym.isNil:
+      result.sym = prev.sym
+      result.sym.typ = result
   else:
     localError(n.info, errTypeExpected, n.renderTree)
     result = errorType(c)
@@ -1177,7 +1185,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         localError(n.info, errGenerated, "invalid type")
     elif n[0].kind notin nkIdentKinds:
-      result = semTypeExpr(c, n)
+      result = semTypeExpr(c, n, prev)
     else:
       let op = considerQuotedIdent(n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
@@ -1218,7 +1226,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
         result = typExpr.typ
       else:
-        result = semTypeExpr(c, n)
+        result = semTypeExpr(c, n, prev)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
@@ -1241,6 +1249,19 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         result = copyType(result, getCurrOwner(), false)
         for i in countup(1, n.len - 1):
           result.rawAddSon(semTypeNode(c, n.sons[i], nil))
+    of mDistinct:
+      result = newOrPrevType(tyDistinct, prev, c)
+      addSonSkipIntLit(result, semTypeNode(c, n[1], nil))
+    of mVar:
+      result = newOrPrevType(tyVar, prev, c)
+      var base = semTypeNode(c, n.sons[1], nil)
+      if base.kind == tyVar:
+        localError(n.info, errVarVarTypeNotAllowed)
+        base = base.sons[0]
+      addSonSkipIntLit(result, base)
+    of mRef: result = semAnyRef(c, n, tyRef, prev)
+    of mPtr: result = semAnyRef(c, n, tyPtr, prev)
+    of mTuple: result = semTuple(c, n, prev)
     else: result = semGeneric(c, n, s, prev)
   of nkDotExpr:
     var typeExpr = semExpr(c, n)
@@ -1370,10 +1391,7 @@ proc processMagicType(c: PContext, m: PSym) =
     setMagicType(m, tyTypeDesc, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   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
+    setMagicType(m, tyVoid, 0)
   of mArray:
     setMagicType(m, tyArray, 0)
   of mOpenArray:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 7ff33f918..12620d55d 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -162,7 +162,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
     discard
   of nkSym:
     result.sym = replaceTypeVarsS(cl, n.sym)
-    if result.sym.typ.kind == tyEmpty:
+    if result.sym.typ.kind == tyVoid:
       # don't add the 'void' field
       result = newNode(nkRecList, n.info)
   of nkRecWhen:
@@ -289,7 +289,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     # but we already raised an error!
     rawAddSon(result, header.sons[i])
 
-  var newbody = replaceTypeVarsT(cl, lastSon(body))
+  let bbody = lastSon body
+  var newbody = replaceTypeVarsT(cl, bbody)
   cl.skipTypedesc = oldSkipTypedesc
   newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
   result.flags = result.flags + newbody.flags - tfInstClearedFlags
@@ -306,25 +307,31 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     # 'deepCopy' needs to be instantiated for
     # generics *when the type is constructed*:
     newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
-                                            attachedDeepCopy)
+                                            attachedDeepCopy, 1)
   let asgn = newbody.assignment
   if asgn != nil and sfFromGeneric notin asgn.flags:
     # '=' needs to be instantiated for generics when the type is constructed:
     newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
-                                              attachedAsgn)
+                                              attachedAsgn, 1)
+  let methods = skipTypes(bbody, abstractPtrs).methods
+  for col, meth in items(methods):
+    # we instantiate the known methods belonging to that type, this causes
+    # them to be registered and that's enough, so we 'discard' the result.
+    discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
+      attachedAsgn, col)
 
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
   # don't deal with '(): void':
-  if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
+  if t.sons[0] != nil and t.sons[0].kind == tyVoid:
     t.sons[0] = nil
 
   for i in 1 .. <t.sonsLen:
     # don't touch any memory unless necessary
-    if t.sons[i].kind == tyEmpty:
+    if t.sons[i].kind == tyVoid:
       var pos = i
       for j in i+1 .. <t.sonsLen:
-        if t.sons[j].kind != tyEmpty:
+        if t.sons[j].kind != tyVoid:
           t.sons[pos] = t.sons[j]
           t.n.sons[pos] = t.n.sons[j]
           inc pos
@@ -504,6 +511,6 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
   popInfoContext()
 
 template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
-                               t: PType): expr =
+                               t: PType): untyped =
   generateTypeInstance(p, pt, arg.info, t)
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 82f878932..ec429968c 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -22,7 +22,7 @@ type
   TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
-  CandidateErrors* = seq[PSym]
+  CandidateErrors* = seq[(PSym,int)]
   TCandidate* = object
     c*: PContext
     exactMatches*: int       # also misused to prefer iters over procs
@@ -49,6 +49,7 @@ type
                              # a distrinct type
     typedescMatched*: bool
     isNoCall*: bool          # misused for generic type instantiations C[T]
+    mutabilityProblem*: uint8 # tyVar mismatch
     inheritancePenalty: int  # to prefer closest father object type
     errors*: CandidateErrors # additional clarifications to be displayed to the
                              # user if overload resolution fails
@@ -96,8 +97,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
   c.calleeSym = nil
   initIdTable(c.bindings)
 
-proc put(t: var TIdTable, key, val: PType) {.inline.} =
-  idTablePut(t, key, val.skipIntLit)
+proc put(c: var TCandidate, key, val: PType) {.inline.} =
+  idTablePut(c.bindings, key, val.skipIntLit)
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
                     binding: PNode, calleeScope = -1) =
@@ -129,7 +130,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
           bound = makeTypeDesc(ctx, bound)
       else:
         bound = bound.skipTypes({tyTypeDesc})
-      put(c.bindings, formalTypeParam, bound)
+      put(c, formalTypeParam, bound)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
@@ -220,13 +221,14 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  result = a.calleeScope - b.calleeScope
-  if result != 0: return
   # the other way round because of other semantics:
   result = b.inheritancePenalty - a.inheritancePenalty
   if result != 0: return
   # prefer more specialized generic over more general generic:
   result = complexDisambiguation(a.callee, b.callee)
+  # only as a last resort, consider scoping:
+  if result != 0: return
+  result = a.calleeScope - b.calleeScope
 
 proc writeMatches*(c: TCandidate) =
   writeLine(stdout, "exact matches: " & $c.exactMatches)
@@ -242,6 +244,8 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
     for i in 1 .. <arg.len:
       result.add(" | ")
       result.add typeToString(arg[i].typ, prefer)
+  elif arg.typ == nil:
+    result = "void"
   else:
     result = arg.typ.typeToString(prefer)
 
@@ -253,15 +257,16 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
     if n.sons[i].kind == nkExprEqExpr:
       add(result, renderTree(n.sons[i].sons[0]))
       add(result, ": ")
-      if arg.typ.isNil:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
+        # XXX we really need to 'tryExpr' here!
         arg = c.semOperand(c, n.sons[i].sons[1])
         n.sons[i].typ = arg.typ
         n.sons[i].sons[1] = arg
     else:
-      if arg.typ.isNil:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
         arg = c.semOperand(c, n.sons[i])
         n.sons[i] = arg
-    if arg.typ.kind == tyError: return
+    if arg.typ != nil and arg.typ.kind == tyError: return
     add(result, argTypeToString(arg, prefer))
     if i != sonsLen(n) - 1: add(result, ", ")
 
@@ -360,6 +365,50 @@ proc isObjectSubtype(a, f: PType): int =
   if t != nil:
     result = depth
 
+type
+  SkippedPtr = enum skippedNone, skippedRef, skippedPtr
+
+proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
+  var r = t
+  # we're allowed to skip one level of ptr/ref:
+  var ptrs = 0
+  while r != nil:
+    case r.kind
+    of tyGenericInvocation:
+      r = r.sons[0]
+    of tyRef:
+      inc ptrs
+      skipped = skippedRef
+      r = r.lastSon
+    of tyPtr:
+      inc ptrs
+      skipped = skippedPtr
+      r = r.lastSon
+    of tyGenericBody, tyGenericInst:
+      r = r.lastSon
+    else:
+      break
+  if r.kind == tyObject and ptrs <= 1: result = r
+
+proc isGenericSubtype(a, f: PType, d: var int): bool =
+  assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
+  var askip = skippedNone
+  var fskip = skippedNone
+  var t = a.skipToObject(askip)
+  let r = f.skipToObject(fskip)
+  if r == nil: return false
+  var depth = 0
+  # XXX sameObjectType can return false here. Need to investigate
+  # why that is but sameObjectType does way too much work here anyway.
+  while t != nil and r.sym != t.sym and askip == fskip:
+    t = t.sons[0]
+    if t != nil: t = t.skipToObject(askip)
+    else: break
+    inc depth
+  if t != nil and askip == fskip:
+    d = depth
+    result = true
+
 proc minRel(a, b: TTypeRelation): TTypeRelation =
   if a <= b: result = a
   else: result = b
@@ -513,7 +562,7 @@ 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:
+  if c.inTypeClass > 4:
     localError(body.n[3].info, $body.n[3] & " too nested for type matching")
     return isNone
 
@@ -531,7 +580,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
         typ = ff.sons[i]
         param: PSym
 
-      template paramSym(kind): expr =
+      template paramSym(kind): untyped =
         newSym(kind, typeParamName, body.sym, body.sym.info)
 
       case typ.kind
@@ -598,6 +647,10 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil)
   result = c.c.semExpr(c.c, instantiated)
 
+template subtypeCheck() =
+  if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}:
+    result = isNone
+
 proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   # typeRel can be used to establish various relationships between types:
   #
@@ -619,7 +672,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   assert(f != nil)
 
   if f.kind == tyExpr:
-    if aOrig != nil: put(c.bindings, f, aOrig)
+    if aOrig != nil: put(c, f, aOrig)
     return isGeneric
 
   assert(aOrig != nil)
@@ -640,10 +693,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   template bindingRet(res) =
     if doBind:
       let bound = aOrig.skipTypes({tyRange}).skipIntLit
-      if doBind: put(c.bindings, f, bound)
+      put(c, f, bound)
     return res
 
-  template considerPreviousT(body: stmt) {.immediate.} =
+  template considerPreviousT(body: untyped) =
     var prev = PType(idTableGet(c.bindings, f))
     if prev == nil: body
     else: return typeRel(c, prev, a)
@@ -737,6 +790,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyVar:
     if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
     else: result = typeRel(c, f.base, aOrig)
+    subtypeCheck()
   of tyArray, tyArrayConstr:
     # tyArrayConstr cannot happen really, but
     # we wanna be safe here
@@ -746,7 +800,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if fRange.kind == tyGenericParam:
         var prev = PType(idTableGet(c.bindings, fRange))
         if prev == nil:
-          put(c.bindings, fRange, a.sons[0])
+          put(c, fRange, a.sons[0])
           fRange = a
         else:
           fRange = prev
@@ -764,7 +818,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           # we must correct for the off-by-one discrepancy between
           # ranges and static params:
           replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
-          put(c.bindings, rangeStaticT, replacementT)
+          put(c, rangeStaticT, replacementT)
           return isGeneric
 
         let len = tryResolvingStaticExpr(c, fRange.n[1])
@@ -851,6 +905,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if sameDistinctTypes(f, a): result = isEqual
       elif f.base.kind == tyAnything: result = isGeneric
       elif c.coerceDistincts: result = typeRel(c, f.base, a)
+    elif a.kind == tyNil and f.base.kind in NilableTypes:
+      result = f.allowsNil
     elif c.coerceDistincts: result = typeRel(c, f.base, a)
   of tySet:
     if a.kind == tySet:
@@ -867,6 +923,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       for i in 0..f.len-2:
         if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone
       result = typeRel(c, f.lastSon, a.lastSon)
+      subtypeCheck()
       if result <= isConvertible: result = isNone
       elif tfNotNil in f.flags and tfNotNil notin a.flags:
         result = isNilConversion
@@ -921,8 +978,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isConvertible
     else: discard
 
-  of tyEmpty:
-    if a.kind == tyEmpty: result = isEqual
+  of tyEmpty, tyVoid:
+    if a.kind == f.kind: result = isEqual
 
   of tyGenericInst:
     result = typeRel(c, lastSon(f), a)
@@ -932,21 +989,26 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if a.kind == tyGenericInst and a.sons[0] == f:
         bindingRet isGeneric
       let ff = lastSon(f)
-      if ff != nil: result = typeRel(c, ff, a)
+      if ff != nil:
+        result = typeRel(c, ff, a)
 
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    var depth = 0
     if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
       #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
       # simply no match for now:
       discard
     elif x.kind == tyGenericInst and
-          (f.sons[0] == x.sons[0]) and
+          ((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) and
           (sonsLen(x) - 1 == sonsLen(f)):
       for i in countup(1, sonsLen(f) - 1):
         if x.sons[i].kind == tyGenericParam:
           internalError("wrong instantiated type!")
-        elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return
+        elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype:
+          # Workaround for regression #4589
+          if f.sons[i].kind != tyTypeDesc: return
+      c.inheritancePenalty += depth
       result = isGeneric
     else:
       let genericBody = f.sons[0]
@@ -963,13 +1025,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         #
         # we steal the generic parameters from the tyGenericBody:
         for i in countup(1, sonsLen(f) - 1):
-          var x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
+          let x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
           if x == nil:
             discard "maybe fine (for eg. a==tyNil)"
           elif x.kind in {tyGenericInvocation, tyGenericParam}:
             internalError("wrong instantiated type!")
           else:
-            put(c.bindings, f.sons[i], x)
+            put(c, f.sons[i], x)
 
   of tyAnd:
     considerPreviousT:
@@ -979,6 +1041,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         if x < isSubtype: return isNone
         # 'and' implies minimum matching result:
         if x < result: result = x
+      if result > isGeneric: result = isGeneric
       bindingRet result
 
   of tyOr:
@@ -989,6 +1052,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         # 'or' implies maximum matching result:
         if x > result: result = x
       if result >= isSubtype:
+        if result > isGeneric: result = isGeneric
         bindingRet result
       else:
         result = isNone
@@ -1005,7 +1069,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     considerPreviousT:
       var concrete = concreteType(c, a)
       if concrete != nil and doBind:
-        put(c.bindings, f, concrete)
+        put(c, f, concrete)
       return isGeneric
 
   of tyBuiltInTypeClass:
@@ -1013,7 +1077,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       let targetKind = f.sons[0].kind
       if targetKind == a.skipTypes({tyRange, tyGenericInst}).kind or
          (targetKind in {tyProc, tyPointer} and a.kind == tyNil):
-        put(c.bindings, f, a)
+        put(c, f, a)
         return isGeneric
       else:
         return isNone
@@ -1022,7 +1086,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     considerPreviousT:
       result = matchUserTypeClass(c.c, c, f, aOrig)
       if result == isGeneric:
-        put(c.bindings, f, a)
+        put(c, f, a)
 
   of tyCompositeTypeClass:
     considerPreviousT:
@@ -1038,7 +1102,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       else:
         result = typeRel(c, rootf.lastSon, a)
       if result != isNone:
-        put(c.bindings, f, a)
+        put(c, f, a)
         result = isGeneric
   of tyGenericParam:
     var x = PType(idTableGet(c.bindings, f))
@@ -1072,7 +1136,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           if doBind and result notin {isNone, isGeneric}:
             let concrete = concreteType(c, a)
             if concrete == nil: return isNone
-            put(c.bindings, f, concrete)
+            put(c, f, concrete)
         else:
           result = isGeneric
 
@@ -1086,7 +1150,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           if concrete == nil:
             return isNone
         if doBind:
-          put(c.bindings, f, concrete)
+          put(c, f, concrete)
       elif result > isGeneric:
         result = isGeneric
     elif a.kind == tyEmpty:
@@ -1105,7 +1169,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         if result != isNone and f.n != nil:
           if not exprStructuralEquivalent(f.n, aOrig.n):
             result = isNone
-        if result != isNone: put(c.bindings, f, aOrig)
+        if result != isNone: put(c, f, aOrig)
       else:
         result = isNone
     elif prev.kind == tyStatic:
@@ -1133,7 +1197,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = typeRel(c, f.base, a.base)
 
       if result != isNone:
-        put(c.bindings, f, a)
+        put(c, f, a)
     else:
       if tfUnresolved in f.flags:
         result = typeRel(c, prev.base, a)
@@ -1151,7 +1215,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyStmt:
     if aOrig != nil and tfOldSchoolExprStmt notin f.flags:
-      put(c.bindings, f, aOrig)
+      put(c, f, aOrig)
     result = isGeneric
 
   of tyProxy:
@@ -1256,6 +1320,13 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
     if r == isGeneric:
       result.typ = getInstantiatedType(c, arg, m, base(f))
     m.baseTypeMatch = true
+    # bug #4545: allow the call to go through a 'var T':
+    let vt = result.sons[0].typ.sons[1]
+    if vt.kind == tyVar:
+      let x = result.sons[1]
+      let va = newNodeIT(nkHiddenAddr, x.info, vt)
+      va.add x
+      result.sons[1] = va
 
 proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   case r
@@ -1399,7 +1470,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isNone:
-    # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
+    # do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
     if a.kind in {tyProxy, tyUnknown}:
       inc(m.genericMatches)
       m.fauxMatch = a.kind
@@ -1420,6 +1491,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
           m.baseTypeMatch = true
         else:
           result = userConvMatch(c, m, base(f), a, arg)
+          if result != nil: m.baseTypeMatch = true
 
 proc paramTypesMatch*(m: var TCandidate, f, a: PType,
                       arg, argOrig: PNode): PNode =
@@ -1532,13 +1604,13 @@ proc incrIndexType(t: PType) =
   assert t.kind == tyArrayConstr
   inc t.sons[0].n.sons[1].intVal
 
-template isVarargsUntyped(x): expr =
+template isVarargsUntyped(x): untyped =
   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.} =
+  template checkConstraint(n: untyped) {.dirty.} =
     if not formal.constraint.isNil:
       if matchNodeKinds(formal.constraint, n):
         # better match over other routines with no such restriction:
@@ -1549,6 +1621,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
     if formal.typ.kind == tyVar:
       if not n.isLValue:
         m.state = csNoMatch
+        m.mutabilityProblem = uint8(f-1)
         return
 
   var
@@ -1568,6 +1641,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
 
   while a < n.len:
     if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
+      incl(marker, formal.position)
       if container.isNil:
         container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
         setSon(m.call, formal.position + 1, container)
@@ -1630,6 +1704,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           # beware of the side-effects in 'prepareOperand'! So only do it for
           # varargs matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
+          incl(marker, formal.position)
           n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
           var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
                                     n.sons[a], nOrig.sons[a])
@@ -1653,30 +1728,39 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
           m.state = csNoMatch
           return
-        m.baseTypeMatch = false
-        n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
-        var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
-                                  n.sons[a], nOrig.sons[a])
-        if arg == nil:
-          m.state = csNoMatch
-          return
-        if m.baseTypeMatch:
-          #assert(container == nil)
+
+        if formal.typ.isVarargsUntyped:
           if container.isNil:
-            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
+            setSon(m.call, formal.position + 1, container)
           else:
             incrIndexType(container.typ)
-          addSon(container, arg)
-          setSon(m.call, formal.position + 1,
-                 implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
-          #if f != formalLen - 1: container = nil
-
-          # pick the formal from the end, so that 'x, y, varargs, z' works:
-          f = max(f, formalLen - n.len + a + 1)
+          addSon(container, n.sons[a])
         else:
-          setSon(m.call, formal.position + 1, arg)
-          inc(f)
-          container = nil
+          m.baseTypeMatch = false
+          n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
+          var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
+                                    n.sons[a], nOrig.sons[a])
+          if arg == nil:
+            m.state = csNoMatch
+            return
+          if m.baseTypeMatch:
+            #assert(container == nil)
+            if container.isNil:
+              container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+            else:
+              incrIndexType(container.typ)
+            addSon(container, arg)
+            setSon(m.call, formal.position + 1,
+                   implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
+            #if f != formalLen - 1: container = nil
+
+            # pick the formal from the end, so that 'x, y, varargs, z' works:
+            f = max(f, formalLen - n.len + a + 1)
+          else:
+            setSon(m.call, formal.position + 1, arg)
+            inc(f)
+            container = nil
         checkConstraint(n.sons[a])
     inc(a)
 
@@ -1707,15 +1791,18 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
       if formal.ast == nil:
         if formal.typ.kind == tyVarargs:
           var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info))
-          addSon(m.call, implicitConv(nkHiddenStdConv, formal.typ,
-                                      container, m, c))
+          setSon(m.call, formal.position + 1,
+                 implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
         else:
           # no default value
           m.state = csNoMatch
           break
       else:
         # use default value:
-        setSon(m.call, formal.position + 1, copyTree(formal.ast))
+        var def = copyTree(formal.ast)
+        if def.kind == nkNilLit:
+          def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
+        setSon(m.call, formal.position + 1, def)
     inc(f)
 
 proc argtypeMatches*(c: PContext, f, a: PType): bool =
@@ -1728,16 +1815,19 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
   result = res != nil
 
 proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
-                      op: TTypeAttachedOp): PSym {.procvar.} =
+                      op: TTypeAttachedOp; col: int): PSym {.procvar.} =
   var m: TCandidate
   initCandidate(c, m, dc.typ)
-  var f = dc.typ.sons[1]
+  if col >= dc.typ.len:
+    localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
+    return nil
+  var f = dc.typ.sons[col]
   if op == attachedDeepCopy:
     if f.kind in {tyRef, tyPtr}: f = f.lastSon
   else:
     if f.kind == tyVar: f = f.lastSon
   if typeRel(m, f, t) == isNone:
-    localError(info, errGenerated, "cannot instantiate 'deepCopy'")
+    localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
   else:
     result = c.semGenerateInstance(c, dc, m.bindings, info)
     assert sfFromGeneric in result.flags
@@ -1745,7 +1835,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
 include suggest
 
 when not declared(tests):
-  template tests(s: stmt) {.immediate.} = discard
+  template tests(s: untyped) = discard
 
 tests:
   var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index bcab6b04a..c127b968c 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -138,7 +138,7 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
     suggestResult(symToSuggest(s, isLocal=true, $ideSug, 100))
     inc outputs
 
-template wholeSymTab(cond, section: expr) {.immediate.} =
+template wholeSymTab(cond, section: untyped) =
   var isLocal = true
   for scope in walkScopes(c.currentScope):
     if scope == c.topLevelScope: isLocal = false
@@ -393,6 +393,8 @@ proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} =
 
 proc markUsed(info: TLineInfo; s: PSym) =
   incl(s.flags, sfUsed)
+  if s.kind == skEnumField and s.owner != nil:
+    incl(s.owner.flags, sfUsed)
   if {sfDeprecated, sfError} * s.flags != {}:
     if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s)
     if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index a4a15ea4a..fc400c524 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -95,7 +95,7 @@ proc getCurrOwner(c: PTransf): PSym =
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
   let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
-  r.typ = skipTypes(typ, {tyGenericInst})
+  r.typ = typ #skipTypes(typ, {tyGenericInst})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
   if owner.isIterator and not c.tooEarly:
@@ -204,14 +204,8 @@ proc transformConstSection(c: PTransf, v: PNode): PTransNode =
       if it.kind != nkConstDef: internalError(it.info, "transformConstSection")
       if it.sons[0].kind != nkSym:
         internalError(it.info, "transformConstSection")
-      if sfFakeConst in it[0].sym.flags:
-        var b = newNodeI(nkConstDef, it.info)
-        addSon(b, it[0])
-        addSon(b, ast.emptyNode)            # no type description
-        addSon(b, transform(c, it[2]).PNode)
-        result[i] = PTransNode(b)
-      else:
-        result[i] = PTransNode(it)
+
+      result[i] = PTransNode(it)
 
 proc hasContinue(n: PNode): bool =
   case n.kind
@@ -414,8 +408,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
         result = newTransNode(nkChckRange, n, 3)
       dest = skipTypes(n.typ, abstractVar)
       result[0] = transform(c, n.sons[1])
-      result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode
-      result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
+      result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode
+      result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode
   of tyFloat..tyFloat128:
     # XXX int64 -> float conversion?
     if skipTypes(n.typ, abstractVar).kind == tyRange:
@@ -480,12 +474,14 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
 
 type
   TPutArgInto = enum
-    paDirectMapping, paFastAsgn, paVarAsgn
+    paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray
 
 proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   # This analyses how to treat the mapping "formal <-> arg" in an
   # inline context.
   if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
+    if arg.kind == nkStmtListExpr:
+      return paComplexOpenarray
     return paDirectMapping    # XXX really correct?
                               # what if ``arg`` has side-effects?
   case arg.kind
@@ -575,6 +571,14 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
       idNodeTablePut(newC.mapping, formal, arg)
       # XXX BUG still not correct if the arg has a side effect!
+    of paComplexOpenarray:
+      let typ = newType(tySequence, formal.owner)
+      addSonSkipIntLit(typ, formal.typ.sons[0])
+      var temp = newTemp(c, typ, formal.info)
+      addVar(v, temp)
+      add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
+      idNodeTablePut(newC.mapping, formal, temp)
+
   var body = iter.getBody.copyTree
   pushInfoContext(n.info)
   # XXX optimize this somehow. But the check "c.inlining" is not correct:
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 659df334b..fdd88c348 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -103,14 +103,16 @@ proc getMagic*(op: PNode): TMagic =
     else: result = mNone
   else: result = mNone
 
-proc treeToSym*(t: PNode): PSym =
-  result = t.sym
-
 proc isConstExpr*(n: PNode): bool =
   result = (n.kind in
       {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
        nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
 
+proc isCaseObj*(n: PNode): bool =
+  if n.kind == nkRecCase: return true
+  for i in 0..<safeLen(n):
+    if n[i].isCaseObj: return true
+
 proc isDeepConstExpr*(n: PNode): bool =
   case n.kind
   of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
@@ -119,11 +121,14 @@ proc isDeepConstExpr*(n: PNode): bool =
   of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
     result = isDeepConstExpr(n.sons[1])
   of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure:
-    for i in 0 .. <n.len:
+    for i in ord(n.kind == nkObjConstr) .. <n.len:
       if not isDeepConstExpr(n.sons[i]): return false
-    # XXX once constant objects are supported by the codegen this needs to be
-    # weakened:
-    result = n.typ.isNil or n.typ.skipTypes({tyGenericInst, tyDistinct}).kind != tyObject
+    if n.typ.isNil: result = true
+    else:
+      let t = n.typ.skipTypes({tyGenericInst, tyDistinct})
+      if t.kind in {tyRef, tyPtr}: return false
+      if t.kind != tyObject or not isCaseObj(t.n):
+        result = true
   else: discard
 
 proc flattenTreeAux(d, a: PNode, op: TMagic) =
diff --git a/compiler/types.nim b/compiler/types.nim
index 9aa991086..312e36ae5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -48,6 +48,8 @@ proc isOrdinalType*(t: PType): bool
 proc enumHasHoles*(t: PType): bool
 
 const
+  # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
+  # replace with typedescX
   abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
                    tyConst, tyMutable, tyTypeDesc}
   abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
@@ -61,6 +63,7 @@ const
 
   skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable,
                tyTypeDesc}
+  # typedescX is used if we're sure tyTypeDesc should be included (or skipped)
   typedescPtrs* = abstractPtrs + {tyTypeDesc}
   typedescInst* = abstractInst + {tyTypeDesc}
 
@@ -148,10 +151,12 @@ proc skipGeneric(t: PType): PType =
 
 proc isOrdinalType(t: PType): bool =
   assert(t != nil)
-  # caution: uint, uint64 are no ordinal types!
-  result = t.kind in {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum} or
-      (t.kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and
-       isOrdinalType(t.sons[0])
+  const
+    # caution: uint, uint64 are no ordinal types!
+    baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
+    parentKinds = {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst,
+                   tyDistinct}
+  t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
 
 proc enumHasHoles(t: PType): bool =
   var b = t
@@ -407,7 +412,8 @@ const
     "!", "varargs[$1]", "iter[$1]", "Error Type",
     "BuiltInTypeClass", "UserTypeClass",
     "UserTypeClassInst", "CompositeTypeClass",
-    "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
+    "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
+    "void"]
 
 const preferToResolveSymbols = {preferName, preferModuleInfo, preferGenericArg}
 
@@ -540,7 +546,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     else:
       result.add typeToString(t.sons[0])
   of tyRange:
-    result = "range " & rangeToStr(t.n)
+    result = "range "
+    if t.n != nil and t.n.kind == nkRange:
+      result.add rangeToStr(t.n)
     if prefer != preferExported:
       result.add("(" & typeToString(t.sons[0]) & ")")
   of tyProc:
@@ -727,6 +735,7 @@ proc equalParam(a, b: PSym): TParamsEquality =
     result = paramsNotEqual
 
 proc sameConstraints(a, b: PNode): bool =
+  if isNil(a) and isNil(b): return true
   internalAssert a.len == b.len
   for i in 1 .. <a.len:
     if not exprStructuralEquivalent(a[i].sym.constraint,
@@ -799,8 +808,10 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
           result = x.name.id == y.name.id
           if not result: break
         else: internalError(a.n.info, "sameTuple")
+    elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags:
+      result = false
 
-template ifFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
+template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
   if tfFromGeneric notin a.flags + b.flags:
     # fast case: id comparison suffices:
     result = a.id == b.id
@@ -908,7 +919,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       while a.kind == tyDistinct: a = a.sons[0]
       if a.kind != b.kind: return false
 
-  if x.kind == tyGenericInst:
+  # this is required by tunique_type but makes no sense really:
+  if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags:
     let
       lhs = x.skipGenericAlias
       rhs = y.skipGenericAlias
@@ -922,7 +934,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
 
   case a.kind
   of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
-     tyInt..tyBigNum, tyStmt, tyExpr:
+     tyInt..tyBigNum, tyStmt, tyExpr, tyVoid:
     result = sameFlags(a, b)
   of tyStatic, tyFromExpr:
     result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
@@ -1108,7 +1120,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     result = nil
   of tyExpr, tyStmt, tyStatic:
     if kind notin {skParam, skResult}: result = t
-  of tyEmpty:
+  of tyVoid:
     if taField notin flags: result = t
   of tyTypeClasses:
     if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
@@ -1153,7 +1165,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
       if result != nil: break
     if result.isNil and t.n != nil:
       result = typeAllowedNode(marker, t.n, kind, flags)
-  of tyProxy:
+  of tyProxy, tyEmpty:
     # for now same as error node; we say it's a valid type as it should
     # prevent cascading errors:
     result = nil
@@ -1313,6 +1325,9 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
   of tyTypeDesc:
     result = computeSizeAux(typ.base, a)
   of tyForward: return szIllegalRecursion
+  of tyStatic:
+    if typ.n != nil: result = computeSizeAux(lastSon(typ), a)
+    else: result = szUnknownSize
   else:
     #internalError("computeSizeAux()")
     result = szUnknownSize
@@ -1448,6 +1463,18 @@ proc skipConv*(n: PNode): PNode =
       result = n.sons[1]
   else: discard
 
+proc skipHidden*(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of nkHiddenStdConv, nkHiddenSubConv:
+      if result.sons[1].typ.classify == result.typ.classify:
+        result = result.sons[1]
+      else: break
+    of nkHiddenDeref, nkHiddenAddr:
+      result = result.sons[0]
+    else: break
+
 proc skipConvTakeType*(n: PNode): PNode =
   result = n.skipConv
   result.typ = n.typ
@@ -1493,3 +1520,12 @@ proc skipHiddenSubConv*(n: PNode): PNode =
       result.typ = dest
   else:
     result = n
+
+proc typeMismatch*(n: PNode, formal, actual: PType) =
+  if formal.kind != tyError and actual.kind != tyError:
+    let named = typeToString(formal)
+    let desc = typeToString(formal, preferDesc)
+    let x = if named == desc: named else: named & " = " & desc
+    localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
+        typeToString(actual) & ") " &
+        `%`(msgKindToString(errButExpectedX), [x]))
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 7220e1b8e..5accf628e 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -10,7 +10,9 @@
 ## This file implements the new evaluation engine for Nim code.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-const debugEchoCode = false
+const
+  debugEchoCode = false
+  traceCode = debugEchoCode
 
 import ast except getstr
 
@@ -90,38 +92,38 @@ when not defined(nimComputedGoto):
 
 proc myreset(n: var TFullReg) = reset(n)
 
-template ensureKind(k: expr) {.immediate, dirty.} =
+template ensureKind(k: untyped) {.dirty.} =
   if regs[ra].kind != k:
     myreset(regs[ra])
     regs[ra].kind = k
 
-template decodeB(k: expr) {.immediate, dirty.} =
+template decodeB(k: untyped) {.dirty.} =
   let rb = instr.regB
   ensureKind(k)
 
-template decodeBC(k: expr) {.immediate, dirty.} =
+template decodeBC(k: untyped) {.dirty.} =
   let rb = instr.regB
   let rc = instr.regC
   ensureKind(k)
 
-template declBC() {.immediate, dirty.} =
+template declBC() {.dirty.} =
   let rb = instr.regB
   let rc = instr.regC
 
-template decodeBImm(k: expr) {.immediate, dirty.} =
+template decodeBImm(k: untyped) {.dirty.} =
   let rb = instr.regB
   let imm = instr.regC - byteExcess
   ensureKind(k)
 
-template decodeBx(k: expr) {.immediate, dirty.} =
+template decodeBx(k: untyped) {.dirty.} =
   let rbx = instr.regBx - wordExcess
   ensureKind(k)
 
-template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b)
+template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b)
 # XXX fix minor 'shallowCopy' overloading bug in compiler
 
 proc createStrKeepNode(x: var TFullReg; keepNode=true) =
-  if x.node.isNil:
+  if x.node.isNil or not keepNode:
     x.node = newNode(nkStrLit)
   elif x.node.kind == nkNilLit and keepNode:
     when defined(useNodeIds):
@@ -160,7 +162,7 @@ proc moveConst(x: var TFullReg, y: TFullReg) =
 
 # this seems to be the best way to model the reference semantics
 # of system.NimNode:
-template asgnRef(x, y: expr) = moveConst(x, y)
+template asgnRef(x, y: untyped) = moveConst(x, y)
 
 proc copyValue(src: PNode): PNode =
   if src == nil or nfIsRef in src.flags:
@@ -231,7 +233,7 @@ proc regToNode(x: TFullReg): PNode =
   of rkRegisterAddr: result = regToNode(x.regAddr[])
   of rkNodeAddr: result = x.nodeAddr[]
 
-template getstr(a: expr): expr =
+template getstr(a: untyped): untyped =
   (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal)))
 
 proc pushSafePoint(f: PStackFrame; pc: int) =
@@ -404,7 +406,8 @@ 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, " rb ", instr.regB, " rc ", instr.regC
+    when traceCode:
+      echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
     #  message(c.debug[pc], warnUser, "Trace")
 
     case instr.opcode
@@ -542,7 +545,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         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'")
+          stackTrace(c, tos, pc, errGenerated, "limited VM support for pointers")
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
@@ -1182,19 +1185,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetType:
       let rb = instr.regB
       let rc = instr.regC
-      if rc == 0:
+      case rc:
+      of 0:
+        # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, errGenerated, "node has no type")
-      else:
+      of 1:
         # typeKind opcode:
         ensureKind(rkInt)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].intVal = ord(regs[rb].node.typ.kind)
         #else:
         #  stackTrace(c, tos, pc, errGenerated, "node has no type")
+      of 2:
+        # getTypeInst opcode:
+        ensureKind(rkNode)
+        if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+          regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc])
+        else:
+          stackTrace(c, tos, pc, errGenerated, "node has no type")
+      else:
+        # getTypeImpl opcode:
+        ensureKind(rkNode)
+        if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
+          regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc])
+        else:
+          stackTrace(c, tos, pc, errGenerated, "node has no type")
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
@@ -1554,7 +1573,7 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
 var evalMacroCounter: int
 
 proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
-  # XXX GlobalError() is ugly here, but I don't know a better solution for now
+  # XXX globalError() is ugly here, but I don't know a better solution for now
   inc(evalMacroCounter)
   if evalMacroCounter > 100:
     globalError(n.info, errTemplateInstantiationTooNested)
@@ -1584,7 +1603,7 @@ 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])
+  tos.slots[0].node = newNodeI(nkEmpty, n.info)
 
   # setup parameters:
   for i in 1.. <sym.typ.len:
@@ -1604,4 +1623,3 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   if cyclicTree(result): globalError(n.info, errCyclicTree)
   dec(evalMacroCounter)
   c.callsite = nil
-  #debug result
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 337e4ec8f..83c1dbf43 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -230,10 +230,10 @@ const
   slotSomeTemp* = slotTempUnknown
   relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
 
-template opcode*(x: TInstr): TOpcode {.immediate.} = TOpcode(x.uint32 and 0xff'u32)
-template regA*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
-template regB*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
-template regC*(x: TInstr): TRegister {.immediate.} = TRegister(x.uint32 shr 24'u32)
-template regBx*(x: TInstr): int {.immediate.} = (x.uint32 shr 16'u32).int
+template opcode*(x: TInstr): TOpcode = TOpcode(x.uint32 and 0xff'u32)
+template regA*(x: TInstr): TRegister = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
+template regB*(x: TInstr): TRegister = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
+template regC*(x: TInstr): TRegister = TRegister(x.uint32 shr 24'u32)
+template regBx*(x: TInstr): int = (x.uint32 shr 16'u32).int
 
-template jmpDiff*(x: TInstr): int {.immediate.} = regBx(x) - wordExcess
+template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index a4f02092d..7dbf6f801 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, securehash
+import ast, types, msgs, os, osproc, streams, options, idents, securehash
 
 proc readOutput(p: Process): string =
   result = ""
@@ -51,7 +51,9 @@ proc opGorge*(cmd, input, cache: string): string =
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
   try:
-    let filename = file.findFile
+    var filename = parentDir(info.toFullPath) / file
+    if not fileExists(filename):
+      filename = file.findFile
     result = readFile(filename)
     # we produce a fake include statement for every slurped filename, so that
     # the module dependencies are accurate:
@@ -61,123 +63,249 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
     localError(info, errCannotOpenFile, file)
     result = ""
 
-proc atomicTypeX(name: string; t: PType; info: TLineInfo): PNode =
+proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
   let sym = newSym(skType, getIdent(name), t.owner, info)
+  sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
   result.typ = t
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode
 
-proc mapTypeToBracket(name: string; t: PType; info: TLineInfo): PNode =
+proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo;
+                       inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-  result.add atomicTypeX(name, t, info)
+  result.add atomicTypeX(name, m, t, info)
   for i in 0 .. < t.len:
     if t.sons[i] == nil:
-      let void = atomicTypeX("void", t, info)
-      void.typ = newType(tyEmpty, t.owner)
+      let void = atomicTypeX("void", mVoid, t, info)
+      void.typ = newType(tyVoid, t.owner)
       result.add void
     else:
-      result.add mapTypeToAst(t.sons[i], info)
+      result.add mapTypeToAstX(t.sons[i], info, inst)
+
+proc objectNode(n: PNode): PNode =
+  if n.kind == nkSym:
+    result = newNodeI(nkIdentDefs, n.info)
+    result.add n  # name
+    result.add mapTypeToAstX(n.sym.typ, n.info, true, false)  # type
+    result.add ast.emptyNode  # no assigned value
+  else:
+    result = copyNode(n)
+    for i in 0 ..< n.safeLen:
+      result.add objectNode(n[i])
+
+proc mapTypeToAstX(t: PType; info: TLineInfo;
+                   inst=false; allowRecursionX=false): PNode =
+  var allowRecursion = allowRecursionX
+  template atomicType(name, m): untyped = atomicTypeX(name, m, t, info)
+  template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst)
+  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
+  template mapTypeToAst(t,i,info): untyped =
+    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
+    else: ast.emptyNode
+  template mapTypeToBracket(name, m, t, info): untyped =
+    mapTypeToBracketX(name, m, t, info, inst)
+  template newNodeX(kind): untyped =
+    newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
+  template newIdent(s): untyped =
+    var r = newNodeX(nkIdent)
+    r.add !s
+    r
+  template newIdentDefs(n,t): untyped =
+    var id = newNodeX(nkIdentDefs)
+    id.add n  # name
+    id.add mapTypeToAst(t, info)  # type
+    id.add ast.emptyNode  # no assigned value
+    id
+  template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
 
-proc mapTypeToAst(t: PType, info: TLineInfo; allowRecursion=false): PNode =
-  template atomicType(name): expr = atomicTypeX(name, t, info)
+  if inst:
+    if t.sym != nil:  # if this node has a symbol
+      if allowRecursion:  # getTypeImpl behavior: turn off recursion
+        allowRecursion = false
+      else:  # getTypeInst behavior: return symbol
+        return atomicType(t.sym.name.s, t.sym.magic)
 
   case t.kind
-  of tyNone: result = atomicType("none")
-  of tyBool: result = atomicType("bool")
-  of tyChar: result = atomicType("char")
-  of tyNil: result = atomicType("nil")
-  of tyExpr: result = atomicType("expr")
-  of tyStmt: result = atomicType("stmt")
-  of tyEmpty: result = atomicType"void"
+  of tyNone: result = atomicType("none", mNone)
+  of tyBool: result = atomicType("bool", mBool)
+  of tyChar: result = atomicType("char", mChar)
+  of tyNil: result = atomicType("nil", mNil)
+  of tyExpr: result = atomicType("expr", mExpr)
+  of tyStmt: result = atomicType("stmt", mStmt)
+  of tyVoid: result = atomicType("void", mVoid)
+  of tyEmpty: result = atomicType("empty", mNone)
   of tyArrayConstr, tyArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    result.add atomicType("array")
-    result.add mapTypeToAst(t.sons[0], info)
+    result.add atomicType("array", mArray)
+    if inst and t.sons[0].kind == tyRange:
+      var rng = newNodeX(nkInfix)
+      rng.add newIdentNode(getIdent(".."), info)
+      rng.add t.sons[0].n.sons[0].copyTree
+      rng.add t.sons[0].n.sons[1].copyTree
+      result.add rng
+    else:
+      result.add mapTypeToAst(t.sons[0], info)
     result.add mapTypeToAst(t.sons[1], info)
   of tyTypeDesc:
     if t.base != nil:
       result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-      result.add atomicType("typeDesc")
+      result.add atomicType("typeDesc", mTypeDesc)
       result.add mapTypeToAst(t.base, info)
     else:
-      result = atomicType"typeDesc"
+      result = atomicType("typeDesc", mTypeDesc)
   of tyGenericInvocation:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     for i in 0 .. < t.len:
       result.add mapTypeToAst(t.sons[i], info)
-  of tyGenericInst, tyGenericBody, tyOrdinal, tyUserTypeClassInst:
+  of tyGenericInst:
+    if inst:
+      if allowRecursion:
+        result = mapTypeToAstR(t.lastSon, info)
+      else:
+        result = newNodeX(nkBracketExpr)
+        result.add mapTypeToAst(t.lastSon, info)
+        for i in 1 .. < t.len-1:
+          result.add mapTypeToAst(t.sons[i], info)
+    else:
+      result = mapTypeToAst(t.lastSon, info)
+  of tyGenericBody, tyOrdinal, tyUserTypeClassInst:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
-    if allowRecursion:
-      result = mapTypeToBracket("distinct", t, info)
+    if inst:
+      result = newNodeX(nkDistinctTy)
+      result.add mapTypeToAst(t.sons[0], info)
     else:
-      result = atomicType(t.sym.name.s)
-  of tyGenericParam, tyForward: result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym == nil:
+        result = mapTypeToBracket("distinct", mDistinct, t, info)
+      else:
+        result = atomicType(t.sym.name.s, t.sym.magic)
+  of tyGenericParam, tyForward:
+    result = atomicType(t.sym.name.s, t.sym.magic)
   of tyObject:
-    if allowRecursion:
-      result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-      if t.sons[0] == nil:
-        result.add ast.emptyNode
+    if inst:
+      result = newNodeX(nkObjectTy)
+      result.add ast.emptyNode  # pragmas not reconstructed yet
+      if t.sons[0] == nil: result.add ast.emptyNode  # handle parent object
       else:
-        result.add mapTypeToAst(t.sons[0], info)
-      result.add copyTree(t.n)
+        var nn = newNodeX(nkOfInherit)
+        nn.add mapTypeToAst(t.sons[0], info)
+        result.add nn
+      if t.n.len > 0:
+        result.add objectNode(t.n)
+      else:
+        result.add ast.emptyNode
     else:
-      result = atomicType(t.sym.name.s)
+      if allowRecursion or t.sym == nil:
+        result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
+        result.add ast.emptyNode
+        if t.sons[0] == nil:
+          result.add ast.emptyNode
+        else:
+          result.add mapTypeToAst(t.sons[0], info)
+        result.add copyTree(t.n)
+      else:
+        result = atomicType(t.sym.name.s, t.sym.magic)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
     result.add copyTree(t.n)
-  of tyTuple: result = mapTypeToBracket("tuple", t, info)
-  of tySet: result = mapTypeToBracket("set", t, info)
-  of tyPtr: result = mapTypeToBracket("ptr", t, info)
-  of tyRef: result = mapTypeToBracket("ref", t, info)
-  of tyVar: result = mapTypeToBracket("var", t, info)
-  of tySequence: result = mapTypeToBracket("seq", t, info)
-  of tyProc: result = mapTypeToBracket("proc", t, info)
-  of tyOpenArray: result = mapTypeToBracket("openArray", t, info)
+  of tyTuple:
+    if inst:
+      result = newNodeX(nkTupleTy)
+      for s in t.n.sons:
+        result.add newIdentDefs(s)
+    else:
+      result = mapTypeToBracket("tuple", mTuple, t, info)
+  of tySet: result = mapTypeToBracket("set", mSet, t, info)
+  of tyPtr:
+    if inst:
+      result = newNodeX(nkPtrTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ptr", mPtr, t, info)
+  of tyRef:
+    if inst:
+      result = newNodeX(nkRefTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("ref", mRef, t, info)
+  of tyVar: result = mapTypeToBracket("var", mVar, t, info)
+  of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
+  of tyProc:
+    if inst:
+      result = newNodeX(nkProcTy)
+      var fp = newNodeX(nkFormalParams)
+      if t.sons[0] == nil:
+        fp.add ast.emptyNode
+      else:
+        fp.add mapTypeToAst(t.sons[0], t.n[0].info)
+      for i in 1..<t.sons.len:
+        fp.add newIdentDefs(t.n[i], t.sons[i])
+      result.add fp
+      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+    else:
+      result = mapTypeToBracket("proc", mNone, t, info)
+  of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
   of tyRange:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    result.add atomicType("range")
+    result.add atomicType("range", mRange)
     result.add t.n.sons[0].copyTree
     result.add t.n.sons[1].copyTree
-  of tyPointer: result = atomicType"pointer"
-  of tyString: result = atomicType"string"
-  of tyCString: result = atomicType"cstring"
-  of tyInt: result = atomicType"int"
-  of tyInt8: result = atomicType"int8"
-  of tyInt16: result = atomicType"int16"
-  of tyInt32: result = atomicType"int32"
-  of tyInt64: result = atomicType"int64"
-  of tyFloat: result = atomicType"float"
-  of tyFloat32: result = atomicType"float32"
-  of tyFloat64: result = atomicType"float64"
-  of tyFloat128: result = atomicType"float128"
-  of tyUInt: result = atomicType"uint"
-  of tyUInt8: result = atomicType"uint8"
-  of tyUInt16: result = atomicType"uint16"
-  of tyUInt32: result = atomicType"uint32"
-  of tyUInt64: result = atomicType"uint64"
-  of tyBigNum: result = atomicType"bignum"
-  of tyConst: result = mapTypeToBracket("const", t, info)
-  of tyMutable: result = mapTypeToBracket("mutable", t, info)
-  of tyVarargs: result = mapTypeToBracket("varargs", t, info)
-  of tyIter: result = mapTypeToBracket("iter", t, info)
-  of tyProxy: result = atomicType"error"
-  of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", t, info)
+  of tyPointer: result = atomicType("pointer", mPointer)
+  of tyString: result = atomicType("string", mString)
+  of tyCString: result = atomicType("cstring", mCString)
+  of tyInt: result = atomicType("int", mInt)
+  of tyInt8: result = atomicType("int8", mInt8)
+  of tyInt16: result = atomicType("int16", mInt16)
+  of tyInt32: result = atomicType("int32", mInt32)
+  of tyInt64: result = atomicType("int64", mInt64)
+  of tyFloat: result = atomicType("float", mFloat)
+  of tyFloat32: result = atomicType("float32", mFloat32)
+  of tyFloat64: result = atomicType("float64", mFloat64)
+  of tyFloat128: result = atomicType("float128", mFloat128)
+  of tyUInt: result = atomicType("uint", mUint)
+  of tyUInt8: result = atomicType("uint8", mUint8)
+  of tyUInt16: result = atomicType("uint16", mUint16)
+  of tyUInt32: result = atomicType("uint32", mUint32)
+  of tyUInt64: result = atomicType("uint64", mUint64)
+  of tyBigNum: result = atomicType("bignum", mNone)
+  of tyConst: result = mapTypeToBracket("const", mNone, t, info)
+  of tyMutable: result = mapTypeToBracket("mutable", mNone, t, info)
+  of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
+  of tyIter: result = mapTypeToBracket("iter", mNone, t, info)
+  of tyProxy: result = atomicType("error", mNone)
+  of tyBuiltInTypeClass:
+    result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
   of tyUserTypeClass:
-    result = mapTypeToBracket("concept", t, info)
+    result = mapTypeToBracket("concept", mNone, t, info)
     result.add t.n.copyTree
-  of tyCompositeTypeClass: result = mapTypeToBracket("compositeTypeClass", t, info)
-  of tyAnd: result = mapTypeToBracket("and", t, info)
-  of tyOr: result = mapTypeToBracket("or", t, info)
-  of tyNot: result = mapTypeToBracket("not", t, info)
-  of tyAnything: result = atomicType"anything"
+  of tyCompositeTypeClass:
+    result = mapTypeToBracket("compositeTypeClass", mNone, t, info)
+  of tyAnd: result = mapTypeToBracket("and", mAnd, t, info)
+  of tyOr: result = mapTypeToBracket("or", mOr, t, info)
+  of tyNot: result = mapTypeToBracket("not", mNot, t, info)
+  of tyAnything: result = atomicType("anything", mNone)
   of tyStatic, tyFromExpr, tyFieldAccessor:
-    result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    result.add atomicType("static")
-    if t.n != nil:
-      result.add t.n.copyTree
+    if inst:
+      if t.n != nil: result = t.n.copyTree
+      else: result = atomicType("void", mVoid)
+    else:
+      result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
+      result.add atomicType("static", mNone)
+      if t.n != nil:
+        result.add t.n.copyTree
 
 proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAst(t, info, true)
+  result = mapTypeToAstX(t, info, false, true)
+
+# the "Inst" version includes generic parameters in the resulting type tree
+# and also tries to look like the corresponding Nim type declaration
+proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, false)
+
+# the "Impl" version includes generic parameters in the resulting type tree
+# and also tries to look like the corresponding Nim type implementation
+proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(t, info, true, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 47f16a013..61ab65360 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -102,6 +102,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
   let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
                            (b.uint32 shl 16'u32) or
                            (c.uint32 shl 24'u32)).TInstr
+  when false:
+    if ctx.code.len == 43:
+      writeStackTrace()
+      echo "generating ", opc
   ctx.code.add(ins)
   ctx.debug.add(n.info)
 
@@ -122,6 +126,11 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
 proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
   # Applies `opc` to `bx` and stores it into register `a`
   # `bx` must be signed and in the range [-32768, 32767]
+  when false:
+    if c.code.len == 43:
+      writeStackTrace()
+      echo "generating ", opc
+
   if bx >= -32768 and bx <= 32767:
     let ins = (opc.uint32 or a.uint32 shl 8'u32 or
               (bx+wordExcess).uint32 shl 16'u32).TInstr
@@ -222,7 +231,7 @@ proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
 proc freeTempRange(c: PCtx; start: TRegister, n: int) =
   for i in start .. start+n-1: c.freeTemp(TRegister(i))
 
-template withTemp(tmp, typ: expr, body: stmt) {.immediate, dirty.} =
+template withTemp(tmp, typ, body: untyped) {.dirty.} =
   var tmp = getTemp(c, typ)
   body
   c.freeTemp(tmp)
@@ -232,7 +241,7 @@ proc popBlock(c: PCtx; oldLen: int) =
     c.patch(f)
   c.prc.blocks.setLen(oldLen)
 
-template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} =
+template withBlock(labl: PSym; body: untyped) {.dirty.} =
   var oldLen {.gensym.} = c.prc.blocks.len
   c.prc.blocks.add TBlock(label: labl, fixups: @[])
   body
@@ -258,7 +267,7 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
   # stmt is different from 'void' in meta programming contexts.
   # So we only set dest to -1 if 'void':
-  if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty):
+  if dest >= 0 and (n.typ.isNil or n.typ.kind == tyVoid):
     c.freeTemp(dest)
     dest = -1
 
@@ -365,7 +374,7 @@ proc canonValue*(n: PNode): PNode =
 
 proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len
-  assert(n.kind != nkCall)
+  #assert(n.kind != nkCall)
   n.flags.incl nfAllConst
   c.constants.add n.canonValue
   internalAssert result < 0x7fff
@@ -488,12 +497,30 @@ proc genReturn(c: PCtx; n: PNode) =
     gen(c, n.sons[0])
   c.gABC(n, opcRet)
 
+
+proc genLit(c: PCtx; n: PNode; dest: var TDest) =
+  # opcLdConst is now always valid. We produce the necessary copy in the
+  # assignments now:
+  #var opc = opcLdConst
+  if dest < 0: dest = c.getTemp(n.typ)
+  #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
+  let lit = genLiteral(c, n)
+  c.gABx(n, opcLdConst, dest, lit)
+
 proc genCall(c: PCtx; n: PNode; dest: var TDest) =
+  # it can happen that due to inlining we have a 'n' that should be
+  # treated as a constant (see issue #537).
+  #if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode:
+  #  genLit(c, n, dest)
+  #  return
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   let x = c.getTempRange(n.len, slotTempUnknown)
   # varargs need 'opcSetType' for the FFI support:
-  let fntyp = n.sons[0].typ
+  let fntyp = skipTypes(n.sons[0].typ, abstractInst)
   for i in 0.. <n.len:
+    #if i > 0 and i < sonsLen(fntyp):
+    #  let paramType = fntyp.n.sons[i]
+    #  if paramType.typ.isCompileTimeOnly: continue
     var r: TRegister = x+i
     c.gen(n.sons[i], r)
     if i >= fntyp.len:
@@ -570,16 +597,27 @@ proc genNew(c: PCtx; n: PNode) =
   c.freeTemp(dest)
 
 proc genNewSeq(c: PCtx; n: PNode) =
-  let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ)
+  let t = n.sons[1].typ
+  let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(t)
              else: c.genx(n.sons[1])
   let tmp = c.genx(n.sons[2])
-  c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(
+  c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes(
                                                   abstractVar-{tyTypeDesc})))
   c.gABx(n, opcNewSeq, tmp, 0)
   c.freeTemp(tmp)
   c.genAsgnPatch(n.sons[1], dest)
   c.freeTemp(dest)
 
+proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) =
+  let t = n.typ
+  let tmp = c.getTemp(n.sons[1].typ)
+  c.gABx(n, opcLdNull, dest, c.genType(t))
+  c.gABx(n, opcLdImmInt, tmp, 0)
+  c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes(
+                                                  abstractVar-{tyTypeDesc})))
+  c.gABx(n, opcNewSeq, tmp, 0)
+  c.freeTemp(tmp)
+
 proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   let tmp = c.genx(n.sons[1])
   if dest < 0: dest = c.getTemp(n.typ)
@@ -626,8 +664,7 @@ proc genNarrowU(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
   # other unsigned types:
-  if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
-      (t.kind == tyInt and t.size == 4):
+  if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
     c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
 
 proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
@@ -704,6 +741,10 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   c.genNarrow(n, dest)
 
 proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
+  if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc:
+    # don't do anything for lambda lifting conversions:
+    gen(c, arg, dest)
+    return
   let tmp = c.genx(arg)
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
@@ -751,6 +792,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNewSeq:
     unused(n, dest)
     c.genNewSeq(n)
+  of mNewSeqOfCap: c.genNewSeqOfCap(n, dest)
   of mNewString:
     genUnaryABC(c, n, dest, opcNewStr)
     # XXX buggy
@@ -804,7 +846,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mLtF64: genBinaryABC(c, n, dest, opcLtFloat)
   of mLePtr, mLeU, mLeU64: genBinaryABC(c, n, dest, opcLeu)
   of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu)
-  of mEqProc, mEqRef, mEqUntracedRef, mEqCString:
+  of mEqProc, mEqRef, mEqUntracedRef:
     genBinaryABC(c, n, dest, opcEqRef)
   of mXor: genBinaryABCnarrowU(c, n, dest, opcXor)
   of mNot: genUnaryABC(c, n, dest, opcNot)
@@ -821,7 +863,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
      mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
      mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
     genConv(c, n, n.sons[1], dest)
-  of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
+  of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
   of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
   of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
   of mEqSet: genBinarySet(c, n, dest, opcEqSet)
@@ -970,7 +1012,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNGetType:
     let tmp = c.genx(n.sons[1])
     if dest < 0: dest = c.getTemp(n.typ)
-    c.gABC(n, opcNGetType, dest, tmp, if n[0].sym.name.s == "typeKind": 1 else: 0)
+    let rc = case n[0].sym.name.s:
+      of "getType": 0
+      of "typeKind": 1
+      of "getTypeInst": 2
+      else: 3  # "getTypeImpl"
+    c.gABC(n, opcNGetType, dest, tmp, rc)
     c.freeTemp(tmp)
     #genUnaryABC(c, n, dest, opcNGetType)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
@@ -1117,7 +1164,10 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
   if isAddr and (let m = canElimAddr(n); m != nil):
     gen(c, m, dest, flags)
     return
-  let newflags = if isAddr: flags+{gfAddrOf} else: flags
+
+  let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess}
+           else: {gfAddrOf}
+  let newflags = if isAddr: flags+af else: flags
   # consider:
   # proc foo(f: var ref int) =
   #   f = new(int)
@@ -1132,7 +1182,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
     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})
+    gen(c, n.sons[0], dest, flags+af)
   else:
     let tmp = c.genx(n.sons[0], newflags)
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1215,7 +1265,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
 proc isTemp(c: PCtx; dest: TDest): bool =
   result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown
 
-template needsAdditionalCopy(n): expr =
+template needsAdditionalCopy(n): untyped =
   not c.isTemp(dest) and not fitsRegister(n.typ)
 
 proc skipDeref(n: PNode): PNode =
@@ -1286,15 +1336,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
     let dest = c.genx(le, {gfAddrOf})
     genAsgn(c, dest, ri, requiresCopy)
 
-proc genLit(c: PCtx; n: PNode; dest: var TDest) =
-  # opcLdConst is now always valid. We produce the necessary copy in the
-  # assignments now:
-  #var opc = opcLdConst
-  if dest < 0: dest = c.getTemp(n.typ)
-  #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
-  let lit = genLiteral(c, n)
-  c.gABx(n, opcLdConst, dest, lit)
-
 proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
   var n = newNode(nkType)
   n.typ = t
@@ -1321,10 +1362,11 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
   #   var decls{.compileTime.}: seq[NimNode] = @[]
   let dest = c.getTemp(s.typ)
   c.gABx(n, opcLdGlobal, dest, s.position)
-  let tmp = c.genx(s.ast)
-  c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp)
-  c.freeTemp(dest)
-  c.freeTemp(tmp)
+  if s.ast != nil:
+    let tmp = c.genx(s.ast)
+    c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp)
+    c.freeTemp(dest)
+    c.freeTemp(tmp)
 
 proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   let s = n.sym
@@ -1361,7 +1403,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       # see tests/t99bott for an example that triggers it:
       cannotEval(n)
 
-template needsRegLoad(): expr =
+template needsRegLoad(): untyped =
   gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar}))
 
 proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
@@ -1447,12 +1489,12 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
   of tyObject:
     result = newNodeIT(nkObjConstr, info, t)
     result.add(newNodeIT(nkEmpty, info, t))
-    getNullValueAux(t.n, result)
     # initialize inherited fields:
     var base = t.sons[0]
     while base != nil:
       getNullValueAux(skipTypes(base, skipPtrs).n, result)
       base = base.sons[0]
+    getNullValueAux(t.n, result)
   of tyArray, tyArrayConstr:
     result = newNodeIT(nkBracket, info, t)
     for i in countup(0, int(lengthOrd(t)) - 1):
@@ -1655,10 +1697,10 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     of skType:
       genTypeLit(c, s.typ, dest)
     of skGenericParam:
-      if c.prc.sym.kind == skMacro:
+      if c.prc.sym != nil and c.prc.sym.kind == skMacro:
         genRdVar(c, n, dest, flags)
       else:
-        internalError(n.info, "cannot generate code for: " & s.name.s)
+        globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
     else:
       globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
@@ -1736,9 +1778,9 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of declarativeDefs:
     unused(n, dest)
   of nkLambdaKinds:
-    let s = n.sons[namePos].sym
-    discard genProc(c, s)
-    genLit(c, n.sons[namePos], dest)
+    #let s = n.sons[namePos].sym
+    #discard genProc(c, s)
+    genLit(c, newSymNode(n.sons[namePos].sym), dest)
   of nkChckRangeF, nkChckRange64, nkChckRange:
     let
       tmp0 = c.genx(n.sons[0])
@@ -1766,6 +1808,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       genConv(c, n, n.sons[1], dest, opcCast)
     else:
       globalError(n.info, errGenerated, "VM is not allowed to 'cast'")
+  of nkTypeOfExpr:
+    genTypeLit(c, n.typ, dest)
   else:
     globalError(n.info, errGenerated, "cannot generate VM code for " & $n)
 
@@ -1865,8 +1909,8 @@ proc optimizeJumps(c: PCtx; start: int) =
     else: discard
 
 proc genProc(c: PCtx; s: PSym): int =
-  let x = s.ast.sons[optimizedCodePos]
-  if x.kind == nkEmpty:
+  var x = s.ast.sons[miscPos]
+  if x.kind == nkEmpty or x[0].kind == nkEmpty:
     #if s.name.s == "outterMacro" or s.name.s == "innerProc":
     #  echo "GENERATING CODE FOR ", s.name.s
     let last = c.code.len-1
@@ -1877,7 +1921,11 @@ proc genProc(c: PCtx; s: PSym): int =
       c.debug.setLen(last)
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
-    s.ast.sons[optimizedCodePos] = newIntNode(nkIntLit, result)
+    if x.kind == nkEmpty:
+      x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode)
+    else:
+      x.sons[0] = newIntNode(nkIntLit, result)
+    s.ast.sons[miscPos] = x
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
     let body = s.getBody
@@ -1912,4 +1960,4 @@ proc genProc(c: PCtx; s: PSym): int =
     c.prc = oldPrc
   else:
     c.prc.maxSlots = s.offset
-    result = x.intVal.int
+    result = x[0].intVal.int
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 3456e893b..548a3af97 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-template setX(k, field) {.immediate, dirty.} =
+template setX(k, field) {.dirty.} =
   var s: seq[TFullReg]
   move(s, cast[seq[TFullReg]](a.slots))
   if s[a.ra].kind != k:
@@ -48,7 +48,7 @@ proc setResult*(a: VmArgs; v: seq[string]) =
   for x in v: n.add newStrNode(nkStrLit, x)
   s[a.ra].node = n
 
-template getX(k, field) {.immediate, dirty.} =
+template getX(k, field) {.dirty.} =
   doAssert i < a.rc-1
   let s = cast[seq[TFullReg]](a.slots)
   doAssert s[i+a.rb+1].kind == k
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index e40e05eff..1c2725a98 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -15,31 +15,36 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
 
 from os import getEnv, existsEnv, dirExists, fileExists, walkDir
 
-template mathop(op) {.immediate, dirty.} =
+template mathop(op) {.dirty.} =
   registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
 
-template osop(op) {.immediate, dirty.} =
+template osop(op) {.dirty.} =
   registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
 
-template systemop(op) {.immediate, dirty.} =
+template systemop(op) {.dirty.} =
   registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
 
-template wrap1f(op) {.immediate, dirty.} =
+template wrap1f_math(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getFloat(a, 0)))
   mathop op
 
-template wrap2f(op) {.immediate, dirty.} =
+template wrap2f_math(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
   mathop op
 
-template wrap1s(op) {.immediate, dirty.} =
+template wrap1s_os(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getString(a, 0)))
   osop op
 
-template wrap2svoid(op) {.immediate, dirty.} =
+template wrap1s_system(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0)))
+  systemop op
+
+template wrap2svoid_system(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     op(getString(a, 0), getString(a, 1))
   systemop op
@@ -55,34 +60,35 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode =
                               newStrNode(nkStrLit, f))
 
 proc registerAdditionalOps*(c: PCtx) =
-  wrap1f(sqrt)
-  wrap1f(ln)
-  wrap1f(log10)
-  wrap1f(log2)
-  wrap1f(exp)
-  wrap1f(round)
-  wrap1f(arccos)
-  wrap1f(arcsin)
-  wrap1f(arctan)
-  wrap2f(arctan2)
-  wrap1f(cos)
-  wrap1f(cosh)
-  wrap2f(hypot)
-  wrap1f(sinh)
-  wrap1f(sin)
-  wrap1f(tan)
-  wrap1f(tanh)
-  wrap2f(pow)
-  wrap1f(trunc)
-  wrap1f(floor)
-  wrap1f(ceil)
-  wrap2f(fmod)
+  wrap1f_math(sqrt)
+  wrap1f_math(ln)
+  wrap1f_math(log10)
+  wrap1f_math(log2)
+  wrap1f_math(exp)
+  wrap1f_math(round)
+  wrap1f_math(arccos)
+  wrap1f_math(arcsin)
+  wrap1f_math(arctan)
+  wrap2f_math(arctan2)
+  wrap1f_math(cos)
+  wrap1f_math(cosh)
+  wrap2f_math(hypot)
+  wrap1f_math(sinh)
+  wrap1f_math(sin)
+  wrap1f_math(tan)
+  wrap1f_math(tanh)
+  wrap2f_math(pow)
+  wrap1f_math(trunc)
+  wrap1f_math(floor)
+  wrap1f_math(ceil)
+  wrap2f_math(fmod)
 
-  wrap1s(getEnv)
-  wrap1s(existsEnv)
-  wrap1s(dirExists)
-  wrap1s(fileExists)
-  wrap2svoid(writeFile)
+  wrap1s_os(getEnv)
+  wrap1s_os(existsEnv)
+  wrap1s_os(dirExists)
+  wrap1s_os(fileExists)
+  wrap2svoid_system(writeFile)
+  wrap1s_system(readFile)
   systemop getCurrentExceptionMsg
   registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
     setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 3e0e05a94..b5ffd51c2 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -36,6 +36,7 @@ type
     wColon, wColonColon, wEquals, wDot, wDotDot,
     wStar, wMinus,
     wMagic, wThread, wFinal, wProfiler, wObjChecks,
+    wIntDefine, wStrDefine,
 
     wDestroy,
 
@@ -121,7 +122,7 @@ const
 
     ":", "::", "=", ".", "..",
     "*", "-",
-    "magic", "thread", "final", "profiler", "objchecks",
+    "magic", "thread", "final", "profiler", "objchecks", "intdefine", "strdefine",
 
     "destroy",