summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/ccgexprs.nim61
-rw-r--r--compiler/ccgstmts.nim9
-rw-r--r--compiler/ccgtypes.nim10
-rw-r--r--compiler/cgen.nim30
-rw-r--r--compiler/cgendata.nim2
-rw-r--r--compiler/cgmeth.nim9
-rw-r--r--compiler/extccomp.nim37
-rw-r--r--compiler/jsgen.nim37
-rw-r--r--compiler/jstypes.nim2
-rw-r--r--compiler/lowerings.nim68
-rw-r--r--compiler/nimblecmd.nim41
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/parser.nim5
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/sempass2.nim6
-rw-r--r--compiler/trees.nim2
-rw-r--r--compiler/vm.nim10
-rw-r--r--compiler/vmdeps.nim7
19 files changed, 241 insertions, 107 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5b68e9712..487b5d1a7 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -741,6 +741,8 @@ type
     OnUnknown,                # location is unknown (stack, heap or static)
     OnStatic,                 # in a static section
     OnStack,                  # location is on hardware stack
+    OnStackShadowDup,         # location is on the stack but also replicated
+                              # on the shadow stack
     OnHeap                    # location is on heap or global
                               # (reference counting needed)
   TLocFlags* = set[TLocFlag]
@@ -750,6 +752,7 @@ type
     flags*: TLocFlags         # location's flags
     t*: PType                 # type of location
     r*: Rope                  # rope value of location (code generators)
+    dup*: Rope                # duplicated location for precise stack scans
 
   # ---------------- end of backend information ------------------------------
 
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index f981f9595..b32f4b66b 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -132,32 +132,12 @@ proc genSetNode(p: BProc, n: PNode): Rope =
   else:
     result = genRawSetData(cs, size)
 
-proc getStorageLoc(n: PNode): TStorageLoc =
-  case n.kind
-  of nkSym:
-    case n.sym.kind
-    of skParam, skTemp:
-      result = OnStack
-    of skVar, skForVar, skResult, skLet:
-      if sfGlobal in n.sym.flags: result = OnHeap
-      else: result = OnStack
-    of skConst:
-      if sfGlobal in n.sym.flags: result = OnHeap
-      else: result = OnUnknown
-    else: result = OnUnknown
-  of nkDerefExpr, nkHiddenDeref:
-    case n.sons[0].typ.kind
-    of tyVar: result = OnUnknown
-    of tyPtr: result = OnStack
-    of tyRef: result = OnHeap
-    else: internalError(n.info, "getStorageLoc")
-  of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
-    result = getStorageLoc(n.sons[0])
-  else: result = OnUnknown
-
 proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   if dest.s == OnStack or not usesNativeGC():
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+  elif dest.s == OnStackShadowDup:
+    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+    linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src))
   elif dest.s == OnHeap:
     # location is on heap
     # now the writer barrier is inlined for performance:
@@ -184,6 +164,8 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   else:
     linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
             addrLoc(dest), rdLoc(src))
+    if preciseStack():
+      linefmt(p, cpsStmts, "$1 = $2;$n", dupLoc(dest), rdLoc(src))
 
 proc asgnComplexity(n: PNode): int =
   if n != nil:
@@ -241,7 +223,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
 
 proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # Consider:
-  # type TMyFastString {.shallow.} = string
+  # type MyFastString {.shallow.} = string
   # Due to the implementation of pragmas this would end up to set the
   # tfShallow flag for the built-in string type too! So we check only
   # here for this flag, where it is reasonably safe to do so
@@ -259,6 +241,9 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   else:
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
             addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
+  if dest.s == OnStackShadowDup:
+    linefmt(p, cpsStmts, "#genericAssignDup((void*)&$1, (void*)$2, $3);$n",
+            dupLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
 
 proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # This function replaces all other methods for generating
@@ -277,12 +262,17 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
               addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
+      if dest.s == OnStackShadowDup:
+        linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
   of tyString:
     if needToCopy notin flags and src.s != OnStatic:
       genRefAssign(p, dest, src, flags)
     else:
       if dest.s == OnStack or not usesNativeGC():
         linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
+      elif dest.s == OnStackShadowDup:
+        linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
+        linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
       elif dest.s == OnHeap:
         # we use a temporary to care for the dreaded self assignment:
         var tmp: TLoc
@@ -293,6 +283,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       else:
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
                addrLoc(dest), rdLoc(src))
+        if preciseStack():
+          linefmt(p, cpsStmts, "$1 = $2;$n", dest.dupLoc, dest.rdLoc)
   of tyProc:
     if needsComplexAssignment(dest.t):
       # optimize closure assignment:
@@ -738,6 +730,9 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   else: internalError(e.info, "genTupleElem")
   addf(r, ".Field$1", [rope(i)])
   putIntoDest(p, d, tupType.sons[i], r, a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(tupType, i))
 
 proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
   var ty = ty
@@ -762,12 +757,18 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     # so we use Field$i
     addf(r, ".Field$1", [rope(f.position)])
     putIntoDest(p, d, f.typ, r, a.s)
+    if a.s == OnStackShadowDup:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1[$2]", a.dup, ithRefInTuple(ty, f.position))
   else:
     let field = lookupFieldAgain(p, ty, f, r)
     if field.loc.r == nil: fillObjectFields(p.module, ty)
     if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
     addf(r, ".$1", [field.loc.r])
     putIntoDest(p, d, field.typ, r, a.s)
+    if a.s == OnStackShadowDup and field.loc.dup != nil:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1.$2", a.dup, field.loc.dup)
   #d.s = a.s
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
@@ -819,6 +820,9 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
     genFieldCheck(p, e, r, field, ty)
     add(r, rfmt(nil, ".$1", field.loc.r))
     putIntoDest(p, d, field.typ, r, a.s)
+    if a.s == OnStackShadowDup and field.loc.dup != nil:
+      d.s = OnStackShadowDup
+      d.dup = ropef("$1.$2", a.dup, field.loc.dup)
   else:
     genRecordField(p, e.sons[0], d)
 
@@ -846,6 +850,9 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
   d.inheritLocation(a)
   putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
               rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[($2)- $3]", a.dup, rdCharLoc(b), first)
 
 proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -866,6 +873,9 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
   if d.k == locNone: d.s = a.s
   putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
               rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
+  if a.s == OnStackShadowDup:
+    d.s = OnStackShadowDup
+    d.dup = ropef("$1[$2]", a.dup, rdCharLoc(b))
 
 proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -1170,6 +1180,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
   var t = e.typ.skipTypes(abstractInst)
   getTemp(p, t, tmp)
   let isRef = t.kind == tyRef
+  let stck = stackPlacement(t)
   var r = rdLoc(tmp)
   if isRef:
     rawGenNew(p, tmp, nil)
@@ -1193,7 +1204,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
     add(tmp2.r, field.loc.r)
     tmp2.k = locTemp
     tmp2.t = field.loc.t
-    tmp2.s = if isRef: OnHeap else: OnStack
+    tmp2.s = if isRef: OnHeap else: stck
     expr(p, it.sons[1], tmp2)
 
   if d.k == locNone:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index cc925b150..1bb26c48d 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -332,7 +332,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
 
   var alreadyPoppedCnt = p.inExceptBlock
   for i in countup(1, howManyTrys):
-    if not p.module.compileToCpp:
+    if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
       # Pop safe points generated by try
       if alreadyPoppedCnt > 0:
         dec alreadyPoppedCnt
@@ -354,7 +354,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
   for i in countdown(howManyTrys-1, 0):
     p.nestedTryStmts.add(stack[i])
 
-  if not p.module.compileToCpp:
+  if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
     # Pop exceptions that was handled by the
     # except-blocks we are in
     for i in countdown(howManyExcepts-1, 0):
@@ -803,6 +803,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
   if optStackTrace in p.options:
     linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
+  if p.gcFrameLen > 0:
+    linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
   inc p.inExceptBlock
   var i = 1
   var catchAllPresent = false
@@ -911,6 +913,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   linefmt(p, cpsStmts, "#popSafePoint();$n")
   if optStackTrace in p.options:
     linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
+  if p.gcFrameLen > 0:
+    linefmt(p, cpsStmts, "#setGcFrame((#GcFrameBase*)&GCF_);$n")
+
   inc p.inExceptBlock
   var i = 1
   while (i < length) and (t.sons[i].kind == nkExceptBranch):
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 5e4bcfe97..f26854824 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -282,7 +282,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
 
 proc fillResult(param: PSym) =
   fillLoc(param.loc, locParam, param.typ, ~"Result",
-          OnStack)
+          stackPlacement(param.typ))
   if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
     incl(param.loc.flags, lfIndirect)
     param.loc.s = OnUnknown
@@ -376,9 +376,9 @@ 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, tyVarargs}:
-    result = OnStack
+  let t = param.typ.skipTypes({tyVar, tyTypeDesc})
+  if t.kind notin {tyArray, tyOpenArray, tyVarargs}:
+    result = stackPlacement(t)
   else:
     result = OnUnknown
 
@@ -988,7 +988,7 @@ proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) =
   if typ.kind == tyObject: genTypeInfoAux(m, typ, origType, name)
   else: genTypeInfoAuxBase(m, typ, origType, name, rope("0"))
   var tmp = getNimNode(m)
-  if not isImportedCppType(typ):
+  if not isImportedType(typ):
     genObjectFields(m, typ, origType, typ.n, tmp)
   addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
   var t = typ.sons[0]
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index fd15d0793..c9b047a49 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -244,6 +244,10 @@ proc addrLoc(a: TLoc): Rope =
   if lfIndirect notin a.flags and mapType(a.t) != ctArray:
     result = "(&" & result & ")"
 
+proc dupLoc(a: TLoc): Rope =
+  result = a.dup
+  assert result != nil
+
 proc rdCharLoc(a: TLoc): Rope =
   # read a location that may need a char-cast:
   result = rdLoc(a)
@@ -288,12 +292,13 @@ proc resetLoc(p: BProc, loc: var TLoc) =
   if not isComplexValueType(typ):
     if containsGcRef:
       var nilLoc: TLoc
-      initLoc(nilLoc, locTemp, loc.t, OnStack)
+      initLoc(nilLoc, locTemp, loc.t, stackPlacement(typ))
       nilLoc.r = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc, {afSrcIsNil})
     else:
       linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
   else:
+    # XXX use stackPlacement here?
     if optNilCheck in p.options:
       linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
     if loc.s != OnStack:
@@ -343,17 +348,21 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
   result.k = locTemp
   result.t = t
-  result.s = OnStack
+  result.s = stackPlacement(t)
   result.flags = {}
   constructLoc(p, result, not needsInit)
 
 proc initGCFrame(p: BProc): Rope =
-  if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
+  if p.gcFrameLen > 0:
+    result = ropegc(p.module, """
+    struct {#GcFrameBase b_; $1} GCF_;$n
+    GCF_.b_.L=$2;$n
+    #pushGcFrame((GcFrameBase*)&GCF_);$n""" % [
+    p.gcFrameType, rope(p.gcFrameLen)])
 
 proc deinitGCFrame(p: BProc): Rope =
-  if p.gcFrameId > 0:
-    result = ropecg(p.module,
-                    "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
+  if p.gcFrameLen > 0:
+    result = ropecg(p.module, "#popGcFrame();$n")
 
 proc localDebugInfo(p: BProc, s: PSym) =
   if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
@@ -370,7 +379,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, mangleLocalName(p, s), OnStack)
+    fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), stackPlacement(s.typ))
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   result = getTypeDesc(p.module, s.typ)
   if s.constraint.isNil:
@@ -1315,8 +1324,11 @@ proc shouldRecompile(code: Rope, cfile: Cfile): bool =
   if optForceFullMake notin gGlobalOptions:
     if not equalsFile(code, cfile.cname):
       if isDefined("nimdiff"):
-        copyFile(cfile.cname, cfile.cname & ".backup")
-        echo "diff ", cfile.cname, ".backup ", cfile.cname
+        if fileExists(cfile.cname):
+          copyFile(cfile.cname, cfile.cname & ".backup")
+          echo "diff ", cfile.cname, ".backup ", cfile.cname
+        else:
+          echo "new file ", cfile.cname
       writeRope(code, cfile.cname)
       return
     if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index be087095f..2f927c83e 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -90,7 +90,7 @@ type
     splitDecls*: int          # > 0 if we are in some context for C++ that
                               # requires 'T x = T()' to become 'T x; x = T()'
                               # (yes, C++ is weird like that)
-    gcFrameId*: Natural       # for the GC stack marking
+    gcFrameLen*: int          # the number of slots in the GC-Frame
     gcFrameType*: Rope        # the struct {} we put the GC markers into
     sigConflicts*: CountTable[string]
 
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index e14306e56..1165ec932 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -233,6 +233,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
   var disp = newNodeI(nkIfStmt, base.info)
   var ands = getSysSym("and")
   var iss = getSysSym("of")
+  for col in countup(1, paramLen - 1):
+    if contains(relevantCols, col):
+      let param = base.typ.n.sons[col].sym
+      if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
+        addSon(nilchecks, newTree(nkCall,
+            newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
   for meth in countup(0, high(methods)):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
@@ -242,9 +248,6 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
         addSon(isn, newSymNode(iss))
         let param = base.typ.n.sons[col].sym
         addSon(isn, newSymNode(param))
-        if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
-          addSon(nilchecks, newTree(nkCall,
-                newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
         addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
         if cond != nil:
           var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 1af113be6..ca4f621e4 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -697,6 +697,17 @@ proc getLinkCmd(projectfile, objfiles: string): string =
         "nim", quoteShell(getPrefixDir()),
         "lib", quoteShell(libpath)])
 
+template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed =
+  try:
+    body
+  except OSError:
+    let ose = (ref OSError)(getCurrentException())
+    if errorPrefix.len > 0:
+      rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
+    else:
+      rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
+    raise
+
 proc callCCompiler*(projectfile: string) =
   var
     linkCmd: string
@@ -721,17 +732,20 @@ proc callCCompiler*(projectfile: string) =
     var res = 0
     if gNumberOfProcessors <= 1:
       for i in countup(0, high(cmds)):
-        res = execWithEcho(cmds[i])
+        tryExceptOSErrorMessage("invocation of external compiler program failed."):
+          res = execWithEcho(cmds[i])
         if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
-    elif optListCmd in gGlobalOptions or gVerbosity > 1:
-      res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
-                          gNumberOfProcessors, afterRunEvent=runCb)
-    elif gVerbosity == 1:
-      res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
-                          gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
     else:
-      res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
-                          gNumberOfProcessors, afterRunEvent=runCb)
+      tryExceptOSErrorMessage("invocation of external compiler program failed."):
+        if optListCmd in gGlobalOptions or gVerbosity > 1:
+          res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
+                              gNumberOfProcessors, afterRunEvent=runCb)
+        elif gVerbosity == 1:
+          res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                              gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
+        else:
+          res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                              gNumberOfProcessors, afterRunEvent=runCb)
     if res != 0:
       if gNumberOfProcessors <= 1:
         rawMessage(errExecutionOfProgramFailed, cmds.join())
@@ -749,8 +763,9 @@ proc callCCompiler*(projectfile: string) =
 
     linkCmd = getLinkCmd(projectfile, objfiles)
     if optCompileOnly notin gGlobalOptions:
-      execExternalProgram(linkCmd,
-        if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
+      tryExceptOSErrorMessage("invocation of external linker program failed."):
+        execExternalProgram(linkCmd,
+          if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
   else:
     linkCmd = ""
   if optGenScript in gGlobalOptions:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 0016a8492..ee35356c9 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -434,22 +434,23 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
 
 proc unsignedTrimmerJS(size: BiggestInt): Rope =
   case size
-    of 1: rope"& 0xff"
-    of 2: rope"& 0xffff"
-    of 4: rope">>> 0"
-    else: rope""
+  of 1: rope"& 0xff"
+  of 2: rope"& 0xffff"
+  of 4: rope">>> 0"
+  else: rope""
 
 proc unsignedTrimmerPHP(size: BiggestInt): Rope =
   case size
-    of 1: rope"& 0xff"
-    of 2: rope"& 0xffff"
-    of 4: rope"& 0xffffffff"
-    else: rope""
+  of 1: rope"& 0xff"
+  of 2: rope"& 0xffff"
+  of 4: rope"& 0xffffffff"
+  else: rope""
 
 template unsignedTrimmer(size: BiggestInt): Rope =
   size.unsignedTrimmerJS | size.unsignedTrimmerPHP
 
-proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: bool = false) =
+proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
+                    reassign = false) =
   var x, y: TCompRes
   gen(p, n.sons[1], x)
   gen(p, n.sons[2], y)
@@ -1633,11 +1634,11 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope =
 
   gen(p, n.sons[1], a)
   if magic == "reprAny":
-    # the pointer argument in reprAny is expandend to 
+    # the pointer argument in reprAny is expandend to
     # (pointedto, pointer), so we need to fill it
     if a.address.isNil:
       add(r.res, a.res)
-      add(r.res, ", null") 
+      add(r.res, ", null")
     else:
       add(r.res, "$1, $2" % [a.address, a.res])
   else:
@@ -1670,7 +1671,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
     genReprAux(p, n, r, "reprSet", genTypeInfo(p, t))
   of tyEmpty, tyVoid:
     localError(n.info, "'repr' doesn't support 'void' type")
-  of tyPointer: 
+  of tyPointer:
     genReprAux(p, n, r, "reprPointer")
   of tyOpenArray, tyVarargs:
     genReprAux(p, n, r, "reprJSONStringify")
@@ -1863,8 +1864,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
 proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
   var
     a, b: TCompRes
-  useMagic(p, "SetConstr")
-  r.res = rope("SetConstr(")
+  useMagic(p, "setConstr")
+  r.res = rope("setConstr(")
   r.kind = resExpr
   for i in countup(0, sonsLen(n) - 1):
     if i > 0: add(r.res, ", ")
@@ -1877,6 +1878,12 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
       gen(p, it, a)
       add(r.res, a.res)
   add(r.res, ")")
+  # emit better code for constant sets:
+  if p.target == targetJS and isDeepConstExpr(n):
+    inc(p.g.unique)
+    let tmp = rope("ConstSet") & rope(p.g.unique)
+    addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res])
+    r.res = tmp
 
 proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
   var a: TCompRes
@@ -2128,6 +2135,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     else: r.res = rope(f.toStrMaxPrecision)
     r.kind = resExpr
   of nkCallKinds:
+    if isEmptyType(n.typ): genLineDir(p, n)
     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
@@ -2264,7 +2272,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   genModule(p, n)
   add(p.g.code, p.locals)
   add(p.g.code, p.body)
-  globals.unique = p.unique
 
 proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
   for prc in globals.forwarded:
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index f49bd7668..ae30861e7 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
         u = rope(lengthOrd(field.typ))
       else: internalError(n.info, "genObjectFields(nkRecCase)")
       if result != nil: add(result, ", " & tnl)
-      addf(result, "[SetConstr($1), $2]",
+      addf(result, "[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
     result = ("{kind: 3, offset: \"$1\", len: $3, " &
         "typ: $2, name: $4, sons: [$5]}") % [
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 245cc5f61..4bd54603d 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -74,9 +74,9 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
+  var temp = newSym(skLet, getIdent("_"), owner, value.info)
   var v = newNodeI(nkLetSection, value.info)
-  let tempAsNode = newIdentNode(getIdent(genPrefix & $temp.id), value.info)
+  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
@@ -115,7 +115,7 @@ proc createObj*(owner: PSym, info: TLineInfo): PType =
   incl result.flags, tfFinal
   result.n = newNodeI(nkRecList, info)
   when true:
-    let s = newSym(skType, getIdent("Env_" & info.toFilename & "_" & $info.line),
+    let s = newSym(skType, getIdent("Env_" & info.toFilename),
                    owner, info)
     incl s.flags, sfAnon
     s.typ = result
@@ -137,10 +137,32 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
+proc lookupInRecord(n: PNode, id: int): PSym =
+  result = nil
+  case n.kind
+  of nkRecList:
+    for i in countup(0, sonsLen(n) - 1):
+      result = lookupInRecord(n.sons[i], id)
+      if result != nil: return
+  of nkRecCase:
+    if n.sons[0].kind != nkSym: return
+    result = lookupInRecord(n.sons[0], id)
+    if result != nil: return
+    for i in countup(1, sonsLen(n) - 1):
+      case n.sons[i].kind
+      of nkOfBranch, nkElse:
+        result = lookupInRecord(lastSon(n.sons[i]), id)
+        if result != nil: return
+      else: discard
+  of nkSym:
+    if n.sym.id == -abs(id): result = n.sym
+  else: discard
+
 proc addField*(obj: PType; s: PSym) =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
+  var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
+  field.id = -s.id
   let t = skipIntLit(s.typ)
   field.typ = t
   assert t.kind != tyStmt
@@ -148,9 +170,9 @@ proc addField*(obj: PType; s: PSym) =
   addSon(obj.n, newSymNode(field))
 
 proc addUniqueField*(obj: PType; s: PSym) =
-  let fieldName = getIdent(s.name.s & $s.id)
-  if lookupInRecord(obj.n, fieldName) == nil:
-    var field = newSym(skField, fieldName, s.owner, s.info)
+  if lookupInRecord(obj.n, s.id) == nil:
+    var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
+    field.id = -s.id
     let t = skipIntLit(s.typ)
     field.typ = t
     assert t.kind != tyStmt
@@ -159,13 +181,36 @@ proc addUniqueField*(obj: PType; s: PSym) =
 
 proc newDotExpr(obj, b: PSym): PNode =
   result = newNodeI(nkDotExpr, obj.info)
-  let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id))
+  let field = lookupInRecord(obj.typ.n, b.id)
   assert field != nil, b.name.s
   addSon(result, newSymNode(obj))
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
-proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
+proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
+  # returns a[].b as a node
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  var t = deref.typ.skipTypes(abstractInst)
+  var field: PSym
+  while true:
+    assert t.kind == tyObject
+    field = lookupInRecord(t.n, b)
+    if field != nil: break
+    t = t.sons[0]
+    if t == nil: break
+    t = t.skipTypes(skipPtrs)
+  #if field == nil:
+  #  echo "FIELD ", b
+  #  debug deref.typ
+  internalAssert field != nil
+  addSon(deref, a)
+  result = newNodeI(nkDotExpr, info)
+  addSon(result, deref)
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.skipTypes(abstractInst).sons[0]
@@ -191,11 +236,10 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
 
 proc getFieldFromObj*(t: PType; v: PSym): PSym =
   assert v.kind != skField
-  let fieldName = getIdent(v.name.s & $v.id)
   var t = t
   while true:
     assert t.kind == tyObject
-    result = getSymFromList(t.n, fieldName)
+    result = lookupInRecord(t.n, v.id)
     if result != nil: break
     t = t.sons[0]
     if t == nil: break
@@ -203,7 +247,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
 
 proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
   # returns a[].b as a node
-  result = indirectAccess(a, b.name.s & $b.id, info)
+  result = indirectAccess(a, b.id, info)
 
 proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
   result = indirectAccess(newSymNode(a), b, info)
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index e6466fc24..5e6d843de 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -17,15 +17,17 @@ proc addPath*(path: string, info: TLineInfo) =
 
 proc versionSplitPos(s: string): int =
   result = s.len-2
-  while result > 1 and s[result] in {'0'..'9', '.'}: dec result
+  #while result > 1 and s[result] in {'0'..'9', '.'}: dec result
+  while result > 1 and s[result] != '-': dec result
   if s[result] != '-': result = s.len
 
 const
-  latest = "head"
+  latest = ""
 
 proc `<.`(a, b: string): bool =
   # wether a has a smaller version than b:
-  if a == latest: return false
+  if a == latest: return true
+  elif b == latest: return false
   var i = 0
   var j = 0
   var verA = 0
@@ -33,8 +35,13 @@ proc `<.`(a, b: string): bool =
   while true:
     let ii = parseInt(a, verA, i)
     let jj = parseInt(b, verB, j)
-    # if A has no number left, but B has, B is preferred:  0.8 vs 0.8.3
-    if ii <= 0 or jj <= 0: return jj > 0
+    if ii <= 0 or jj <= 0:
+      # if A has no number and B has but A has no number whatsoever ("#head"),
+      # A is preferred:
+      if ii > 0 and jj <= 0 and j == 0: return true
+      if ii <= 0 and jj > 0 and i == 0: return false
+      # if A has no number left, but B has, B is preferred:  0.8 vs 0.8.3
+      return jj > 0
     if verA < verB: return true
     elif verA > verB: return false
     # else: same version number; continue:
@@ -46,12 +53,9 @@ proc `<.`(a, b: string): bool =
 proc addPackage(packages: StringTableRef, p: string) =
   let x = versionSplitPos(p)
   let name = p.substr(0, x-1)
-  if x < p.len:
-    let version = p.substr(x+1)
-    if packages.getOrDefault(name) <. version:
-      packages[name] = version
-  else:
-    packages[name] = latest
+  let version = if x < p.len: p.substr(x+1) else: ""
+  if packages.getOrDefault(name) <. version:
+    packages[name] = version
 
 iterator chosen(packages: StringTableRef): string =
   for key, val in pairs(packages):
@@ -76,3 +80,18 @@ proc addPathRec(dir: string, info: TLineInfo) =
 proc nimblePath*(path: string, info: TLineInfo) =
   addPathRec(path, info)
   addNimblePath(path, info)
+
+when isMainModule:
+  var rr = newStringTable()
+  addPackage rr, "irc-#head"
+  addPackage rr, "irc-0.1.0"
+  addPackage rr, "irc"
+  addPackage rr, "another"
+  addPackage rr, "another-0.1"
+
+  addPackage rr, "ab-0.1.3"
+  addPackage rr, "ab-0.1"
+  addPackage rr, "justone"
+
+  for p in rr.chosen:
+    echo p
diff --git a/compiler/options.nim b/compiler/options.nim
index c4a57f41c..3e681c8d1 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -140,12 +140,13 @@ var
   gEvalExpr* = ""             # expression for idetools --eval
   gLastCmdTime*: float        # when caas is enabled, we measure each command
   gListFullPaths*: bool
-  isServing*: bool = false
+  gPreciseStack*: bool = false
   gNoNimblePath* = false
   gExperimentalMode*: bool
 
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
+template preciseStack*(): bool = gPreciseStack
 
 template compilationCachePresent*: untyped =
   {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
diff --git a/compiler/parser.nim b/compiler/parser.nim
index e4c04c0f7..e2d17b455 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1246,10 +1246,7 @@ proc parseExprStmt(p: var TParser): PNode =
         addSon(result, e)
         if p.tok.tokType != tkComma: break
     elif p.tok.indent < 0 and isExprStart(p):
-      if a.kind == nkCommand:
-        result = a
-      else:
-        result = newNode(nkCommand, a.info, @[a])
+      result = newNode(nkCommand, a.info, @[a])
       while true:
         var e = parseExpr(p)
         addSon(result, e)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 4baef5385..8ff5fdd9c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1269,7 +1269,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
     #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
   of tyTuple:
-    checkSonsLen(n, 2)
+    if n.len != 2: return nil
     n.sons[0] = makeDeref(n.sons[0])
     c.p.bracketExpr = n.sons[0]
     # [] operator for tuples requires constant expression:
@@ -1279,9 +1279,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
       var idx = getOrdValue(n.sons[1])
       if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
       else: localError(n.info, errInvalidIndexValueForTuple)
+      result = n
     else:
-      localError(n.info, errIndexTypesDoNotMatch)
-    result = n
+      result = nil
   else:
     let s = if n.sons[0].kind == nkSym: n.sons[0].sym
             elif n[0].kind in nkSymChoices: n.sons[0][0].sym
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 88c05faa4..96bdc6cba 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -517,7 +517,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
 proc procVarcheck(n: PNode) =
   if n.kind in nkSymChoices:
     for x in n: procVarCheck(x)
-  elif n.kind == nkSym and n.sym.magic != mNone:
+  elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
     localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s)
 
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@@ -783,6 +783,10 @@ proc track(tracked: PEffects, n: PNode) =
           notNilCheck(tracked, last, child.sons[i].typ)
       # since 'var (a, b): T = ()' is not even allowed, there is always type
       # inference for (a, b) and thus no nil checking is necessary.
+  of nkConstSection:
+    for child in n:
+      let last = lastSon(child)
+      track(tracked, last)
   of nkCaseStmt: trackCase(tracked, n)
   of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
   of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 424fba14c..8f0af89d3 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -96,7 +96,7 @@ proc isDeepConstExpr*(n: PNode): bool =
     result = true
   of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
     result = isDeepConstExpr(n.sons[1])
-  of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure:
+  of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange:
     for i in ord(n.kind == nkObjConstr) .. <n.len:
       if not isDeepConstExpr(n.sons[i]): return false
     if n.typ.isNil: result = true
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 14800fb13..043506e62 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1657,8 +1657,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
   for i in 0 .. <gp.len:
     if sfImmediate notin sym.flags:
       let idx = sym.typ.len + i
-      tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+      if idx < n.len:
+        tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+      else:
+        dec(evalMacroCounter)
+        c.callsite = nil
+        localError(n.info, "expected " & $gp.len &
+                   " generic parameter(s)")
     elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
+      dec(evalMacroCounter)
+      c.callsite = nil
       globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
   # temporary storage:
   #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 8c7388643..b2b1ec92b 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -175,7 +175,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
           result.add mapTypeToAst(t.sons[i], info)
     else:
       result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
-  of tyGenericBody, tyOrdinal:
+  of tyGenericBody:
+    if inst:
+      result = mapTypeToAstX(t.lastSon, info, inst, true)
+    else:
+      result = mapTypeToAst(t.lastSon, info)
+  of tyOrdinal:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
     if inst: