summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@googlemail.com>2017-02-07 18:34:05 +0100
committerGitHub <noreply@github.com>2017-02-07 18:34:05 +0100
commitd5bb5e832bdc42153794a7ce08661d4515a46662 (patch)
tree131cb69514f840f427f542f8584b5998f10b9fe8 /compiler
parentb7d7feffee9d02c60f80745606edf4ac00321aae (diff)
parent7c15120247f93cc1b729d33f0af592bc8e5e9937 (diff)
downloadNim-d5bb5e832bdc42153794a7ce08661d4515a46662.tar.gz
Merge branch 'devel' into feature/3691
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/ccgexprs.nim8
-rw-r--r--compiler/ccgstmts.nim36
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtrav.nim2
-rw-r--r--compiler/ccgtypes.nim107
-rw-r--r--compiler/ccgutils.nim56
-rw-r--r--compiler/cgen.nim49
-rw-r--r--compiler/cgendata.nim14
-rw-r--r--compiler/commands.nim16
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/docgen.nim40
-rw-r--r--compiler/extccomp.nim14
-rw-r--r--compiler/installer.ini12
-rw-r--r--compiler/main.nim4
-rw-r--r--compiler/modulegraphs.nim9
-rw-r--r--compiler/msgs.nim4
-rw-r--r--compiler/ndi.nim40
-rw-r--r--compiler/nim.nim12
-rw-r--r--compiler/nimconf.nim106
-rw-r--r--compiler/options.nim13
-rw-r--r--compiler/pragmas.nim10
-rw-r--r--compiler/scriptconfig.nim19
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/seminst.nim11
-rw-r--r--compiler/semtempl.nim11
-rw-r--r--compiler/semtypes.nim13
-rw-r--r--compiler/suggest.nim14
-rw-r--r--compiler/vm.nim8
-rw-r--r--compiler/vmgen.nim18
30 files changed, 411 insertions, 245 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 4ea68dc99..8fbec64cf 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1541,7 +1541,8 @@ proc skipGenericOwner*(s: PSym): PSym =
   ## Generic instantiations are owned by their originating generic
   ## symbol. This proc skips such owners and goes straight to the owner
   ## of the generic itself (the module or the enclosing proc).
-  result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
+  result = if s.kind in skProcKinds and {sfGenSym, sfFromGeneric} * s.flags ==
+                                                  {sfFromGeneric}:
              s.owner.owner
            else:
              s.owner
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index eabcdd66a..ade2cb41f 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1210,7 +1210,7 @@ proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) =
 
 proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
   var elem, a, arr: TLoc
-  if t.kind == nkBracket:
+  if t.sons[1].kind == nkBracket:
     t.sons[1].typ = t.typ
     genSeqConstr(p, t.sons[1], d)
     return
@@ -1383,7 +1383,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   var a, b: TLoc
   assert(d.k == locNone)
-  initLocExpr(p, e.sons[1], a)
+  var x = e.sons[1]
+  if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
+  initLocExpr(p, x, a)
   initLocExpr(p, e.sons[2], b)
   let t = skipTypes(e.sons[1].typ, {tyVar})
   let setLenPattern = if not p.module.compileToCpp:
@@ -2001,7 +2003,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
         if emulatedThreadVars():
-          putIntoDest(p, d, sym.loc.t, "NimTV->" & sym.loc.r)
+          putIntoDest(p, d, sym.loc.t, "NimTV_->" & sym.loc.r)
         else:
           putLocIntoDest(p, d, sym.loc)
       else:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index b3d21c35e..45d675f64 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -64,7 +64,7 @@ proc genVarTuple(p: BProc, n: PNode) =
       field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
       if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
-      field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]
+      field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)]
     putLocIntoDest(p, v.loc, field)
 
 proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false)
@@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} =
 proc blockBody(b: var TBlock): Rope =
   result = b.sections[cpsLocals]
   if b.frameLen > 0:
-    result.addf("FR.len+=$1;$n", [b.frameLen.rope])
+    result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
   result.add(b.sections[cpsInit])
   result.add(b.sections[cpsStmts])
 
@@ -123,7 +123,7 @@ proc endBlock(p: BProc) =
       ~"}$n"
   let frameLen = p.blocks[topBlock].frameLen
   if frameLen > 0:
-    blockEnd.addf("FR.len-=$1;$n", [frameLen.rope])
+    blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
   endBlock(p, blockEnd)
 
 proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@@ -156,7 +156,7 @@ proc genGotoState(p: BProc, n: PNode) =
   initLocExpr(p, n.sons[0], a)
   lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
   p.beforeRetNeeded = true
-  lineF(p, cpsStmts, "case -1: goto BeforeRet;$n", [])
+  lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
   for i in 0 .. lastOrd(n.sons[0].typ):
     lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)])
   lineF(p, cpsStmts, "}$n", [])
@@ -373,7 +373,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
     # consume it before we return.
     var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
     linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
-  lineF(p, cpsStmts, "goto BeforeRet;$n", [])
+  lineF(p, cpsStmts, "goto BeforeRet_;$n", [])
 
 proc genGotoForCase(p: BProc; caseStmt: PNode) =
   for i in 1 .. <caseStmt.len:
@@ -411,11 +411,11 @@ proc genComputedGoto(p: BProc; n: PNode) =
     localError(n.info, "no case statement found for computed goto"); return
   var id = p.labels+1
   inc p.labels, arraySize+1
-  let tmp = "TMP$1" % [id.rope]
+  let tmp = "TMP$1_" % [id.rope]
   var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
   for i in 1..arraySize-1:
-    gotoArray.addf("&&TMP$#, ", [(id+i).rope])
-  gotoArray.addf("&&TMP$#};$n", [(id+arraySize).rope])
+    gotoArray.addf("&&TMP$#_, ", [(id+i).rope])
+  gotoArray.addf("&&TMP$#_};$n", [(id+arraySize).rope])
   line(p, cpsLocals, gotoArray)
 
   let topBlock = p.blocks.len-1
@@ -445,7 +445,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
         localError(it.info, "range notation not available for computed goto")
         return
       let val = getOrdValue(it.sons[j])
-      lineF(p, cpsStmts, "TMP$#:$n", [intLiteral(val+id+1)])
+      lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)])
     genStmts(p, it.lastSon)
     #for j in casePos+1 .. <n.len: genStmts(p, n.sons[j]) # tailB
     #for j in 0 .. casePos-1: genStmts(p, n.sons[j])  # tailA
@@ -600,7 +600,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
   for i in 1..until:
     # bug #4230: avoid false sharing between branches:
     if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
-    lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)])
+    lineF(p, cpsStmts, "LA$1_: ;$n", [rope(labId + i)])
     if t.sons[i].kind == nkOfBranch:
       var length = sonsLen(t.sons[i])
       exprBlock(p, t.sons[i].sons[length - 1], d)
@@ -618,15 +618,15 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
     inc(p.labels)
     if t.sons[i].kind == nkOfBranch: # else statement
       genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
-                           "LA" & rope(p.labels))
+                           "LA" & rope(p.labels) & "_")
     else:
-      lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
+      lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
   if until < t.len-1:
     inc(p.labels)
     var gotoTarget = p.labels
-    lineF(p, cpsStmts, "goto LA$1;$n", [rope(gotoTarget)])
+    lineF(p, cpsStmts, "goto LA$1_;$n", [rope(gotoTarget)])
     result = genCaseSecondPass(p, t, d, labId, until)
-    lineF(p, cpsStmts, "LA$1: ;$n", [rope(gotoTarget)])
+    lineF(p, cpsStmts, "LA$1_: ;$n", [rope(gotoTarget)])
   else:
     result = genCaseSecondPass(p, t, d, labId, until)
 
@@ -664,7 +664,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
     for i in countup(1, sonsLen(t) - 1):
       inc(p.labels)
       if t.sons[i].kind == nkOfBranch:
-        genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels),
+        genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels) & "_",
                             branches)
       else:
         # else statement: nothing to do yet
@@ -678,7 +678,7 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
              [intLiteral(j), branches[j]])
     lineF(p, cpsStmts, "}$n", []) # else statement:
     if t.sons[sonsLen(t)-1].kind != nkOfBranch:
-      lineF(p, cpsStmts, "goto LA$1;$n", [rope(p.labels)])
+      lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
     # third pass: generate statements
     var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
     fixLabel(p, lend)
@@ -802,7 +802,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   let length = sonsLen(t)
   endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
   if optStackTrace in p.options:
-    linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
+    linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
   inc p.inExceptBlock
   var i = 1
   var catchAllPresent = false
@@ -910,7 +910,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   startBlock(p, "else {$n")
   linefmt(p, cpsStmts, "#popSafePoint();$n")
   if optStackTrace in p.options:
-    linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR);$n")
+    linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
   inc p.inExceptBlock
   var i = 1
   while (i < length) and (t.sons[i].kind == nkExceptBranch):
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index 81af89249..3efddbf30 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -19,9 +19,9 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
   if emulatedThreadVars() and not p.threadVarAccessed:
     p.threadVarAccessed = true
     incl p.module.flags, usesThreadVars
-    addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
+    addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", [])
     add(p.procSec(cpsInit),
-      ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
+      ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n"))
 
 var
   nimtv: Rope                 # Nim thread vars; the struct body
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 6dab003fd..547504afb 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -145,7 +145,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
 
   if sfThread in s.flags and emulatedThreadVars():
     accessThreadLocalVar(p, s)
-    sLoc = "NimTV->" & sLoc
+    sLoc = "NimTV_->" & sLoc
 
   c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
   c.p = p
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 8a11f954f..e30fe5598 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -22,12 +22,10 @@ proc isKeyword(w: PIdent): bool =
      ord(wInline): return true
   else: return false
 
-proc mangleField(name: PIdent): string =
+proc mangleField(m: BModule; name: PIdent): string =
   result = mangle(name.s)
-  if isKeyword(name):
-    result[0] = result[0].toUpperAscii
-    # Mangling makes everything lowercase,
-    # but some identifiers are C keywords
+  if isKeyword(name) or m.g.config.cppDefines.contains(result):
+    result.add "_0"
 
 when false:
   proc hashOwner(s: PSym): SigHash =
@@ -67,55 +65,50 @@ proc idOrSig(m: BModule; s: PSym): Rope =
 proc mangleName(m: BModule; s: PSym): Rope =
   result = s.loc.r
   if result == nil:
-    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, m.idOrSig(s))
+    add(result, m.idOrSig(s))
+    s.loc.r = result
+    writeMangledName(m.ndi, s)
+
+proc mangleParamName(m: BModule; s: PSym): Rope =
+  ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
+  ## Fortunately C's scoping rules are sane enough so that that doesn't
+  ## cause any trouble.
+  result = s.loc.r
+  if result == nil:
+    var res = s.name.s.mangle
+    if isKeyword(s.name) or m.g.config.cppDefines.contains(res):
+      res.add "_0"
+    result = res.rope
+    s.loc.r = result
+    writeMangledName(m.ndi, s)
+
+proc mangleLocalName(p: BProc; s: PSym): Rope =
+  assert s.kind in skLocalVars+{skTemp}
+  assert sfGlobal notin s.flags
+  result = s.loc.r
+  if result == nil:
+    var key = s.name.s.mangle
+    shallow(key)
+    let counter = p.sigConflicts.getOrDefault(key)
+    result = key.rope
+    if s.kind == skTemp:
+      # speed up conflict search for temps (these are quite common):
+      if counter != 0: result.add "_" & rope(counter+1)
+    elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
+      result.add "_" & rope(counter+1)
+    p.sigConflicts.inc(key)
     s.loc.r = result
+    if s.kind != skTemp: writeMangledName(p.module.ndi, s)
 
+proc scopeMangledParam(p: BProc; param: PSym) =
+  ## parameter generation only takes BModule, not a BProc, so we have to
+  ## remember these parameter names are already in scope to be able to
+  ## generate unique identifiers reliably (consider that ``var a = a`` is
+  ## even an idiom in Nim).
+  var key = param.name.s.mangle
+  shallow(key)
+  p.sigConflicts.inc(key)
 
 const
   irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
@@ -393,7 +386,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     var param = t.n.sons[i].sym
     if isCompileTimeOnly(param.typ): continue
     if params != nil: add(params, ~", ")
-    fillLoc(param.loc, locParam, param.typ, mangleName(m, param),
+    fillLoc(param.loc, locParam, param.typ, mangleParamName(m, param),
             param.paramStorageLoc)
     if ccgIntroducedPtr(param):
       add(params, getTypeDescWeak(m, param.typ, check))
@@ -436,12 +429,12 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
   else: add(params, ")")
   params = "(" & params
 
-proc mangleRecFieldName(field: PSym, rectype: PType): Rope =
+proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope =
   if (rectype.sym != nil) and
       ({sfImportc, sfExportc} * rectype.sym.flags != {}):
     result = field.loc.r
   else:
-    result = rope(mangleField(field.name))
+    result = rope(mangleField(m, field.name))
   if result == nil: internalError(field.info, "mangleRecFieldName")
 
 proc genRecordFieldsAux(m: BModule, n: PNode,
@@ -480,7 +473,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
     let field = n.sym
     if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
-    let sname = mangleRecFieldName(field, rectype)
+    let sname = mangleRecFieldName(m, field, rectype)
     let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
              else: sname
     fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
@@ -1103,13 +1096,11 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
     discard cgsym(m, "TNimType")
     discard cgsym(m, "TNimNode")
     addf(m.s[cfsVars], "extern TNimType $1;$n", [result])
-    #return "(&".rope & result & ")".rope
-    #result = "NTI$1" % [rope($sig)]
     # also store in local type section:
     m.typeInfoMarker[sig] = result
     return "(&".rope & result & ")".rope
 
-  result = "NTI$1" % [rope($sig)]
+  result = "NTI$1_" % [rope($sig)]
   m.typeInfoMarker[sig] = result
 
   let owner = t.skipTypes(typedescPtrs).owner.getModule
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 4d6ba858c..d42f0438f 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -164,28 +164,50 @@ proc makeSingleLineCString*(s: string): string =
   result.add('\"')
 
 proc mangle*(name: string): string =
-  ## Lowercases the given name and manges any non-alphanumeric characters
-  ## so they are represented as `HEX____`. If the name starts with a number,
-  ## `N` is prepended
   result = newStringOfCap(name.len)
-  case name[0]
-  of Letters:
-    result.add(name[0])
-  of Digits:
-    result.add("N" & name[0])
-  else:
-    result = "HEX" & toHex(ord(name[0]), 2)
-  for i in 1..(name.len-1):
+  var start = 0
+  if name[0] in Digits:
+    result.add("X" & name[0])
+    start = 1
+  var requiresUnderscore = false
+  template special(x) =
+    result.add x
+    requiresUnderscore = true
+  for i in start..(name.len-1):
     let c = name[i]
     case c
-    of 'A'..'Z':
-      add(result, c.toLowerAscii)
-    of '_':
-      discard
-    of 'a'..'z', '0'..'9':
+    of 'a'..'z', '0'..'9', 'A'..'Z':
       add(result, c)
+    of '_':
+      # we generate names like 'foo_9' for scope disambiguations and so
+      # disallow this here:
+      if i < name.len-1 and name[i] in Digits:
+        discard
+      else:
+        add(result, c)
+    of '$': special "dollar"
+    of '%': special "percent"
+    of '&': special "amp"
+    of '^': special "roof"
+    of '!': special "emark"
+    of '?': special "qmark"
+    of '*': special "star"
+    of '+': special "plus"
+    of '-': special "minus"
+    of '/': special "slash"
+    of '=': special "eq"
+    of '<': special "lt"
+    of '>': special "gt"
+    of '~': special "tilde"
+    of ':': special "colon"
+    of '.': special "dot"
+    of '@': special "at"
+    of '|': special "bar"
     else:
-      add(result, "HEX" & toHex(ord(c), 2))
+      add(result, "X" & toHex(ord(c), 2))
+      requiresUnderscore = true
+  if requiresUnderscore:
+    result.add "_"
 
 proc makeLLVMString*(s: string): Rope =
   const MaxLineLength = 64
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 476b1362f..62ed9ad6e 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -14,7 +14,7 @@ import
   nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
   ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
   condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
-  lowerings, semparallel, tables
+  lowerings, semparallel, tables, sets, ndi
 
 import strutils except `%` # collides with ropes.`%`
 
@@ -216,7 +216,7 @@ proc genLineDir(p: BProc, t: PNode) =
       {optLineTrace, optStackTrace}) and
       (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0:
     if freshLineInfo(p, tt.info):
-      linefmt(p, cpsStmts, "nimln($1, $2);$n",
+      linefmt(p, cpsStmts, "nimln_($1, $2);$n",
               line.rope, tt.info.quotedFilename)
 
 proc postStmtActions(p: BProc) {.inline.} =
@@ -338,7 +338,7 @@ 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)
+  result.r = "T" & rope(p.labels) & "_"
   linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
   result.k = locTemp
   result.t = t
@@ -347,12 +347,12 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   constructLoc(p, result, not needsInit)
 
 proc initGCFrame(p: BProc): Rope =
-  if p.gcFrameId > 0: result = "struct {$1} GCFRAME;$n" % [p.gcFrameType]
+  if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
 
 proc deinitGCFrame(p: BProc): Rope =
   if p.gcFrameId > 0:
     result = ropecg(p.module,
-                    "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n")
+                    "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
 
 proc localDebugInfo(p: BProc, s: PSym) =
   if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
@@ -361,7 +361,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
   var a = "&" & s.loc.r
   if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
   lineF(p, cpsInit,
-       "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n",
+       "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
        [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
         genTypeInfo(p.module, s.loc.t)])
   inc(p.maxFrameLen)
@@ -369,7 +369,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
 
 proc localVarDecl(p: BProc; s: PSym): Rope =
   if s.loc.k == locNone:
-    fillLoc(s.loc, locLocalVar, s.typ, mangleName(p.module, s), OnStack)
+    fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack)
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   result = getTypeDesc(p.module, s.typ)
   if s.constraint.isNil:
@@ -434,6 +434,7 @@ proc assignGlobalVar(p: BProc, s: PSym) =
 
 proc assignParam(p: BProc, s: PSym) =
   assert(s.loc.r != nil)
+  scopeMangledParam(p, s)
   localDebugInfo(p, s)
 
 proc fillProcLoc(m: BModule; sym: PSym) =
@@ -442,7 +443,7 @@ proc fillProcLoc(m: BModule; sym: PSym) =
 
 proc getLabel(p: BProc): TLabel =
   inc(p.labels)
-  result = "LA" & rope(p.labels)
+  result = "LA" & rope(p.labels) & "_"
 
 proc fixLabel(p: BProc, labl: TLabel) =
   lineF(p, cpsStmts, "$1: ;$n", [labl])
@@ -520,7 +521,7 @@ proc mangleDynLibProc(sym: PSym): Rope =
     # NOTE: sym.loc.r is the external name!
     result = rope(sym.name.s)
   else:
-    result = "Dl_$1" % [rope(sym.id)]
+    result = "Dl_$1_" % [rope(sym.id)]
 
 proc symInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
@@ -608,11 +609,11 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
   discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
     discard cgsym(p.module, "VarSlot")
-    result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
+    result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4)$N",
                   procname, filename, p.maxFrameLen.rope,
                   p.blocks[0].frameLen.rope)
   else:
-    result = rfmt(nil, "\tnimfr($1, $2)$N", procname, filename)
+    result = rfmt(nil, "\tnimfr_($1, $2)$N", procname, filename)
 
 proc deinitFrame(p: BProc): Rope =
   result = rfmt(p.module, "\t#popFrame();$n")
@@ -707,7 +708,7 @@ proc genProcAux(m: BModule, prc: PSym) =
     if p.beforeRetNeeded: add(generatedProc, "{")
     add(generatedProc, p.s(cpsInit))
     add(generatedProc, p.s(cpsStmts))
-    if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet: ;$n")
+    if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet_: ;$n")
     add(generatedProc, deinitGCFrame(p))
     if optStackTrace in prc.options: add(generatedProc, deinitFrame(p))
     add(generatedProc, returnStmt)
@@ -846,7 +847,8 @@ proc genVarPrototype(m: BModule, sym: PSym) =
   genVarPrototypeAux(m, sym)
 
 proc addIntTypes(result: var Rope) {.inline.} =
-  addf(result, "#define NIM_INTBITS $1" & tnl, [
+  addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
+               "#define NIM_INTBITS $1" & tnl, [
     platform.CPU[targetCPU].intSize.rope])
 
 proc getCopyright(cfile: Cfile): Rope =
@@ -1058,7 +1060,7 @@ proc genInitCode(m: BModule) =
       var procname = makeCString(m.module.name.s)
       add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
-      add(prc, ~"\tTFrame FR; FR.len = 0;$N")
+      add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
 
   add(prc, genSectionStart(cpsInit))
   add(prc, m.preInitProc.s(cpsInit))
@@ -1123,7 +1125,7 @@ proc initProcOptions(m: BModule): TOptions =
 
 proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   new(result)
-  result.tmpBase = rope("T" & $hashOwner(module) & "_")
+  result.tmpBase = rope("TM" & $hashOwner(module) & "_")
   initLinkedList(result.headerFiles)
   result.declaredThings = initIntSet()
   result.declaredProtos = initIntSet()
@@ -1150,6 +1152,9 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
     incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
     excl(result.postInitProc.options, optStackTrace)
+  let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi")
+                else: ""
+  open(result.ndi, ndiName)
 
 proc nullify[T](arr: var T) =
   for i in low(arr)..high(arr):
@@ -1212,16 +1217,16 @@ proc newModule(g: BModuleList; module: PSym): BModule =
     if (sfDeadCodeElim in module.flags):
       internalError("added pending module twice: " & module.filename)
 
-template injectG() {.dirty.} =
+template injectG(config) {.dirty.} =
   if graph.backend == nil:
-    graph.backend = newModuleList()
+    graph.backend = newModuleList(config)
   let g = BModuleList(graph.backend)
 
 proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  injectG()
+  injectG(graph.config)
   result = newModule(g, module)
   if optGenIndex in gGlobalOptions and g.generatedHeader == nil:
-    let f = if headerFile.len > 0: headerFile else: gProjectFull
+    let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull
     g.generatedHeader = rawNewModule(g, module,
       changeFileExt(completeCFilePath(f), hExt))
     incl g.generatedHeader.flags, isHeaderFile
@@ -1258,7 +1263,7 @@ proc getCFile(m: BModule): string =
   result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext)
 
 proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
-  injectG()
+  injectG(graph.config)
   assert optSymbolFiles in gGlobalOptions
   var m = newModule(g, module)
   readMergeInfo(getCFile(m), m)
@@ -1341,6 +1346,7 @@ proc writeModule(m: BModule, pending: bool) =
     var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
     if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
     addFileToCompile(cf)
+  close(m.ndi)
 
 proc updateCachedModule(m: BModule) =
   let cfile = getCFile(m)
@@ -1373,11 +1379,12 @@ proc myClose(b: PPassContext, n: PNode): PNode =
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
 
-proc cgenWriteModules*(backend: RootRef) =
+proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
   let g = BModuleList(backend)
   # we need to process the transitive closure because recursive module
   # deps are allowed (and the system module is processed in the wrong
   # order anyway)
+  g.config = config
   if g.generatedHeader != nil: finishModule(g.generatedHeader)
   while g.forwardedProcsCounter > 0:
     for m in cgenModules(g):
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 94d34c5cf..565399ead 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -11,7 +11,7 @@
 
 import
   ast, astalgo, ropes, passes, options, intsets, lists, platform, sighashes,
-  tables
+  tables, ndi
 
 from msgs import TLineInfo
 
@@ -56,7 +56,7 @@ type
   BProc* = ref TCProc
   TBlock*{.final.} = object
     id*: int                  # the ID of the label; positive means that it
-    label*: Rope             # generated text for the label
+    label*: Rope              # generated text for the label
                               # nil if label is not used
     sections*: TCProcSections # the code beloging
     isLoop*: bool             # whether block is a loop
@@ -76,7 +76,7 @@ type
                               # leaving such scopes by raise or by return must
                               # execute any applicable finally blocks
     finallySafePoints*: seq[Rope]  # For correctly cleaning up exceptions when
-                                    # using return in finally statements
+                                   # using return in finally statements
     labels*: Natural          # for generating unique labels in the C proc
     blocks*: seq[TBlock]      # nested blocks
     breakIdx*: int            # the block that will be exited
@@ -92,6 +92,7 @@ type
                               # (yes, C++ is weird like that)
     gcFrameId*: Natural       # for the GC stack marking
     gcFrameType*: Rope        # the struct {} we put the GC markers into
+    sigConflicts*: CountTable[string]
 
   TTypeSeq* = seq[PType]
   TypeCache* = Table[SigHash, Rope]
@@ -115,6 +116,7 @@ type
     breakPointId*: int
     breakpoints*: Rope # later the breakpoints are inserted into the main proc
     typeInfoMarker*: TypeCache
+    config*: ConfigRef
 
   TCGen = object of TPassContext # represents a C source file
     s*: TCFileSections        # sections of the C file
@@ -144,6 +146,7 @@ type
     injectStmt*: Rope
     sigConflicts*: CountTable[SigHash]
     g*: BModuleList
+    ndi*: NdiFile
 
 proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
   # section in the current block
@@ -162,9 +165,10 @@ proc newProc*(prc: PSym, module: BModule): BProc =
   newSeq(result.blocks, 1)
   result.nestedTryStmts = @[]
   result.finallySafePoints = @[]
+  result.sigConflicts = initCountTable[string]()
 
-proc newModuleList*(): BModuleList =
-  BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope]())
+proc newModuleList*(config: ConfigRef): BModuleList =
+  BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: config)
 
 iterator cgenModules*(g: BModuleList): BModule =
   for i in 0..high(g.modules):
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 61189fba1..74503a414 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -47,7 +47,8 @@ type
     passPP                    # preprocessor called processCommand()
 
 proc processCommand*(switch: string, pass: TCmdLinePass)
-proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo)
+proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
+                    config: ConfigRef = nil)
 
 # implementation
 
@@ -312,7 +313,8 @@ proc dynlibOverride(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     expectArg(switch, arg, pass, info)
     options.inclDynlibOverride(arg)
 
-proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
+proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
+                   config: ConfigRef = nil) =
   var
     theOS: TSystemOS
     cpu: TSystemCPU
@@ -509,10 +511,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     else: localError(info, errGuiConsoleOrLibExpectedButXFound, arg)
   of "passc", "t":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg)
+    if pass in {passCmd2, passPP}: extccomp.addCompileOptionCmd(arg)
   of "passl", "l":
     expectArg(switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg)
+    if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(arg)
   of "cincludes":
     expectArg(switch, arg, pass, info)
     if pass in {passCmd2, passPP}: cIncludes.add arg.processPath(info)
@@ -523,7 +525,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     expectArg(switch, arg, pass, info)
     if pass in {passCmd2, passPP}: cLinkedLibs.add arg.processPath(info)
   of "header":
-    headerFile = arg
+    if config != nil: config.headerFile = arg
     incl(gGlobalOptions, optGenIndex)
   of "index":
     processOnOffSwitchG({optGenIndex}, arg, pass, info)
@@ -646,6 +648,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optNoCppExceptions)
     defineSymbol("noCppExceptions")
+  of "cppdefine":
+    expectArg(switch, arg, pass, info)
+    if config != nil:
+      config.cppDefine(arg)
   else:
     if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
     else: invalidCmdLineOption(pass, switch, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 98c72f862..a738ddb48 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -101,3 +101,4 @@ proc initDefines*() =
   defineSymbol("nimImmediateDeprecated")
   defineSymbol("nimNewShiftOps")
   defineSymbol("nimDistros")
+  defineSymbol("nimHasCppDefine")
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 211544924..26dd889ce 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -209,26 +209,26 @@ proc getPlainDocstring(n: PNode): string =
         result = getPlainDocstring(n.sons[i])
         if result.len > 0: return
 
-
-proc findDocComment(n: PNode): PNode =
-  if n == nil: return nil
-  if not isNil(n.comment) and startsWith(n.comment, "##"): return n
-  for i in countup(0, safeLen(n)-1):
-    result = findDocComment(n.sons[i])
-    if result != nil: return
-
-proc extractDocComment*(s: PSym, d: PDoc = nil): string =
-  let n = findDocComment(s.ast)
-  result = ""
-  if not n.isNil:
-    if not d.isNil:
-      var dummyHasToc: bool
-      renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
-                                   toLinenumber(n.info), toColumn(n.info),
-                                   dummyHasToc, d.options + {roSkipPounds}),
-                     result)
-    else:
-      result = n.comment.substr(2).replace("\n##", "\n").strip
+when false:
+  proc findDocComment(n: PNode): PNode =
+    if n == nil: return nil
+    if not isNil(n.comment) and startsWith(n.comment, "##"): return n
+    for i in countup(0, safeLen(n)-1):
+      result = findDocComment(n.sons[i])
+      if result != nil: return
+
+  proc extractDocComment*(s: PSym, d: PDoc = nil): string =
+    let n = findDocComment(s.ast)
+    result = ""
+    if not n.isNil:
+      if not d.isNil:
+        var dummyHasToc: bool
+        renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+                                     toLinenumber(n.info), toColumn(n.info),
+                                     dummyHasToc, d.options + {roSkipPounds}),
+                       result)
+      else:
+        result = n.comment.substr(2).replace("\n##", "\n").strip
 
 proc isVisible(n: PNode): bool =
   result = false
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 8ca34223b..0f283b208 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -392,6 +392,8 @@ type
 var
   externalToLink: TLinkedList # files to link in addition to the file
                               # we compiled
+  linkOptionsCmd: string = ""
+  compileOptionsCmd: seq[string] = @[]
   linkOptions: string = ""
   compileOptions: string = ""
   ccompilerpath: string = ""
@@ -450,6 +452,12 @@ proc addCompileOption*(option: string) =
   if strutils.find(compileOptions, option, 0) < 0:
     addOpt(compileOptions, option)
 
+proc addLinkOptionCmd*(option: string) =
+  addOpt(linkOptionsCmd, option)
+
+proc addCompileOptionCmd*(option: string) =
+  compileOptionsCmd.add(option)
+
 proc initVars*() =
   # we need to define the symbol here, because ``CC`` may have never been set!
   for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
@@ -524,6 +532,10 @@ proc add(s: var string, many: openArray[string]) =
 
 proc cFileSpecificOptions(cfilename: string): string =
   result = compileOptions
+  for option in compileOptionsCmd:
+    if strutils.find(result, option, 0) < 0:
+      addOpt(result, option)
+
   var trunk = splitFile(cfilename).name
   if optCDebug in gGlobalOptions:
     var key = trunk & ".debug"
@@ -544,7 +556,7 @@ proc getCompileOptions: string =
   result = cFileSpecificOptions("__dummy__")
 
 proc getLinkOptions: string =
-  result = linkOptions
+  result = linkOptions & " " & linkOptionsCmd & " "
   for linkedLib in items(cLinkedLibs):
     result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
   for libDir in items(cLibs):
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 2263e030f..31c6f7728 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -46,7 +46,7 @@ Start: "doc/html/overview.html"
 
 
 [Other]
-Files: "readme.txt;install.txt;contributors.txt;copying.txt"
+Files: "readme.txt;copying.txt"
 Files: "makefile"
 Files: "koch.nim"
 Files: "install_nimble.nims"
@@ -94,15 +94,17 @@ Files: "bin/vccexe.exe"
 
 Files: "koch.exe"
 Files: "finish.exe"
+Files: "downloader.exe"
+
 ; Files: "dist/mingw"
 Files: r"tools\start.bat"
 BinPath: r"bin;dist\mingw\bin;dist"
 
 ;           Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
-Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
-Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
-Download: r"Support DLLs|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip"
-Download: r"Aporia Text Editor|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
+Download: r"Documentation|doc|docs.zip|13824|https://nim-lang.org/download/docs-${version}.zip|overview.html"
+Download: r"C Compiler (MingW)|dist|mingw.zip|82944|https://nim-lang.org/download/${mingw}.zip"
+Download: r"Support DLLs|bin|nim_dlls.zip|479|https://nim-lang.org/download/dlls.zip"
+Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
 ; for now only NSIS supports optional downloads
 
 [WinBin]
diff --git a/compiler/main.nim b/compiler/main.nim
index 888f89ad5..2acb7620c 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -72,7 +72,7 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
   #registerPass(cleanupPass())
 
   compileProject(graph, cache)
-  cgenWriteModules(graph.backend)
+  cgenWriteModules(graph.backend, graph.config)
   if gCmd != cmdRun:
     let proj = changeFileExt(gProjectFull, "")
     extccomp.callCCompiler(proj)
@@ -294,4 +294,4 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
 
   resetAttributes()
 
-proc mainCommand*() = mainCommand(newModuleGraph(), newIdentCache())
+proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 466e12e64..87a35b290 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -25,7 +25,7 @@
 ## - Its dependent module stays the same.
 ##
 
-import ast, intsets, tables
+import ast, intsets, tables, options
 
 type
   ModuleGraph* = ref object
@@ -39,16 +39,21 @@ type
     importStack*: seq[int32]  # The current import stack. Used for detecting recursive
                               # module dependencies.
     backend*: RootRef # minor hack so that a backend can extend this easily
+    config*: ConfigRef
 
 {.this: g.}
 
-proc newModuleGraph*(): ModuleGraph =
+proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
   result = ModuleGraph()
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.modules = @[]
   result.importStack = @[]
   result.inclToMod = initTable[int32, int32]()
+  if config.isNil:
+    result.config = newConfigRef()
+  else:
+    result.config = config
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e6a2b75a6..49e4fa184 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -12,7 +12,7 @@ import
 
 type
   TMsgKind* = enum
-    errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
+    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
     errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
     errIntLiteralExpected, errInvalidCharacterConstant,
     errClosingTripleQuoteExpected, errClosingQuoteExpected,
@@ -135,8 +135,8 @@ type
 const
   MsgKindToStr*: array[TMsgKind, string] = [
     errUnknown: "unknown error",
-    errIllFormedAstX: "illformed AST: $1",
     errInternal: "internal error: $1",
+    errIllFormedAstX: "illformed AST: $1",
     errCannotOpenFile: "cannot open \'$1\'",
     errGenerated: "$1",
     errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++",
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
new file mode 100644
index 000000000..a7ca02193
--- /dev/null
+++ b/compiler/ndi.nim
@@ -0,0 +1,40 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the generation of ``.ndi`` files for better debugging
+## support of Nim code. "ndi" stands for "Nim debug info".
+
+import ast, msgs, ropes
+
+type
+  NdiFile* = object
+    enabled: bool
+    f: File
+    buf: string
+
+proc doWrite(f: var NdiFile; s: PSym) =
+  f.buf.setLen 0
+  f.buf.add s.info.line.int
+  f.buf.add "\t"
+  f.buf.add s.info.col.int
+  f.f.write(s.name.s, "\t")
+  f.f.writeRope(s.loc.r)
+  f.f.writeLine("\t", s.info.toFullPath, "\t", f.buf)
+
+template writeMangledName*(f: NdiFile; s: PSym) =
+  if f.enabled: doWrite(f, s)
+
+proc open*(f: var NdiFile; filename: string) =
+  f.enabled = filename.len > 0
+  if f.enabled:
+    f.f = open(filename, fmWrite, 8000)
+    f.buf = newStringOfCap(20)
+
+proc close*(f: var NdiFile) =
+  if f.enabled: close(f.f)
diff --git a/compiler/nim.nim b/compiler/nim.nim
index c458f76f9..56885e9f1 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -37,7 +37,7 @@ proc prependCurDir(f: string): string =
   else:
     result = f
 
-proc handleCmdLine(cache: IdentCache) =
+proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
   if paramCount() == 0:
     writeCommandLineUsage()
   else:
@@ -59,22 +59,22 @@ proc handleCmdLine(cache: IdentCache) =
       gProjectName = p.name
     else:
       gProjectPath = canonicalizePath getCurrentDir()
-    loadConfigs(DefaultConfig) # load all config files
+    loadConfigs(DefaultConfig, config) # load all config files
     let scriptFile = gProjectFull.changeFileExt("nims")
     if fileExists(scriptFile):
-      runNimScript(cache, scriptFile, freshDefines=false)
+      runNimScript(cache, scriptFile, freshDefines=false, config)
       # '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(cache, gProjectPath / "config.nims", freshDefines=false)
+      runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
     processCmdLine(passCmd2, "")
     if options.command == "":
       rawMessage(errNoCommand, command)
-    mainCommand(newModuleGraph(), cache)
+    mainCommand(newModuleGraph(config), cache)
     if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
     if msgs.gErrorCounter == 0:
@@ -118,5 +118,5 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
 condsyms.initDefines()
 
 when not defined(selftest):
-  handleCmdLine(newIdentCache())
+  handleCmdLine(newIdentCache(), newConfigRef())
   msgQuit(int8(msgs.gErrorCounter > 0))
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 4bf2fbc9a..808159b8f 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -21,37 +21,37 @@ proc ppGetTok(L: var TLexer, tok: var TToken) =
   rawGetTok(L, tok)
   while tok.tokType in {tkComment}: rawGetTok(L, tok)
 
-proc parseExpr(L: var TLexer, tok: var TToken): bool
-proc parseAtom(L: var TLexer, tok: var TToken): bool =
+proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool
+proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
   if tok.tokType == tkParLe:
     ppGetTok(L, tok)
-    result = parseExpr(L, tok)
+    result = parseExpr(L, tok, config)
     if tok.tokType == tkParRi: ppGetTok(L, tok)
     else: lexMessage(L, errTokenExpected, "\')\'")
   elif tok.ident.id == ord(wNot):
     ppGetTok(L, tok)
-    result = not parseAtom(L, tok)
+    result = not parseAtom(L, tok, config)
   else:
     result = isDefined(tok.ident)
     ppGetTok(L, tok)
 
-proc parseAndExpr(L: var TLexer, tok: var TToken): bool =
-  result = parseAtom(L, tok)
+proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+  result = parseAtom(L, tok, config)
   while tok.ident.id == ord(wAnd):
     ppGetTok(L, tok)          # skip "and"
-    var b = parseAtom(L, tok)
+    var b = parseAtom(L, tok, config)
     result = result and b
 
-proc parseExpr(L: var TLexer, tok: var TToken): bool =
-  result = parseAndExpr(L, tok)
+proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+  result = parseAndExpr(L, tok, config)
   while tok.ident.id == ord(wOr):
     ppGetTok(L, tok)          # skip "or"
-    var b = parseAndExpr(L, tok)
+    var b = parseAndExpr(L, tok, config)
     result = result or b
 
-proc evalppIf(L: var TLexer, tok: var TToken): bool =
+proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
   ppGetTok(L, tok)            # skip 'if' or 'elif'
-  result = parseExpr(L, tok)
+  result = parseExpr(L, tok, config)
   if tok.tokType == tkColon: ppGetTok(L, tok)
   else: lexMessage(L, errTokenExpected, "\':\'")
 
@@ -66,20 +66,20 @@ type
   TJumpDest = enum
     jdEndif, jdElseEndif
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest)
-proc doElse(L: var TLexer, tok: var TToken) =
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
+proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
   if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
   ppGetTok(L, tok)
   if tok.tokType == tkColon: ppGetTok(L, tok)
-  if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif)
+  if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
 
-proc doElif(L: var TLexer, tok: var TToken) =
+proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
   if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
-  var res = evalppIf(L, tok)
-  if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif)
+  var res = evalppIf(L, tok, config)
+  if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
   else: condStack[high(condStack)] = true
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
+proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
   var nestedIfs = 0
   while true:
     if tok.ident != nil and tok.ident.s == "@":
@@ -89,11 +89,11 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
         inc(nestedIfs)
       of wElse:
         if dest == jdElseEndif and nestedIfs == 0:
-          doElse(L, tok)
+          doElse(L, tok, config)
           break
       of wElif:
         if dest == jdElseEndif and nestedIfs == 0:
-          doElif(L, tok)
+          doElif(L, tok, config)
           break
       of wEnd:
         if nestedIfs == 0:
@@ -108,16 +108,16 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
     else:
       ppGetTok(L, tok)
 
-proc parseDirective(L: var TLexer, tok: var TToken) =
+proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
   ppGetTok(L, tok)            # skip @
   case whichKeyword(tok.ident)
   of wIf:
     setLen(condStack, len(condStack) + 1)
-    let res = evalppIf(L, tok)
+    let res = evalppIf(L, tok, config)
     condStack[high(condStack)] = res
-    if not res: jumpToDirective(L, tok, jdElseEndif)
-  of wElif: doElif(L, tok)
-  of wElse: doElse(L, tok)
+    if not res: jumpToDirective(L, tok, jdElseEndif, config)
+  of wElif: doElif(L, tok, config)
+  of wElse: doElse(L, tok, config)
   of wEnd: doEnd(L, tok)
   of wWrite:
     ppGetTok(L, tok)
@@ -146,58 +146,58 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
       ppGetTok(L, tok)
     else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
 
-proc confTok(L: var TLexer, tok: var TToken) =
+proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
   ppGetTok(L, tok)
   while tok.ident != nil and tok.ident.s == "@":
-    parseDirective(L, tok)    # else: give the token to the parser
+    parseDirective(L, tok, config)    # else: give the token to the parser
 
 proc checkSymbol(L: TLexer, tok: TToken) =
   if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
     lexMessage(L, errIdentifierExpected, tokToStr(tok))
 
-proc parseAssignment(L: var TLexer, tok: var TToken) =
+proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
   if tok.ident.s == "-" or tok.ident.s == "--":
-    confTok(L, tok)           # skip unnecessary prefix
+    confTok(L, tok, config)           # skip unnecessary prefix
   var info = getLineInfo(L, tok) # save for later in case of an error
   checkSymbol(L, tok)
   var s = tokToStr(tok)
-  confTok(L, tok)             # skip symbol
+  confTok(L, tok, config)             # skip symbol
   var val = ""
   while tok.tokType == tkDot:
     add(s, '.')
-    confTok(L, tok)
+    confTok(L, tok, config)
     checkSymbol(L, tok)
     add(s, tokToStr(tok))
-    confTok(L, tok)
+    confTok(L, tok, config)
   if tok.tokType == tkBracketLe:
     # BUGFIX: val, not s!
     # BUGFIX: do not copy '['!
-    confTok(L, tok)
+    confTok(L, tok, config)
     checkSymbol(L, tok)
     add(val, tokToStr(tok))
-    confTok(L, tok)
-    if tok.tokType == tkBracketRi: confTok(L, tok)
+    confTok(L, tok, config)
+    if tok.tokType == tkBracketRi: confTok(L, tok, config)
     else: lexMessage(L, errTokenExpected, "']'")
     add(val, ']')
   let percent = tok.ident != nil and tok.ident.s == "%="
   if tok.tokType in {tkColon, tkEquals} or percent:
     if len(val) > 0: add(val, ':')
-    confTok(L, tok)           # skip ':' or '=' or '%'
+    confTok(L, tok, config)           # skip ':' or '=' or '%'
     checkSymbol(L, tok)
     add(val, tokToStr(tok))
-    confTok(L, tok)           # skip symbol
+    confTok(L, tok, config)           # skip symbol
     while tok.ident != nil and tok.ident.s == "&":
-      confTok(L, tok)
+      confTok(L, tok, config)
       checkSymbol(L, tok)
       add(val, tokToStr(tok))
-      confTok(L, tok)
+      confTok(L, tok, config)
   if percent:
     processSwitch(s, strtabs.`%`(val, options.gConfigVars,
-                                {useEnvironment, useEmpty}), passPP, info)
+                                {useEnvironment, useEmpty}), passPP, info, config)
   else:
-    processSwitch(s, val, passPP, info)
+    processSwitch(s, val, passPP, info, config)
 
-proc readConfigFile(filename: string; cache: IdentCache) =
+proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
   var
     L: TLexer
     tok: TToken
@@ -207,8 +207,8 @@ proc readConfigFile(filename: string; cache: IdentCache) =
     initToken(tok)
     openLexer(L, filename, stream, cache)
     tok.tokType = tkEof       # to avoid a pointless warning
-    confTok(L, tok)           # read in the first token
-    while tok.tokType != tkEof: parseAssignment(L, tok)
+    confTok(L, tok, config)           # read in the first token
+    while tok.tokType != tkEof: parseAssignment(L, tok, config)
     if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
     closeLexer(L)
     rawMessage(hintConf, filename)
@@ -225,22 +225,22 @@ proc getSystemConfigPath(filename: string): string =
     if not existsFile(result): result = joinPath([p, "etc", filename])
     if not existsFile(result): result = "/etc/" & filename
 
-proc loadConfigs*(cfg: string; cache: IdentCache) =
+proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
   setDefaultLibpath()
 
   if optSkipConfigFile notin gGlobalOptions:
-    readConfigFile(getSystemConfigPath(cfg), cache)
+    readConfigFile(getSystemConfigPath(cfg), cache, config)
 
   if optSkipUserConfigFile notin gGlobalOptions:
-    readConfigFile(getUserConfigPath(cfg), cache)
+    readConfigFile(getUserConfigPath(cfg), cache, config)
 
   var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
   if optSkipParentConfigFiles notin gGlobalOptions:
     for dir in parentDirs(pd, fromRoot=true, inclusive=false):
-      readConfigFile(dir / cfg, cache)
+      readConfigFile(dir / cfg, cache, config)
 
   if optSkipProjConfigFile notin gGlobalOptions:
-    readConfigFile(pd / cfg, cache)
+    readConfigFile(pd / cfg, cache, config)
 
     if gProjectName.len != 0:
       # new project wide config file:
@@ -251,8 +251,8 @@ proc loadConfigs*(cfg: string; cache: IdentCache) =
         projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
         if fileExists(projectConfig):
           rawMessage(warnDeprecated, projectConfig)
-      readConfigFile(projectConfig, cache)
+      readConfigFile(projectConfig, cache, config)
 
-proc loadConfigs*(cfg: string) =
+proc loadConfigs*(cfg: string; config: ConfigRef = nil) =
   # for backwards compatibility only.
-  loadConfigs(cfg, newIdentCache())
+  loadConfigs(cfg, newIdentCache(), config)
diff --git a/compiler/options.nim b/compiler/options.nim
index 746ee9044..2295bbf93 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -102,6 +102,17 @@ type
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
     ideHighlight, ideOutline
 
+  ConfigRef* = ref object ## eventually all global configuration should be moved here
+    cppDefines*: HashSet[string]
+    headerFile*: string
+
+proc newConfigRef*(): ConfigRef =
+  result = ConfigRef(cppDefines: initSet[string](),
+    headerFile: "")
+
+proc cppDefine*(c: ConfigRef; define: string) =
+  c.cppDefines.incl define
+
 var
   gIdeCmd*: IdeCmd
 
@@ -122,7 +133,7 @@ var
   outFile*: string = ""
   docSeeSrcUrl*: string = ""  # if empty, no seeSrc will be generated. \
   # The string uses the formatting variables `path` and `line`.
-  headerFile*: string = ""
+  #headerFile*: string = ""
   gVerbosity* = 1             # how verbose the compiler is
   gNumberOfProcessors*: int   # number of processors
   gWholeProject*: bool        # for 'doc2': output any dependency
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index e750cc390..04dbd3612 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -665,9 +665,14 @@ 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"), it.info)
+      of wImportc:
+        let name = getOptionalStr(c, it, "$1")
+        cppDefine(c.graph.config, name)
+        makeExternImport(sym, name, it.info)
       of wImportCompilerProc:
-        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"), it.info)
+        let name = getOptionalStr(c, it, "$1")
+        cppDefine(c.graph.config, name)
+        processImportCompilerProc(sym, name, it.info)
       of wExtern: setExternName(sym, expectStrLit(c, it), it.info)
       of wImmediate:
         if sym.kind in {skTemplate, skMacro}:
@@ -758,6 +763,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         processDynLib(c, it, sym)
       of wCompilerproc:
         noVal(it)           # compilerproc may not get a string!
+        cppDefine(c.graph.config, sym.name.s)
         if sfFromGeneric notin sym.flags: markCompilerProc(sym)
       of wProcVar:
         noVal(it)
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 75ecf4b02..9e94f1c19 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -25,7 +25,8 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
     if kind in filter: result.add path
   setResult(a, result)
 
-proc setupVM*(module: PSym; cache: IdentCache; scriptName: string): PEvalContext =
+proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
+              config: ConfigRef = nil): PEvalContext =
   # For Nimble we need to export 'setupVM'.
   result = newCtx(module, cache)
   result.mode = emRepl
@@ -109,10 +110,13 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string): PEvalContext
     let arg = a.getString 1
     if arg.len > 0:
       gProjectName = arg
+      let path =
+        if gProjectName.isAbsolute: gProjectName
+        else: gProjectPath / gProjectName
       try:
-        gProjectFull = canonicalizePath(gProjectPath / gProjectName)
+        gProjectFull = canonicalizePath(path)
       except OSError:
-        gProjectFull = gProjectName
+        gProjectFull = path
   cbconf getCommand:
     setResult(a, options.command)
   cbconf switch:
@@ -133,12 +137,15 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string): PEvalContext
     gModuleOverrides[key] = val
   cbconf selfExe:
     setResult(a, os.getAppFilename())
+  cbconf cppDefine:
+    if config != nil:
+      options.cppDefine(config, a.getString(0))
 
 proc runNimScript*(cache: IdentCache; scriptName: string;
-                   freshDefines=true) =
+                   freshDefines=true; config: ConfigRef=nil) =
   passes.gIncludeFile = includeModule
   passes.gImportModule = importModule
-  let graph = newModuleGraph()
+  let graph = newModuleGraph(config)
   if freshDefines: initDefines()
 
   defineSymbol("nimscript")
@@ -150,7 +157,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  vm.globalCtx = setupVM(m, cache, scriptName)
+  vm.globalCtx = setupVM(m, cache, scriptName, config)
 
   graph.compileSystemModule(cache)
   discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 54a301322..57674735a 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1052,6 +1052,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # work without now. template/tsymchoicefield doesn't like an early exit
   # here at all!
   #if isSymChoice(n.sons[1]): return
+  when defined(nimsuggest):
+    if gCmd == cmdIdeTools: suggestExpr(c, n)
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
   if s != nil:
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index e1a65da74..9c57be023 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -97,10 +97,17 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
       if inst.compilesId == id and sameInstantiation(entry, inst[]):
         return inst.sym
 
+when false:
+  proc `$`(x: PSym): string =
+    result = x.name.s & " " & " id " & $x.id
+
 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 and
-      (n.sym.owner == orig or n.sym.owner.kind == skPackage):
+  #if n.kind == nkSym and sfGenSym in n.sym.flags:
+  #  if n.sym.owner != orig:
+  #    echo "symbol ", n.sym.name.s, " orig ", orig, " owner ", n.sym.owner
+  if n.kind == nkSym and {sfGenSym, sfFromGeneric} * n.sym.flags == {sfGenSym}: # and
+    #  (n.sym.owner == orig or n.sym.owner.kind in {skPackage}):
     let s = n.sym
     var x = PSym(idTableGet(symMap, s))
     if x == nil:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index c9a70e9bc..a69fe477b 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -112,6 +112,7 @@ type
     toBind, toMixin, toInject: IntSet
     owner: PSym
     cursorInBody: bool # only for nimsuggest
+    scopeN: int
     bracketExpr: PNode
 
 template withBracketExpr(ctx, x, body: untyped) =
@@ -141,8 +142,13 @@ proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode
 
-proc openScope(c: var TemplCtx) = openScope(c.c)
-proc closeScope(c: var TemplCtx) = closeScope(c.c)
+proc openScope(c: var TemplCtx) =
+  openScope(c.c)
+  inc c.scopeN
+
+proc closeScope(c: var TemplCtx) =
+  dec c.scopeN
+  closeScope(c.c)
 
 proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
   openScope(c)
@@ -166,6 +172,7 @@ proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
   result = newSym(kind, considerQuotedIdent(n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
+  if c.scopeN == 0: incl(result.flags, sfFromGeneric)
 
 proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
   # locals default to 'gensym':
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 028baa555..17c065b49 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -659,7 +659,8 @@ proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
   addInheritedFieldsAux(c, check, pos, obj.n)
 
 proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
-  if n.sonsLen == 0: return newConstraint(c, tyObject)
+  if n.sonsLen == 0:
+    return newConstraint(c, tyObject)
   var check = initIntSet()
   var pos = 0
   var base, realBase: PType = nil
@@ -1159,8 +1160,16 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
     result.sym = prev.sym
     assignType(prev, result)
 
+proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
+  if prev != nil:
+    let result = newTypeS(tyAlias, c)
+    result.rawAddSon typExpr.typ
+    result.sym = prev.sym
+    assignType(prev, result)
+
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
+
   if gCmd == cmdIdeTools: suggestExpr(c, n)
   case n.kind
   of nkEmpty: discard
@@ -1168,6 +1177,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     # for ``type(countup(1,3))``, see ``tests/ttoseq``.
     checkSonsLen(n, 1)
     let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
+    fixupTypeOf(c, prev, typExpr)
     result = typExpr.typ
   of nkPar:
     if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
@@ -1234,6 +1244,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       elif op.id == ord(wType):
         checkSonsLen(n, 2)
         let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
+        fixupTypeOf(c, prev, typExpr)
         result = typExpr.typ
       else:
         result = semTypeExpr(c, n, prev)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index f3c03d680..66876b9b5 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -41,6 +41,20 @@ var
 
 template origModuleName(m: PSym): string = m.name.s
 
+proc findDocComment(n: PNode): PNode =
+  if n == nil: return nil
+  if not isNil(n.comment): return n
+  for i in countup(0, safeLen(n)-1):
+    result = findDocComment(n.sons[i])
+    if result != nil: return
+
+proc extractDocComment(s: PSym): string =
+  let n = findDocComment(s.ast)
+  if not n.isNil:
+    result = n.comment.replace("\n##", "\n").strip
+  else:
+    result = ""
+
 proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
                   quality: range[0..100]): Suggest =
   result.section = parseIdeCmd(section)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 7ce96f7df..ea82a3155 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -559,7 +559,8 @@ 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 pointers")
+          ensureKind(rkNode)
+          regs[ra].node = regs[rb].node
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
@@ -932,7 +933,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                             c.module
         var macroCall = newNodeI(nkCall, c.debug[pc])
         macroCall.add(newSymNode(prc))
-        for i in 1 .. rc-1: macroCall.add(regs[rb+i].regToNode)
+        for i in 1 .. rc-1:
+          let node = regs[rb+i].regToNode
+          node.info = c.debug[pc]
+          macroCall.add(node)
         let a = evalTemplate(macroCall, prc, genSymOwner)
         a.recSetFlagIsRef
         ensureKind(rkNode)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index e0f737f08..125fe8ae0 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1259,6 +1259,13 @@ proc isTemp(c: PCtx; dest: TDest): bool =
 template needsAdditionalCopy(n): untyped =
   not c.isTemp(dest) and not fitsRegister(n.typ)
 
+proc genAdditionalCopy(c: PCtx; n: PNode; opc: TOpcode;
+                       dest, idx, value: TRegister) =
+  var cc = c.getTemp(n.typ)
+  c.gABC(n, whichAsgnOpc(n), cc, value, 0)
+  c.gABC(n, opc, dest, idx, cc)
+  c.freeTemp(cc)
+
 proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
                        dest, idx, value: TRegister) =
   # opcLdObj et al really means "load address". We sometimes have to create a
@@ -1266,10 +1273,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
   # mylocal = a.b  # needs a copy of the data!
   assert n.typ != nil
   if needsAdditionalCopy(n):
-    var cc = c.getTemp(n.typ)
-    c.gABC(n, whichAsgnOpc(n), cc, value, 0)
-    c.gABC(n, opc, dest, idx, cc)
-    c.freeTemp(cc)
+    genAdditionalCopy(c, n, opc, dest, idx, value)
   else:
     c.gABC(n, opc, dest, idx, value)
 
@@ -1352,7 +1356,7 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
   c.gABx(n, opcLdGlobal, dest, s.position)
   if s.ast != nil:
     let tmp = c.genx(s.ast)
-    c.preventFalseAlias(n, opcWrDeref, dest, 0, tmp)
+    c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
     c.freeTemp(dest)
     c.freeTemp(tmp)
 
@@ -1506,7 +1510,7 @@ proc genVarSection(c: PCtx; n: PNode) =
     #assert(a.sons[0].kind == nkSym) can happen for transformed vars
     if a.kind == nkVarTuple:
       for i in 0 .. a.len-3:
-        setSlot(c, a[i].sym)
+        if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
         checkCanEval(c, a[i])
       c.gen(lowerTupleUnpacking(a, c.getOwner))
     elif a.sons[0].kind == nkSym:
@@ -1525,7 +1529,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind != nkEmpty:
           let tmp = c.genx(a.sons[0], {gfAddrOf})
           let val = c.genx(a.sons[2])
-          c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val)
+          c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else: