summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgstmts.nim4
-rw-r--r--compiler/cgen.nim9
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/evaltempl.nim60
-rw-r--r--compiler/installer.ini2
-rw-r--r--compiler/jsgen.nim16
-rw-r--r--compiler/lambdalifting.nim2
-rw-r--r--compiler/main.nim3
-rw-r--r--compiler/modules.nim20
-rw-r--r--compiler/msgs.nim3
-rw-r--r--compiler/nim.cfg (renamed from compiler/nim.nim.cfg)0
-rw-r--r--compiler/nim.nim11
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/parampatterns.nim20
-rw-r--r--compiler/platform.nim7
-rw-r--r--compiler/pragmas.nim544
-rw-r--r--compiler/rodread.nim2
-rw-r--r--compiler/scriptconfig.nim119
-rw-r--r--compiler/sem.nim6
-rw-r--r--compiler/semcall.nim18
-rw-r--r--compiler/semdestruct.nim9
-rw-r--r--compiler/semexprs.nim13
-rw-r--r--compiler/semfold.nim16
-rw-r--r--compiler/semgnrc.nim128
-rw-r--r--compiler/seminst.nim32
-rw-r--r--compiler/semmagic.nim6
-rw-r--r--compiler/semstmts.nim25
-rw-r--r--compiler/semtempl.nim23
-rw-r--r--compiler/semtypes.nim8
-rw-r--r--compiler/transf.nim83
-rw-r--r--compiler/vm.nim52
-rw-r--r--compiler/vmdeps.nim4
-rw-r--r--compiler/vmgen.nim59
-rw-r--r--compiler/vmhooks.nim12
35 files changed, 851 insertions, 474 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index f12a24fa2..f4a7c4400 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -102,7 +102,7 @@ proc assignLabel(b: var TBlock): Rope {.inline.} =
 proc blockBody(b: var TBlock): Rope =
   result = b.sections[cpsLocals]
   if b.frameLen > 0:
-    result.addf("F.len+=$1;$n", [b.frameLen.rope])
+    result.addf("FR.len+=$1;$n", [b.frameLen.rope])
   result.add(b.sections[cpsInit])
   result.add(b.sections[cpsStmts])
 
@@ -123,7 +123,7 @@ proc endBlock(p: BProc) =
       ~"}$n"
   let frameLen = p.blocks[topBlock].frameLen
   if frameLen > 0:
-    blockEnd.addf("F.len-=$1;$n", [frameLen.rope])
+    blockEnd.addf("FR.len-=$1;$n", [frameLen.rope])
   endBlock(p, blockEnd)
 
 proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 2e95918cc..13514818e 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -375,7 +375,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
   var a = "&" & s.loc.r
   if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
   lineF(p, cpsInit,
-       "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n",
+       "FR.s[$1].address = (void*)$3; FR.s[$1].typ = $4; FR.s[$1].name = $2;$n",
        [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
         genTypeInfo(p.module, s.loc.t)])
   inc(p.maxFrameLen)
@@ -597,7 +597,7 @@ proc cgsym(m: BModule, name: string): Rope =
     of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
     of skVar, skResult, skLet: genVarPrototype(m, sym)
     of skType: discard getTypeDesc(m, sym.typ)
-    else: internalError("cgsym: " & name)
+    else: internalError("cgsym: " & name & ": " & $sym.kind)
   else:
     # we used to exclude the system module from this check, but for DLL
     # generation support this sloppyness leads to hard to detect bugs, so
@@ -623,7 +623,7 @@ proc retIsNotVoid(s: PSym): bool =
 proc initFrame(p: BProc, procname, filename: Rope): Rope =
   discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
-    discard cgsym(p.module, "TVarSlot")
+    discard cgsym(p.module, "VarSlot")
     result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
                   procname, filename, p.maxFrameLen.rope,
                   p.blocks[0].frameLen.rope)
@@ -1017,7 +1017,7 @@ proc genInitCode(m: BModule) =
       var procname = makeCString(m.module.name.s)
       add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
-      add(prc, ~"\tTFrame F; F.len = 0;$N")
+      add(prc, ~"\tTFrame F; FR.len = 0;$N")
 
   add(prc, genSectionStart(cpsInit))
   add(prc, m.preInitProc.s(cpsInit))
@@ -1329,4 +1329,3 @@ proc cgenWriteModules* =
   if generatedHeader != nil: writeHeader(generatedHeader)
 
 const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
-
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 29d08f327..dba117516 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -645,7 +645,7 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser) =
 proc processArgument*(pass: TCmdLinePass; p: OptParser;
                       argsCount: var int): bool =
   if argsCount == 0:
-    options.command = p.key
+    if pass != passCmd2: options.command = p.key
   else:
     if pass == passCmd1: options.commandArgs.add p.key
     if argsCount == 1:
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index aecbde66e..297b865b2 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -90,3 +90,4 @@ proc initDefines*() =
   defineSymbol("nimnode")
   defineSymbol("nimnomagic64")
   defineSymbol("nimvarargstyped")
+  defineSymbol("nimtypedescfixed")
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 2b3112909..82c4e8f57 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   if ctx.instLines: result.info = b.info
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
+  template handleParam(param) =
+    let x = param
+    if x.kind == nkArgList:
+      for y in items(x): result.add(y)
+    else:
+      result.add copyTree(x)
+  
   case templ.kind
   of nkSym:
     var s = templ.sym
     if s.owner.id == c.owner.id:
       if s.kind == skParam and sfGenSym notin s.flags:
-        let x = actual.sons[s.position]
-        if x.kind == nkArgList:
-          for y in items(x): result.add(y)
-        else:
-          result.add copyTree(x)
+        handleParam actual.sons[s.position]
+      elif s.kind == skGenericParam:
+        handleParam actual.sons[s.owner.typ.len + s.position - 1]
       else:
         internalAssert sfGenSym in s.flags
         var x = PSym(idTableGet(c.mapping, s))
@@ -56,22 +61,45 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
 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)
+  var totalParams = case n.kind
+    of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len
+    else: 0
+
+  var
+    # XXX: Since immediate templates are not subjected to the
+    # standard sigmatching algorithm, they will have a number
+    # of deficiencies when it comes to generic params:
+    # Type dependencies between the parameters won't be honoured
+    # and the bound generic symbols won't be resolvable within
+    # their bodies. We could try to fix this, but it may be
+    # wiser to just deprecate immediate templates and macros
+    # now that we have working untyped parameters.
+    genericParams = if sfImmediate in s.flags: 0
+                    else: s.ast[genericParamsPos].len
+    expectedRegularParams = <s.typ.len
+    givenRegularParams = totalParams - genericParams
 
+  if totalParams > expectedRegularParams + genericParams:
+    globalError(n.info, errWrongNumberOfArguments)
+  
   result = newNodeI(nkArgList, n.info)
-  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:
+  for i in 1 .. givenRegularParams:
+    result.addSon n.sons[i]
+
+  # handle parameters with default values, which were
+  # not supplied by the user
+  for i in givenRegularParams+1 .. expectedRegularParams:
+    let default = s.typ.n.sons[i].sym.ast
+    internalAssert default != nil
+    if default.kind == nkEmpty:
       localError(n.info, errWrongNumberOfArguments)
       addSon(result, ast.emptyNode)
     else:
-      addSon(result, arg)
+      addSon(result, default.copyTree)
+  
+  # add any generic paramaters
+  for i in 1 .. genericParams:
+    result.addSon n.sons[givenRegularParams + i]
 
 var evalTemplateCounter* = 0
   # to prevent endless recursion in templates instantiation
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 52a3d0886..c8af38886 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,7 +6,7 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
+  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
   macosx: i386;amd64;powerpc64
   solaris: i386;amd64;sparc
   freebsd: i386;amd64
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 6e317fb7e..89a1b84a2 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -729,14 +729,13 @@ proc genBreakStmt(p: PProc, n: PNode) =
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
   addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)])
 
-proc genAsmStmt(p: PProc, n: PNode) =
+proc genAsmOrEmitStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
-  assert(n.kind == nkAsmStmt)
   for i in countup(0, sonsLen(n) - 1):
     case n.sons[i].kind
     of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
     of nkSym: add(p.body, mangleName(n.sons[i].sym))
-    else: internalError(n.sons[i].info, "jsgen: genAsmStmt()")
+    else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
   var cond, stmt: TCompRes
@@ -1578,6 +1577,12 @@ proc genStmt(p: PProc, n: PNode) =
   gen(p, n, r)
   if r.res != nil: addf(p.body, "$#;$n", [r.res])
 
+proc genPragma(p: PProc, n: PNode) =
+  for it in n.sons:
+    case whichPragma(it)
+    of wEmit: genAsmOrEmitStmt(p, it.sons[1])
+    else: discard
+
 proc gen(p: PProc, n: PNode, r: var TCompRes) =
   r.typ = etyNone
   r.kind = resNone
@@ -1677,12 +1682,13 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     if n.sons[0].kind != nkEmpty:
       genLineDir(p, n)
       gen(p, n.sons[0], r)
-  of nkAsmStmt: genAsmStmt(p, n)
+  of nkAsmStmt: genAsmOrEmitStmt(p, n)
   of nkTryStmt: genTry(p, n, r)
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef: discard
+  of nkPragma: genPragma(p, n)
   of nkProcDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
     if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index d11776cf6..c669fc745 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   var params = routine.ast.sons[paramsPos]
   # -1 is correct here as param.position is 0 based but we have at position 0
   # some nkEffect node:
-  param.position = params.len-1
+  param.position = routine.typ.n.len-1
   addSon(params, newSymNode(param))
   incl(routine.typ.flags, tfCapturesEnv)
   assert sfFromGeneric in param.flags
diff --git a/compiler/main.nim b/compiler/main.nim
index 47fae7fa7..014605cc9 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -116,7 +116,7 @@ proc interactivePasses =
   #incl(gGlobalOptions, optSafeCode)
   #setTarget(osNimrodVM, cpuNimrodVM)
   initDefines()
-  defineSymbol("nimrodvm")
+  defineSymbol("nimscript")
   when hasFFI: defineSymbol("nimffi")
   registerPass(verbosePass)
   registerPass(semPass)
@@ -356,6 +356,7 @@ proc mainCommand* =
     gGlobalOptions.incl(optCaasEnabled)
     msgs.gErrorMax = high(int)  # do not stop after first error
     serve(mainCommand)
+  of "nop": discard
   else:
     rawMessage(errInvalidCommandX, command)
 
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 2d0267c93..ad68e6315 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -85,6 +85,15 @@ proc resetAllModules* =
   resetPackageCache()
   # for m in cgenModules(): echo "CGEN MODULE FOUND"
 
+proc resetAllModulesHard* =
+  resetPackageCache()
+  gCompiledModules.setLen 0
+  gMemCacheData.setLen 0
+  magicsys.resetSysTypes()
+  # XXX
+  #gOwners = @[]
+  #rangeDestructorProc = nil
+
 proc checkDepMem(fileIdx: int32): TNeedRecompile =
   template markDirty =
     resetModule(fileIdx)
@@ -205,9 +214,8 @@ proc compileProject*(projectFileIdx = -1'i32) =
     compileSystemModule()
     discard compileModule(projectFile, {sfMainModule})
 
-var stdinModule: PSym
-proc makeStdinModule*(): PSym =
-  if stdinModule == nil:
-    stdinModule = newModule(fileInfoIdx"stdin")
-    stdinModule.id = getID()
-  result = stdinModule
+proc makeModule*(filename: string): PSym =
+  result = newModule(fileInfoIdx filename)
+  result.id = getID()
+
+proc makeStdinModule*(): PSym = makeModule"stdin"
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e739bb4b9..1b1f0a76e 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -82,7 +82,7 @@ type
     errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
     errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
     errNoReturnTypeDeclared,
-    errInvalidCommandX, errXOnlyAtModuleScope,
+    errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
     errXNeedsParamObjectType,
     errTemplateInstantiationTooNested, errInstantiationFrom,
     errInvalidIndexValueForTuple, errCommandExpectsFilename,
@@ -318,6 +318,7 @@ const
     errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
     errXNeedsReturnType: "$1 needs a return type",
     errNoReturnTypeDeclared: "no return type declared",
+    errNoCommand: "no command given",
     errInvalidCommandX: "invalid command: \'$1\'",
     errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
     errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
diff --git a/compiler/nim.nim.cfg b/compiler/nim.cfg
index 64631a437..64631a437 100644
--- a/compiler/nim.nim.cfg
+++ b/compiler/nim.cfg
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 5da87dfa3..51f4cae92 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -16,7 +16,7 @@ when defined(gcc) and defined(windows):
 import
   commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
   extccomp, strutils, os, osproc, platform, main, parseopt, service,
-  nodejs
+  nodejs, scriptconfig
 
 when hasTinyCBackend:
   import tccgen
@@ -54,10 +54,17 @@ proc handleCmdLine() =
     else:
       gProjectPath = getCurrentDir()
     loadConfigs(DefaultConfig) # load all config files
+    let scriptFile = gProjectFull.changeFileExt("nims")
+    if fileExists(scriptFile):
+      runNimScript(scriptFile)
+      # 'nim foo.nims' means to just run the NimScript file and do nothing more:
+      if scriptFile == gProjectFull: return
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
     processCmdLine(passCmd2, "")
+    if options.command == "":
+      rawMessage(errNoCommand, command)
     mainCommand()
     if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
diff --git a/compiler/options.nim b/compiler/options.nim
index af1e21e60..adf2017d6 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -210,7 +210,7 @@ proc removeTrailingDirSep*(path: string): string =
   else:
     result = path
 
-proc getGeneratedPath: string =
+proc getNimcacheDir*: string =
   result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
                                                          genSubDir
 
@@ -266,7 +266,7 @@ proc toGeneratedFile*(path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
   var (head, tail) = splitPath(path)
   #if len(head) > 0: head = shortenDir(head & dirSep)
-  result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)])
+  result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
   #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
 
 when noTimeMachine:
@@ -294,7 +294,7 @@ when noTimeMachine:
 proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
   var (head, tail) = splitPath(f)
   #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
-  var subdir = getGeneratedPath() # / head
+  var subdir = getNimcacheDir() # / head
   if createSubDir:
     try:
       createDir(subdir)
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index b7fe269df..ae391945a 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -178,13 +178,14 @@ type
     arDiscriminant,           # is a discriminant
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
-proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
+proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
   ## 'owner' can be nil!
   result = arNone
   case n.kind
   of nkSym:
-    # don't list 'skLet' here:
-    if n.sym.kind in {skVar, skResult, skTemp}:
+    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+                else: {skVar, skResult, skTemp}
+    if n.sym.kind in kinds:
       if owner != nil and owner.id == n.sym.owner.id and
           sfGlobal notin n.sym.flags:
         result = arLocalLValue
@@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
     if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
       result = arDiscriminant
   of nkBracketExpr:
@@ -208,23 +209,24 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     # Object and tuple conversions are still addressable, so we skip them
     # XXX why is 'tyOpenArray' allowed here?
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
     elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
       # types that are equal modulo distinction preserve l-value:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
   of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
     result = arLValue
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = isAssignable(owner, n.sons[0])
+    result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkCallKinds:
     # builtin slice keeps lvalue-ness:
-    if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1])
+    if getMagic(n) == mSlice:
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
   else:
     discard
 
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 4dd5d8836..c4e7d453a 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -158,8 +158,8 @@ type
   TSystemCPU* = enum # Also add CPU for in initialization section and
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
-    cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm,
-    cpuJS, cpuNimrodVM, cpuAVR
+    cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
+    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR
 
 type
   TEndian* = enum
@@ -175,12 +175,15 @@ const
     (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
+    (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
     (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+    (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
     (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 650b0e195..5f317ed24 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -593,280 +593,282 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
                   validPragmas: TSpecialWords): bool =
   var it = n.sons[i]
   var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
-  if key.kind == nkIdent:
-    var userPragma = strTableGet(c.userPragmas, key.ident)
-    if userPragma != nil:
-      inc c.instCounter
-      if c.instCounter > 100:
-        globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
-      pragma(c, sym, userPragma.ast, validPragmas)
-      # ensure the pragma is also remember for generic instantiations in other
-      # modules:
-      n.sons[i] = userPragma.ast
-      dec c.instCounter
-    else:
-      var k = whichKeyword(key.ident)
-      if k in validPragmas:
-        case k
-        of wExportc:
-          makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
-          incl(sym.flags, sfUsed) # avoid wrong hints
-        of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
-        of wImportCompilerProc:
-          processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
-        of wExtern: setExternName(sym, expectStrLit(c, it))
-        of wImmediate:
-          if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
-          else: invalidPragma(it)
-        of wDirty:
-          if sym.kind == skTemplate: incl(sym.flags, sfDirty)
-          else: invalidPragma(it)
-        of wImportCpp:
-          processImportCpp(sym, getOptionalStr(c, it, "$1"))
-        of wImportObjC:
-          processImportObjC(sym, getOptionalStr(c, it, "$1"))
-        of wAlign:
-          if sym.typ == nil: invalidPragma(it)
-          var align = expectIntLit(c, it)
-          if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.align = align.int16
-        of wSize:
-          if sym.typ == nil: invalidPragma(it)
-          var size = expectIntLit(c, it)
-          if not isPowerOfTwo(size) or size <= 0 or size > 8:
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.size = size
-        of wNodecl:
-          noVal(it)
-          incl(sym.loc.flags, lfNoDecl)
-        of wPure, wAsmNoStackFrame:
-          noVal(it)
-          if sym != nil:
-            if k == wPure and sym.kind in routineKinds: invalidPragma(it)
-            else: incl(sym.flags, sfPure)
-        of wVolatile:
-          noVal(it)
-          incl(sym.flags, sfVolatile)
-        of wRegister:
-          noVal(it)
-          incl(sym.flags, sfRegister)
-        of wThreadVar:
-          noVal(it)
-          incl(sym.flags, sfThread)
-        of wDeadCodeElim: pragmaDeadCodeElim(c, it)
-        of wNoForward: pragmaNoForward(c, it)
-        of wMagic: processMagic(c, it, sym)
-        of wCompileTime:
-          noVal(it)
-          incl(sym.flags, sfCompileTime)
-          incl(sym.loc.flags, lfNoDecl)
-        of wGlobal:
-          noVal(it)
-          incl(sym.flags, sfGlobal)
-          incl(sym.flags, sfPure)
-        of wMerge:
-          # only supported for backwards compat, doesn't do anything anymore
-          noVal(it)
-        of wConstructor:
-          noVal(it)
-          incl(sym.flags, sfConstructor)
-        of wHeader:
-          var lib = getLib(c, libHeader, getStrLitNode(c, it))
-          addToLib(lib, sym)
-          incl(sym.flags, sfImportc)
-          incl(sym.loc.flags, lfHeader)
-          incl(sym.loc.flags, lfNoDecl)
-          # implies nodecl, because otherwise header would not make sense
-          if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
-        of wDestructor:
-          sym.flags.incl sfOverriden
-          if sym.name.s.normalize != "destroy":
-            localError(n.info, errGenerated, "destructor has to be named 'destroy'")
-        of wOverride:
-          sym.flags.incl sfOverriden
-        of wNosideeffect:
-          noVal(it)
-          incl(sym.flags, sfNoSideEffect)
-          if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
-        of wSideeffect:
-          noVal(it)
-          incl(sym.flags, sfSideEffect)
-        of wNoreturn:
-          noVal(it)
-          incl(sym.flags, sfNoReturn)
-        of wDynlib:
-          processDynLib(c, it, sym)
-        of wCompilerproc:
-          noVal(it)           # compilerproc may not get a string!
-          if sfFromGeneric notin sym.flags: markCompilerProc(sym)
-        of wProcVar:
-          noVal(it)
-          incl(sym.flags, sfProcvar)
-        of wDeprecated:
-          if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
-          elif sym != nil: incl(sym.flags, sfDeprecated)
-          else: incl(c.module.flags, sfDeprecated)
-        of wVarargs:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfVarargs)
-        of wBorrow:
-          if sym.kind == skType:
-            typeBorrow(sym, it)
-          else:
-            noVal(it)
-            incl(sym.flags, sfBorrow)
-        of wFinal:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfFinal)
-        of wInheritable:
-          noVal(it)
-          if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
-          else: incl(sym.typ.flags, tfInheritable)
-        of wAcyclic:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfAcyclic)
-        of wShallow:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfShallow)
-        of wThread:
-          noVal(it)
-          incl(sym.flags, sfThread)
-          incl(sym.flags, sfProcvar)
-          if sym.typ != nil: incl(sym.typ.flags, tfThread)
-        of wGcSafe:
-          noVal(it)
-          if sym.kind != skType: incl(sym.flags, sfThread)
-          if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
-          else: invalidPragma(it)
-        of wPacked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfPacked)
-        of wHint: message(it.info, hintUser, expectStrLit(c, it))
-        of wWarning: message(it.info, warnUser, expectStrLit(c, it))
-        of wError:
-          if sym != nil and sym.isRoutine:
-            # This is subtle but correct: the error *statement* is only
-            # allowed for top level statements. Seems to be easier than
-            # distinguishing properly between
-            # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
-            noVal(it)
-            incl(sym.flags, sfError)
-          else:
-            localError(it.info, errUser, expectStrLit(c, it))
-        of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
-        of wDefine: processDefine(c, it)
-        of wUndef: processUndef(c, it)
-        of wCompile: processCompile(c, it)
-        of wLink: processCommonLink(c, it, linkNormal)
-        of wLinksys: processCommonLink(c, it, linkSys)
-        of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
-        of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
-        of wBreakpoint: pragmaBreakpoint(c, it)
-        of wWatchPoint: pragmaWatchpoint(c, it)
-        of wPush:
-          processPush(c, n, i + 1)
-          result = true
-        of wPop: processPop(c, it)
-        of wPragma:
-          processPragma(c, n, i)
-          result = true
-        of wDiscardable:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfDiscardable)
-        of wNoInit:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfNoInit)
-        of wCodegenDecl: processCodegenDecl(c, it, sym)
-        of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
-           wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-           wLinedir, wStacktrace, wLinetrace, wOptimization,
-           wCallconv,
-           wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
-           wPatterns:
-          if processOption(c, it):
-            # calling conventions (boring...):
-            localError(it.info, errOptionExpected)
-        of FirstCallConv..LastCallConv:
-          assert(sym != nil)
-          if sym.typ == nil: invalidPragma(it)
-          else: sym.typ.callConv = wordToCallConv(k)
-        of wEmit: pragmaEmit(c, it)
-        of wUnroll: pragmaUnroll(c, it)
-        of wLinearScanEnd, wComputedGoto: noVal(it)
-        of wEffects:
-          # is later processed in effect analysis:
-          noVal(it)
-        of wIncompleteStruct:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfIncompleteStruct)
-        of wUnchecked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUncheckedArray)
-        of wUnion:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUnion)
-        of wRequiresInit:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfNeedsInit)
-        of wByRef:
-          noVal(it)
-          if sym == nil or sym.typ == nil:
-            if processOption(c, it): localError(it.info, errOptionExpected)
-          else:
-            incl(sym.typ.flags, tfByRef)
-        of wByCopy:
-          noVal(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)
-        of wRaises, wTags: pragmaRaisesOrTags(c, it)
-        of wLocks:
-          if sym == nil: pragmaLockStmt(c, it)
-          elif sym.typ == nil: invalidPragma(it)
-          else: sym.typ.lockLevel = pragmaLocks(c, it)
-        of wGuard:
-          if sym == nil or sym.kind notin {skVar, skLet, skField}:
-            invalidPragma(it)
-          else:
-            sym.guard = pragmaGuard(c, it, sym.kind)
-        of wGoto:
-          if sym == nil or sym.kind notin {skVar, skLet}:
-            invalidPragma(it)
-          else:
-            sym.flags.incl sfGoto
-        of wInjectStmt:
-          if it.kind != nkExprColonExpr:
-            localError(it.info, errExprExpected)
-          else:
-            it.sons[1] = c.semExpr(c, it.sons[1])
-        of wExperimental:
-          noVal(it)
-          if isTopLevel(c):
-            c.module.flags.incl sfExperimental
-          else:
-            localError(it.info, "'experimental' pragma only valid as toplevel statement")
-        of wNoRewrite:
+  if key.kind == nkBracketExpr:
+    processNote(c, it)
+    return
+  let ident = considerQuotedIdent(key)
+  var userPragma = strTableGet(c.userPragmas, ident)
+  if userPragma != nil:
+    inc c.instCounter
+    if c.instCounter > 100:
+      globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
+    pragma(c, sym, userPragma.ast, validPragmas)
+    # ensure the pragma is also remember for generic instantiations in other
+    # modules:
+    n.sons[i] = userPragma.ast
+    dec c.instCounter
+  else:
+    var k = whichKeyword(ident)
+    if k in validPragmas:
+      case k
+      of wExportc:
+        makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
+        incl(sym.flags, sfUsed) # avoid wrong hints
+      of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
+      of wImportCompilerProc:
+        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
+      of wExtern: setExternName(sym, expectStrLit(c, it))
+      of wImmediate:
+        if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
+        else: invalidPragma(it)
+      of wDirty:
+        if sym.kind == skTemplate: incl(sym.flags, sfDirty)
+        else: invalidPragma(it)
+      of wImportCpp:
+        processImportCpp(sym, getOptionalStr(c, it, "$1"))
+      of wImportObjC:
+        processImportObjC(sym, getOptionalStr(c, it, "$1"))
+      of wAlign:
+        if sym.typ == nil: invalidPragma(it)
+        var align = expectIntLit(c, it)
+        if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.align = align.int16
+      of wSize:
+        if sym.typ == nil: invalidPragma(it)
+        var size = expectIntLit(c, it)
+        if not isPowerOfTwo(size) or size <= 0 or size > 8:
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.size = size
+      of wNodecl:
+        noVal(it)
+        incl(sym.loc.flags, lfNoDecl)
+      of wPure, wAsmNoStackFrame:
+        noVal(it)
+        if sym != nil:
+          if k == wPure and sym.kind in routineKinds: invalidPragma(it)
+          else: incl(sym.flags, sfPure)
+      of wVolatile:
+        noVal(it)
+        incl(sym.flags, sfVolatile)
+      of wRegister:
+        noVal(it)
+        incl(sym.flags, sfRegister)
+      of wThreadVar:
+        noVal(it)
+        incl(sym.flags, sfThread)
+      of wDeadCodeElim: pragmaDeadCodeElim(c, it)
+      of wNoForward: pragmaNoForward(c, it)
+      of wMagic: processMagic(c, it, sym)
+      of wCompileTime:
+        noVal(it)
+        incl(sym.flags, sfCompileTime)
+        incl(sym.loc.flags, lfNoDecl)
+      of wGlobal:
+        noVal(it)
+        incl(sym.flags, sfGlobal)
+        incl(sym.flags, sfPure)
+      of wMerge:
+        # only supported for backwards compat, doesn't do anything anymore
+        noVal(it)
+      of wConstructor:
+        noVal(it)
+        incl(sym.flags, sfConstructor)
+      of wHeader:
+        var lib = getLib(c, libHeader, getStrLitNode(c, it))
+        addToLib(lib, sym)
+        incl(sym.flags, sfImportc)
+        incl(sym.loc.flags, lfHeader)
+        incl(sym.loc.flags, lfNoDecl)
+        # implies nodecl, because otherwise header would not make sense
+        if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
+      of wDestructor:
+        sym.flags.incl sfOverriden
+        if sym.name.s.normalize != "destroy":
+          localError(n.info, errGenerated, "destructor has to be named 'destroy'")
+      of wOverride:
+        sym.flags.incl sfOverriden
+      of wNosideeffect:
+        noVal(it)
+        incl(sym.flags, sfNoSideEffect)
+        if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
+      of wSideeffect:
+        noVal(it)
+        incl(sym.flags, sfSideEffect)
+      of wNoreturn:
+        noVal(it)
+        incl(sym.flags, sfNoReturn)
+      of wDynlib:
+        processDynLib(c, it, sym)
+      of wCompilerproc:
+        noVal(it)           # compilerproc may not get a string!
+        if sfFromGeneric notin sym.flags: markCompilerProc(sym)
+      of wProcVar:
+        noVal(it)
+        incl(sym.flags, sfProcvar)
+      of wDeprecated:
+        if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
+        elif sym != nil: incl(sym.flags, sfDeprecated)
+        else: incl(c.module.flags, sfDeprecated)
+      of wVarargs:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfVarargs)
+      of wBorrow:
+        if sym.kind == skType:
+          typeBorrow(sym, it)
+        else:
           noVal(it)
+          incl(sym.flags, sfBorrow)
+      of wFinal:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfFinal)
+      of wInheritable:
+        noVal(it)
+        if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
+        else: incl(sym.typ.flags, tfInheritable)
+      of wAcyclic:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfAcyclic)
+      of wShallow:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfShallow)
+      of wThread:
+        noVal(it)
+        incl(sym.flags, sfThread)
+        incl(sym.flags, sfProcvar)
+        if sym.typ != nil: incl(sym.typ.flags, tfThread)
+      of wGcSafe:
+        noVal(it)
+        if sym.kind != skType: incl(sym.flags, sfThread)
+        if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
         else: invalidPragma(it)
+      of wPacked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfPacked)
+      of wHint: message(it.info, hintUser, expectStrLit(c, it))
+      of wWarning: message(it.info, warnUser, expectStrLit(c, it))
+      of wError:
+        if sym != nil and sym.isRoutine:
+          # This is subtle but correct: the error *statement* is only
+          # allowed for top level statements. Seems to be easier than
+          # distinguishing properly between
+          # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
+          noVal(it)
+          incl(sym.flags, sfError)
+        else:
+          localError(it.info, errUser, expectStrLit(c, it))
+      of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
+      of wDefine: processDefine(c, it)
+      of wUndef: processUndef(c, it)
+      of wCompile: processCompile(c, it)
+      of wLink: processCommonLink(c, it, linkNormal)
+      of wLinksys: processCommonLink(c, it, linkSys)
+      of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
+      of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
+      of wBreakpoint: pragmaBreakpoint(c, it)
+      of wWatchPoint: pragmaWatchpoint(c, it)
+      of wPush:
+        processPush(c, n, i + 1)
+        result = true
+      of wPop: processPop(c, it)
+      of wPragma:
+        processPragma(c, n, i)
+        result = true
+      of wDiscardable:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfDiscardable)
+      of wNoInit:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfNoInit)
+      of wCodegenDecl: processCodegenDecl(c, it, sym)
+      of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
+         wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
+         wLinedir, wStacktrace, wLinetrace, wOptimization,
+         wCallconv,
+         wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
+         wPatterns:
+        if processOption(c, it):
+          # calling conventions (boring...):
+          localError(it.info, errOptionExpected)
+      of FirstCallConv..LastCallConv:
+        assert(sym != nil)
+        if sym.typ == nil: invalidPragma(it)
+        else: sym.typ.callConv = wordToCallConv(k)
+      of wEmit: pragmaEmit(c, it)
+      of wUnroll: pragmaUnroll(c, it)
+      of wLinearScanEnd, wComputedGoto: noVal(it)
+      of wEffects:
+        # is later processed in effect analysis:
+        noVal(it)
+      of wIncompleteStruct:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfIncompleteStruct)
+      of wUnchecked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUncheckedArray)
+      of wUnion:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUnion)
+      of wRequiresInit:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfNeedsInit)
+      of wByRef:
+        noVal(it)
+        if sym == nil or sym.typ == nil:
+          if processOption(c, it): localError(it.info, errOptionExpected)
+        else:
+          incl(sym.typ.flags, tfByRef)
+      of wByCopy:
+        noVal(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)
+      of wRaises, wTags: pragmaRaisesOrTags(c, it)
+      of wLocks:
+        if sym == nil: pragmaLockStmt(c, it)
+        elif sym.typ == nil: invalidPragma(it)
+        else: sym.typ.lockLevel = pragmaLocks(c, it)
+      of wGuard:
+        if sym == nil or sym.kind notin {skVar, skLet, skField}:
+          invalidPragma(it)
+        else:
+          sym.guard = pragmaGuard(c, it, sym.kind)
+      of wGoto:
+        if sym == nil or sym.kind notin {skVar, skLet}:
+          invalidPragma(it)
+        else:
+          sym.flags.incl sfGoto
+      of wInjectStmt:
+        if it.kind != nkExprColonExpr:
+          localError(it.info, errExprExpected)
+        else:
+          it.sons[1] = c.semExpr(c, it.sons[1])
+      of wExperimental:
+        noVal(it)
+        if isTopLevel(c):
+          c.module.flags.incl sfExperimental
+        else:
+          localError(it.info, "'experimental' pragma only valid as toplevel statement")
+      of wNoRewrite:
+        noVal(it)
       else: invalidPragma(it)
-  else: processNote(c, it)
+    else: invalidPragma(it)
 
 proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
                       validPragmas: TSpecialWords) =
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 92ce00240..e4530c2cc 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -898,6 +898,8 @@ proc getBody*(s: PSym): PNode =
   ## it may perform an expensive reload operation. Otherwise it's a simple
   ## accessor.
   assert s.kind in routineKinds
+  # prevent crashes due to incorrect macro transformations (bug #2377)
+  if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
   result = s.ast.sons[bodyPos]
   if result == nil:
     assert s.offset != 0
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
new file mode 100644
index 000000000..1e4fc25af
--- /dev/null
+++ b/compiler/scriptconfig.nim
@@ -0,0 +1,119 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements the new configuration system for Nim. Uses Nim as a scripting
+## language.
+
+import
+  ast, modules, passes, passaux, condsyms,
+  options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
+  os, times
+
+# we support 'cmpIgnoreStyle' natively for efficiency:
+from strutils import cmpIgnoreStyle
+
+proc listDirs(a: VmArgs, filter: set[PathComponent]) =
+  let dir = getString(a, 0)
+  var result: seq[string] = @[]
+  for kind, path in walkDir(dir):
+    if kind in filter: result.add path
+  setResult(a, result)
+
+proc setupVM(module: PSym; scriptName: string): PEvalContext =
+  result = newCtx(module)
+  result.mode = emRepl
+  registerAdditionalOps(result)
+
+  # captured vars:
+  var errorMsg: string
+  var vthisDir = scriptName.splitFile.dir
+
+  template cbconf(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        body
+
+  template cbos(name, body) {.dirty.} =
+    result.registerCallback "stdlib.system." & astToStr(name),
+      proc (a: VmArgs) =
+        try:
+          body
+        except OSError:
+          errorMsg = getCurrentExceptionMsg()
+
+  # Idea: Treat link to file as a file, but ignore link to directory to prevent
+  # endless recursions out of the box.
+  cbos listFiles:
+    listDirs(a, {pcFile, pcLinkToFile})
+  cbos listDirs:
+    listDirs(a, {pcDir})
+  cbos removeDir:
+    os.removeDir getString(a, 0)
+  cbos removeFile:
+    os.removeFile getString(a, 0)
+  cbos createDir:
+    os.createDir getString(a, 0)
+  cbos getOsError:
+    setResult(a, errorMsg)
+  cbos setCurrentDir:
+    os.setCurrentDir getString(a, 0)
+  cbos getCurrentDir:
+    setResult(a, os.getCurrentDir())
+  cbos moveFile:
+    os.moveFile(getString(a, 0), getString(a, 1))
+  cbos getLastModificationTime:
+    setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
+
+  cbconf thisDir:
+    setResult(a, vthisDir)
+  cbconf put:
+    options.setConfigVar(getString(a, 0), getString(a, 1))
+  cbconf get:
+    setResult(a, options.getConfigVar(a.getString 0))
+  cbconf exists:
+    setResult(a, options.existsConfigVar(a.getString 0))
+  cbconf nimcacheDir:
+    setResult(a, options.getNimcacheDir())
+  cbconf paramStr:
+    setResult(a, os.paramStr(int a.getInt 0))
+  cbconf paramCount:
+    setResult(a, os.paramCount())
+  cbconf cmpIgnoreStyle:
+    setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
+  cbconf setCommand:
+    options.command = a.getString 0
+  cbconf getCommand:
+    setResult(a, options.command)
+  cbconf switch:
+    processSwitch(a.getString 0, a.getString 1, passPP, unknownLineInfo())
+
+
+proc runNimScript*(scriptName: string) =
+  passes.gIncludeFile = includeModule
+  passes.gImportModule = importModule
+  initDefines()
+
+  defineSymbol("nimscript")
+  defineSymbol("nimconfig")
+  registerPass(semPass)
+  registerPass(evalPass)
+
+  appendStr(searchPaths, options.libpath)
+
+  var m = makeModule(scriptName)
+  incl(m.flags, sfMainModule)
+  vm.globalCtx = setupVM(m, scriptName)
+
+  compileSystemModule()
+  processModule(m, llStreamOpen(scriptName, fmRead), nil)
+
+  # ensure we load 'system.nim' again for the real non-config stuff!
+  resetAllModulesHard()
+  vm.globalCtx = nil
+  initDefines()
diff --git a/compiler/sem.nim b/compiler/sem.nim
index d23dd1543..041524f84 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
   result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
+  proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower
+
   # like newSymS, but considers gensym'ed symbols
   if n.kind == nkSym:
     # and sfGenSym in n.sym.flags:
     result = n.sym
-    internalAssert result.kind == kind
+    if result.kind != kind:
+      localError(n.info, "cannot use symbol of kind '" &
+                 $result.kind & "' as a '" & $kind & "'")
     # when there is a nested proc inside a template, semtmpl
     # will assign a wrong owner during the first pass over the
     # template; we must fix it here: see #909
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 571504c3a..e2ff16c6e 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -305,8 +305,22 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
     if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
       result.typ = newTypeS(x.fauxMatch, c)
     return
-  if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
-    finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
+  let gp = finalCallee.ast.sons[genericParamsPos]
+  if gp.kind != nkEmpty:
+    if x.calleeSym.kind notin {skMacro, skTemplate}:
+      finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
+    else:
+      # For macros and templates, the resolved generic params
+      # are added as normal params.
+      for s in instantiateGenericParamList(c, gp, x.bindings):
+        case s.kind
+          of skConst:
+            x.call.add s.ast
+          of skType:
+            x.call.add newSymNode(s, n.info)
+          else:
+            internalAssert false
+  
   result = x.call
   instGenericConvertersSons(c, result, x)
   result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index aaab49a10..af671f6e0 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -177,6 +177,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType =
   else:
     return nil
 
+proc createDestructorCall(c: PContext, s: PSym): PNode =
+  let varTyp = s.typ
+  if varTyp == nil or sfGlobal in s.flags: return
+  let destructableT = instantiateDestructor(c, varTyp)
+  if destructableT != nil:
+    let call = semStmt(c, newNode(nkCall, s.info, @[
+      useSym(destructableT.destructor), useSym(s)]))
+    result = newNode(nkDefer, s.info, @[call])
+
 proc insertDestructors(c: PContext,
                        varSection: PNode): tuple[outer, inner: PNode] =
   # Accepts a var or let section.
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fba64776d..bb3ec9df0 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -597,8 +597,8 @@ proc skipObjConv(n: PNode): PNode =
   of nkObjUpConv, nkObjDownConv: result = n.sons[0]
   else: result = n
 
-proc isAssignable(c: PContext, n: PNode): TAssignableResult =
-  result = parampatterns.isAssignable(c.p.owner, n)
+proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
+  result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
 
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
   if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
@@ -1700,7 +1700,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   case s.magic # magics that need special treatment
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
@@ -1720,6 +1720,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
     result.typ = getSysType(tyString)
   of mParallel:
+    if not experimentalMode(c):
+      localError(n.info, "use the {.experimental.} pragma to enable 'parallel'")
     result = setMs(n, s)
     var x = n.lastSon
     if x.kind == nkDo: x = x.sons[bodyPos]
@@ -2259,7 +2261,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkStaticStmt:
     result = semStaticStmt(c, n)
   of nkDefer:
-    localError(n.info, errGenerated, "'defer' not allowed in this context")
+    n.sons[0] = semExpr(c, n.sons[0])
+    if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
+      localError(n.info, errGenerated, "'defer' takes a 'void' expression")
+    #localError(n.info, errGenerated, "'defer' not allowed in this context")
   else:
     localError(n.info, errInvalidExpressionX,
                renderTree(n, {renderNoComments}))
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 729222220..2ab43a9c9 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -307,12 +307,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
   of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
   of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
-  of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n)
-  of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n)
-  of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n)
-  of mAddI: result = newIntNodeT(getInt(a) + getInt(b), n)
-  of mSubI: result = newIntNodeT(getInt(a) - getInt(b), n)
-  of mMulI: result = newIntNodeT(getInt(a) * getInt(b), n)
+  of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
+  of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
+  of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
+  of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
+  of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
+  of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
   of mMinI:
     if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
     else: result = newIntNodeT(getInt(a), n)
@@ -338,11 +338,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mDivI:
     let y = getInt(b)
     if y != 0:
-      result = newIntNodeT(getInt(a) div y, n)
+      result = newIntNodeT(`|div|`(getInt(a), y), n)
   of mModI:
     let y = getInt(b)
     if y != 0:
-      result = newIntNodeT(getInt(a) mod y, n)
+      result = newIntNodeT(`|mod|`(getInt(a), y), n)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
   of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
   of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index db910600b..e3b598919 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -39,9 +39,9 @@ type
 proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode
 
-proc semGenericStmtScope(c: PContext, n: PNode, 
+proc semGenericStmtScope(c: PContext, n: PNode,
                          flags: TSemGenericFlags,
-                         ctx: var GenericCtx): PNode = 
+                         ctx: var GenericCtx): PNode =
   openScope(c)
   result = semGenericStmt(c, n, flags, ctx)
   closeScope(c)
@@ -57,7 +57,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of skProc, skMethod, skIterators, skConverter:
+  of skProc, skMethod, skIterators, skConverter, skModule:
     result = symChoice(c, n, s, scOpen)
   of skTemplate:
     if macroToExpand(s):
@@ -73,7 +73,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
       result = semGenericStmt(c, result, {}, ctx)
     else:
       result = symChoice(c, n, s, scOpen)
-  of skGenericParam: 
+  of skGenericParam:
     if s.typ != nil and s.typ.kind == tyStatic:
       if s.typ.n != nil:
         result = s.typ.n
@@ -85,18 +85,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skParam:
     result = n
     styleCheckUse(n.info, s)
-  of skType: 
+  of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
       result = newSymNodeTypeDesc(s, n.info)
-    else: 
+    else:
       result = n
     styleCheckUse(n.info, s)
   else:
     result = newSymNode(s, n.info)
     styleCheckUse(n.info, s)
 
-proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
+proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var GenericCtx): PNode =
   result = n
   let ident = considerQuotedIdent(n)
@@ -118,13 +118,13 @@ proc newDot(n, b: PNode): PNode =
   result.add(n.sons[0])
   result.add(b)
 
-proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, 
+proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
                  ctx: var GenericCtx; isMacro: var bool): PNode =
   assert n.kind == nkDotExpr
   semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared} else: {}
-  
+
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
     result = semGenericStmtSymbol(c, n, s, ctx)
@@ -141,18 +141,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
       elif s.name.id in ctx.toMixin:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
-        let sym = semGenericStmtSymbol(c, n, s, ctx)
-        if sym.kind == nkSym:
-          result = newDot(result, symChoice(c, n, s, scForceOpen))
+        let syms = semGenericStmtSymbol(c, n, s, ctx)
+        if syms.kind == nkSym:
+          let choice = symChoice(c, n, s, scForceOpen)
+          choice.kind = nkClosedSymChoice
+          result = newDot(result, choice)
         else:
-          result = newDot(result, sym)
+          result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   let s = newSymS(skUnknown, getIdentNode(n), c)
   addPrelimDecl(c, s)
   styleCheckDef(n.info, s, kind)
 
-proc semGenericStmt(c: PContext, n: PNode, 
+proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
   result = n
   #if gCmd == cmdIdeTools: suggestStmt(c, n)
@@ -181,16 +183,16 @@ proc semGenericStmt(c: PContext, n: PNode,
     result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
   of nkMixinStmt:
     result = semMixinStmt(c, n, ctx.toMixin)
-  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: 
+  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
     let fn = n.sons[0]
     var s = qualifiedLookUp(c, fn, {})
     if s == nil and withinMixin notin flags and
-        fn.kind in {nkIdent, nkAccQuoted} and 
+        fn.kind in {nkIdent, nkAccQuoted} and
         considerQuotedIdent(fn).id notin ctx.toMixin:
       localError(n.info, errUndeclaredIdentifier, fn.renderTree)
-    
+
     var first = 0
     var mixinContext = false
     if s != nil:
@@ -220,19 +222,19 @@ proc semGenericStmt(c: PContext, n: PNode,
         # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent
         # the famous "undeclared identifier: it" bug:
         mixinContext = true
-      of skUnknown, skParam: 
+      of skUnknown, skParam:
         # Leave it as an identifier.
         discard
-      of skProc, skMethod, skIterators, skConverter:
+      of skProc, skMethod, skIterators, skConverter, skModule:
         result.sons[0] = symChoice(c, fn, s, scOption)
         first = 1
       of skGenericParam:
         result.sons[0] = newSymNodeTypeDesc(s, fn.info)
         styleCheckUse(fn.info, s)
         first = 1
-      of skType: 
+      of skType:
         # bad hack for generics:
-        if (s.typ != nil) and (s.typ.kind != tyGenericParam): 
+        if (s.typ != nil) and (s.typ.kind != tyGenericParam):
           result.sons[0] = newSymNodeTypeDesc(s, fn.info)
           styleCheckUse(fn.info, s)
           first = 1
@@ -244,34 +246,34 @@ proc semGenericStmt(c: PContext, n: PNode,
       result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
       first = 1
     # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
-    # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which 
+    # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
     # is not exported and yet the generic 'threadProcWrapper' works correctly.
     let flags = if mixinContext: flags+{withinMixin} else: flags
     for i in countup(first, sonsLen(result) - 1):
       result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
-  of nkIfStmt: 
-    for i in countup(0, sonsLen(n)-1): 
+  of nkIfStmt:
+    for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx)
   of nkWhenStmt:
     for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx)
-  of nkWhileStmt: 
+  of nkWhileStmt:
     openScope(c)
-    for i in countup(0, sonsLen(n)-1): 
+    for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
     closeScope(c)
-  of nkCaseStmt: 
+  of nkCaseStmt:
     openScope(c)
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1): 
+    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): 
+      for j in countup(0, L-2):
         a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
       a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
     closeScope(c)
-  of nkForStmt, nkParForStmt: 
+  of nkForStmt, nkParForStmt:
     var L = sonsLen(n)
     openScope(c)
     n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
@@ -279,27 +281,27 @@ proc semGenericStmt(c: PContext, n: PNode,
       addTempDecl(c, n.sons[i], skForVar)
     n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
     closeScope(c)
-  of nkBlockStmt, nkBlockExpr, nkBlockType: 
+  of nkBlockStmt, nkBlockExpr, nkBlockType:
     checkSonsLen(n, 2)
     openScope(c)
-    if n.sons[0].kind != nkEmpty: 
+    if n.sons[0].kind != nkEmpty:
       addTempDecl(c, n.sons[0], skLabel)
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
     closeScope(c)
-  of nkTryStmt: 
+  of nkTryStmt:
     checkMinSonsLen(n, 2)
     n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1): 
+    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): 
+      for j in countup(0, L-2):
         a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
-  of nkVarSection, nkLetSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkVarSection, nkLetSection:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
@@ -307,49 +309,49 @@ proc semGenericStmt(c: PContext, n: PNode,
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
       for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skVar)
-  of nkGenericParams: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkGenericParams:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
       if (a.kind != nkIdentDefs): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
-      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) 
-      # do not perform symbol lookup for default expressions 
-      for j in countup(0, L-3): 
+      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
+      # do not perform symbol lookup for default expressions
+      for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skType)
-  of nkConstSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkConstSection:
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkConstDef): illFormedAst(a)
       checkSonsLen(a, 3)
       addTempDecl(c, getIdentNode(a.sons[0]), skConst)
       a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
       a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
   of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a)
       checkSonsLen(a, 3)
       addTempDecl(c, getIdentNode(a.sons[0]), skType)
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
+      if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a)
       checkSonsLen(a, 3)
-      if a.sons[1].kind != nkEmpty: 
+      if a.sons[1].kind != nkEmpty:
         openScope(c)
         a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
         closeScope(c)
-      else: 
+      else:
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
-  of nkEnumTy: 
+  of nkEnumTy:
     if n.sonsLen > 0:
-      if n.sons[0].kind != nkEmpty: 
+      if n.sons[0].kind != nkEmpty:
         n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-      for i in countup(1, sonsLen(n) - 1): 
+      for i in countup(1, sonsLen(n) - 1):
         var a: PNode
         case n.sons[i].kind
         of nkEnumFieldDef: a = n.sons[i].sons[0]
@@ -360,26 +362,26 @@ proc semGenericStmt(c: PContext, n: PNode,
     discard
   of nkFormalParams:
     checkMinSonsLen(n, 1)
-    if n.sons[0].kind != nkEmpty: 
+    if n.sons[0].kind != nkEmpty:
       n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-    for i in countup(1, sonsLen(n) - 1): 
+    for i in countup(1, sonsLen(n) - 1):
       var a = n.sons[i]
       if (a.kind != nkIdentDefs): illFormedAst(a)
       checkMinSonsLen(a, 3)
       var L = sonsLen(a)
       a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
-      for j in countup(0, L-3): 
+      for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skParam)
-  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, 
-     nkIteratorDef, nkLambdaKinds: 
+  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
+     nkIteratorDef, nkLambdaKinds:
     checkSonsLen(n, bodyPos + 1)
     if n.sons[namePos].kind != nkEmpty:
       addTempDecl(c, getIdentNode(n.sons[0]), skProc)
     openScope(c)
-    n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], 
+    n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
                                               flags, ctx)
-    if n.sons[paramsPos].kind != nkEmpty: 
+    if n.sons[paramsPos].kind != nkEmpty:
       if n.sons[paramsPos].sons[0].kind != nkEmpty:
         addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
@@ -394,7 +396,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     checkMinSonsLen(n, 2)
     result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
   else:
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n) - 1):
       result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
 
 proc semGenericStmt(c: PContext, n: PNode): PNode =
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index b2aef63a8..370990326 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -10,14 +10,10 @@
 # This module implements the instantiation of generic procs.
 # included from sem.nim
 
-proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
-                                 entry: var TInstantiation) =
-  if n.kind != nkGenericParams:
-    internalError(n.info, "instantiateGenericParamList; no generic params")
-  newSeq(entry.concreteTypes, n.len)
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+  internalAssert n.kind == nkGenericParams
   for i, a in n.pairs:
-    if a.kind != nkSym:
-      internalError(a.info, "instantiateGenericParamList; no symbol")
+    internalAssert a.kind == nkSym
     var q = a.sym
     if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
       continue
@@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
       #t = ReplaceTypeVarsT(cl, t)
     s.typ = t
     if t.kind == tyStatic: s.ast = t.n
-    addDecl(c, s)
-    entry.concreteTypes[i] = t
+    yield s
 
 proc sameInstantiation(a, b: TInstantiation): bool =
   if a.concreteTypes.len == b.concreteTypes.len:
@@ -217,7 +212,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   ## The `pt` parameter is a type-unsafe mapping table used to link generic
   ## parameters to their concrete types within the generic instance.
   # no need to instantiate generic templates/macros:
-  if fn.kind in {skTemplate, skMacro}: return fn
+  internalAssert fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
   if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
   inc(c.instCounter)
@@ -226,20 +221,27 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
   c.friendModules.add(getModule(fn))
-  #let oldScope = c.currentScope
-  #c.currentScope = fn.scope
+  let oldScope = c.currentScope
+  while not isTopLevel(c): c.currentScope = c.currentScope.parent
   result = copySym(fn, false)
   incl(result.flags, sfFromGeneric)
   result.owner = fn
   result.ast = n
   pushOwner(result)
+
   openScope(c)
-  internalAssert n.sons[genericParamsPos].kind != nkEmpty
+  let gp = n.sons[genericParamsPos]
+  internalAssert gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
   pushInfoContext(info)
   var entry = TInstantiation.new
   entry.sym = result
-  instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
+  newSeq(entry.concreteTypes, gp.len)
+  var i = 0
+  for s in instantiateGenericParamList(c, gp, pt):
+    addDecl(c, s)
+    entry.concreteTypes[i] = s.typ
+    inc i
   pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
   n.sons[genericParamsPos] = ast.emptyNode
@@ -264,7 +266,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popInfoContext()
   closeScope(c)           # close scope for parameters
   popOwner()
-  #c.currentScope = oldScope
+  c.currentScope = oldScope
   discard c.friendModules.pop()
   dec(c.instCounter)
   if result.kind == skMethod: finishMethod(c, result)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 0a7846f1d..0afbf1f07 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -10,10 +10,10 @@
 # This include file implements the semantic checking for magics.
 # included from sem.nim
 
-proc semAddr(c: PContext; n: PNode): PNode =
+proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
   result = newNodeI(nkAddr, n.info)
   let x = semExprWithType(c, n)
-  if isAssignable(c, x) notin {arLValue, arLocalLValue}:
+  if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
     localError(n.info, errExprHasNoAddress)
   result.add x
   result.typ = makePtrType(c, x.typ)
@@ -119,7 +119,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 84a09a7e6..ffda6a1bb 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
   else:
     result.add identDefs
 
+proc addDefer(c: PContext; result: var PNode; s: PSym) =
+  let deferDestructorCall = createDestructorCall(c, s)
+  if deferDestructorCall != nil:
+    if result.kind != nkStmtList:
+      let oldResult = result
+      result = newNodeI(nkStmtList, result.info)
+      result.add oldResult
+    result.add deferDestructorCall
+
 proc isDiscardUnderscore(v: PSym): bool =
   if v.name.s == "_":
     v.flags.incl(sfGenSym)
@@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         if def.kind == nkPar: v.ast = def[j]
         v.typ = tup.sons[j]
         b.sons[j] = newSymNode(v)
+      addDefer(c, result, v)
       checkNilable(v)
       if sfCompileTime in v.flags: hasCompileTime = true
   if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
@@ -1210,6 +1220,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   if n.sons[patternPos].kind != nkEmpty:
     c.patterns.add(s)
   if isAnon: result.typ = s.typ
+  if isTopLevel(c) and s.kind != skClosureIterator and
+      s.typ.callConv == ccClosure:
+    message(s.info, warnDeprecated, "top level '.closure' calling convention")
 
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
@@ -1371,7 +1384,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
   for i in countup(0, length - 1):
     let k = n.sons[i].kind
     case k
-    of nkFinally, nkExceptBranch, nkDefer:
+    of nkFinally, nkExceptBranch:
       # stand-alone finally and except blocks are
       # transformed into regular try blocks:
       #
@@ -1424,21 +1437,13 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
         n.typ = n.sons[i].typ
         if not isEmptyType(n.typ): n.kind = nkStmtListExpr
       case n.sons[i].kind
-      of nkVarSection, nkLetSection:
-        let (outer, inner) = insertDestructors(c, n.sons[i])
-        if outer != nil:
-          n.sons[i] = outer
-          var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
-          inner.addSon(semStmtList(c, rest, flags))
-          n.sons.setLen(i+1)
-          return
       of LastBlockStmts:
         for j in countup(i + 1, length - 1):
           case n.sons[j].kind
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: discard
-  if result.len == 1:
+  if result.len == 1 and result.sons[0].kind != nkDefer:
     result = result.sons[0]
   when defined(nimfix):
     if result.kind == nkCommentStmt and not result.comment.isNil and
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index a138981b7..4d1eae48f 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -184,10 +184,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
   else:
     let ident = getIdentNode(c, n)
     if not isTemplParam(c, ident):
-      let local = newGenSym(k, ident, c)
-      addPrelimDecl(c.c, local)
-      styleCheckDef(n.info, local)
-      replaceIdentBySym(n, newSymNode(local, n.info))
+      # fix #2670, consider:
+      #
+      # when b:
+      #    var a = "hi"
+      # else:
+      #    var a = 5
+      # echo a
+      #
+      # We need to ensure that both 'a' produce the same gensym'ed symbol.
+      # So we need only check the *current* scope.
+      let s = localSearchInScope(c.c, considerQuotedIdent ident)
+      if s != nil and s.owner == c.owner and sfGenSym in s.flags:
+        styleCheckUse(n.info, s)
+        replaceIdentBySym(n, newSymNode(s, n.info))
+      else:
+        let local = newGenSym(k, ident, c)
+        addPrelimDecl(c.c, local)
+        styleCheckDef(n.info, local)
+        replaceIdentBySym(n, newSymNode(local, n.info))
     else:
       replaceIdentBySym(n, ident)
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index b518f0fb9..5ae3d16c0 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if n.sonsLen == 0: return newConstraint(c, tyEnum)
+  elif n.sonsLen == 1:
+    # don't create an empty tyEnum; fixes #3052
+    return errorType(c)
   var
     counter, x: BiggestInt
     e: PSym
@@ -505,8 +508,9 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
   if not isOrdinalType(typ):
     localError(n.info, errSelectorMustBeOrdinal)
-  elif firstOrd(typ) < 0:
-    localError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s)
+  elif firstOrd(typ) != 0:
+    localError(n.info, errGenerated, "low(" & $a.sons[0].sym.name.s &
+                                     ") must be 0 for discriminant")
   elif lengthOrd(typ) > 0x00007FFF:
     localError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
   var chckCovered = true
diff --git a/compiler/transf.nim b/compiler/transf.nim
index dddbd51c4..5c7472a39 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -16,6 +16,7 @@
 # * converts "continue" to "break"; disambiguates "break"
 # * introduces method dispatchers
 # * performs lambda lifting for closure support
+# * transforms 'defer' into a 'try finally' statement
 
 import
   intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
@@ -44,6 +45,7 @@ type
     inlining: int            # > 0 if we are in inlining context (copy vars)
     nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
+    deferDetected: bool
   PTransf = ref TTransfContext
 
 proc newTransNode(a: PNode): PTransNode {.inline.} =
@@ -680,6 +682,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode =
       result = n
 
 proc transform(c: PTransf, n: PNode): PTransNode =
+  when false:
+    var oldDeferAnchor: PNode
+    if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
+                  nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally,
+                  nkBlockStmt, nkBlockExpr}:
+      oldDeferAnchor = c.deferAnchor
+      c.deferAnchor = n
+
   case n.kind
   of nkSym:
     result = transformSym(c, n)
@@ -712,13 +722,36 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     result = transformFor(c, n)
   of nkParForStmt:
     result = transformSons(c, n)
-  of nkCaseStmt: result = transformCase(c, n)
+  of nkCaseStmt:
+    result = transformCase(c, n)
+  of nkWhileStmt: result = transformWhile(c, n)
+  of nkBlockStmt, nkBlockExpr:
+    result = transformBlock(c, n)
+  of nkDefer:
+    c.deferDetected = true
+    result = transformSons(c, n)
+    when false:
+      let deferPart = newNodeI(nkFinally, n.info)
+      deferPart.add n.sons[0]
+      let tryStmt = newNodeI(nkTryStmt, n.info)
+      if c.deferAnchor.isNil:
+        tryStmt.add c.root
+        c.root = tryStmt
+        result = PTransNode(tryStmt)
+      else:
+        # modify the corresponding *action*, don't rely on nkStmtList:
+        let L = c.deferAnchor.len-1
+        tryStmt.add c.deferAnchor.sons[L]
+        c.deferAnchor.sons[L] = tryStmt
+        result = newTransNode(nkCommentStmt, n.info, 0)
+      tryStmt.addSon(deferPart)
+      # disable the original 'defer' statement:
+      n.kind = nkCommentStmt
   of nkContinueStmt:
     result = PTransNode(newNodeI(nkBreakStmt, n.info))
     var labl = c.contSyms[c.contSyms.high]
     add(result, PTransNode(newSymNode(labl)))
   of nkBreakStmt: result = transformBreak(c, n)
-  of nkWhileStmt: result = transformWhile(c, n)
   of nkCallKinds:
     result = transformCall(c, n)
   of nkAddr, nkHiddenAddr:
@@ -754,8 +787,6 @@ proc transform(c: PTransf, n: PNode): PTransNode =
       result = transformYield(c, n)
     else:
       result = transformSons(c, n)
-  of nkBlockStmt, nkBlockExpr:
-    result = transformBlock(c, n)
   of nkIdentDefs, nkConstDef:
     result = transformSons(c, n)
     # XXX comment handling really sucks:
@@ -764,6 +795,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   of nkClosure: return PTransNode(n)
   else:
     result = transformSons(c, n)
+  when false:
+    if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
   var cnst = getConstExpr(c.module, PNode(result))
   # we inline constants if they are not complex constants:
   if cnst != nil and not dontInlineConstant(n, cnst):
@@ -785,12 +818,52 @@ proc openTransf(module: PSym, filename: string): PTransf =
   result.breakSyms = @[]
   result.module = module
 
+proc flattenStmts(n: PNode) =
+  var goOn = true
+  while goOn:
+    goOn = false
+    for i in 0..<n.len:
+      let it = n[i]
+      if it.kind in {nkStmtList, nkStmtListExpr}:
+        n.sons[i..i] = it.sons[0..<it.len]
+        goOn = true
+
+proc liftDeferAux(n: PNode) =
+  if n.kind in {nkStmtList, nkStmtListExpr}:
+    flattenStmts(n)
+    var goOn = true
+    while goOn:
+      goOn = false
+      let last = n.len-1
+      for i in 0..last:
+        if n.sons[i].kind == nkDefer:
+          let deferPart = newNodeI(nkFinally, n.sons[i].info)
+          deferPart.add n.sons[i].sons[0]
+          var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
+          var body = newNodeI(n.kind, n.sons[i].info)
+          if i < last:
+            body.sons = n.sons[(i+1)..last]
+          tryStmt.addSon(body)
+          tryStmt.addSon(deferPart)
+          n.sons[i] = tryStmt
+          n.sons.setLen(i+1)
+          n.typ = n.sons[i].typ
+          goOn = true
+          break
+  for i in 0..n.safeLen-1:
+    liftDeferAux(n.sons[i])
+
+template liftDefer(c, root) =
+  if c.deferDetected:
+    liftDeferAux(root)
+
 proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
   if nfTransf in n.flags or prc.kind in {skTemplate}:
     result = n
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, prc)
+    liftDefer(c, result)
     result = liftLambdas(prc, result)
     #if prc.kind == skClosureIterator:
     #  result = lambdalifting.liftIterator(prc, result)
@@ -805,6 +878,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, module)
+    liftDefer(c, result)
     result = liftLambdasForTopLevel(module, result)
     incl(result.flags, nfTransf)
     when useEffectSystem: trackTopLevelStmt(module, result)
@@ -815,4 +889,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
   else:
     var c = openTransf(module, "")
     result = processTransf(c, n, module)
+    liftDefer(c, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 40d273ceb..57ed8397c 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -120,10 +120,10 @@ template decodeBx(k: expr) {.immediate, dirty.} =
 template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b)
 # XXX fix minor 'shallowCopy' overloading bug in compiler
 
-proc createStrKeepNode(x: var TFullReg) =
+proc createStrKeepNode(x: var TFullReg; keepNode=true) =
   if x.node.isNil:
     x.node = newNode(nkStrLit)
-  elif x.node.kind == nkNilLit:
+  elif x.node.kind == nkNilLit and keepNode:
     when defined(useNodeIds):
       let id = x.node.id
     system.reset(x.node[])
@@ -385,6 +385,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     #if c.traceActive:
     #  echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
     #  message(c.debug[pc], warnUser, "Trace")
+
     case instr.opcode
     of opcEof: return regs[ra]
     of opcRet:
@@ -407,8 +408,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkInt)
       regs[ra].intVal = regs[rb].intVal
     of opcAsgnStr:
-      decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      decodeBC(rkNode)
+      createStrKeepNode regs[ra], rc != 0
       regs[ra].node.strVal = regs[rb].node.strVal
     of opcAsgnFloat:
       decodeB(rkFloat)
@@ -431,7 +432,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         assert regs[rb].kind == rkNode
         let nb = regs[rb].node
         case nb.kind
-        of nkCharLit..nkInt64Lit:
+        of nkCharLit..nkUInt64Lit:
           ensureKind(rkInt)
           regs[ra].intVal = nb.intVal
         of nkFloatLit..nkFloat64Lit:
@@ -509,8 +510,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       of rkNode:
         if regs[rb].node.kind == nkNilLit:
           stackTrace(c, tos, pc, errNilAccess)
-        assert regs[rb].node.kind == nkRefTy
-        regs[ra].node = regs[rb].node.sons[0]
+        if regs[rb].node.kind == nkRefTy:
+          regs[ra].node = regs[rb].node.sons[0]
+        else:
+          stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'")
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
@@ -1404,7 +1407,7 @@ include vmops
 # storing&loading the 'globals' environment to get what a component system
 # requires.
 var
-  globalCtx: PCtx
+  globalCtx*: PCtx
 
 proc setupGlobalCtx(module: PSym) =
   if globalCtx.isNil:
@@ -1464,12 +1467,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
 proc setupCompileTimeVar*(module: PSym, n: PNode) =
   discard evalConstExprAux(module, nil, n, emStaticStmt)
 
-proc setupMacroParam(x: PNode): PNode =
-  result = x
-  if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
-  result = canonValue(result)
-  result.flags.incl nfIsRef
-  result.typ = x.typ
+proc setupMacroParam(x: PNode, typ: PType): TFullReg =
+  case typ.kind
+  of tyStatic:
+    putIntoReg(result, x)
+  of tyTypeDesc:
+    putIntoReg(result, x)
+  else:
+    result.kind = rkNode
+    var n = x
+    if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
+    n = n.canonValue
+    n.flags.incl nfIsRef
+    n.typ = x.typ
+    result.node = n
 
 var evalMacroCounter: int
 
@@ -1505,10 +1516,17 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   # return value:
   tos.slots[0].kind = rkNode
   tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
+
   # setup parameters:
-  for i in 1 .. < min(tos.slots.len, L):
-    tos.slots[i].kind = rkNode
-    tos.slots[i].node = setupMacroParam(n.sons[i])
+  for i in 1.. <sym.typ.len:
+    tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
+
+  if sfImmediate notin sym.flags:
+    let gp = sym.ast[genericParamsPos]
+    for i in 0 .. <gp.len:
+      let idx = sym.typ.len + i
+      tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+
   # temporary storage:
   #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 73016108d..2cc4a107b 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -30,7 +30,7 @@ proc opGorge*(cmd, input, cache: string): string =
       return
     var readSuccessful = false
     try:
-      var p = startProcess(cmd, options={poEvalCommand})
+      var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout})
       if input.len != 0:
         p.inputStream.write(input)
         p.inputStream.close()
@@ -41,7 +41,7 @@ proc opGorge*(cmd, input, cache: string): string =
       if not readSuccessful: result = ""
   else:
     try:
-      var p = startProcess(cmd, options={poEvalCommand})
+      var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout})
       if input.len != 0:
         p.inputStream.write(input)
         p.inputStream.close()
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 7abcbdb92..919c38e08 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -74,8 +74,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
       result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
                   i+x.regBx-wordExcess)
     elif opc in {opcLdConst, opcAsgnConst}:
-      result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
-        c.constants[x.regBx-wordExcess].renderTree)
+      let idx = x.regBx-wordExcess
+      result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
+        c.constants[idx].renderTree, $idx)
     elif opc in {opcMarshalLoad, opcMarshalStore}:
       let y = c.code[i+1]
       result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
@@ -172,7 +173,8 @@ const
 proc bestEffort(c: PCtx): TLineInfo =
   (if c.prc == nil: c.module.info else: c.prc.sym.info)
 
-proc getTemp(cc: PCtx; typ: PType): TRegister =
+proc getTemp(cc: PCtx; tt: PType): TRegister =
+  let typ = tt.skipTypesOrNil({tyStatic})
   let c = cc.prc
   # we prefer the same slot kind here for efficiency. Unfortunately for
   # discardable return types we may not know the desired type. This can happen
@@ -184,7 +186,7 @@ proc getTemp(cc: PCtx; typ: PType): TRegister =
       return TRegister(i)
 
   # if register pressure is high, we re-use more aggressively:
-  if c.maxSlots >= HighRegisterPressure:
+  if c.maxSlots >= HighRegisterPressure and false:
     for i in 0 .. c.maxSlots-1:
       if not c.slots[i].inUse:
         c.slots[i] = (inUse: true, kind: k)
@@ -706,7 +708,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
   c.gABx(n, opc, 0, genType(c, n.typ))
-  c.gABx(n, opc, 0, genType(c, arg.typ))
+  c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
   c.freeTemp(tmp)
 
 proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@@ -1073,6 +1075,7 @@ const
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
 
 proc fitsRegister*(t: PType): bool =
+  assert t != nil
   t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
     tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
 
@@ -1103,6 +1106,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
   # nkAddr we must not use 'unneededIndirection', but for deref we use it.
   if not isAddr and unneededIndirection(n.sons[0]):
     gen(c, n.sons[0], dest, newflags)
+    if gfAddrOf notin flags and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
   elif isAddr and isGlobal(n.sons[0]):
     gen(c, n.sons[0], dest, flags+{gfAddrOf})
   else:
@@ -1110,6 +1115,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
     if dest < 0: dest = c.getTemp(n.typ)
     if not isAddr:
       gABC(c, n, opc, dest, tmp)
+      assert n.typ != nil
       if gfAddrOf notin flags and fitsRegister(n.typ):
         c.gABC(n, opcNodeToReg, dest, dest)
     elif c.prc.slots[tmp].kind >= slotTempUnknown:
@@ -1143,7 +1149,7 @@ proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
 proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
   let tmp = c.genx(ri)
   assert dest >= 0
-  gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
+  gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy))
   c.freeTemp(tmp)
 
 proc setSlot(c: PCtx; v: PSym) =
@@ -1177,7 +1183,10 @@ proc checkCanEval(c: PCtx; n: PNode) =
   let s = n.sym
   if {sfCompileTime, sfGlobal} <= s.flags: return
   if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
-      not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
+      not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
+    cannotEval(n)
+  elif s.kind in {skProc, skConverter, skMethod,
+                  skIterator, skClosureIterator} and sfForward in s.flags:
     cannotEval(n)
 
 proc isTemp(c: PCtx; dest: TDest): bool =
@@ -1194,9 +1203,10 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
   # opcLdObj et al really means "load address". We sometimes have to create a
   # copy in order to not introduce false aliasing:
   # mylocal = a.b  # needs a copy of the data!
+  assert n.typ != nil
   if needsAdditionalCopy(n):
     var cc = c.getTemp(n.typ)
-    c.gABC(n, whichAsgnOpc(n), cc, value)
+    c.gABC(n, whichAsgnOpc(n), cc, value, 0)
     c.gABC(n, opc, dest, idx, cc)
     c.freeTemp(cc)
   else:
@@ -1241,10 +1251,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
       internalAssert s.position > 0 or (s.position == 0 and
                                         s.kind in {skParam,skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
+      assert le.typ != nil
       if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}:
         var cc = c.getTemp(le.typ)
         gen(c, ri, cc)
-        c.gABC(le, whichAsgnOpc(le), dest, cc)
+        c.gABC(le, whichAsgnOpc(le), dest, cc, 1)
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
@@ -1303,6 +1314,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       if sfImportc in s.flags: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
+    assert s.typ != nil
     if gfAddrOf notin flags and fitsRegister(s.typ):
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
@@ -1426,6 +1438,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
     globalError(info, "cannot create null element for: " & $t.kind)
 
 proc ldNullOpcode(t: PType): TOpcode =
+  assert t != nil
   if fitsRegister(t): opcLdNullReg else: opcLdNull
 
 proc genVarSection(c: PCtx; n: PNode) =
@@ -1453,7 +1466,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind != nkEmpty:
           let tmp = c.genx(a.sons[0], {gfAddrOf})
           let val = c.genx(a.sons[2])
-          c.preventFalseAlias(a, opcWrDeref, tmp, 0, val)
+          c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else:
@@ -1461,13 +1474,15 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind == nkEmpty:
           c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
         else:
+          assert s.typ != nil
           if not fitsRegister(s.typ):
             c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
           let le = a.sons[0]
+          assert le.typ != nil
           if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}:
             var cc = c.getTemp(le.typ)
             gen(c, a.sons[2], cc)
-            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc)
+            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1)
             c.freeTemp(cc)
           else:
             gen(c, a.sons[2], s.position.TRegister)
@@ -1608,6 +1623,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
         c.gABx(n, opcLdConst, dest, lit)
     of skType:
       genTypeLit(c, s.typ, dest)
+    of skGenericParam:
+      if c.prc.sym.kind == skMacro:
+        genRdVar(c, n, dest, flags)
+      else:
+        internalError(n.info, "cannot generate code for: " & s.name.s)
     else:
       globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
@@ -1694,7 +1714,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     c.freeTemp(tmp1)
     c.freeTemp(tmp2)
     if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+      gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1)
       c.freeTemp(tmp0)
     else:
       dest = tmp0
@@ -1758,6 +1778,14 @@ proc finalJumpTarget(c: PCtx; pc, diff: int) =
   c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
                 uint32(diff+wordExcess) shl 16'u32).TInstr
 
+proc genGenericParams(c: PCtx; gp: PNode) =
+  var base = c.prc.maxSlots
+  for i in 0.. <gp.len:
+    var param = gp.sons[i].sym
+    param.position = base + i # XXX: fix this earlier; make it consistent with templates
+    c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
+  c.prc.maxSlots = base + gp.len
+
 proc optimizeJumps(c: PCtx; start: int) =
   const maxIterations = 10
   for i in start .. <c.code.len:
@@ -1822,6 +1850,13 @@ proc genProc(c: PCtx; s: PSym): int =
     c.prc = p
     # iterate over the parameters and allocate space for them:
     genParams(c, s.typ.n)
+
+    # allocate additional space for any generically bound parameters
+    if s.kind == skMacro and
+       sfImmediate notin s.flags and
+       s.ast[genericParamsPos].kind != nkEmpty:
+      genGenericParams(c, s.ast[genericParamsPos])
+
     if tfCapturesEnv in s.typ.flags:
       #let env = s.ast.sons[paramsPos].lastSon.sym
       #assert env.position == 2
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 6ec5f6044..5dd27feda 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -17,7 +17,7 @@ template setX(k, field) {.immediate, dirty.} =
 
 proc setResult*(a: VmArgs; v: BiggestInt) = setX(rkInt, intVal)
 proc setResult*(a: VmArgs; v: BiggestFloat) = setX(rkFloat, floatVal)
-proc setResult*(a: VmArgs; v: bool) = 
+proc setResult*(a: VmArgs; v: bool) =
   let v = v.ord
   setX(rkInt, intVal)
 
@@ -30,6 +30,16 @@ proc setResult*(a: VmArgs; v: string) =
   s[a.ra].node = newNode(nkStrLit)
   s[a.ra].node.strVal = v
 
+proc setResult*(a: VmArgs; v: seq[string]) =
+  var s: seq[TFullReg]
+  move(s, cast[seq[TFullReg]](a.slots))
+  if s[a.ra].kind != rkNode:
+    myreset(s[a.ra])
+    s[a.ra].kind = rkNode
+  var n = newNode(nkBracket)
+  for x in v: n.add newStrNode(nkStrLit, x)
+  s[a.ra].node = n
+
 template getX(k, field) {.immediate, dirty.} =
   doAssert i < a.rc-1
   let s = cast[seq[TFullReg]](a.slots)