summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-08-20 01:13:13 +0200
committerAraq <rumpf_a@web.de>2012-08-20 01:13:13 +0200
commit5e15dec1757e6013cbeb5d6baf9d9579cf025361 (patch)
tree2157b7f03f5f596060ad9ed52427118964912e50
parent0cac8d9b6fa2c982468899db109a964df979b186 (diff)
downloadNim-5e15dec1757e6013cbeb5d6baf9d9579cf025361.tar.gz
first steps to make templates hygienic
-rwxr-xr-xcompiler/ast.nim18
-rwxr-xr-xcompiler/astalgo.nim9
-rw-r--r--compiler/ccgmerge.nim7
-rwxr-xr-xcompiler/condsyms.nim2
-rwxr-xr-xcompiler/evals.nim58
-rw-r--r--compiler/evaltempl.nim83
-rw-r--r--compiler/lambdalifting.nim9
-rwxr-xr-xcompiler/nimconf.nim42
-rwxr-xr-xcompiler/pragmas.nim25
-rwxr-xr-xcompiler/sem.nim15
-rwxr-xr-xcompiler/semexprs.nim6
-rwxr-xr-xcompiler/semgnrc.nim22
-rwxr-xr-xcompiler/semstmts.nim26
-rwxr-xr-xcompiler/semtempl.nim267
-rwxr-xr-xcompiler/semtypes.nim31
-rwxr-xr-xcompiler/suggest.nim10
-rwxr-xr-xcompiler/types.nim7
-rwxr-xr-xcompiler/wordrecg.nim4
-rwxr-xr-xdoc/manual.txt4
-rwxr-xr-xlib/impure/re.nim5
-rwxr-xr-xlib/pure/pegs.nim5
-rwxr-xr-xtests/tester.nim7
-rwxr-xr-xtodo.txt9
23 files changed, 485 insertions, 186 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index cf1737c32..90885a71f 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -234,7 +234,7 @@ type
     sfDiscriminant,   # field is a discriminant in a record/object
     sfDeprecated,     # symbol is deprecated
     sfError,          # usage of symbol should trigger a compile-time error
-    sfInnerProc,      # proc is an inner proc
+    sfShadowed,       # a symbol that was shadowed in some inner scope
     sfThread,         # proc will run as a thread
                       # variable is a thread variable
     sfCompileTime,    # proc can be evaluated at compile time
@@ -247,7 +247,7 @@ type
                       # language; for interfacing with Objective C
     sfDiscardable     # returned value may be discarded implicitely
     sfDestructor      # proc is destructor
-    sfByCopy          # a variable is to be captured by value in a closure
+    sfGenSym          # symbol is 'gensym'ed; do not add to symbol table
 
   TSymFlags* = set[TSymFlag]
 
@@ -264,10 +264,7 @@ const
     # symbol name that was generated by the compiler
     # the compiler will avoid printing such names 
     # in user messages.
-  
-  sfShadowed* = sfInnerProc
-    # a variable that was shadowed in some inner scope
-    
+      
   sfHoist* = sfVolatile ## proc return value can be hoisted
 
 const
@@ -785,15 +782,6 @@ const                         # for all kind of hash tables:
   GrowthFactor* = 2           # must be power of 2, > 0
   StartSize* = 8              # must be power of 2, > 0
 
-proc ValueToString*(a: PNode): string = 
-  case a.kind
-  of nkCharLit..nkUInt64Lit: result = $(a.intVal)
-  of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
-  of nkStrLit..nkTripleStrLit: result = a.strVal
-  else: 
-    InternalError(a.info, "valueToString")
-    result = ""
-
 proc copyStrTable(dest: var TStrTable, src: TStrTable) = 
   dest.counter = src.counter
   if isNil(src.data): return 
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index c40d89d84..9da3e21e4 100755
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -166,7 +166,9 @@ proc SameValue*(a, b: PNode): bool =
   of nkStrLit..nkTripleStrLit: 
     if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal == b.strVal
   else:
-    InternalError(a.info, "SameValue")
+    # don't raise an internal error for 'nimrod check':
+    #InternalError(a.info, "SameValue")
+    nil
 
 proc leValue*(a, b: PNode): bool = 
   # a <= b?
@@ -178,7 +180,10 @@ proc leValue*(a, b: PNode): bool =
     if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
   of nkStrLit..nkTripleStrLit: 
     if b.kind in {nkStrLit..nkTripleStrLit}: result = a.strVal <= b.strVal
-  else: InternalError(a.info, "leValue")
+  else: 
+    # don't raise an internal error for 'nimrod check':
+    #InternalError(a.info, "leValue")
+    nil
 
 proc lookupInRecord(n: PNode, field: PIdent): PSym = 
   result = nil
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 311711f5a..8c490a693 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -224,13 +224,16 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
     of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
     of "hasframe":  m.FrameDeclared = decodeVInt(L.buf, L.bufpos) != 0
     else: InternalError("ccgmerge: unkown key: " & k)
+
+when not defined(nimhygiene):
+  {.pragma: inject.}
   
 template withCFile(cfilename: string, body: stmt) = 
   var s = LLStreamOpen(cfilename, fmRead)
   if s == nil: return
-  var L: TBaseLexer
+  var L {.inject.}: TBaseLexer
   openBaseLexer(L, s)
-  var k = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
+  var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
   while true:
     skipUntilCmd(L)
     if ^L.bufpos == '\0': break
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index b0ecef816..d273f5335 100755
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -55,6 +55,8 @@ proc countDefinedSymbols*(): int =
 proc InitDefines*() = 
   initStrTable(gSymbols)
   DefineSymbol("nimrod") # 'nimrod' is always defined
+  # for bootstrapping purposes and old code:
+  DefineSymbol("nimhygiene")
   
   # add platform specific symbols:
   case targetCPU
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 04e8cd650..e17ab2c6c 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -16,7 +16,7 @@
 import 
   strutils, magicsys, lists, options, ast, astalgo, trees, treetab, nimsets, 
   msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, 
-  parser, ropes, rodread, idgen, osproc, streams
+  parser, ropes, rodread, idgen, osproc, streams, evaltempl
 
 type 
   PStackFrame* = ref TStackFrame
@@ -850,56 +850,6 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
   result = parseString(code.getStrValue, code.info.toFilename,
                        code.stringStartingLine)
   result.typ = newType(tyStmt, c.module)
-
-proc evalTemplateAux*(templ, actual: PNode, sym: PSym): PNode = 
-  inc genSymBaseId
-  case templ.kind
-  of nkSym: 
-    var p = templ.sym
-    if (p.kind == skParam) and (p.owner.id == sym.id): 
-      result = copyTree(actual.sons[p.position])
-    else: 
-      result = copyNode(templ)
-  of nkNone..nkIdent, nkType..nkNilLit: # atom
-    result = copyNode(templ)
-  else: 
-    result = copyNode(templ)
-    newSons(result, sonsLen(templ))
-    for i in countup(0, sonsLen(templ) - 1): 
-      result.sons[i] = evalTemplateAux(templ.sons[i], actual, sym)
-
-proc evalTemplateArgs(n: PNode, s: PSym): PNode =
-  # if the template has zero arguments, it can be called without ``()``
-  # `n` is then a nkSym or something similar
-  var a: int
-  case n.kind
-  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
-    a = sonsLen(n)
-  else: a = 0
-  var f = s.typ.sonsLen
-  if a > f: GlobalError(n.info, errWrongNumberOfArguments)
-
-  result = copyNode(n)
-  for i in countup(1, f - 1):
-    var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
-    if arg == nil or arg.kind == nkEmpty:
-      LocalError(n.info, errWrongNumberOfArguments)
-    addSon(result, arg)
-
-var evalTemplateCounter* = 0
-  # to prevent endless recursion in templates instantation
-
-proc evalTemplate*(n: PNode, sym: PSym): PNode = 
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > 100:
-    GlobalError(n.info, errTemplateInstantiationTooNested)
-    result = n
-
-  # replace each param by the corresponding node:
-  var args = evalTemplateArgs(n, sym)
-  result = evalTemplateAux(sym.getBody, args, sym)
-  
-  dec(evalTemplateCounter)
  
 proc evalTypeTrait*(n: PNode, context: PSym): PNode =
   ## XXX: This should be pretty much guaranteed to be true
@@ -964,7 +914,11 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode =
 
   case expandedSym.kind
   of skTemplate:
-    result = evalTemplate(macroCall, expandedSym)
+    let genSymOwner = if c.tos != nil and c.tos.prc != nil: 
+        c.tos.prc 
+      else:
+        c.module
+    result = evalTemplate(macroCall, expandedSym, genSymOwner)
   of skMacro:
     # At this point macroCall.sons[0] is nkSym node.
     # To be completely compatible with normal macro invocation,
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
new file mode 100644
index 000000000..d7006d34d
--- /dev/null
+++ b/compiler/evaltempl.nim
@@ -0,0 +1,83 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Template evaluation engine. Now hygienic.
+
+import
+  strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, 
+  rodread
+
+type
+  TemplCtx {.pure, final.} = object
+    owner, genSymOwner: PSym
+    mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
+                      # new symbol
+
+proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx): PNode =
+  #inc genSymBaseId
+  case templ.kind
+  of nkSym:
+    var s = templ.sym
+    if s.owner.id == c.owner.id:
+      if s.kind == skParam:
+        result = copyTree(actual.sons[s.position])
+      else:
+        InternalAssert sfGenSym in s.flags
+        var x = PSym(IdTableGet(c.mapping, s))
+        if x == nil:
+          x = copySym(s, false)
+          x.owner = c.genSymOwner
+          IdTablePut(c.mapping, s, x)
+        result = newSymNode(x, templ.info)
+    else:
+      result = copyNode(templ)
+  of nkNone..nkIdent, nkType..nkNilLit: # atom
+    result = copyNode(templ)
+  else:
+    result = copyNode(templ)
+    newSons(result, sonsLen(templ))
+    for i in countup(0, sonsLen(templ) - 1): 
+      result.sons[i] = evalTemplateAux(templ.sons[i], actual, c)
+
+proc evalTemplateArgs(n: PNode, s: PSym): PNode =
+  # if the template has zero arguments, it can be called without ``()``
+  # `n` is then a nkSym or something similar
+  var a: int
+  case n.kind
+  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
+    a = sonsLen(n)
+  else: a = 0
+  var f = s.typ.sonsLen
+  if a > f: GlobalError(n.info, errWrongNumberOfArguments)
+
+  result = copyNode(n)
+  for i in countup(1, f - 1):
+    var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
+    if arg == nil or arg.kind == nkEmpty:
+      LocalError(n.info, errWrongNumberOfArguments)
+    addSon(result, arg)
+
+var evalTemplateCounter* = 0
+  # to prevent endless recursion in templates instantiation
+
+proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
+  inc(evalTemplateCounter)
+  if evalTemplateCounter > 100:
+    GlobalError(n.info, errTemplateInstantiationTooNested)
+    result = n
+
+  # replace each param by the corresponding node:
+  var args = evalTemplateArgs(n, tmpl)
+  var ctx: TemplCtx
+  ctx.owner = tmpl
+  ctx.genSymOwner = genSymOwner
+  initIdTable(ctx.mapping)
+  result = evalTemplateAux(tmpl.getBody, args, ctx)
+  
+  dec(evalTemplateCounter)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 68d7e62c2..057444b2a 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -471,7 +471,8 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
   # add assignment statements:
   for local in scope.capturedVars:
     let fieldAccess = indirectAccess(env, local, env.info)
-    if sfByCopy in local.flags or local.kind == skParam:
+    if local.kind == skParam:
+      # maybe later: (sfByCopy in local.flags)
       # add ``env.param = param``
       result.add(newAsgnStmt(fieldAccess, newSymNode(local)))
     IdNodeTablePut(o.localsToAccess, local, fieldAccess)
@@ -515,9 +516,9 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
       scope.sons[0] = generateClosureCreation(o, env)
     
     # change 'local' to 'closure.local', unless it's a 'byCopy' variable:
-    if sfByCopy notin local.flags:
-      result = IdNodeTableGet(o.localsToAccess, local)
-      assert result != nil, "cannot find: " & local.name.s
+    # if sfByCopy notin local.flags:
+    result = IdNodeTableGet(o.localsToAccess, local)
+    assert result != nil, "cannot find: " & local.name.s
     # else it is captured by copy and this means that 'outer' should continue
     # to access the local as a local.
   of nkLambdaKinds:
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index c3592e03a..f4f6d0a42 100755
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -111,7 +111,7 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest) =
 proc parseDirective(L: var TLexer, tok: var TToken) = 
   ppGetTok(L, tok)            # skip @
   case whichKeyword(tok.ident)
-  of wIf: 
+  of wIf:
     setlen(condStack, len(condStack) + 1)
     var res = EvalppIf(L, tok)
     condStack[high(condStack)] = res
@@ -123,25 +123,27 @@ proc parseDirective(L: var TLexer, tok: var TToken) =
     ppGetTok(L, tok)
     msgs.MsgWriteln(tokToStr(tok))
     ppGetTok(L, tok)
-  of wPutEnv: 
-    ppGetTok(L, tok)
-    var key = tokToStr(tok)
-    ppGetTok(L, tok)
-    os.putEnv(key, tokToStr(tok))
-    ppGetTok(L, tok)
-  of wPrependEnv: 
-    ppGetTok(L, tok)
-    var key = tokToStr(tok)
-    ppGetTok(L, tok)
-    os.putEnv(key, tokToStr(tok) & os.getenv(key))
-    ppGetTok(L, tok)
-  of wAppendenv: 
-    ppGetTok(L, tok)
-    var key = tokToStr(tok)
-    ppGetTok(L, tok)
-    os.putEnv(key, os.getenv(key) & tokToStr(tok))
-    ppGetTok(L, tok)
-  else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
+  else:
+    case tok.ident.s.normalize
+    of "putenv": 
+      ppGetTok(L, tok)
+      var key = tokToStr(tok)
+      ppGetTok(L, tok)
+      os.putEnv(key, tokToStr(tok))
+      ppGetTok(L, tok)
+    of "prependenv": 
+      ppGetTok(L, tok)
+      var key = tokToStr(tok)
+      ppGetTok(L, tok)
+      os.putEnv(key, tokToStr(tok) & os.getenv(key))
+      ppGetTok(L, tok)
+    of "appendenv":
+      ppGetTok(L, tok)
+      var key = tokToStr(tok)
+      ppGetTok(L, tok)
+      os.putEnv(key, os.getenv(key) & tokToStr(tok))
+      ppGetTok(L, tok)
+    else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
   
 proc confTok(L: var TLexer, tok: var TToken) = 
   ppGetTok(L, tok)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 107660762..08cdb46c5 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -23,16 +23,17 @@ const
     wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, 
     wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, 
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
-    wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist}
+    wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist,
+    wGenSym, wInject}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas
-  templatePragmas* = {wImmediate, wDeprecated, wError}
+  templatePragmas* = {wImmediate, wDeprecated, wError, wGenSym, wInject}
   macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
     wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
-    wImportcpp, wImportobjc, wError, wDiscardable}
+    wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, 
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
-    wImportcpp, wImportobjc, wError, wDiscardable}
+    wImportcpp, wImportobjc, wError, wDiscardable, wGenSym, wInject}
   exprPragmas* = {wLine}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
     wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
@@ -46,14 +47,16 @@ const
     wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
   typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, 
     wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, 
-    wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef}
+    wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
+    wGenSym, wInject}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, 
     wImportcpp, wImportobjc, wError}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, 
     wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
-    wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal}
+    wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal,
+    wGenSym, wInject}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
-    wExtern, wImportcpp, wImportobjc, wError}
+    wExtern, wImportcpp, wImportobjc, wError, wGenSym, wInject}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
                       wThread}
@@ -655,9 +658,13 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
               incl(sym.typ.flags, tfByRef)
           of wByCopy:
             noVal(it)
-            if sym.kind != skType: incl(sym.flags, sfByCopy)
-            elif sym.typ == nil: invalidPragma(it)
+            if sym.kind != skType or sym.typ == nil: invalidPragma(it)
             else: incl(sym.typ.flags, tfByCopy)
+          of wInject, wGenSym:
+            # We check for errors, but do nothing with these pragmas otherwise
+            # as they are handled directly in 'evalTemplate'.
+            noVal(it)
+            if sym == nil: invalidPragma(it)
           of wLine: PragmaLine(c, it)
           else: invalidPragma(it)
         else: invalidPragma(it)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 391bf840c..cfdd936ad 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -14,7 +14,8 @@ import
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
-  semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting
+  semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
+  evaltempl
 
 proc semPass*(): TPass
 # implementation
@@ -56,7 +57,17 @@ proc isTopLevel(c: PContext): bool {.inline.} =
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = 
   result = newSym(kind, considerAcc(n), getCurrOwner())
   result.info = n.info
-  
+
+proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
+  # like newSymS, but considers gensym'ed symbols
+  if n.kind == nkSym: 
+    result = n.sym
+    InternalAssert sfGenSym in result.flags
+    InternalAssert result.kind == kind
+  else:
+    result = newSym(kind, considerAcc(n), getCurrOwner())
+    result.info = n.info
+
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
   # identifier with visability
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index c0b890ffb..e4e449c9e 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -24,7 +24,7 @@ proc restoreOldStyleType(n: PNode) =
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = 
   markUsed(n, s)
   pushInfoContext(n.info)
-  result = evalTemplate(n, s)
+  result = evalTemplate(n, s, getCurrOwner())
   if semCheck: result = semAfterMacroCall(c, result, s)
   popInfoContext()
 
@@ -1372,7 +1372,9 @@ proc semBlockExpr(c: PContext, n: PNode): PNode =
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
   openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty: addDecl(c, newSymS(skLabel, n.sons[0], c))
+  if n.sons[0].kind notin {nkEmpty, nkSym}:
+    # nkSym for gensym'ed labels:
+    addDecl(c, newSymS(skLabel, n.sons[0], c))
   n.sons[1] = semStmtListExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
   closeScope(c.tab)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 59e8a2dff..de73d6d86 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -17,11 +17,20 @@
 
 # included from sem.nim
 
-type 
-  TSemGenericFlag = enum 
+type
+  TSemGenericFlag = enum
     withinBind, withinTypeDesc
   TSemGenericFlags = set[TSemGenericFlag]
 
+proc getIdentNode(n: PNode): PNode =
+  case n.kind
+  of nkPostfix: result = getIdentNode(n.sons[1])
+  of nkPragmaExpr: result = getIdentNode(n.sons[0])
+  of nkIdent, nkAccQuoted, nkSym: result = n
+  else:
+    illFormedAst(n)
+    result = n
+
 proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
                     toBind: var TIntSet): PNode
 proc semGenericStmtScope(c: PContext, n: PNode, 
@@ -54,15 +63,6 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
       result = n
   else: result = newSymNode(s, n.info)
   
-proc getIdentNode(n: PNode): PNode = 
-  case n.kind
-  of nkPostfix: result = getIdentNode(n.sons[1])
-  of nkPragmaExpr: result = getIdentNode(n.sons[0])
-  of nkIdent, nkAccQuoted: result = n
-  else: 
-    illFormedAst(n)
-    result = n
-
 proc semGenericStmt(c: PContext, n: PNode, 
                     flags: TSemGenericFlags, toBind: var TIntSet): PNode = 
   result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index df51b94df..d531118c5 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -91,10 +91,11 @@ proc semBlock(c: PContext, n: PNode): PNode =
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
   openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty: 
-    var labl = newSymS(skLabel, n.sons[0], c)
-    addDecl(c, labl)
-    n.sons[0] = newSymNode(labl)
+  if n.sons[0].kind != nkEmpty:
+    var labl = newSymG(skLabel, n.sons[0], c)
+    if sfGenSym notin labl.flags:
+      addDecl(c, labl)
+      n.sons[0] = newSymNode(labl, n.sons[0].info)
     suggestSym(n.sons[0], labl)
   n.sons[1] = semStmt(c, n.sons[1])
   closeScope(c.tab)
@@ -284,7 +285,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       Message(a.info, warnEachIdentIsTuple)
     for j in countup(0, length-3):
       var v = semIdentDef(c, a.sons[j], symkind)
-      addInterfaceDecl(c, v)
+      if sfGenSym notin v.flags: addInterfaceDecl(c, v)
       when oKeepVariableNames:
         if c.InUnrolledContext > 0: v.flags.incl(sfShadowed)
         else:
@@ -332,7 +333,7 @@ proc semConst(c: PContext, n: PNode): PNode =
       continue
     v.typ = typ
     v.ast = def               # no need to copy
-    addInterfaceDecl(c, v)
+    if sfGenSym notin v.flags: addInterfaceDecl(c, v)
     var b = newNodeI(nkConstDef, a.info)
     addSon(b, newSymNode(v))
     addSon(b, ast.emptyNode)            # no type description
@@ -422,25 +423,25 @@ proc semForVars(c: PContext, n: PNode): PNode =
   # and thus no tuple unpacking:
   if iter.kind != tyTuple or length == 3: 
     if length == 3:
-      var v = newSymS(skForVar, n.sons[0], c)
+      var v = newSymG(skForVar, n.sons[0], c)
       if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
       # BUGFIX: don't use `iter` here as that would strip away
       # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
       # for an example:
       v.typ = n.sons[length-2].typ
       n.sons[0] = newSymNode(v)
-      addDecl(c, v)
+      if sfGenSym notin v.flags: addDecl(c, v)
     else:
       LocalError(n.info, errWrongNumberOfVariables)
   elif length-2 != sonsLen(iter):
     LocalError(n.info, errWrongNumberOfVariables)
   else:
     for i in countup(0, length - 3): 
-      var v = newSymS(skForVar, n.sons[i], c)
+      var v = newSymG(skForVar, n.sons[i], c)
       if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
       v.typ = iter.sons[i]
       n.sons[i] = newSymNode(v)
-      addDecl(c, v)
+      if sfGenSym notin v.flags: addDecl(c, v)
   Inc(c.p.nestedLoopCounter)
   n.sons[length-1] = SemStmt(c, n.sons[length-1])
   Dec(c.p.nestedLoopCounter)
@@ -539,7 +540,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     if a.sons[0].kind == nkPragmaExpr:
       pragma(c, s, a.sons[0].sons[1], typePragmas)
     # add it here, so that recursive types are possible:
-    addInterfaceDecl(c, s)
+    if sfGenSym notin s.flags: addInterfaceDecl(c, s)
     a.sons[0] = newSymNode(s)
 
 proc typeSectionRightSidePass(c: PContext, n: PNode) =
@@ -780,7 +781,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     s.typ.callConv = lastOptionEntry(c).defaultCC 
     # add it here, so that recursive procs are possible:
     # -2 because we have a scope open for parameters
-    if kind in OverloadableSyms: 
+    if sfGenSym in s.flags: nil
+    elif kind in OverloadableSyms: 
       addInterfaceOverloadableSymAt(c, s, c.tab.tos - 2)
     else: 
       addInterfaceDeclAt(c, s, c.tab.tos - 2)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 960650a61..4849f6d6c 100755
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -9,6 +9,37 @@
 
 # included from sem.nim
 
+discard """
+  hygienic templates: 
+  
+    template `||` (a, b: expr): expr =
+      let aa = a
+      (if aa: aa else: b)
+    
+    var
+      a, b: T
+      
+    a || b || a
+    
+  Each evaluation context has to be different and we need to perform
+  some form of preliminary symbol lookup in template definitions. Hygiene is
+  a way to achieve lexical scoping at compile time.
+"""
+
+type
+  TSymBinding = enum
+    spNone, spGenSym, spInject
+
+proc symBinding(n: PNode): TSymBinding =
+  for i in countup(0, sonsLen(n) - 1):
+    var it = n.sons[i]
+    var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
+    if key.kind == nkIdent:
+      case whichKeyword(key.ident)
+      of wGenSym: return spGenSym
+      of wInject: return spInject
+      else: nil
+
 proc symChoice(c: PContext, n: PNode, s: PSym): PNode = 
   var 
     a: PSym
@@ -20,17 +51,16 @@ proc symChoice(c: PContext, n: PNode, s: PSym): PNode =
     inc(i)
     if i > 1: break
   if i <= 1: 
-    result = newSymNode(s)
-    result.info = n.info
+    result = newSymNode(s, n.info)
     markUsed(n, s)
-  else: 
+  else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
     result = newNodeIT(nkSymChoice, n.info, newTypeS(tyNone, c))
     a = initOverloadIter(o, c, n)
     while a != nil:
       incl(a.flags, sfUsed)
-      addSon(result, newSymNode(a))
+      addSon(result, newSymNode(a, n.info))
       a = nextOverloadIter(o, c, n)
 
 proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
@@ -45,37 +75,217 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var TIntSet): PNode =
     else:
       illFormedAst(a)
   result = newNodeI(nkEmpty, n.info)
+  
+proc replaceIdentBySym(n: var PNode, s: PNode) =
+  case n.kind
+  of nkPostfix: replaceIdentBySym(n.sons[1], s)
+  of nkPragmaExpr: replaceIdentBySym(n.sons[0], s)
+  of nkIdent, nkAccQuoted, nkSym: n = s
+  else: illFormedAst(n)
 
-proc resolveTemplateParams(c: PContext, n: PNode, owner: PSym, 
-                           toBind: var TIntSet): PNode = 
-  var s: PSym
+type
+  TemplCtx {.pure, final.} = object
+    c: PContext
+    toBind: TIntSet
+    owner: PSym
+
+proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
   case n.kind
+  of nkPostfix: result = getIdentNode(c, n.sons[1])
+  of nkPragmaExpr: result = getIdentNode(c, n.sons[0])
   of nkIdent:
     result = n
-    let s = QualifiedLookUp(c, n, {})
+    let s = QualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == owner and s.kind == skParam:
-        result = newSymNode(s)
-        result.info = n.info
-      elif Contains(toBind, s.id):
-        result = symChoice(c, n, s)
-    
-  of nkEmpty, nkSym..nkNilLit:         # atom
+      if s.owner == c.owner and s.kind == skParam:
+        result = newSymNode(s, n.info)
+  of nkAccQuoted, nkSym: result = n
+  else:
+    illFormedAst(n)
     result = n
+
+proc isTemplParam(n: PNode): bool {.inline.} =
+  result = n.kind == nkSym and n.sym.kind == skParam and
+           n.sym.owner.kind == skTemplate
+
+proc semTemplBody(c: var TemplCtx, n: PNode): PNode
+
+proc openScope(c: var TemplCtx) = openScope(c.c.tab)
+proc closeScope(c: var TemplCtx) = closeScope(c.c.tab)
+
+proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = 
+  openScope(c)
+  result = semTemplBody(c, n)
+  closeScope(c)
+
+proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
+  result = newSym(kind, considerAcc(n), c.owner)
+  incl(result.flags, sfGenSym)
+  incl(result.flags, sfShadowed)
+
+proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
+  # locals default to 'gensym':
+  if n.kind != nkPragmaExpr or symBinding(n.sons[1]) != spInject:
+    let ident = getIdentNode(c, n)    
+    if not isTemplParam(ident):
+      let local = newGenSym(k, ident, c)
+      addPrelimDecl(c.c, local)
+      replaceIdentBySym(n, newSymNode(local, n.info))
+    else:
+      replaceIdentBySym(n, ident)
+  else:
+    n = semTemplBody(c, n)
+
+proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
+  result = n
+  checkSonsLen(n, bodyPos + 1)
+  # routines default to 'inject':
+  if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym:
+    let ident = getIdentNode(c, n.sons[namePos])
+    if not isTemplParam(ident):
+      let s = newGenSym(k, ident, c)
+      addPrelimDecl(c.c, s)
+      n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
+    else:
+      n.sons[namePos] = ident
+  else:
+    n.sons[namePos] = semTemplBody(c, n.sons[namePos])
+  openScope(c)
+  n.sons[genericParamsPos] = semTemplBody(c, n.sons[genericParamsPos])
+  n.sons[paramsPos] = semTemplBody(c, n.sons[paramsPos])
+  n.sons[pragmasPos] = semTemplBody(c, n.sons[pragmasPos])
+  n.sons[bodyPos] = semTemplBodyScope(c, n.sons[bodyPos])
+  closeScope(c)
+
+proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 
+  result = n
+  case n.kind
+  of nkIdent:
+    let s = QualifiedLookUp(c.c, n, {})
+    if s != nil:
+      if s.owner == c.owner and s.kind == skParam:
+        result = newSymNode(s, n.info)
+      elif Contains(c.toBind, s.id):
+        result = symChoice(c.c, n, s)
+      elif s.owner == c.owner:
+        InternalAssert sfGenSym in s.flags
+        incl(s.flags, sfUsed)
+        result = newSymNode(s, n.info)
   of nkBind:
-    result = resolveTemplateParams(c, n.sons[0], owner, toBind)
+    result = semTemplBody(c, n.sons[0])
   of nkBindStmt:
-    result = semBindStmt(c, n, toBind)
+    result = semBindStmt(c.c, n, c.toBind)
+  of nkEmpty, nkSym..nkNilLit:
+    nil
+  of nkIfStmt: 
+    for i in countup(0, sonsLen(n)-1): 
+      n.sons[i] = semTemplBodyScope(c, n.sons[i])
+  of nkWhileStmt: 
+    openScope(c)
+    for i in countup(0, sonsLen(n)-1): 
+      n.sons[i] = semTemplBody(c, n.sons[i])
+    closeScope(c)
+  of nkCaseStmt:
+    openScope(c)
+    n.sons[0] = semTemplBody(c, n.sons[0])
+    for i in countup(1, sonsLen(n)-1): 
+      var a = n.sons[i]
+      checkMinSonsLen(a, 1)
+      var L = sonsLen(a)
+      for j in countup(0, L-2): 
+        a.sons[j] = semTemplBody(c, a.sons[j])
+      a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
+    closeScope(c)
+  of nkForStmt, nkParForStmt: 
+    var L = sonsLen(n)
+    openScope(c)
+    n.sons[L-2] = semTemplBody(c, n.sons[L-2])
+    for i in countup(0, L - 3):
+      addLocalDecl(c, n.sons[i], skForVar)
+    n.sons[L-1] = semTemplBody(c, n.sons[L-1])
+    closeScope(c)
+  of nkBlockStmt, nkBlockExpr, nkBlockType:
+    checkSonsLen(n, 2)
+    openScope(c)
+    if n.sons[0].kind != nkEmpty:
+      # labels are always 'gensym'ed:
+      let s = newGenSym(skLabel, n.sons[0], c)
+      addPrelimDecl(c.c, s)
+      n.sons[0] = newSymNode(s, n.sons[0].info)
+    n.sons[1] = semTemplBody(c, n.sons[1])
+    closeScope(c)
+  of nkTryStmt: 
+    checkMinSonsLen(n, 2)
+    n.sons[0] = semTemplBodyScope(c, n.sons[0])
+    for i in countup(1, sonsLen(n)-1): 
+      var a = n.sons[i]
+      checkMinSonsLen(a, 1)
+      var L = sonsLen(a)
+      for j in countup(0, L-2): 
+        a.sons[j] = semTemplBody(c, a.sons[j])
+      a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
+  of nkVarSection, nkLetSection:
+    let symKind = if n.kind == nkLetSection: skLet else: skVar
+    for i in countup(0, sonsLen(n) - 1): 
+      var a = n.sons[i]
+      if a.kind == nkCommentStmt: continue 
+      if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a)
+      checkMinSonsLen(a, 3)
+      var L = sonsLen(a)
+      a.sons[L-2] = semTemplBody(c, a.sons[L-2])
+      a.sons[L-1] = semTemplBody(c, a.sons[L-1])
+      for j in countup(0, L-3):
+        addLocalDecl(c, a.sons[j], symKind)
+  of nkConstSection:
+    for i in countup(0, sonsLen(n) - 1): 
+      var a = n.sons[i]
+      if a.kind == nkCommentStmt: continue 
+      if (a.kind != nkConstDef): IllFormedAst(a)
+      checkSonsLen(a, 3)
+      addLocalDecl(c, a.sons[0], skConst)
+      a.sons[1] = semTemplBody(c, a.sons[1])
+      a.sons[2] = semTemplBody(c, a.sons[2])
+  of nkTypeSection: 
+    for i in countup(0, sonsLen(n) - 1): 
+      var a = n.sons[i]
+      if a.kind == nkCommentStmt: continue 
+      if (a.kind != nkTypeDef): IllFormedAst(a)
+      checkSonsLen(a, 3)
+      addLocalDecl(c, a.sons[0], skType)
+    for i in countup(0, sonsLen(n) - 1):
+      var a = n.sons[i]
+      if a.kind == nkCommentStmt: continue 
+      if (a.kind != nkTypeDef): IllFormedAst(a)
+      checkSonsLen(a, 3)
+      if a.sons[1].kind != nkEmpty: 
+        openScope(c)
+        a.sons[1] = semTemplBody(c, a.sons[1])
+        a.sons[2] = semTemplBody(c, a.sons[2])
+        closeScope(c)
+      else: 
+        a.sons[2] = semTemplBody(c, a.sons[2])
+  of nkProcDef, nkLambdaKinds:
+    result = semRoutineInTemplBody(c, n, skProc)
+  of nkMethodDef:
+    result = semRoutineInTemplBody(c, n, skMethod)
+  of nkIteratorDef:
+    result = semRoutineInTemplBody(c, n, skIterator)
+  of nkTemplateDef:
+    result = semRoutineInTemplBody(c, n, skTemplate)
+  of nkMacroDef:
+    result = semRoutineInTemplBody(c, n, skMacro)
+  of nkConverterDef:
+    result = semRoutineInTemplBody(c, n, skConverter)
   else:
     # dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
     if n.kind == nkDotExpr or n.kind == nkAccQuoted:
-      let s = QualifiedLookUp(c, n, {})
-      if s != nil and Contains(toBind, s.id):
-        return symChoice(c, n, s)
+      let s = QualifiedLookUp(c.c, n, {})
+      if s != nil and Contains(c.toBind, s.id):
+        return symChoice(c.c, n, s)
     result = n
-    for i in countup(0, sonsLen(n) - 1): 
-      result.sons[i] = resolveTemplateParams(c, n.sons[i], owner, toBind)
+    for i in countup(0, sonsLen(n) - 1):
+      result.sons[i] = semTemplBody(c, n.sons[i])
   
 proc transformToExpr(n: PNode): PNode = 
   var realStmt: int
@@ -94,8 +304,8 @@ proc transformToExpr(n: PNode): PNode =
     else: n.kind = nkStmtListExpr
   of nkBlockStmt: 
     n.kind = nkBlockExpr
-    #nkIfStmt: n.kind := nkIfExpr; // this is not correct!
-  else: 
+    #nkIfStmt: n.kind = nkIfExpr // this is not correct!
+  else:
     nil
 
 proc semTemplateDef(c: PContext, n: PNode): PNode = 
@@ -108,7 +318,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   # check parameter list:
   pushOwner(s)
   openScope(c.tab)
-  n.sons[namePos] = newSymNode(s)
+  n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
   if n.sons[pragmasPos].kind != nkEmpty:
     pragma(c, s, n.sons[pragmasPos], templatePragmas)
   # check that no generic parameters exist:
@@ -126,8 +336,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
       # use ``stmt`` as implicit result type
       s.typ.sons[0] = newTypeS(tyStmt, c)
       s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
-  var toBind = initIntSet()
-  n.sons[bodyPos] = resolveTemplateParams(c, n.sons[bodyPos], s, toBind)
+  var ctx: TemplCtx
+  ctx.toBind = initIntSet()
+  ctx.c = c
+  ctx.owner = s
+  n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
   if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
     n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) 
     # only parameters are resolved, no type checking is performed
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 35b841c71..a54c3a297 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -78,7 +78,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       incl(e.flags, sfExported) # BUGFIX
       StrTableAdd(c.module.tab, e) # BUGFIX
     addSon(result.n, newSymNode(e))
-    addDeclAt(c, e, c.tab.tos - 1)
+    if sfGenSym notin e.flags: addDeclAt(c, e, c.tab.tos - 1)
     inc(counter)
 
 proc semSet(c: PContext, n: PNode, prev: PType): PType = 
@@ -264,7 +264,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
     if a.sons[length - 1].kind != nkEmpty: 
       LocalError(a.sons[length - 1].info, errInitHereNotAllowed)
     for j in countup(0, length - 3): 
-      var field = newSymS(skField, a.sons[j], c)
+      var field = newSymG(skField, a.sons[j], c)
       field.typ = typ
       field.position = counter
       inc(counter)
@@ -279,7 +279,9 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
   # identifier with visibility
   if n.kind == nkPostfix: 
     if sonsLen(n) == 2 and n.sons[0].kind == nkIdent: 
-      result = newSymS(kind, n.sons[1], c)
+      # for gensym'ed identifiers the identifier may already have been
+      # transformed to a symbol and we need to use that here:
+      result = newSymG(kind, n.sons[1], c)
       var v = n.sons[0].ident
       if sfExported in allowed and v.id == ord(wStar): 
         incl(result.flags, sfExported)
@@ -288,7 +290,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
     else:
       illFormedAst(n)
   else:
-    result = newSymS(kind, n, c)
+    result = newSymG(kind, n, c)
   
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, 
                         allowed: TSymFlags): PSym = 
@@ -469,6 +471,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
       typ = semTypeNode(c, n.sons[length-2], nil)
     for i in countup(0, sonsLen(n)-3):
       var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
+      suggestSym(n.sons[i], f)
       f.typ = typ
       f.position = pos
       if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and
@@ -548,9 +551,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
     let nn = getSysSym"PNimrodNode"
     var a = copySym(param)
     a.typ = nn.typ
-    addDecl(c, a)
+    if sfGenSym notin a.flags: addDecl(c, a)
   else:
-    addDecl(c, param)
+    if sfGenSym notin param.flags: addDecl(c, param)
 
 proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
   tuple[typ: PType, id: PIdent] =
@@ -672,7 +675,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       
     if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
     for j in countup(0, length-3): 
-      var arg = newSymS(skParam, a.sons[j], c)
+      var arg = newSymG(skParam, a.sons[j], c)
       var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s,
                                     arg.info).skipIntLit
       arg.typ = finalType
@@ -711,7 +714,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
   openScope(c.tab)
-  if n.sons[0].kind != nkEmpty: 
+  if n.sons[0].kind notin {nkEmpty, nkSym}:
     addDecl(c, newSymS(skLabel, n.sons[0], c))
   result = semStmtListType(c, n.sons[1], prev)
   n.sons[1].typ = result
@@ -947,7 +950,7 @@ proc semGenericConstraints(c: PContext, n: PNode, result: PType) =
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = 
   result = copyNode(n)
   if n.kind != nkGenericParams: 
-    InternalError(n.info, "semGenericParamList")
+    illFormedAst(n)
     return
   for i in countup(0, sonsLen(n)-1): 
     var a = n.sons[i]
@@ -965,27 +968,27 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
     for j in countup(0, L-3): 
       var s: PSym
       if typ == nil:
-        s = newSymS(skType, a.sons[j], c)
+        s = newSymG(skType, a.sons[j], c)
         s.typ = newTypeS(tyGenericParam, c)
       else:
         case typ.kind
         of tyTypeDesc: 
-          s = newSymS(skType, a.sons[j], c)
+          s = newSymG(skType, a.sons[j], c)
           s.typ = newTypeS(tyGenericParam, c)
         of tyExpr:
           #echo "GENERIC EXPR ", a.info.toFileLineCol
           # not a type param, but an expression
           # proc foo[x: expr](bar: int) what is this?
-          s = newSymS(skGenericParam, a.sons[j], c)
+          s = newSymG(skGenericParam, a.sons[j], c)
           s.typ = typ
         else:
           # This handles cases like proc foo[t: tuple] 
           # XXX: we want to turn that into a type class
-          s = newSymS(skType, a.sons[j], c)
+          s = newSymG(skType, a.sons[j], c)
           s.typ = typ
       if def.kind != nkEmpty: s.ast = def
       s.typ.sym = s
       if father != nil: addSonSkipIntLit(father, s.typ)
       s.position = i
       addSon(result, newSymNode(s))
-      addDecl(c, s)
+      if sfGenSym notin s.flags: addDecl(c, s)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 9ca02689e..3b9c77336 100755
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -54,9 +54,13 @@ proc suggestField(c: PContext, s: PSym, outputs: var int) =
     OutWriteln(SymToStr(s, isLocal=true, sectionSuggest))
     inc outputs
 
-template wholeSymTab(cond, section: expr) {.immediate.} = 
-  for i in countdown(c.tab.tos-1, 0): 
-    for it in items(c.tab.stack[i]): 
+when not defined(nimhygiene):
+  {.pragma: inject.}
+
+template wholeSymTab(cond, section: expr) {.immediate.} =
+  for i in countdown(c.tab.tos-1, 0):
+    for item in items(c.tab.stack[i]):
+      let it {.inject.} = item
       if cond:
         OutWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section))
         inc outputs
diff --git a/compiler/types.nim b/compiler/types.nim
index 69ace9772..8dae14924 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -380,6 +380,13 @@ proc mutateType(t: PType, iter: TTypeMutator, closure: PObject): PType =
   var marker = InitIntSet()
   result = mutateTypeAux(marker, t, iter, closure)
 
+proc ValueToString(a: PNode): string = 
+  case a.kind
+  of nkCharLit..nkUInt64Lit: result = $(a.intVal)
+  of nkFloatLit..nkFloat128Lit: result = $(a.floatVal)
+  of nkStrLit..nkTripleStrLit: result = a.strVal
+  else: result = "<invalid value>"
+
 proc rangeToStr(n: PNode): string = 
   assert(n.kind == nkRange)
   result = ValueToString(n.sons[0]) & ".." & ValueToString(n.sons[1])
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 382b6c765..43bdf4fa4 100755
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -60,7 +60,7 @@ type
     wFieldChecks, 
     wWatchPoint, wSubsChar, 
     wAcyclic, wShallow, wUnroll, wLinearScanEnd,
-    wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
+    wWrite, wGensym, wInject, wInheritable, wThreadVar, wEmit, wNoStackFrame,
     wImplicitStatic, wGlobal, wHoist
 
     wAuto, wBool, wCatch, wChar, wClass,
@@ -138,7 +138,7 @@ const
     "passc", "passl", "borrow", "discardable", "fieldchecks",
     "watchpoint",
     "subschar", "acyclic", "shallow", "unroll", "linearscanend",
-    "write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
+    "write", "gensym", "inject", "inheritable", "threadvar", "emit",
     "nostackframe", "implicitstatic", "global", "hoist",
     
     "auto", "bool", "catch", "char", "class",
diff --git a/doc/manual.txt b/doc/manual.txt
index 5933691b0..813f1a5b4 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -908,7 +908,7 @@ converts the list of arguments to an array implicitely:
     write(f, "\n")
 
   myWriteln(stdout, "abc", "def", "xyz")
-  # is transformed by the compiler to:
+  # is transformed to:
   myWriteln(stdout, ["abc", "def", "xyz"])
 
 This transformation is only done if the varargs parameter is the
@@ -922,7 +922,7 @@ type conversions in this context:
     write(f, "\n")
 
   myWriteln(stdout, 123, "abc", 4.0)
-  # is transformed by the compiler to:
+  # is transformed to:
   myWriteln(stdout, [$123, $"def", $4.0])
 
 In this example ``$`` is applied to any argument that is passed to the 
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 7ef3d247a..57dc3a313 100755
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -218,6 +218,9 @@ proc findAll*(s: string, pattern: TRegEx, start = 0): seq[string] =
   ## If it does not match, @[] is returned.
   accumulateResult(findAll(s, pattern, start))
 
+when not defined(nimhygiene):
+  {.pragma: inject.}
+
 template `=~` *(s: string, pattern: TRegEx): expr = 
   ## This calls ``match`` with an implicit declared ``matches`` array that 
   ## can be used in the scope of the ``=~`` call: 
@@ -237,7 +240,7 @@ template `=~` *(s: string, pattern: TRegEx): expr =
   ##     echo("syntax error")
   ##
   when not definedInScope(matches):
-    var matches: array[0..re.maxSubPatterns-1, string]
+    var matches {.inject.}: array[0..re.maxSubPatterns-1, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index efa77199b..e37c072f1 100755
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -845,6 +845,9 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
   ## returns all matching *substrings* of `s` that match `pattern`.
   ## If it does not match, @[] is returned.
   accumulateResult(findAll(s, pattern, start))
+
+when not defined(nimhygiene):
+  {.pragma: inject.}
   
 template `=~`*(s: string, pattern: TPeg): bool =
   ## This calls ``match`` with an implicit declared ``matches`` array that 
@@ -865,7 +868,7 @@ template `=~`*(s: string, pattern: TPeg): bool =
   ##     echo("syntax error")
   ##  
   when not definedInScope(matches):
-    var matches: array[0..pegs.maxSubpatterns-1, string]
+    var matches {.inject.}: array[0..pegs.maxSubpatterns-1, string]
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
diff --git a/tests/tester.nim b/tests/tester.nim
index 725e76ead..8c9f7f782 100755
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -62,12 +62,15 @@ proc extractSpec(filename: string): string =
     #echo "warning: file does not contain spec: " & filename
     result = ""
 
+when not defined(nimhygiene):
+  {.pragma: inject.}
+
 template parseSpecAux(fillResult: stmt) =
   var ss = newStringStream(extractSpec(filename))
-  var p: TCfgParser
+  var p {.inject.}: TCfgParser
   open(p, ss, filename, 1)
   while true:
-    var e = next(p)
+    var e {.inject.} = next(p)
     case e.kind
     of cfgEof: break
     of cfgSectionStart, cfgOption, cfgError:
diff --git a/todo.txt b/todo.txt
index fadd7fb9e..7ddd2c573 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,16 +1,19 @@
 version 0.9.0
 =============
 
+- make templates hygienic by default: 'gensym', 'inject' pragmas
+- make 'bind' default for templates and introduce 'mixin'
+- use ``\`` for comment continuations
+- ``final`` should be the default for objects
 - implement "closure tuple consists of a single 'ref'" optimization
 - implement for loop transformation for first class iterators
-- make templates hygienic by default: 'gensym', 'inject' pragmas
 
 - implicit deref for parameter matching
-- ``final`` should be the default for objects
 - optimize genericAssign in the code generator
 - the lookup rules for generics really are too permissive
 - fix remaining closure bugs:
-  - fix evals.nim with closures
+  - test evals.nim with closures
+  - what about macros with closures?
 
 
 Bugs