summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/commands.nim3
-rw-r--r--compiler/docgen.nim168
-rw-r--r--compiler/hlo.nim3
-rw-r--r--compiler/lambdalifting.nim72
-rw-r--r--compiler/nimrod.nimrod.cfg1
-rw-r--r--compiler/options.nim2
-rw-r--r--compiler/renderer.nim28
-rw-r--r--compiler/sem.nim19
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim23
-rw-r--r--compiler/semfold.nim18
-rw-r--r--compiler/semgnrc.nim8
-rw-r--r--compiler/semtempl.nim38
-rw-r--r--compiler/semtypes.nim10
-rw-r--r--compiler/sigmatch.nim2
-rw-r--r--compiler/transf.nim1
-rw-r--r--compiler/types.nim4
-rw-r--r--compiler/typesrenderer.nim113
-rw-r--r--compiler/vm.nim92
-rw-r--r--compiler/vmgen.nim17
21 files changed, 497 insertions, 129 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 990153fc7..311149cb3 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -138,7 +138,7 @@ proc genBreakState(p: BProc, n: PNode) =
   if n.sons[0].kind == nkClosure:
     # XXX this produces quite inefficient code!
     initLocExpr(p, n.sons[0].sons[1], a)
-    lineF(p, cpsStmts, "if (($1->Field0) < 0) break;$n", [rdLoc(a)])
+    lineF(p, cpsStmts, "if (((NI*) $1)[0] < 0) break;$n", [rdLoc(a)])
   else:
     initLocExpr(p, n.sons[0], a)
     # the environment is guaranteed to contain the 'state' field at offset 0:
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 18bdb54d3..8339219ed 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -267,6 +267,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "out", "o": 
     expectArg(switch, arg, pass, info)
     options.outFile = arg
+  of "docseesrcurl":
+    expectArg(switch, arg, pass, info)
+    options.docSeeSrcUrl = arg
   of "mainmodule", "m":
     expectArg(switch, arg, pass, info)
     optMainModule = arg
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index d8c439c9c..6948c4979 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -14,7 +14,7 @@
 import
   ast, strutils, strtabs, options, msgs, os, ropes, idents,
   wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
-  importer, sempass2, json
+  importer, sempass2, json, xmltree, cgi, typesrenderer
 
 type
   TSections = array[TSymKind, PRope]
@@ -23,8 +23,9 @@ type
     id: int                  # for generating IDs
     toc, section: TSections
     indexValFilename: string
+    seenSymbols: PStringTable # avoids duplicate symbol generation for HTML.
 
-  PDoc* = ref TDocumentor
+  PDoc* = ref TDocumentor ## Alias to type less.
 
 proc compilerMsgHandler(filename: string, line, col: int,
                         msgKind: rst.TMsgKind, arg: string) {.procvar.} =
@@ -59,6 +60,7 @@ proc newDocumentor*(filename: string, config: PStringTable): PDoc =
   initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
                    options.gConfigVars, filename, {roSupportRawDirective},
                    docgenFindFile, compilerMsgHandler)
+  result.seenSymbols = newStringTable(modeCaseInsensitive)
   result.id = 100
 
 proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
@@ -144,6 +146,23 @@ proc genRecComment(d: PDoc, n: PNode): PRope =
   else:
     n.comment = nil
 
+proc getPlainDocstring(n: PNode): string =
+  ## Gets the plain text docstring of a node non destructively.
+  ##
+  ## You need to call this before genRecComment, whose side effects are removal
+  ## of comments from the tree. The proc will recursively scan and return all
+  ## the concatenated ``##`` comments of the node.
+  result = ""
+  if n == nil: return
+  if n.comment != nil and startsWith(n.comment, "##"):
+    result = n.comment
+  if result.len < 1:
+    if n.kind notin {nkEmpty..nkNilLit}:
+      for i in countup(0, len(n)-1):
+        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
@@ -205,14 +224,111 @@ proc getRstName(n: PNode): PRstNode =
     internalError(n.info, "getRstName()")
     result = nil
 
+proc newUniquePlainSymbol(d: PDoc, original: string): string =
+  ## Returns a new unique plain symbol made up from the original.
+  ##
+  ## When a collision is found in the seenSymbols table, new numerical variants
+  ## with underscore + number will be generated.
+  if not d.seenSymbols.hasKey(original):
+    result = original
+    d.seenSymbols[original] = ""
+    return
+
+  # Iterate over possible numeric variants of the original name.
+  var count = 2
+
+  while true:
+    result = original & "_" & $count
+    if not d.seenSymbols.hasKey(result):
+      d.seenSymbols[result] = ""
+      break
+    count += 1
+
+
+proc complexName(k: TSymKind, n: PNode, baseName: string): string =
+  ## Builds a complex unique href name for the node.
+  ##
+  ## Pass as ``baseName`` the plain symbol obtained from the nodeName. The
+  ## format of the returned symbol will be ``baseName(.callable type)?,(param
+  ## type)?(,param type)*``. The callable type part will be added only if the
+  ## node is not a proc, as those are the common ones. The suffix will be a dot
+  ## and a single letter representing the type of the callable. The parameter
+  ## types will be added with a preceeding dash. Return types won't be added.
+  ##
+  ## If you modify the output of this proc, please update the anchor generation
+  ## section of ``doc/docgen.txt``.
+  result = baseName
+  case k:
+  of skProc: result.add(defaultParamSeparator)
+  of skMacro: result.add(".m" & defaultParamSeparator)
+  of skMethod: result.add(".e" & defaultParamSeparator)
+  of skIterator: result.add(".i" & defaultParamSeparator)
+  of skTemplate: result.add(".t" & defaultParamSeparator)
+  of skConverter: result.add(".c" & defaultParamSeparator)
+  else: discard
+
+  if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams:
+    result.add(renderParamTypes(n[paramsPos]))
+
+
+proc isCallable(n: PNode): bool =
+  ## Returns true if `n` contains a callable node.
+  case n.kind
+  of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
+    nkConverterDef: result = true
+  else:
+    result = false
+
+
+proc docstringSummary(rstText: string): string =
+  ## Returns just the first line or a brief chunk of text from a rst string.
+  ##
+  ## Most docstrings will contain a one liner summary, so stripping at the
+  ## first newline is usually fine. If after that the content is still too big,
+  ## it is stripped at the first comma, colon or dot, usual english sentence
+  ## separators.
+  ##
+  ## No guarantees are made on the size of the output, but it should be small.
+  ## Also, we hope to not break the rst, but maybe we do. If there is any
+  ## trimming done, an ellipsis unicode char is added.
+  const maxDocstringChars = 100
+  assert (rstText.len < 2 or (rstText[0] == '#' and rstText[1] == '#'))
+  result = rstText.substr(2).strip
+  var pos = result.find('\L')
+  if pos > 0:
+    result.delete(pos, result.len - 1)
+    result.add("…")
+  if pos < maxDocstringChars:
+    return
+  # Try to keep trimming at other natural boundaries.
+  pos = result.find({'.', ',', ':'})
+  let last = result.len - 1
+  if pos > 0 and pos < last:
+    result.delete(pos, last)
+    result.add("…")
+
+
 proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
   if not isVisible(nameNode): return
-  var name = toRope(getName(d, nameNode))
+  let
+    name = getName(d, nameNode)
+    nameRope = name.toRope
+    plainDocstring = getPlainDocstring(n) # call here before genRecComment!
   var result: PRope = nil
-  var literal = ""
+  var literal, plainName = ""
   var kind = tkEof
   var comm = genRecComment(d, n)  # call this here for the side-effect!
   var r: TSrcGen
+  # Obtain the plain rendered string for hyperlink titles.
+  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
+    renderNoPragmas, renderNoProcDefs})
+  while true:
+    getNextTok(r, kind, literal)
+    if kind == tkEof:
+      break
+    plainName.add(literal)
+
+  # Render the HTML hyperlink.
   initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
   while true:
     getNextTok(r, kind, literal)
@@ -253,13 +369,47 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
       dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
             [toRope(esc(d.target, literal))])
   inc(d.id)
+  let
+    plainNameRope = toRope(xmltree.escape(plainName.strip))
+    cleanPlainSymbol = renderPlainSymbolName(nameNode)
+    complexSymbol = complexName(k, n, cleanPlainSymbol)
+    plainSymbolRope = toRope(cleanPlainSymbol)
+    plainSymbolEncRope = toRope(URLEncode(cleanPlainSymbol))
+    itemIDRope = toRope(d.id)
+    symbolOrId = d.newUniquePlainSymbol(complexSymbol)
+    symbolOrIdRope = symbolOrId.toRope
+    symbolOrIdEncRope = URLEncode(symbolOrId).toRope
+
+  var seeSrcRope: PRope = nil
+  let docItemSeeSrc = getConfigVar("doc.item.seesrc")
+  if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0:
+    let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl,
+      ["path", "line"], [n.info.toFilename.toRope, toRope($n.info.line)])
+    dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
+        ["path", "line", "url"], [n.info.toFilename.toRope,
+        toRope($n.info.line), urlRope])])
+
   app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
-                                        ["name", "header", "desc", "itemID"],
-                                        [name, result, comm, toRope(d.id)]))
+    ["name", "header", "desc", "itemID", "header_plain", "itemSym",
+      "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
+    [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
+      symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
   app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
-                                    ["name", "header", "desc", "itemID"], [
-      toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
-  setIndexTerm(d[], $d.id, getName(d, nameNode))
+    ["name", "header", "desc", "itemID", "header_plain", "itemSym",
+      "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"],
+    [toRope(getName(d, nameNode, d.splitAfter)), result, comm,
+      itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
+      plainSymbolEncRope, symbolOrIdEncRope]))
+
+  # Ironically for types the complexSymbol is *cleaner* than the plainName
+  # because it doesn't include object fields or documentation comments. So we
+  # use the plain one for callable elements, and the complex for the rest.
+  var linkTitle = changeFileExt(extractFilename(d.filename), "") & " : "
+  if n.isCallable: linkTitle.add(xmltree.escape(plainName.strip))
+  else: linkTitle.add(xmltree.escape(complexSymbol.strip))
+
+  setIndexTerm(d[], symbolOrId, name, linkTitle,
+    xmltree.escape(plainDocstring.docstringSummary))
 
 proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode =
   if not isVisible(nameNode): return
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 7982d4aa1..c75d6519f 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -68,7 +68,8 @@ proc hlo(c: PContext, n: PNode): PNode =
     result = n
   else:
     if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
-        n.sons[0].kind == nkSym and sfGlobal in n.sons[0].sym.flags:
+        n.sons[0].kind == nkSym and 
+        {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
       # do not optimize 'var g {.global} = re(...)' again!
       return n
     result = applyPatterns(c, n)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 6da156ba6..440468ac4 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -121,14 +121,14 @@ type
     capturedVars: seq[PSym] # captured variables in this environment
     deps: seq[TDep]         # dependencies
     up: PEnv
-    tup: PType
+    obj: PType
   
-  TInnerContext {.final.} = object
+  TInnerContext = object
     fn: PSym
     closureParam: PSym
     localsToAccess: TIdNodeTable
     
-  TOuterContext {.final.} = object
+  TOuterContext = object
     fn: PSym # may also be a module!
     currentEnv: PEnv
     isIter: bool   # first class iterator?
@@ -139,8 +139,13 @@ type
     up: POuterContext
 
     closureParam, state, resultSym: PSym # only if isIter
-    tup: PType # only if isIter
+    obj: PType # only if isIter
 
+proc createObj(owner: PSym, info: TLineInfo): PType =
+  result = newType(tyObject, owner)
+  rawAddSon(result, nil)
+  incl result.flags, tfFinal
+  result.n = newNodeI(nkRecList, info)
 
 proc getStateType(iter: PSym): PType =
   var n = newNodeI(nkRange, iter.info)
@@ -185,13 +190,14 @@ proc getEnvParam(routine: PSym): PSym =
   if hidden.kind == nkSym and hidden.sym.name.s == paramName:
     result = hidden.sym
     
-proc addField(tup: PType, s: PSym) =
-  var field = newSym(skField, s.name, s.owner, s.info)
+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)
   let t = skipIntLit(s.typ)
   field.typ = t
-  field.position = sonsLen(tup)
-  addSon(tup.n, newSymNode(field))
-  rawAddSon(tup, t)
+  field.position = sonsLen(obj.n)
+  addSon(obj.n, newSymNode(field))
 
 proc initIterContext(c: POuterContext, iter: PSym) =
   c.fn = iter
@@ -199,25 +205,24 @@ proc initIterContext(c: POuterContext, iter: PSym) =
 
   var cp = getEnvParam(iter)
   if cp == nil:
-    c.tup = newType(tyTuple, iter)
-    c.tup.n = newNodeI(nkRecList, iter.info)
+    c.obj = createObj(iter, iter.info)
 
     cp = newSym(skParam, getIdent(paramName), iter, iter.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = newType(tyRef, iter)
-    rawAddSon(cp.typ, c.tup)
+    rawAddSon(cp.typ, c.obj)
     addHiddenParam(iter, cp)
 
     c.state = createStateField(iter)
-    addField(c.tup, c.state)
+    addField(c.obj, c.state)
   else:
-    c.tup = cp.typ.sons[0]
-    assert c.tup.kind == tyTuple
-    if c.tup.len > 0:
-      c.state = c.tup.n[0].sym
+    c.obj = cp.typ.sons[0]
+    assert c.obj.kind == tyObject
+    if c.obj.n.len > 0:
+      c.state = c.obj.n[0].sym
     else:
       c.state = createStateField(iter)
-      addField(c.tup, c.state)
+      addField(c.obj, c.state)
 
   c.closureParam = cp
   if iter.typ.sons[0] != nil:
@@ -244,8 +249,7 @@ proc newEnv(outerProc: PSym, up: PEnv, n: PNode): PEnv =
   new(result)
   result.deps = @[]
   result.capturedVars = @[]
-  result.tup = newType(tyTuple, outerProc)
-  result.tup.n = newNodeI(nkRecList, outerProc.info)
+  result.obj = createObj(outerProc, outerProc.info)
   result.up = up
   result.attachedNode = n
 
@@ -254,28 +258,28 @@ proc addCapturedVar(e: PEnv, v: PSym) =
     if x == v: return
   # XXX meh, just add the state field for every closure for now, it's too
   # hard to figure out if it comes from a closure iterator:
-  if e.tup.len == 0: addField(e.tup, createStateField(v.owner))
+  if e.obj.n.len == 0: addField(e.obj, createStateField(v.owner))
   e.capturedVars.add(v)
-  addField(e.tup, v)
+  addField(e.obj, v)
   
 proc addDep(e, d: PEnv, owner: PSym): PSym =
   for x, field in items(e.deps):
     if x == d: return field
-  var pos = sonsLen(e.tup)
+  var pos = sonsLen(e.obj.n)
   result = newSym(skField, getIdent(upName & $pos), owner, owner.info)
   result.typ = newType(tyRef, owner)
   result.position = pos
-  assert d.tup != nil
-  rawAddSon(result.typ, d.tup)
-  addField(e.tup, result)
+  assert d.obj != nil
+  rawAddSon(result.typ, d.obj)
+  addField(e.obj, result)
   e.deps.add((d, result))
   
 proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = 
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.sons[0]
-  assert deref.typ.kind == tyTuple
-  let field = getSymFromList(deref.typ.n, b.name)
+  assert deref.typ.kind == tyObject
+  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
   assert field != nil, b.name.s
   addSon(deref, a)
   result = newNodeI(nkDotExpr, info)
@@ -302,11 +306,11 @@ proc addClosureParam(i: PInnerContext, e: PEnv) =
     cp = newSym(skParam, getIdent(paramName), i.fn, i.fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = newType(tyRef, i.fn)
-    rawAddSon(cp.typ, e.tup)
+    rawAddSon(cp.typ, e.obj)
     addHiddenParam(i.fn, cp)
   else:
-    e.tup = cp.typ.sons[0]
-    assert e.tup.kind == tyTuple
+    e.obj = cp.typ.sons[0]
+    assert e.obj.kind == tyObject
   i.closureParam = cp
   #echo "closure param added for ", i.fn.name.s, " ", i.fn.id
 
@@ -357,7 +361,7 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym,
     access = indirectAccess(access, addDep(e, it, i.fn), info)
   access = indirectAccess(access, local, info)
   if o.isIter:
-    if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local)
+    if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local)
   else:
     incl(o.capturedVars, local.id)
   idNodeTablePut(i.localsToAccess, local, access)
@@ -543,7 +547,7 @@ proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym =
   result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
   incl(result.flags, sfShadowed)
   result.typ = newType(tyRef, o.fn)
-  result.typ.rawAddSon(e.tup)
+  result.typ.rawAddSon(e.obj)
 
 proc getClosureVar(o: POuterContext; e: PEnv): PSym =
   if e.createdVar == nil:
@@ -669,7 +673,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
     var local = n.sym
 
     if o.isIter and interestingIterVar(local) and o.fn.id == local.owner.id:
-      if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local)
+      if not containsOrIncl(o.capturedVars, local.id): addField(o.obj, local)
       return indirectAccess(newSymNode(o.closureParam), local, n.info)
 
     var closure = PEnv(idTableGet(o.lambdasToEnv, local))
diff --git a/compiler/nimrod.nimrod.cfg b/compiler/nimrod.nimrod.cfg
index cc27d9f36..2c6e6f249 100644
--- a/compiler/nimrod.nimrod.cfg
+++ b/compiler/nimrod.nimrod.cfg
@@ -20,3 +20,4 @@ import:testability
 define:useStdoutAsStdmsg
 
 cs:partial
+#define:useNodeIds
diff --git a/compiler/options.nim b/compiler/options.nim
index 102ebc386..fa8b77ead 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -100,6 +100,8 @@ var
   gSelectedGC* = gcRefc       # the selected GC
   searchPaths*, lazyPaths*: TLinkedList
   outFile*: string = ""
+  docSeeSrcUrl*: string = ""  # if empty, no seeSrc will be generated. \
+  # The string uses the formatting variables `path` and `line`.
   headerFile*: string = ""
   gVerbosity* = 1             # how verbose the compiler is
   gNumberOfProcessors*: int   # number of processors
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 2d2310914..fa119eba9 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -15,7 +15,7 @@ import
 type 
   TRenderFlag* = enum 
     renderNone, renderNoBody, renderNoComments, renderDocComments, 
-    renderNoPragmas, renderIds
+    renderNoPragmas, renderIds, renderNoProcDefs
   TRenderFlags* = set[TRenderFlag]
   TRenderTok*{.final.} = object 
     kind*: TTokType
@@ -51,10 +51,17 @@ proc isKeyword*(s: string): bool =
       (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): 
     result = true
 
-proc renderDefinitionName*(s: PSym): string =
+proc renderDefinitionName*(s: PSym, noQuotes = false): string =
+  ## Returns the definition name of the symbol.
+  ##
+  ## If noQuotes is false the symbol may be returned in backticks. This will
+  ## happen if the name happens to be a keyword or the first character is not
+  ## part of the SymStartChars set.
   let x = s.name.s
-  if x[0] in SymStartChars and not renderer.isKeyword(x): result = x
-  else: result = '`' & x & '`'
+  if noQuotes or (x[0] in SymStartChars and not renderer.isKeyword(x)):
+    result = x
+  else:
+    result = '`' & x & '`'
 
 const 
   IndentWidth = 2
@@ -1119,22 +1126,22 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkStaticStmt: gstaticStmt(g, n)
   of nkAsmStmt: gasm(g, n)
   of nkProcDef: 
-    putWithSpace(g, tkProc, "proc")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
     gproc(g, n)
   of nkConverterDef:
-    putWithSpace(g, tkConverter, "converter")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
     gproc(g, n)
   of nkMethodDef: 
-    putWithSpace(g, tkMethod, "method")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkMethod, "method")
     gproc(g, n)
   of nkIteratorDef: 
-    putWithSpace(g, tkIterator, "iterator")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkIterator, "iterator")
     gproc(g, n)
   of nkMacroDef: 
-    putWithSpace(g, tkMacro, "macro")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkMacro, "macro")
     gproc(g, n)
   of nkTemplateDef: 
-    putWithSpace(g, tkTemplate, "template")
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkTemplate, "template")
     gproc(g, n)
   of nkTypeSection: 
     gsection(g, n, emptyContext, tkType, "type")
@@ -1336,4 +1343,3 @@ proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) =
     inc(r.idx)
   else: 
     kind = tkEof
-  
diff --git a/compiler/sem.nim b/compiler/sem.nim
index c35cff027..b34972219 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -161,12 +161,13 @@ proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
     localError(typ.n.info, errXisNoType, typeToString(typ))
 
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
-proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
 proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
 proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
 proc isOpImpl(c: PContext, n: PNode): PNode
+proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
+                     flags: TExprFlags = {}): PNode
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
-                  semCheck: bool = true): PNode
+                  flags: TExprFlags = {}): PNode
 
 proc symFromType(t: PType, info: TLineInfo): PSym =
   if t.sym != nil: return t.sym
@@ -265,7 +266,8 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
 
 include hlo, seminst, semcall
 
-proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode = 
+proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
+                       flags: TExprFlags): PNode =
   inc(evalTemplateCounter)
   if evalTemplateCounter > 100:
     globalError(s.info, errTemplateInstantiationTooNested)
@@ -281,7 +283,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
       # BUGFIX: we cannot expect a type here, because module aliases would not 
       # work then (see the ``tmodulealias`` test)
       # semExprWithType(c, result)
-      result = semExpr(c, result)
+      result = semExpr(c, result, flags)
     of tyStmt:
       result = semStmt(c, result)
     of tyTypeDesc:
@@ -290,14 +292,14 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
       result.typ = makeTypeDesc(c, typ)
       #result = symNodeFromType(c, typ, n.info)
     else:
-      result = semExpr(c, result)
+      result = semExpr(c, result, flags)
       result = fitNode(c, s.typ.sons[0], result)
       #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
   dec(evalTemplateCounter)
   c.friendModule = oldFriend
 
-proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, 
-                  semCheck: bool = true): PNode = 
+proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
+                  flags: TExprFlags = {}): PNode =
   markUsed(n, sym)
   if sym == c.p.owner:
     globalError(n.info, errRecursiveDependencyX, sym.name.s)
@@ -306,7 +308,8 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   #  c.evalContext = c.createEvalContext(emStatic)
 
   result = evalMacroCall(c.module, n, nOrig, sym)
-  if semCheck: result = semAfterMacroCall(c, result, sym)
+  if efNoSemCheck notin flags:
+    result = semAfterMacroCall(c, result, sym, flags)
 
 proc forceBool(c: PContext, n: PNode): PNode = 
   result = fitNode(c, getSysType(tyBool), n)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index b46d83a92..4cb5ad38c 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -42,7 +42,7 @@ type
 
   TExprFlag* = enum 
     efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
-    efAllowDestructor, efWantValue, efOperand
+    efAllowDestructor, efWantValue, efOperand, efNoSemCheck
   TExprFlags* = set[TExprFlag]
 
   PContext* = ref TContext
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 1fd9075e8..9b3b2d73e 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -10,11 +10,12 @@
 # this module does the semantic checking for expressions
 # included from sem.nim
 
-proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = 
+proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
+                     flags: TExprFlags = {}): PNode =
   markUsed(n, s)
   pushInfoContext(n.info)
   result = evalTemplate(n, s, getCurrOwner())
-  if semCheck: result = semAfterMacroCall(c, result, s)
+  if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
   popInfoContext()
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
@@ -95,8 +96,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       else: result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
-  of skMacro: result = semMacroExpr(c, n, n, s)
-  of skTemplate: result = semTemplateExpr(c, n, s)
+  of skMacro: result = semMacroExpr(c, n, n, s, flags)
+  of skTemplate: result = semTemplateExpr(c, n, s, flags)
   of skVar, skLet, skResult, skParam, skForVar:
     markUsed(n, s)
     # if a proc accesses a global variable, it is not side effect free:
@@ -793,8 +794,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
   result = n
   let callee = result.sons[0].sym
   case callee.kind
-  of skMacro: result = semMacroExpr(c, result, orig, callee)
-  of skTemplate: result = semTemplateExpr(c, result, callee)
+  of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
+  of skTemplate: result = semTemplateExpr(c, result, callee, flags)
   else:
     semFinishOperands(c, result)
     activate(c, result)
@@ -954,8 +955,10 @@ proc readTypeParameter(c: PContext, typ: PType,
 proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if it's not a built-in field access
   checkSonsLen(n, 2)
-  # early exit for this; see tests/compile/tbindoverload.nim:
-  if isSymChoice(n.sons[1]): return
+  # tests/bind/tbindoverload.nim wants an early exit here, but seems to
+  # work without now. template/tsymchoicefield doesn't like an early exit
+  # here at all!
+  #if isSymChoice(n.sons[1]): return
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
   if s != nil:
@@ -1964,13 +1967,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
           result = semDirectOp(c, n, flags)
         else:
           var p = fixImmediateParams(n)
-          result = semMacroExpr(c, p, p, s)
+          result = semMacroExpr(c, p, p, s, flags)
       of skTemplate:
         if sfImmediate notin s.flags:
           result = semDirectOp(c, n, flags)
         else:
           var p = fixImmediateParams(n)
-          result = semTemplateExpr(c, p, s)
+          result = semTemplateExpr(c, p, s, flags)
       of skType:
         # XXX think about this more (``set`` procs)
         if n.len == 2:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 925a80832..caaab2291 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -113,12 +113,18 @@ proc pickMaxInt(n: PNode): BiggestInt =
     internalError(n.info, "pickMaxInt")
 
 proc makeRange(typ: PType, first, last: BiggestInt): PType = 
-  var n = newNode(nkRange)
-  addSon(n, newIntNode(nkIntLit, min(first, last)))
-  addSon(n, newIntNode(nkIntLit, max(first, last)))
-  result = newType(tyRange, typ.owner)
-  result.n = n
-  addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
+  let minA = min(first, last)
+  let maxA = max(first, last)
+  let lowerNode = newIntNode(nkIntLit, minA)
+  if typ.kind == tyInt and minA == maxA:
+    result = getIntLitType(lowerNode)
+  else:
+    var n = newNode(nkRange)
+    addSon(n, lowerNode)
+    addSon(n, newIntNode(nkIntLit, maxA))
+    result = newType(tyRange, typ.owner)
+    result.n = n
+    addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
 
 proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
   var n = newNode(nkRange)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index ffc1a43b2..da4a2a132 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -47,12 +47,12 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
   of skTemplate:
     if macroToExpand(s):
       let n = fixImmediateParams(n)
-      result = semTemplateExpr(c, n, s, false)
+      result = semTemplateExpr(c, n, s, {efNoSemCheck})
     else:
       result = symChoice(c, n, s, scOpen)
   of skMacro: 
     if macroToExpand(s):
-      result = semMacroExpr(c, n, n, s, false)
+      result = semMacroExpr(c, n, n, s, {efNoSemCheck})
     else:
       result = symChoice(c, n, s, scOpen)
   of skGenericParam: 
@@ -126,14 +126,14 @@ proc semGenericStmt(c: PContext, n: PNode,
       case s.kind
       of skMacro:
         if macroToExpand(s):
-          result = semMacroExpr(c, n, n, s, false)
+          result = semMacroExpr(c, n, n, s, {efNoSemCheck})
         else:
           n.sons[0] = symChoice(c, n.sons[0], s, scOption)
           result = n
       of skTemplate: 
         if macroToExpand(s):
           let n = fixImmediateParams(n)
-          result = semTemplateExpr(c, n, s, false)
+          result = semTemplateExpr(c, n, s, {efNoSemCheck})
         else:
           n.sons[0] = symChoice(c, n.sons[0], s, scOption)
           result = n
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index b71198119..ad34eea65 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -138,6 +138,18 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
   result = semTemplBody(c, n)
   closeScope(c)
 
+proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
+  result = n
+  if n.kind == nkIdent:
+    let s = qualifiedLookUp(c.c, n, {})
+    if s != nil:
+      if s.owner == c.owner and s.kind == skParam:
+        incl(s.flags, sfUsed)
+        result = newSymNode(s, n.info)
+  else:
+    for i in 0 .. <n.safeLen:
+      result.sons[i] = onlyReplaceParams(c, n.sons[i])
+
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
   result = newSym(kind, considerAcc(n), c.owner, n.info)
   incl(result.flags, sfGenSym)
@@ -154,7 +166,13 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
       of nkPostfix: x = x[1]
       of nkPragmaExpr: x = x[0]
       of nkIdent: break
-      else: illFormedAst(x)
+      of nkAccQuoted:
+        # consider:  type `T TemplParam` {.inject.}
+        # it suffices to return to treat it like 'inject':
+        n = onlyReplaceParams(c, n)
+        return
+      else:
+        illFormedAst(x)
     let ident = getIdentNode(c, x)
     if not isTemplParam(c, ident):
       c.toInject.incl(x.ident.id)
@@ -232,18 +250,6 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
     for j in countup(0, L-3):
       addLocalDecl(c, a.sons[j], symKind)
 
-proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
-  result = n
-  if n.kind == nkIdent:
-    let s = qualifiedLookUp(c.c, n, {})
-    if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
-        incl(s.flags, sfUsed)
-        result = newSymNode(s, n.info)
-  else:
-    for i in 0 .. <n.safeLen:
-      result.sons[i] = onlyReplaceParams(c, n.sons[i])
-
 proc semPattern(c: PContext, n: PNode): PNode
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 
   result = n
@@ -380,7 +386,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkPragma:
     result = onlyReplaceParams(c, n)
   else:
-    # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
+    # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
     if n.kind == nkDotExpr or n.kind == nkAccQuoted:
       let s = qualifiedLookUp(c.c, n, {})
@@ -533,7 +539,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       elif contains(c.toBind, s.id):
         result = symChoice(c.c, n, s, scClosed)
       elif templToExpand(s):
-        result = semPatternBody(c, semTemplateExpr(c.c, n, s, false))
+        result = semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
       else:
         discard
         # we keep the ident unbound for matching instantiated symbols and
@@ -578,7 +584,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       if s.owner == c.owner and s.kind == skParam: discard
       elif contains(c.toBind, s.id): discard
       elif templToExpand(s):
-        return semPatternBody(c, semTemplateExpr(c.c, n, s, false))
+        return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
     
     if n.kind == nkInfix and n.sons[0].kind == nkIdent:
       # we interpret `*` and `|` only as pattern operators if they occur in
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index c53dc0f7d..a563cf06c 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -225,8 +225,16 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
         # properly filled-out in semtypinst (see how tyStaticExpr
         # is handled there).
         indx = makeRangeWithStaticExpr(c, e)
-      else:
+      elif e.kind == nkIdent:
         indx = e.typ.skipTypes({tyTypeDesc})
+      else:
+        let x = semConstExpr(c, e)
+        if x.kind in {nkIntLit..nkUInt64Lit}:
+          indx = makeRangeType(c, 0, x.intVal-1, n.info, 
+                               x.typ.skipTypes({tyTypeDesc}))
+        else:
+          indx = x.typ.skipTypes({tyTypeDesc})
+          #localError(n[1].info, errConstExprExpected)
     addSonSkipIntLit(result, indx)
     if indx.kind == tyGenericInst: indx = lastSon(indx)
     if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index b9fb0c957..b83c27d22 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -668,6 +668,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isNone
     else: discard
   of tyOpenArray, tyVarargs:
+    # varargs[expr] is special
+    if f.kind == tyVarargs and f.sons[0].kind == tyExpr: return
     case a.kind
     of tyOpenArray, tyVarargs:
       result = typeRel(c, base(f), base(a))
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 7922acbe9..fb5e321b6 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -166,6 +166,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
         idNodeTablePut(c.transCon.mapping, it.sons[j].sym, newSymNode(newVar))
         defs[j] = newSymNode(newVar).PTransNode
       assert(it.sons[L-2].kind == nkEmpty)
+      defs[L-2] = ast.emptyNode.PTransNode
       defs[L-1] = transform(c, it.sons[L-1])
       result[i] = defs
 
diff --git a/compiler/types.nim b/compiler/types.nim
index ae31d24de..c80f6ff35 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -385,8 +385,8 @@ proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
 
 proc valueToString(a: PNode): string =
   case a.kind
-  of nkCharLit..nkUInt64Lit: result = $(a.intVal)
-  of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
+  of nkCharLit..nkUInt64Lit: result = $a.intVal
+  of nkFloatLit..nkFloat128Lit: result = $a.floatVal
   of nkStrLit..nkTripleStrLit: result = a.strVal
   else: result = "<invalid value>"
 
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
new file mode 100644
index 000000000..ebb9b7e15
--- /dev/null
+++ b/compiler/typesrenderer.nim
@@ -0,0 +1,113 @@
+import renderer, strutils, ast, msgs, types
+
+const defaultParamSeparator* = ","
+
+proc renderPlainSymbolName*(n: PNode): string =
+  ## Returns the first non '*' nkIdent node from the tree.
+  ##
+  ## Use this on documentation name nodes to extract the *raw* symbol name,
+  ## without decorations, parameters, or anything. That can be used as the base
+  ## for the HTML hyperlinks.
+  result = ""
+  case n.kind
+  of nkPostfix:
+    for i in 0 .. <n.len:
+      result = renderPlainSymbolName(n[<n.len])
+      if result.len > 0:
+        return
+  of nkIdent:
+    if n.ident.s != "*":
+      result = n.ident.s
+  of nkSym:
+    result = n.sym.renderDefinitionName(noQuotes = true)
+  of nkPragmaExpr:
+    result = renderPlainSymbolName(n[0])
+  of nkAccQuoted:
+    result = renderPlainSymbolName(n[<n.len])
+  else:
+    internalError(n.info, "renderPlainSymbolName() with " & $n.kind)
+  assert (not result.isNil)
+
+proc renderType(n: PNode): string =
+  ## Returns a string with the node type or the empty string.
+  case n.kind:
+  of nkIdent: result = n.ident.s
+  of nkSym: result = typeToString(n.sym.typ)
+  of nkVarTy:
+    assert len(n) == 1
+    result = renderType(n[0])
+  of nkRefTy:
+    assert len(n) == 1
+    result = "ref." & renderType(n[0])
+  of nkPtrTy:
+    assert len(n) == 1
+    result = "ptr." & renderType(n[0])
+  of nkProcTy:
+    assert len(n) > 1
+    let params = n[0]
+    assert params.kind == nkFormalParams
+    assert len(params) > 0
+    result = "proc("
+    for i in 1 .. <len(params): result.add(renderType(params[i]) & ',')
+    result[<len(result)] = ')'
+  of nkIdentDefs:
+    assert len(n) >= 3
+    let typePos = len(n) - 2
+    let typeStr = renderType(n[typePos])
+    result = typeStr
+    for i in 1 .. <typePos:
+      assert n[i].kind == nkIdent
+      result.add(',' & typeStr)
+  of nkTupleTy:
+    assert len(n) > 0
+    result = "tuple["
+    for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
+    result[<len(result)] = ']'
+  of nkBracketExpr:
+    assert len(n) >= 2
+    result = renderType(n[0]) & '['
+    for i in 1 .. <len(n): result.add(renderType(n[i]) & ',')
+    result[<len(result)] = ']'
+  else: result = ""
+  assert (not result.isNil)
+
+
+proc renderParamTypes(found: var seq[string], n: PNode) =
+  ## Recursive helper, adds to `found` any types, or keeps diving the AST.
+  ##
+  ## The normal `doc` generator doesn't include .typ information, so the
+  ## function won't render types for parameters with default values. The `doc2`
+  ## generator does include the information.
+  case n.kind
+  of nkFormalParams:
+    for i in 1 .. <len(n): renderParamTypes(found, n[i])
+  of nkIdentDefs:
+    # These are parameter names + type + default value node.
+    let typePos = len(n) - 2
+    assert typePos > 0
+    var typeStr = renderType(n[typePos])
+    if typeStr.len < 1:
+      # Try with the last node, maybe its a default value.
+      assert n[typePos+1].kind != nkEmpty
+      let typ = n[typePos+1].typ
+      if not typ.isNil: typeStr = typeToString(typ, preferExported)
+      if typeStr.len < 1:
+        return
+    for i in 0 .. <typePos:
+      assert n[i].kind == nkIdent
+      found.add(typeStr)
+  else:
+    internalError(n.info, "renderParamTypes(found,n) with " & $n.kind)
+
+proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string =
+  ## Returns the types contained in `n` joined by `sep`.
+  ##
+  ## This proc expects to be passed as `n` the parameters of any callable. The
+  ## string output is meant for the HTML renderer. If there are no parameters,
+  ## the empty string is returned. The parameters will be joined by `sep` but
+  ## other characters may appear too, like ``[]`` or ``|``.
+  result = ""
+  var found: seq[string] = @[]
+  renderParamTypes(found, n)
+  if found.len > 0:
+    result = found.join(sep)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 0d5386502..fb8749250 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -48,9 +48,17 @@ type
                               # XXX 'break' should perform cleanup actions
                               # What does the C backend do for it?
 
-proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) =
+proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
   if x != nil:
-    stackTraceAux(c, x.next, x.comesFrom)
+    if recursionLimit == 0:
+      var calls = 0
+      var x = x
+      while x != nil:
+        inc calls
+        x = x.next
+      msgWriteln($calls & " calls omitted\n")
+      return
+    stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
     var info = c.debug[pc]
     # we now use the same format as in system/except.nim
     var s = toFilename(info)
@@ -222,13 +230,14 @@ proc pushSafePoint(f: PStackFrame; pc: int) =
 
 proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
 
-proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int =
+proc cleanUpOnException(c: PCtx; tos: PStackFrame): 
+                                              tuple[pc: int, f: PStackFrame] =
   let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
   var f = tos
   while true:
     while f.safePoints.isNil or f.safePoints.len == 0:
       f = f.next
-      if f.isNil: return -1
+      if f.isNil: return (-1, nil)
     var pc2 = f.safePoints[f.safePoints.high]
 
     var nextExceptOrFinally = -1
@@ -244,13 +253,13 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int =
         c.currentExceptionB = c.currentExceptionA
         c.currentExceptionA = nil
         # execute the corresponding handler:
-        return pc2
+        return (pc2, f)
       inc pc2
     if nextExceptOrFinally >= 0:
       pc2 = nextExceptOrFinally
     if c.code[pc2].opcode == opcFinally:
       # execute the corresponding handler, but don't quit walking the stack:
-      return pc2
+      return (pc2, f)
     # not the right one:
     discard f.safePoints.pop
 
@@ -495,18 +504,46 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else: stackTrace(c, tos, pc, errNilAccess)
     of opcAddInt:
       decodeBC(rkInt)
-      regs[ra].intVal = regs[rb].intVal + regs[rc].intVal
+      let
+        bVal = regs[rb].intVal
+        cVal = regs[rc].intVal
+        sum = bVal +% cVal
+      if (sum xor bVal) >= 0 or (sum xor cVal) >= 0:
+        regs[ra].intVal = sum
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcAddImmInt:
       decodeBImm(rkInt)
       #message(c.debug[pc], warnUser, "came here")
       #debug regs[rb].node
-      regs[ra].intVal = regs[rb].intVal + imm
+      let
+        bVal = regs[rb].intVal
+        cVal = imm
+        sum = bVal +% cVal
+      if (sum xor bVal) >= 0 or (sum xor cVal) >= 0:
+        regs[ra].intVal = sum
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcSubInt:
       decodeBC(rkInt)
-      regs[ra].intVal = regs[rb].intVal - regs[rc].intVal
+      let
+        bVal = regs[rb].intVal
+        cVal = regs[rc].intVal
+        diff = bVal -% cVal
+      if (diff xor bVal) >= 0 or (diff xor not cVal) >= 0:
+        regs[ra].intVal = diff
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcSubImmInt:
       decodeBImm(rkInt)
-      regs[ra].intVal = regs[rb].intVal - imm
+      let
+        bVal = regs[rb].intVal
+        cVal = imm
+        diff = bVal -% cVal
+      if (diff xor bVal) >= 0 or (diff xor not cVal) >= 0:
+        regs[ra].intVal = diff
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcLenSeq:
       decodeBImm(rkInt)
       #assert regs[rb].kind == nkBracket
@@ -539,7 +576,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].intVal = nimsets.cardSet(regs[rb].node)
     of opcMulInt:
       decodeBC(rkInt)
-      regs[ra].intVal = regs[rb].intVal * regs[rc].intVal
+      let
+        bVal = regs[rb].intVal
+        cVal = regs[rc].intVal
+        product = bVal *% cVal
+        floatProd = toBiggestFloat(bVal) * toBiggestFloat(cVal)
+        resAsFloat = toBiggestFloat(product)
+      if resAsFloat == floatProd:
+        regs[ra].intVal = product
+      elif 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
+        regs[ra].intVal = product
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcDivInt:
       decodeBC(rkInt)
       if regs[rc].intVal == 0: stackTrace(c, tos, pc, errConstantDivisionByZero)
@@ -632,7 +680,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcUnaryMinusInt:
       decodeB(rkInt)
       assert regs[rb].kind == rkInt
-      regs[ra].intVal = -regs[rb].intVal
+      let val = regs[rb].intVal
+      if val != int64.low:
+        regs[ra].intVal = -val
+      else:
+        stackTrace(c, tos, pc, errOverOrUnderflow)
     of opcUnaryMinusFloat:
       decodeB(rkFloat)
       assert regs[rb].kind == rkFloat
@@ -826,19 +878,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcFinallyEnd:
       if c.currentExceptionA != nil:
         # we are in a cleanup run:
-        pc = cleanUpOnException(c, tos, regs)-1
-        if pc < 0: 
+        let (newPc, newTos) = cleanUpOnException(c, tos)
+        if newPc-1 < 0:
           bailOut(c, tos)
           return
+        pc = newPc-1
+        if tos != newTos:
+          tos = newTos
+          move(regs, tos.slots)
     of opcRaise:
       let raised = regs[ra].node
       c.currentExceptionA = raised
       c.exceptionInstr = pc
+      let (newPc, newTos) = cleanUpOnException(c, tos)
       # -1 because of the following 'inc'
-      pc = cleanUpOnException(c, tos, regs) - 1
-      if pc < 0:
+      if pc-1 < 0:
         bailOut(c, tos)
         return
+      pc = newPc -1
+      if tos != newTos:
+        tos = newTos
+        move(regs, tos.slots)
     of opcNew:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 3c0f8dbc9..7c0c3d4f5 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -328,6 +328,7 @@ proc canonValue*(n: PNode): PNode =
 
 proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len
+  assert(n.kind != nkCall)
   c.constants.add n.canonValue
   internalAssert result < 0x7fff
 
@@ -1033,6 +1034,7 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
 proc setSlot(c: PCtx; v: PSym) =
   # XXX generate type initialization here?
   if v.position == 0:
+    if c.prc.maxSlots == 0: c.prc.maxSlots = 1
     v.position = c.prc.maxSlots
     c.prc.slots[v.position] = (inUse: true,
         kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
@@ -1285,16 +1287,13 @@ proc genVarSection(c: PCtx; n: PNode) =
         if s.position == 0:
           if sfImportc in s.flags: c.importcSym(a.info, s)
           else:
-            let sa = if s.ast.isNil: getNullValue(s.typ, a.info) 
-                     else: canonValue(s.ast)
+            let sa = getNullValue(s.typ, a.info)
+            #if s.ast.isNil: getNullValue(s.typ, a.info)
+            #else: canonValue(s.ast)
+            assert sa.kind != nkCall
             c.globals.add(sa)
             s.position = c.globals.len
-        if a.sons[2].kind == nkEmpty:
-          when false:
-            withTemp(tmp, s.typ):
-              c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
-              c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
-        else:
+        if a.sons[2].kind != nkEmpty:
           let tmp = c.genx(a.sons[0], {gfAddrOf})
           let val = c.genx(a.sons[2])
           c.gABC(a, opcWrDeref, tmp, val)
@@ -1502,7 +1501,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     else:
       localError(n.info, errGenerated, "VM is not allowed to 'cast'")
   else:
-    internalError n.info, "too implement " & $n.kind
+    internalError n.info, "cannot generate VM code for " & n.renderTree
 
 proc removeLastEof(c: PCtx) =
   let last = c.code.len-1