summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-08-23 16:15:02 +0200
committerGitHub <noreply@github.com>2019-08-23 16:15:02 +0200
commitb07694cd90ab7c6eb4660971ddb818b461d4eed8 (patch)
treea158993297748d4c55b6e069a6636eefcacd9865
parentf28a47ea7b9c579b172653c15dc2cc054adf599a (diff)
downloadNim-b07694cd90ab7c6eb4660971ddb818b461d4eed8.tar.gz
new gensym handling (#11985)
* new .gensym implementation
* make astspec test green again
* introduce a --useVersion switch to group compatibility switches
* fixes #10180
* fixes #11494 
* fixes #11483
* object constructor fields and named parameters are also not gensym'ed
* disabled broken package
-rw-r--r--changelog.md7
-rw-r--r--compiler/commands.nim9
-rw-r--r--compiler/evaltempl.nim14
-rw-r--r--compiler/main.nim2
-rw-r--r--compiler/options.nim1
-rw-r--r--compiler/semexprs.nim3
-rw-r--r--compiler/semtempl.nim62
-rw-r--r--compiler/suggest.nim6
-rw-r--r--compiler/vm.nim2
-rw-r--r--doc/advopt.txt2
-rw-r--r--doc/manual.rst39
-rw-r--r--lib/core/locks.nim4
-rw-r--r--lib/system/alloc.nim12
-rw-r--r--testament/important_packages.nim8
-rw-r--r--tests/astspec/tastspec.nim219
-rw-r--r--tests/template/tmore_regressions.nim44
-rw-r--r--tests/template/tparams_gensymed.nim15
-rw-r--r--tests/template/tredefinition.nim13
18 files changed, 347 insertions, 115 deletions
diff --git a/changelog.md b/changelog.md
index 41d371b60..f29a4c69d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -10,6 +10,13 @@
   to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
   feature.
 
+- The language definition and compiler are now stricter about ``gensym``'ed
+  symbols in hygienic templates. See the section in the
+  [manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
+  for further details. Use the compiler switch `--useVersion:0.19` for a
+  transition period.
+
+
 ### Breaking changes in the standard library
 
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 3874ea38f..662df9c84 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -788,6 +788,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "expandmacro":
     expectArg(conf, switch, arg, pass, info)
     conf.macrosToExpand[arg] = "T"
+  of "useversion":
+    expectArg(conf, switch, arg, pass, info)
+    case arg
+    of "0.19":
+      conf.globalOptions.incl optNimV019
+    of "1.0":
+      discard "the default"
+    else:
+      localError(conf, info, "unknown Nim version; currently supported values are: {0.19, 1.0}")
   of "":
     conf.projectName = "-"
   else:
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index d3d3e5f77..d941f6c46 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -10,7 +10,7 @@
 ## Template evaluation engine. Now hygienic.
 
 import
-  strutils, options, ast, astalgo, msgs, renderer, lineinfos
+  strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
 
 type
   TemplCtx = object
@@ -20,6 +20,7 @@ type
     mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
                       # new symbol
     config: ConfigRef
+    ic: IdentCache
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
@@ -52,7 +53,11 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
           #if x.kind == skParam and x.owner.kind == skModule:
           #  internalAssert c.config, false
           idTablePut(c.mapping, s, x)
-        result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
+        if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions:
+          result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id),
+            if c.instLines: actual.info else: templ.info)
+        else:
+          result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
     else:
       result.add copyNode(c, templ, actual)
   of nkNone..nkIdent, nkType..nkNilLit: # atom
@@ -160,7 +165,9 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
     result.typ = res.typ
 
 proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
-                   conf: ConfigRef; fromHlo=false): PNode =
+                   conf: ConfigRef;
+                   ic: IdentCache;
+                   fromHlo=false): PNode =
   inc(conf.evalTemplateCounter)
   if conf.evalTemplateCounter > evalTemplateLimit:
     globalError(conf, n.info, errTemplateInstantiationTooNested)
@@ -172,6 +179,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
   ctx.owner = tmpl
   ctx.genSymOwner = genSymOwner
   ctx.config = conf
+  ctx.ic = ic
   initIdTable(ctx.mapping)
 
   let body = tmpl.getBody
diff --git a/compiler/main.nim b/compiler/main.nim
index 8cd8a52d4..877b82dd9 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -18,7 +18,7 @@ import
   sem, idents, passes, extccomp,
   cgen, json, nversion,
   platform, nimconf, passaux, depends, vm, idgen,
-  parser, modules,
+  modules,
   modulegraphs, tables, rod, lineinfos, pathutils
 
 when not defined(leanCompiler):
diff --git a/compiler/options.nim b/compiler/options.nim
index 75eec4756..52ecd61bc 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -84,6 +84,7 @@ type                          # please make sure we have under 32 options
     optDynlibOverrideAll
     optNimV2
     optMultiMethods
+    optNimV019
 
   TGlobalOptions* = set[TGlobalOption]
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 8048111cd..57d9aae4b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -30,7 +30,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   # Note: This is n.info on purpose. It prevents template from creating an info
   # context when called from an another template
   pushInfoContext(c.config, n.info, s.detailedInfo)
-  result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
+  result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
   popInfoContext(c.config)
 
@@ -1236,6 +1236,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
     result = newSymNode(s, n.info)
   else:
     let info = getCallLineInfo(n)
+    #if efInCall notin flags:
     markUsed(c, info, s)
     onUse(info, s)
     result = newSymNode(s, info)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index ddc0667e4..907d2174e 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -47,7 +47,8 @@ type
   TSymChoiceRule = enum
     scClosed, scOpen, scForceOpen
 
-proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
+proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
+               isField = false): PNode =
   var
     a: PSym
     o: TOverloadIter
@@ -63,9 +64,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     # XXX this makes more sense but breaks bootstrapping for now:
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
-    result = newSymNode(s, info)
-    markUsed(c, info, s)
-    onUse(info, s)
+    if not isField or sfGenSym notin s.flags:
+      result = newSymNode(s, info)
+      markUsed(c, info, s)
+      onUse(info, s)
+    else:
+      result = n
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
@@ -74,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     result = newNodeIT(kind, info, newTypeS(tyNone, c))
     a = initOverloadIter(o, c, n)
     while a != nil:
-      if a.kind != skModule:
+      if a.kind != skModule and (not isField or sfGenSym notin s.flags):
         incl(a.flags, sfUsed)
         addSon(result, newSymNode(a, info))
         onUse(info, a)
@@ -119,6 +123,7 @@ type
     owner: PSym
     cursorInBody: bool # only for nimsuggest
     scopeN: int
+    noGenSym: int
 
 template withBracketExpr(ctx, x, body: untyped) =
   body
@@ -228,7 +233,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
     else:
       replaceIdentBySym(c.c, n, ident)
 
-proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
+proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
   incl(s.flags, sfUsed)
   # we do not call onUse here, as the identifier is not really
   # resolved here. We will fixup the used identifiers later.
@@ -237,15 +242,18 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
     # Introduced in this pass! Leave it as an identifier.
     result = n
   of OverloadableSyms:
-    result = symChoice(c, n, s, scOpen)
+    result = symChoice(c, n, s, scOpen, isField)
   of skGenericParam:
-    result = newSymNodeTypeDesc(s, n.info)
+    if isField: result = n
+    else: result = newSymNodeTypeDesc(s, n.info)
   of skParam:
     result = n
   of skType:
-    result = newSymNodeTypeDesc(s, n.info)
+    if isField: result = n
+    else: result = newSymNodeTypeDesc(s, n.info)
   else:
-    result = newSymNode(s, n.info)
+    if isField: result = n
+    else: result = newSymNode(s, n.info)
 
 proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
   result = n
@@ -322,22 +330,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     if n.ident.id in c.toInject: return n
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
+      if s.owner == c.owner and s.kind == skParam and
+          (sfGenSym notin s.flags or c.noGenSym == 0):
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
         onUse(n.info, s)
       elif contains(c.toBind, s.id):
-        result = symChoice(c.c, n, s, scClosed)
+        result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
       elif contains(c.toMixin, s.name.id):
-        result = symChoice(c.c, n, s, scForceOpen)
-      elif s.owner == c.owner and sfGenSym in s.flags:
+        result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
+      elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0:
         # template tmp[T](x: var seq[T]) =
         # var yz: T
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
         onUse(n.info, s)
       else:
-        result = semTemplSymbol(c.c, n, s)
+        result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
   of nkBind:
     result = semTemplBody(c, n.sons[0])
   of nkBindStmt:
@@ -524,12 +533,27 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         onUse(n.info, s)
         return newSymNode(s, n.info)
       elif contains(c.toBind, s.id):
-        return symChoice(c.c, n, s, scClosed)
+        return symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
       elif contains(c.toMixin, s.name.id):
-        return symChoice(c.c, n, s, scForceOpen)
+        return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
       else:
-        return symChoice(c.c, n, s, scOpen)
-    result = semTemplBodySons(c, n)
+        return symChoice(c.c, n, s, scOpen, c.noGenSym > 0)
+    if n.kind == nkDotExpr:
+      result = n
+      result.sons[0] = semTemplBody(c, n.sons[0])
+      inc c.noGenSym
+      result.sons[1] = semTemplBody(c, n.sons[1])
+      dec c.noGenSym
+    else:
+      result = semTemplBodySons(c, n)
+  of nkExprColonExpr, nkExprEqExpr:
+    if n.len == 2:
+      inc c.noGenSym
+      result.sons[0] = semTemplBody(c, n.sons[0])
+      dec c.noGenSym
+      result.sons[1] = semTemplBody(c, n.sons[1])
+    else:
+      result = semTemplBodySons(c, n)
   else:
     result = semTemplBodySons(c, n)
 
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index dc01916d1..9680bc846 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -261,15 +261,15 @@ proc getQuality(s: PSym): range[0..100] =
     if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50
   return 100
 
-template wholeSymTab(cond, section: untyped) =
+template wholeSymTab(cond, section: untyped) {.dirty.} =
   var isLocal = true
   var scopeN = 0
   for scope in walkScopes(c.currentScope):
     if scope == c.topLevelScope: isLocal = false
     dec scopeN
     for item in scope.symbols:
-      let it {.inject.} = item
-      var pm {.inject.}: PrefixMatch
+      let it = item
+      var pm: PrefixMatch
       if cond:
         outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
                                  pm, c.inTypeContext > 0, scopeN))
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 75f6cc1c3..31dec418f 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1137,7 +1137,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
           macroCall.add(node)
-        var a = evalTemplate(macroCall, prc, genSymOwner, c.config)
+        var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache)
         if a.kind == nkStmtList and a.len == 1: a = a[0]
         a.recSetFlagIsRef
         ensureKind(rkNode)
diff --git a/doc/advopt.txt b/doc/advopt.txt
index f5359c0c0..e3d7f018c 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -122,7 +122,7 @@ Advanced options:
                             enable experimental language feature
   --legacy:$2
                             enable obsolete/legacy language feature
-                            legacy code.
+  --useVersion:0.19|1.0     emulate Nim version X of the Nim compiler
   --newruntime              use an alternative runtime that uses destructors
                             and that uses a shared heap via -d:useMalloc
   --profiler:on|off         enable profiling; requires `import nimprof`, and
diff --git a/doc/manual.rst b/doc/manual.rst
index e1fb2fa59..7dad44423 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -4899,6 +4899,45 @@ no semantics outside of a template definition and cannot be abstracted over:
 To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for
 a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates.
 
+``gensym``'ed symbols cannot be used as ``field`` in the ``x.field`` syntax.
+Nor can they be used in the ``ObjectConstruction(field: value)``
+and ``namedParameterCall(field = value)`` syntactic constructs.
+
+The reason for this is that code like
+
+.. code-block:: nim
+    :test: "nim c $1"
+
+  type
+    T = object
+      f: int
+
+  template tmp(x: T) =
+    let f = 34
+    echo x.f, T(f: 4)
+
+
+should work as expected.
+
+However, this means that the method call syntax is not available for
+``gensym``'ed symbols:
+
+.. code-block:: nim
+    :test: "nim c $1"
+    :status: 1
+
+  template tmp(x) =
+    type
+      T {.gensym.} = int
+
+    echo x.T # invalid: instead use:  'echo T(x)'.
+
+  tmp(12)
+
+
+**Note**: The Nim compiler prior to version 1 was more lenient about this
+requirement. Use the ``--useVersion:0.19`` switch for a transition period.
+
 
 
 Limitations of the method call syntax
diff --git a/lib/core/locks.nim b/lib/core/locks.nim
index d6d579ba0..0143957ce 100644
--- a/lib/core/locks.nim
+++ b/lib/core/locks.nim
@@ -60,11 +60,11 @@ template withLock*(a: Lock, body: untyped) =
   ## Acquires the given lock, executes the statements in body and
   ## releases the lock after the statements finish executing.
   mixin acquire, release
-  a.acquire()
+  acquire(a)
   {.locks: [a].}:
     try:
       body
     finally:
-      a.release()
+      release(a)
 
 {.pop.}
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 9c47d9de9..efbd95089 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -984,7 +984,7 @@ when defined(nimTypeNames):
 
 # ---------------------- thread memory region -------------------------------
 
-template instantiateForRegion(allocator: untyped) =
+template instantiateForRegion(allocator: untyped) {.dirty.} =
   {.push stackTrace: off.}
 
   when defined(fulldebug):
@@ -1006,8 +1006,8 @@ template instantiateForRegion(allocator: untyped) =
   proc dealloc(p: pointer) =
     dealloc(allocator, p)
 
-  proc realloc(p: pointer, newsize: Natural): pointer =
-    result = realloc(allocator, p, newsize)
+  proc realloc(p: pointer, newSize: Natural): pointer =
+    result = realloc(allocator, p, newSize)
 
   when false:
     proc countFreeMem(): int =
@@ -1054,13 +1054,13 @@ template instantiateForRegion(allocator: untyped) =
     else:
       dealloc(p)
 
-  proc reallocShared(p: pointer, newsize: Natural): pointer =
+  proc reallocShared(p: pointer, newSize: Natural): pointer =
     when hasThreadSupport:
       acquireSys(heapLock)
-      result = realloc(sharedHeap, p, newsize)
+      result = realloc(sharedHeap, p, newSize)
       releaseSys(heapLock)
     else:
-      result = realloc(p, newsize)
+      result = realloc(p, newSize)
 
   when hasThreadSupport:
     template sharedMemStatsShared(v: int) =
diff --git a/testament/important_packages.nim b/testament/important_packages.nim
index 93aeb1bbc..a96310ee5 100644
--- a/testament/important_packages.nim
+++ b/testament/important_packages.nim
@@ -5,9 +5,9 @@ template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): unty
 var packages*: seq[tuple[name, cmd: string; hasDeps: bool; url: string]] = @[]
 
 
-pkg "argparse"
+#pkg "argparse"
 pkg "arraymancer", "nim c -r src/arraymancer.nim", true
-pkg "ast_pattern_matching", "nim c -r tests/test1.nim"
+pkg "ast_pattern_matching", "nim c -r --useVersion=0.19 tests/test1.nim"
 pkg "binaryheap", "nim c -r binaryheap.nim"
 pkg "blscurve", "", true
 pkg "bncurve", "", true
@@ -61,7 +61,7 @@ pkg "npeg"
 pkg "ormin", "nim c -o:orminn ormin.nim", true
 pkg "parsetoml"
 pkg "patty"
-pkg "plotly", "nim c examples/all.nim", true
+pkg "plotly", "nim c --useVersion:0.19 examples/all.nim", true
 pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim", true
 pkg "regex", "nim c src/regex", true
 pkg "result", "nim c -r result.nim"
@@ -71,7 +71,7 @@ pkg "sdl2_nim", "nim c -r sdl2/sdl.nim"
 pkg "snip", "", false, "https://github.com/genotrance/snip"
 pkg "stint", "nim c -o:stintt -r stint.nim"
 pkg "strunicode", "nim c -r src/strunicode.nim", true
-pkg "telebot", "nim c -o:tbot -r telebot.nim", true
+pkg "telebot", "nim c -o:tbot --useVersion:0.19 -r telebot.nim", true
 pkg "tiny_sqlite"
 pkg "unicodedb"
 pkg "unicodeplus", "", true
diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim
index 82c32f130..f9e35804d 100644
--- a/tests/astspec/tastspec.nim
+++ b/tests/astspec/tastspec.nim
@@ -6,6 +6,74 @@ action: compile
 
 import ../ast_pattern_matching
 
+template expectNimNode(arg: untyped): NimNode = arg
+  ## This template here is just to be injected by `myquote`, so that
+  ## a nice error message appears when the captured symbols are not of
+  ## type `NimNode`.
+
+proc substitudeComments(symbols, values, n: NimNode): NimNode =
+  ## substitudes all nodes of kind nnkCommentStmt to parameter
+  ## symbols. Consumes the argument `n`.
+  if n.kind == nnkCommentStmt:
+    values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal))
+    # Gensym doesn't work for parameters. These identifiers won't
+    # clash unless an argument is constructed to clash here.
+    symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV")
+    return symbols[^1]
+  for i in 0 ..< n.len:
+    n[i] = substitudeComments(symbols, values, n[i])
+  return n
+
+macro myquote*(args: varargs[untyped]): untyped =
+  expectMinLen(args, 1)
+
+  # This is a workaround for #10430 where comments are removed in
+  # template expansions. This workaround lifts all comments
+  # statements to be arguments of the temporary template.
+
+  let extraCommentSymbols = newNimNode(nnkBracket)
+  let extraCommentGenExpr = newNimNode(nnkBracket)
+  let body = substitudeComments(
+    extraCommentSymbols, extraCommentGenExpr, args[^1]
+  )
+
+  let formalParams = nnkFormalParams.newTree(ident"untyped")
+  for i in 0 ..< args.len-1:
+    formalParams.add nnkIdentDefs.newTree(
+      args[i], ident"untyped", newEmptyNode()
+    )
+  for sym in extraCommentSymbols:
+    formalParams.add nnkIdentDefs.newTree(
+      sym, ident"untyped", newEmptyNode()
+    )
+
+  let templateSym = genSym(nskTemplate)
+  let templateDef = nnkTemplateDef.newTree(
+    templateSym,
+    newEmptyNode(),
+    newEmptyNode(),
+    formalParams,
+    nnkPragma.newTree(ident"dirty"),
+    newEmptyNode(),
+    args[^1]
+  )
+
+  let templateCall = newCall(templateSym)
+  for i in 0 ..< args.len-1:
+    let symName = args[i]
+    # identifiers and quoted identifiers are allowed.
+    if symName.kind == nnkAccQuoted:
+      symName.expectLen 1
+      symName[0].expectKind nnkIdent
+    else:
+      symName.expectKind nnkIdent
+    templateCall.add newCall(bindSym"expectNimNode", symName)
+  for expr in extraCommentGenExpr:
+    templateCall.add expr
+  let getAstCall = newCall(bindSym"getAst", templateCall)
+  result = newStmtList(templateDef, getAstCall)
+
+
 macro testAddrAst(arg: typed): bool =
   arg.expectKind nnkStmtListExpr
   arg[0].expectKind(nnkVarSection)
@@ -49,34 +117,35 @@ static:
       echo "OK"
 
 
-  testPattern nnkIntLit(intVal = 42)            , 42
-  testPattern nnkInt8Lit(intVal = 42)           , 42'i8
-  testPattern nnkInt16Lit(intVal = 42)          , 42'i16
-  testPattern nnkInt32Lit(intVal = 42)          , 42'i32
-  testPattern nnkInt64Lit(intVal = 42)          , 42'i64
-  testPattern nnkUInt8Lit(intVal = 42)          , 42'u8
-  testPattern nnkUInt16Lit(intVal = 42)         , 42'u16
-  testPattern nnkUInt32Lit(intVal = 42)         , 42'u32
-  testPattern nnkUInt64Lit(intVal = 42)         , 42'u64
-  #testPattern nnkFloat64Lit(floatVal = 42.0)      , 42.0
-  testPattern nnkFloat32Lit(floatVal = 42.0)      , 42.0'f32
-  #testPattern nnkFloat64Lit(floatVal = 42.0)      , 42.0'f64
-  testPattern nnkStrLit(strVal = "abc")         , "abc"
-  testPattern nnkRStrLit(strVal = "abc")        , r"abc"
-  testPattern nnkTripleStrLit(strVal = "abc")   , """abc"""
-  testPattern nnkCharLit(intVal = 32)           , ' '
-  testPattern nnkNilLit()              , nil
-  testPattern nnkIdent(strVal = "myIdentifier") , myIdentifier
-
-  testPatternFail nnkInt8Lit(intVal = 42)           , 42'i16
-  testPatternFail nnkInt16Lit(intVal = 42)          , 42'i8
+  testPattern nnkIntLit(intVal = 42), 42
+  testPattern nnkInt8Lit(intVal = 42), 42'i8
+  testPattern nnkInt16Lit(intVal = 42), 42'i16
+  testPattern nnkInt32Lit(intVal = 42), 42'i32
+  testPattern nnkInt64Lit(intVal = 42), 42'i64
+  testPattern nnkUInt8Lit(intVal = 42), 42'u8
+  testPattern nnkUInt16Lit(intVal = 42), 42'u16
+  testPattern nnkUInt32Lit(intVal = 42), 42'u32
+  testPattern nnkUInt64Lit(intVal = 42), 42'u64
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0
+  testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64
+  testPattern nnkStrLit(strVal = "abc"), "abc"
+  testPattern nnkRStrLit(strVal = "abc"), r"abc"
+  testPattern nnkTripleStrLit(strVal = "abc"), """abc"""
+  testPattern nnkCharLit(intVal = 32), ' '
+  testPattern nnkNilLit(), nil
+  testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier
+
+  testPatternFail nnkInt8Lit(intVal = 42), 42'i16
+  testPatternFail nnkInt16Lit(intVal = 42), 42'i8
+
 
 
 # this should be just `block` but it doesn't work that way anymore because of VM.
 macro scope(arg: untyped): untyped =
   let procSym = genSym(nskProc)
   result = quote do:
-    proc `procSym`(): void {.compileTime.} =
+    proc `procSym`() {.compileTime.} =
       `arg`
 
     `procSym`()
@@ -85,7 +154,7 @@ static:
   ## Command call
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       echo "abc", "xyz"
 
     ast.matchAst:
@@ -95,7 +164,7 @@ static:
   ## Call with ``()``
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       echo("abc", "xyz")
 
     ast.matchAst:
@@ -140,7 +209,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       ? "xyz"
 
     ast.matchAst(err):
@@ -155,7 +224,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       proc identifier*
 
     ast[0].matchAst(err):
@@ -185,7 +254,7 @@ static:
 
   ## Call with raw string literal
   scope:
-    let ast = quote do:
+    let ast = myquote:
       echo"abc"
 
 
@@ -230,7 +299,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       cast[T](x)
 
     ast.matchAst:
@@ -242,7 +311,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       x.y
 
     ast.matchAst:
@@ -264,7 +333,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       (1, 2, (3))
 
     ast.matchAst:
@@ -276,7 +345,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       {1, 2, 3}
 
     ast.matchAst:
@@ -285,7 +354,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       {a: 3, b: 5}
 
     ast.matchAst:
@@ -300,7 +369,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       [1, 2, 3]
 
     ast.matchAst:
@@ -312,7 +381,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       1..3
 
     ast.matchAst:
@@ -328,7 +397,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       if cond1: expr1 elif cond2: expr2 else: expr3
 
     ast.matchAst:
@@ -343,7 +412,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       ## This is a comment
       ## This is part of the first comment
       stmt1
@@ -357,12 +426,12 @@ static:
     ):
       echo "ok"
     else:
-      echo "NOT OK!!!"
+      echo "warning!"
       echo ast.treeRepr
       echo "TEST causes no fail, because of a regression in Nim."
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       {.emit: "#include <stdio.h>".}
 
     ast.matchAst:
@@ -375,7 +444,7 @@ static:
       echo "ok"
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       {.pragma: cdeclRename, cdecl.}
 
     ast.matchAst:
@@ -391,7 +460,7 @@ static:
 
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       if cond1:
         stmt1
       elif cond2:
@@ -413,7 +482,7 @@ static:
 
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       x = 42
 
     ast.matchAst:
@@ -423,7 +492,7 @@ static:
 
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       stmt1
       stmt2
       stmt3
@@ -439,7 +508,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       case expr1
       of expr2, expr3..expr4:
         stmt1
@@ -464,7 +533,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       while expr1:
         stmt1
 
@@ -477,7 +546,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       for ident1, ident2 in expr1:
         stmt1
 
@@ -490,7 +559,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       try:
         stmt1
       except e1, e2:
@@ -517,7 +586,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       return expr1
 
     ast.matchAst:
@@ -528,7 +597,7 @@ static:
   ## Continue statement
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       continue
 
     ast.matchAst:
@@ -539,7 +608,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       break otherLocation
 
     ast.matchAst:
@@ -550,10 +619,12 @@ static:
 
   scope:
 
-    let ast = quote do:
+    template blockStatement {.dirty.} =
       block name:
         discard
 
+    let ast = getAst(blockStatement())
+
     ast.matchAst:
     of nnkBlockStmt(ident"name", nnkStmtList):
       echo "ok"
@@ -562,7 +633,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       asm """some asm"""
 
     ast.matchAst:
@@ -576,7 +647,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       import math
 
     ast.matchAst:
@@ -585,7 +656,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       import math except pow
 
     ast.matchAst:
@@ -594,7 +665,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       import strutils as su
 
     ast.matchAst:
@@ -611,7 +682,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       from math import pow
 
     ast.matchAst:
@@ -622,7 +693,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       export unsigned
 
     ast.matchAst:
@@ -631,7 +702,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       export math except pow # we're going to implement our own exponentiation
 
     ast.matchAst:
@@ -642,7 +713,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       include blocks
 
     ast.matchAst:
@@ -653,7 +724,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       var a = 3
 
     ast.matchAst:
@@ -670,7 +741,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       let a = 3
 
     ast.matchAst:
@@ -687,7 +758,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       const a = 3
 
     ast.matchAst:
@@ -704,7 +775,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type A = int
 
     ast.matchAst:
@@ -719,7 +790,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type MyInt = distinct int
 
     ast.peelOff({nnkTypeSection}).matchAst:
@@ -735,7 +806,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type A[T] = expr1
 
     ast.matchAst:
@@ -757,7 +828,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type IO = object of RootObj
 
     ast.peelOff(nnkTypeSection).matchAst:
@@ -840,7 +911,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type X = enum
         First
 
@@ -853,7 +924,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       type Con = concept x,y,z
         (x & y & z) is string
 
@@ -865,7 +936,7 @@ static:
 
   scope:
 
-    let astX = quote do:
+    let astX = myquote:
       type
         A[T: static[int]] = object
 
@@ -880,7 +951,7 @@ static:
 
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       type MyProc[T] = proc(x: T)
 
     ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err):
@@ -952,7 +1023,7 @@ static:
     proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
 
   scope:
-    var ast = quote do:
+    var ast = myquote:
       proc foobar(a, b: int): void
 
     ast = ast[3]
@@ -971,7 +1042,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       proc hello(): var int
 
     ast[3].matchAst: # subAst
@@ -986,7 +1057,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       iterator nonsense[T](x: seq[T]): float {.closure.} =
         discard
 
@@ -998,7 +1069,7 @@ static:
 
   scope:
 
-    let ast = quote do:
+    let ast = myquote:
       converter toBool(x: float): bool
 
     ast.matchAst:
@@ -1008,7 +1079,7 @@ static:
   ## Template declaration
 
   scope:
-    let ast = quote do:
+    let ast = myquote:
       template optOpt{expr1}(a: int): int
 
     ast.matchAst:
diff --git a/tests/template/tmore_regressions.nim b/tests/template/tmore_regressions.nim
new file mode 100644
index 000000000..8b4b5fa4c
--- /dev/null
+++ b/tests/template/tmore_regressions.nim
@@ -0,0 +1,44 @@
+discard """
+output: '''0
+
+0.0'''
+"""
+
+# bug #11494
+import macros
+
+macro staticForEach(arr: untyped, body: untyped): untyped =
+    result = newNimNode(nnkStmtList)
+
+    arr.expectKind(nnkBracket)
+    for n in arr:
+        let b = copyNimTree(body)
+        result.add quote do:
+            block:
+                type it {.inject.} = `n`
+                `b`
+
+template forEveryMatchingEntity*() =
+    staticForEach([int, string, float]):
+        var a: it
+        echo a
+
+forEveryMatchingEntity()
+
+
+# bug #11483
+proc main =
+  template first(body) =
+    template second: var int =
+      var o: int
+      var i  = addr(o)
+      i[]
+
+    body
+
+  first:
+    second = 5
+    second = 6
+
+main()
+
diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim
index b19ed7afc..f7a02efa0 100644
--- a/tests/template/tparams_gensymed.nim
+++ b/tests/template/tparams_gensymed.nim
@@ -8,6 +8,7 @@ output: '''
 1
 2
 3
+wth
 '''
 """
 # bug #1915
@@ -130,3 +131,17 @@ template test() =
       doAssert(foo.len == 3)
 
 test()
+
+# regression found in PMunch's parser generator
+
+proc namedcall(arg: string) =
+  discard
+
+macro m(): untyped =
+  result = quote do:
+    (proc (arg: string) =
+      namedcall(arg = arg)
+      echo arg)
+
+let meh = m()
+meh("wth")
diff --git a/tests/template/tredefinition.nim b/tests/template/tredefinition.nim
new file mode 100644
index 000000000..8efc5ae2f
--- /dev/null
+++ b/tests/template/tredefinition.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "redefinition of 'a`gensym"
+  line: 9
+"""
+# bug #10180
+proc f() =
+  template t() =
+    var a = 1
+    var a = 2
+    echo a
+  t()
+
+f()