summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim9
-rw-r--r--compiler/astalgo.nim1
-rw-r--r--compiler/ccgexprs.nim11
-rw-r--r--compiler/ccgstmts.nim11
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtypes.nim8
-rw-r--r--compiler/ccgutils.nim2
-rw-r--r--compiler/cgen.nim43
-rw-r--r--compiler/cgendata.nim10
-rw-r--r--compiler/cgmeth.nim65
-rw-r--r--compiler/commands.nim14
-rw-r--r--compiler/compilerlog.nim9
-rw-r--r--compiler/docgen2.nim4
-rw-r--r--compiler/extccomp.nim51
-rw-r--r--compiler/jsgen.nim10
-rw-r--r--compiler/lambdalifting.nim2
-rw-r--r--compiler/lists.nim117
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/main.nim8
-rw-r--r--compiler/modulegraphs.nim14
-rw-r--r--compiler/msgs.nim9
-rw-r--r--compiler/nimblecmd.nim8
-rw-r--r--compiler/nimeval.nim4
-rw-r--r--compiler/nimfix/nimfix.nim6
-rw-r--r--compiler/options.nim32
-rw-r--r--compiler/passaux.nim18
-rw-r--r--compiler/passes.nim24
-rw-r--r--compiler/patterns.nim2
-rw-r--r--compiler/plugins/locals/locals.nim2
-rw-r--r--compiler/pragmas.nim63
-rw-r--r--compiler/renderer.nim2
-rw-r--r--compiler/rodwrite.nim2
-rw-r--r--compiler/scriptconfig.nim4
-rw-r--r--compiler/sem.nim25
-rw-r--r--compiler/semasgn.nim7
-rw-r--r--compiler/semcall.nim4
-rw-r--r--compiler/semdata.nim72
-rw-r--r--compiler/semdestruct.nim13
-rw-r--r--compiler/semexprs.nim60
-rw-r--r--compiler/semfields.nim2
-rw-r--r--compiler/semfold.nim9
-rw-r--r--compiler/semgnrc.nim6
-rw-r--r--compiler/seminst.nim15
-rw-r--r--compiler/semmagic.nim2
-rw-r--r--compiler/semstmts.nim195
-rw-r--r--compiler/semtempl.nim22
-rw-r--r--compiler/semtypes.nim25
-rw-r--r--compiler/sigmatch.nim10
-rw-r--r--compiler/suggest.nim28
-rw-r--r--compiler/transf.nim16
-rw-r--r--compiler/trees.nim4
-rw-r--r--compiler/types.nim51
-rw-r--r--compiler/vm.nim5
-rw-r--r--compiler/wordrecg.nim4
54 files changed, 612 insertions, 534 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 8fbec64cf..66fbe577c 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,7 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists,
+  msgs, hashes, nversion, options, strutils, securehash, ropes, idents,
   intsets, idgen
 
 type
@@ -736,13 +736,15 @@ type
 
   TLibKind* = enum
     libHeader, libDynamic
-  TLib* = object of lists.TListEntry # also misused for headers!
+    
+  TLib* = object              # also misused for headers!
     kind*: TLibKind
     generated*: bool          # needed for the backends:
     isOverriden*: bool
     name*: Rope
     path*: PNode              # can be a string literal!
 
+    
   CompilesId* = int ## id that is used for the caching logic within
                     ## ``system.compiles``. See the seminst module.
   TInstantiation* = object
@@ -1541,8 +1543,7 @@ proc skipGenericOwner*(s: PSym): PSym =
   ## Generic instantiations are owned by their originating generic
   ## symbol. This proc skips such owners and goes straight to the owner
   ## of the generic itself (the module or the enclosing proc).
-  result = if s.kind in skProcKinds and {sfGenSym, sfFromGeneric} * s.flags ==
-                                                  {sfFromGeneric}:
+  result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
              s.owner.owner
            else:
              s.owner
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index affbdffd9..226d5ee42 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -388,6 +388,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
              [istr, makeYamlString($n.kind)]
     addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
     if maxRecDepth != 0:
+      addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)])
       case n.kind
       of nkCharLit..nkUInt64Lit:
         addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 95a7beada..e9c78b48b 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -220,7 +220,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
                      optAsgnLoc(src, t, field), newflags)
 
 proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
-                      t: PNode) =
+                      t: PNode, typ: PType) =
   if t == nil: return
   let newflags =
     if src.s == OnStatic:
@@ -232,10 +232,11 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
   case t.kind
   of nkSym:
     let field = t.sym
+    if field.loc.r == nil: fillObjectFields(p.module, typ)
     genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
                      optAsgnLoc(src, field.typ, field.loc.r), newflags)
   of nkRecList:
-    for child in items(t): genOptAsgnObject(p, dest, src, newflags, child)
+    for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
   else: discard
 
 proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
@@ -315,9 +316,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       genGenericAsgn(p, dest, src, flags)
     elif needsComplexAssignment(ty):
       if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4:
-        discard getTypeDesc(p.module, dest.t)
+        discard getTypeDesc(p.module, ty)
         internalAssert ty.n != nil
-        genOptAsgnObject(p, dest, src, flags, ty.n)
+        genOptAsgnObject(p, dest, src, flags, ty.n, ty)
       else:
         genGenericAsgn(p, dest, src, flags)
     else:
@@ -943,7 +944,7 @@ proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
   internalAssert n.kind == nkBracket
-  discard lists.includeStr(p.module.headerFiles, "<stdio.h>")
+  p.module.includeHeader("<stdio.h>")
   var args: Rope = nil
   var a: TLoc
   for i in countup(0, n.len-1):
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 45d675f64..3a861ebd4 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -540,7 +540,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
     # named break?
     assert(t.sons[0].kind == nkSym)
     var sym = t.sons[0].sym
-    assert(sym.loc.k == locOther)
+    doAssert(sym.loc.k == locOther)
     idx = sym.position-1
   else:
     # an unnamed 'break' can only break a loop after 'transf' pass:
@@ -884,7 +884,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   #
   if not isEmptyType(t.typ) and d.k == locNone:
     getTemp(p, t.typ, d)
-  discard lists.includeStr(p.module.headerFiles, "<setjmp.h>")
+  p.module.includeHeader("<setjmp.h>")
   genLineDir(p, t)
   var safePoint = getTempName(p.module)
   if getCompilerProc("Exception") != nil:
@@ -1103,8 +1103,8 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
   genAssignment(p, a, tmp, {})
 
 proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
-  genLineDir(p, e)
   if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
+    genLineDir(p, e)
     genGotoVar(p, e.sons[1])
   elif not fieldDiscriminantCheckNeeded(p, e):
     var a: TLoc
@@ -1114,8 +1114,11 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
       initLocExpr(p, e.sons[0], a)
     if fastAsgn: incl(a.flags, lfNoDeepCopy)
     assert(a.t != nil)
-    loadInto(p, e.sons[0], e.sons[1], a)
+    let ri = e.sons[1]
+    genLineDir(p, ri)
+    loadInto(p, e.sons[0], ri, a)
   else:
+    genLineDir(p, e)
     asgnFieldDiscriminant(p, e)
 
 proc genStmts(p: BProc, t: PNode) =
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index 3efddbf30..505b69eab 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -57,8 +57,8 @@ proc generateThreadLocalStorage(m: BModule) =
 
 proc generateThreadVarsSize(m: BModule) =
   if nimtv != nil:
-    let externc = if gCmd != cmdCompileToCpp and
-                       sfCompileToCpp in m.module.flags: "extern \"C\""
+    let externc = if gCmd == cmdCompileToCpp or
+                       sfCompileToCpp in m.module.flags: "extern \"C\" "
                   else: ""
     addf(m.s[cfsProcs],
       "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index e30fe5598..29d4e23c9 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -24,7 +24,13 @@ proc isKeyword(w: PIdent): bool =
 
 proc mangleField(m: BModule; name: PIdent): string =
   result = mangle(name.s)
-  if isKeyword(name) or m.g.config.cppDefines.contains(result):
+  # fields are tricky to get right and thanks to generic types producing
+  # duplicates we can end up mangling the same field multiple times. However
+  # if we do so, the 'cppDefines' table might be modified in the meantime
+  # meaning we produce inconsistent field names (see bug #5404).
+  # Hence we do not check for ``m.g.config.cppDefines.contains(result)`` here
+  # anymore:
+  if isKeyword(name):
     result.add "_0"
 
 when false:
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index d42f0438f..a00ce3505 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -10,7 +10,7 @@
 # This module declares some helpers for the C code generator.
 
 import
-  ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
+  ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg,
   platform, trees
 
 proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 62ed9ad6e..7fef34a27 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -11,7 +11,7 @@
 
 import
   ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
-  nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
+  nversion, nimsets, msgs, securehash, bitsets, idents, types,
   ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
   condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
   lowerings, semparallel, tables, sets, ndi
@@ -75,12 +75,13 @@ proc isSimpleConst(typ: PType): bool =
 proc useStringh(m: BModule) =
   if includesStringh notin m.flags:
     incl m.flags, includesStringh
-    discard lists.includeStr(m.headerFiles, "<string.h>")
+    m.includeHeader("<string.h>")
 
 proc useHeader(m: BModule, sym: PSym) =
   if lfHeader in sym.loc.flags:
     assert(sym.annex != nil)
-    discard lists.includeStr(m.headerFiles, getStr(sym.annex.path))
+    let str = getStr(sym.annex.path)
+    m.includeHeader(str)
 
 proc cgsym(m: BModule, name: string): Rope
 
@@ -594,15 +595,14 @@ proc cgsym(m: BModule, name: string): Rope =
 
 proc generateHeaders(m: BModule) =
   add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
-  var it = PStrEntry(m.headerFiles.head)
-  while it != nil:
-    if it.data[0] == '#':
-      add(m.s[cfsHeaders], rope(it.data.replace('`', '"') & tnl))
-    elif it.data[0] notin {'\"', '<'}:
-      addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it.data)])
+  
+  for it in m.headerFiles:
+    if it[0] == '#':
+      add(m.s[cfsHeaders], rope(it.replace('`', '"') & tnl))
+    elif it[0] notin {'\"', '<'}:
+      addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
     else:
-      addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)])
-    it = PStrEntry(it.next)
+      addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
   add(m.s[cfsHeaders], "#undef linux" & tnl)
 
 proc initFrame(p: BProc, procname, filename: Rope): Rope =
@@ -715,10 +715,13 @@ proc genProcAux(m: BModule, prc: PSym) =
     add(generatedProc, ~"}$N")
   add(m.s[cfsProcs], generatedProc)
 
-proc crossesCppBoundary(m: BModule; sym: PSym): bool {.inline.} =
-  result = sfCompileToCpp in m.module.flags and
+proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
+  result = (sfCompileToCpp in m.module.flags and
            sfCompileToCpp notin sym.getModule().flags and
-           gCmd != cmdCompileToCpp
+           gCmd != cmdCompileToCpp) or (
+           sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and
+           sym.magic == mNone and
+           gCmd == cmdCompileToCpp)
 
 proc genProcPrototype(m: BModule, sym: PSym) =
   useHeader(m, sym)
@@ -732,7 +735,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
     var header = genProcHeader(m, sym)
     if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
       header = "__declspec(noreturn) " & header
-    if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
+    if sym.typ.callConv != ccInline and requiresExternC(m, sym):
       header = "extern \"C\" " & header
     if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
       header.add(" __attribute__((naked))")
@@ -971,7 +974,7 @@ proc genMainProc(m: BModule) =
     else:
       nimMain = WinNimDllMain
       otherMain = WinCDllMain
-    discard lists.includeStr(m.headerFiles, "<windows.h>")
+    m.includeHeader("<windows.h>")
   elif optGenDynLib in gGlobalOptions:
     nimMain = PosixNimDllMain
     otherMain = PosixCDllMain
@@ -1126,7 +1129,7 @@ proc initProcOptions(m: BModule): TOptions =
 proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   new(result)
   result.tmpBase = rope("TM" & $hashOwner(module) & "_")
-  initLinkedList(result.headerFiles)
+  result.headerFiles = @[]
   result.declaredThings = initIntSet()
   result.declaredProtos = initIntSet()
   result.cfilename = filename
@@ -1163,7 +1166,7 @@ proc nullify[T](arr: var T) =
 proc resetModule*(m: BModule) =
   # between two compilations in CAAS mode, we can throw
   # away all the data that was written to disk
-  initLinkedList(m.headerFiles)
+  m.headerFiles = @[]
   m.declaredProtos = initIntSet()
   m.forwTypeCache = initTable[SigHash, Rope]()
   m.initProc = newProc(nil, m)
@@ -1363,7 +1366,7 @@ proc updateCachedModule(m: BModule) =
     cf.flags = {CfileFlag.Cached}
   addFileToCompile(cf)
 
-proc myClose(b: PPassContext, n: PNode): PNode =
+proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   result = n
   if b == nil or passes.skipCodegen(n): return
   var m = BModule(b)
@@ -1375,7 +1378,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
 
   if sfMainModule in m.module.flags:
     incl m.flags, objHasKidsValid
-    var disp = generateMethodDispatchers()
+    var disp = generateMethodDispatchers(graph)
     for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym)
     genMainProc(m)
 
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 565399ead..be087095f 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -10,7 +10,7 @@
 ## This module contains the data structures for the C code generation phase.
 
 import
-  ast, astalgo, ropes, passes, options, intsets, lists, platform, sighashes,
+  ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
   tables, ndi
 
 from msgs import TLineInfo
@@ -130,7 +130,7 @@ type
     forwTypeCache*: TypeCache # cache for forward declarations of types
     declaredThings*: IntSet   # things we have declared in this .c file
     declaredProtos*: IntSet   # prototypes we have declared in this .c file
-    headerFiles*: TLinkedList # needed headers to include
+    headerFiles*: seq[string] # needed headers to include
     typeInfoMarker*: TypeCache # needed for generating type information
     initProc*: BProc          # code for init procedure
     postInitProc*: BProc      # code to be executed after the init proc
@@ -148,9 +148,13 @@ type
     g*: BModuleList
     ndi*: NdiFile
 
+proc includeHeader*(this: BModule; header: string) =
+  if not this.headerFiles.contains header:
+    this.headerFiles.add header
+
 proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
   # section in the current block
-  result = p.blocks[p.blocks.len - 1].sections[s]
+  result = p.blocks[^1].sections[s]
 
 proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
   # top level proc sections
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index eda80be30..1d7f5a6e1 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils
+  sempass2, strutils, modulegraphs
 
 proc genConv(n: PNode, d: PType, downcast: bool): PNode =
   var dest = skipTypes(d, abstractPtrs)
@@ -35,19 +35,24 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
   else:
     result = n
 
+proc getDispatcher*(s: PSym): PSym =
+  ## can return nil if is has no dispatcher.
+  let dispn = lastSon(s.ast)
+  if dispn.kind == nkSym:
+    let disp = dispn.sym
+    if sfDispatcher in disp.flags: result = disp
+
 proc methodCall*(n: PNode): PNode =
   result = n
   # replace ordinary method by dispatcher method:
-  var disp = lastSon(result.sons[0].sym.ast).sym
-  assert sfDispatcher in disp.flags
-  result.sons[0].sym = disp
-  # change the arguments to up/downcasts to fit the dispatcher's parameters:
-  for i in countup(1, sonsLen(result)-1):
-    result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
-
-# save for incremental compilation:
-var
-  gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
+  let disp = getDispatcher(result.sons[0].sym)
+  if disp != nil:
+    result.sons[0].sym = disp
+    # change the arguments to up/downcasts to fit the dispatcher's parameters:
+    for i in countup(1, sonsLen(result)-1):
+      result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
+  else:
+    localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
 
 type
   MethodResult = enum No, Invalid, Yes
@@ -55,8 +60,8 @@ type
 proc sameMethodBucket(a, b: PSym): MethodResult =
   if a.name.id != b.name.id: return
   if sonsLen(a.typ) != sonsLen(b.typ):
-    return                    # check for return type:
-  if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
+    return
+
   for i in countup(1, sonsLen(a.typ) - 1):
     var aa = a.typ.sons[i]
     var bb = b.typ.sons[i]
@@ -84,6 +89,14 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
         return No
     else:
       return No
+  if result == Yes:
+    # check for return type:
+    if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]):
+      if b.typ.sons[0] != nil and b.typ.sons[0].kind == tyExpr:
+        # infer 'auto' from the base to make it consistent:
+        b.typ.sons[0] = a.typ.sons[0]
+      else:
+        return No
 
 proc attachDispatcher(s: PSym, dispatcher: PNode) =
   var L = s.ast.len-1
@@ -144,27 +157,27 @@ proc fixupDispatcher(meth, disp: PSym) =
       if disp.typ.lockLevel < meth.typ.lockLevel:
         disp.typ.lockLevel = meth.typ.lockLevel
 
-proc methodDef*(s: PSym, fromCache: bool) =
-  let L = len(gMethods)
+proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
+  let L = len(g.methods)
   var witness: PSym
   for i in countup(0, L - 1):
-    let disp = gMethods[i].dispatcher
+    let disp = g.methods[i].dispatcher
     case sameMethodBucket(disp, s)
     of Yes:
-      add(gMethods[i].methods, s)
+      add(g.methods[i].methods, s)
       attachDispatcher(s, lastSon(disp.ast))
       fixupDispatcher(s, disp)
       #echo "fixup ", disp.name.s, " ", disp.id
       when useEffectSystem: checkMethodEffects(disp, s)
-      if sfBase in s.flags and gMethods[i].methods[0] != s:
+      if sfBase in s.flags and g.methods[i].methods[0] != s:
         # already exists due to forwarding definition?
         localError(s.info, "method is not a base")
       return
     of No: discard
     of Invalid:
-      if witness.isNil: witness = gMethods[i].methods[0]
+      if witness.isNil: witness = g.methods[i].methods[0]
   # create a new dispatcher:
-  add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
+  add(g.methods, (methods: @[s], dispatcher: createDispatcher(s)))
   #echo "adding ", s.info
   #if fromCache:
   #  internalError(s.info, "no method dispatcher found")
@@ -258,12 +271,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
       disp = ret
   result.ast.sons[bodyPos] = disp
 
-proc generateMethodDispatchers*(): PNode =
+proc generateMethodDispatchers*(g: ModuleGraph): PNode =
   result = newNode(nkStmtList)
-  for bucket in countup(0, len(gMethods) - 1):
+  for bucket in countup(0, len(g.methods) - 1):
     var relevantCols = initIntSet()
-    for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
-      if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
-    sortBucket(gMethods[bucket].methods, relevantCols)
+    for col in countup(1, sonsLen(g.methods[bucket].methods[0].typ) - 1):
+      if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
+    sortBucket(g.methods[bucket].methods, relevantCols)
     addSon(result,
-           newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))
+           newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols)))
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 74503a414..b75e953de 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -26,8 +26,8 @@ bootSwitch(usedGoGC, defined(gogc), "--gc:go")
 bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
-  os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists,
-  wordrecg, parseutils, nimblecmd, idents, parseopt
+  os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
+  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils
 
 # but some have deps to imported modules. Yay.
 bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@@ -335,12 +335,14 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "excludepath":
     expectArg(switch, arg, pass, info)
     let path = processPath(arg, info)
-    lists.excludePath(options.searchPaths, path)
-    lists.excludePath(options.lazyPaths, path)
+    
+    options.searchPaths.keepItIf( cmpPaths(it, path) != 0 )
+    options.lazyPaths.keepItIf( cmpPaths(it, path) != 0 )
+    
     if (len(path) > 0) and (path[len(path) - 1] == DirSep):
       let strippedPath = path[0 .. (len(path) - 2)]
-      lists.excludePath(options.searchPaths, strippedPath)
-      lists.excludePath(options.lazyPaths, strippedPath)
+      options.searchPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
+      options.lazyPaths.keepItIf( cmpPaths(it, strippedPath) != 0 )
   of "nimcache":
     expectArg(switch, arg, pass, info)
     options.nimcacheDir = processPath(arg, info, true)
diff --git a/compiler/compilerlog.nim b/compiler/compilerlog.nim
new file mode 100644
index 000000000..fbf8cd9aa
--- /dev/null
+++ b/compiler/compilerlog.nim
@@ -0,0 +1,9 @@
+
+from os import getHomeDir, `/`
+
+proc logStr*(line: string) =
+  var f: File
+  if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
+    f.writeLine(line)
+    f.close()
+
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 9504ab52f..118f1c7c5 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -33,11 +33,11 @@ template closeImpl(body: untyped) {.dirty.} =
     except IOError:
       discard
 
-proc close(p: PPassContext, n: PNode): PNode =
+proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
     writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
 
-proc closeJson(p: PPassContext, n: PNode): PNode =
+proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
     writeOutputJson(g.doc, g.module.filename, ".json", useWarning)
 
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 0f283b208..dab643d50 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -13,7 +13,7 @@
 # nim files.
 
 import
-  lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
+  ropes, os, strutils, osproc, platform, condsyms, options, msgs,
   securehash, streams
 
 #from debuginfo import writeDebugInfo
@@ -390,8 +390,8 @@ type
   CfileList = seq[Cfile]
 
 var
-  externalToLink: TLinkedList # files to link in addition to the file
-                              # we compiled
+  externalToLink: seq[string] = @[] # files to link in addition to the file
+                                    # we compiled
   linkOptionsCmd: string = ""
   compileOptionsCmd: seq[string] = @[]
   linkOptions: string = ""
@@ -486,11 +486,10 @@ proc resetCompilationLists* =
   # when the module is loaded/unloaded it adds/removes its items
   # That's because we still need to hash check the external files
   # Maybe we can do that in checkDep on the other hand?
-  initLinkedList(externalToLink)
+  externalToLink.setLen 0
 
 proc addExternalFileToLink*(filename: string) =
-  prependStr(externalToLink, filename)
-  # BUGFIX: was ``appendStr``
+  externalToLink.insert(filename, 0)
 
 proc execWithEcho(cmd: string, msg = hintExecuting): int =
   rawMessage(msg, cmd)
@@ -527,9 +526,6 @@ proc noAbsolutePaths: bool {.inline.} =
   # `optGenMapping` is included here for niminst.
   result = gGlobalOptions * {optGenScript, optGenMapping} != {}
 
-proc add(s: var string, many: openArray[string]) =
-  s.add many.join
-
 proc cFileSpecificOptions(cfilename: string): string =
   result = compileOptions
   for option in compileOptionsCmd:
@@ -560,22 +556,24 @@ proc getLinkOptions: string =
   for linkedLib in items(cLinkedLibs):
     result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
   for libDir in items(cLibs):
-    result.add([CC[cCompiler].linkDirCmd, libDir.quoteShell])
+    result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
 
 proc needsExeExt(): bool {.inline.} =
   result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
            (platform.hostOS == osWindows)
 
-proc getCompilerExe(compiler: TSystemCC): string =
-  result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
-           else: CC[compiler].compilerExe
+proc getCompilerExe(compiler: TSystemCC; cfile: string): string =
+  result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"):
+             CC[compiler].cppCompiler
+           else:
+             CC[compiler].compilerExe
   if result.len == 0:
     rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
 
 proc getLinkerExe(compiler: TSystemCC): string =
   result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
            elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
-           else: compiler.getCompilerExe
+           else: compiler.getCompilerExe("")
 
 proc getCompileCFileCmd*(cfile: Cfile): string =
   var c = cCompiler
@@ -596,7 +594,7 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
 
   var options = cFileSpecificOptions(cfile.cname)
   var exe = getConfigVar(c, ".exe")
-  if exe.len == 0: exe = c.getCompilerExe
+  if exe.len == 0: exe = c.getCompilerExe(cfile.cname)
 
   if needsExeExt(): exe = addFileExt(exe, "exe")
   if optGenDynLib in gGlobalOptions and
@@ -609,12 +607,12 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
     includeCmd = CC[c].includeCmd & quoteShell(libpath)
 
     for includeDir in items(cIncludes):
-      includeCmd.add([CC[c].includeCmd, includeDir.quoteShell])
+      includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
 
     compilePattern = joinPath(ccompilerpath, exe)
   else:
     includeCmd = ""
-    compilePattern = c.getCompilerExe
+    compilePattern = c.getCompilerExe(cfile.cname)
 
   var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
            else: cfile.cname
@@ -784,14 +782,12 @@ proc callCCompiler*(projectfile: string) =
         rawMessage(errExecutionOfProgramFailed, cmds.join())
   if optNoLinking notin gGlobalOptions:
     # call the linker:
-    var it = PStrEntry(externalToLink.head)
     var objfiles = ""
-    while it != nil:
-      let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
+    for it in externalToLink:
+      let objFile = if noAbsolutePaths(): it.extractFilename else: it
       add(objfiles, ' ')
       add(objfiles, quoteShell(
           addFileExt(objFile, CC[cCompiler].objExt)))
-      it = PStrEntry(it.next)
     for x in toCompile:
       add(objfiles, ' ')
       add(objfiles, quoteShell(x.obj))
@@ -834,15 +830,14 @@ proc writeJsonBuildInstructions*(projectfile: string) =
       else:
         lit "],\L"
 
-  proc linkfiles(f: File; buf, objfiles: var string; toLink: TLinkedList) =
-    var it = PStrEntry(toLink.head)
-    while it != nil:
-      let objfile = addFileExt(it.data, CC[cCompiler].objExt)
-      str objfile
+  proc linkfiles(f: File; buf, objfiles: var string; toLink: seq[string]) =
+    for i, it in toLink:
+      let objfile = addFileExt(it, CC[cCompiler].objExt)
+      str(objfile)
       add(objfiles, ' ')
       add(objfiles, quoteShell(objfile))
-      it = PStrEntry(it.next)
-      if it == nil:
+      
+      if i == toLink.high:
         lit "\L"
       else:
         lit ",\L"
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 29a72c86e..8f819686d 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -31,7 +31,7 @@ implements the required case distinction.
 
 import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
-  nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
+  nversion, nimsets, msgs, securehash, bitsets, idents, types, os,
   times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
   intsets, cgmeth, lowerings
 
@@ -2237,13 +2237,13 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   add(p.g.code, p.body)
   globals.unique = p.unique
 
-proc wholeCode*(m: BModule): Rope =
+proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
   for prc in globals.forwarded:
     if not globals.generatedSyms.containsOrIncl(prc.id):
       var p = newProc(globals, m, nil, m.module.options)
       attachProc(p, prc)
 
-  var disp = generateMethodDispatchers()
+  var disp = generateMethodDispatchers(graph)
   for i in 0..sonsLen(disp)-1:
     let prc = disp.sons[i].sym
     if not globals.generatedSyms.containsOrIncl(prc.id):
@@ -2277,7 +2277,7 @@ proc genClass(obj: PType; content: Rope; ext: string) =
   let outfile = changeFileExt(completeCFilePath($cls), ext)
   discard writeRopeIfNotEqual(result, outfile)
 
-proc myClose(b: PPassContext, n: PNode): PNode =
+proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   if passes.skipCodegen(n): return n
   result = myProcess(b, n)
   var m = BModule(b)
@@ -2285,7 +2285,7 @@ proc myClose(b: PPassContext, n: PNode): PNode =
     let ext = if m.target == targetJS: "js" else: "php"
     let f = if globals.classes.len == 0: m.module.filename
             else: "nimsystem"
-    let code = wholeCode(m)
+    let code = wholeCode(graph, m)
     let outfile =
       if options.outFile.len > 0:
         if options.outFile.isAbsolute: options.outFile
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 8d4badb4e..cd2ccfe53 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -10,7 +10,7 @@
 # This include file implements lambda lifting for the transformator.
 
 import
-  intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
+  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
   idents, renderer, types, magicsys, rodread, lowerings, tables
 
 discard """
diff --git a/compiler/lists.nim b/compiler/lists.nim
index 27e373342..bfd052204 100644
--- a/compiler/lists.nim
+++ b/compiler/lists.nim
@@ -7,113 +7,22 @@
 #    distribution, for details about the copyright.
 #
 
-# This module implements a generic doubled linked list.
-# TODO Remove this and replace it with something sensible
-import os
-type
-  PListEntry* = ref TListEntry
-  TListEntry* = object of RootObj
-    prev*, next*: PListEntry
-
-  TStrEntry* = object of TListEntry
-    data*: string
-
-  PStrEntry* = ref TStrEntry
-  TLinkedList* = object       # for the "find" operation:
-    head*, tail*: PListEntry
-    counter*: int
-
-  TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
-
-proc initLinkedList*(list: var TLinkedList) =
-  list.counter = 0
-  list.head = nil
-  list.tail = nil
-
-proc append*(list: var TLinkedList, entry: PListEntry) =
-  inc(list.counter)
-  entry.next = nil
-  entry.prev = list.tail
-  if list.tail != nil:
-    assert(list.tail.next == nil)
-    list.tail.next = entry
-  list.tail = entry
-  if list.head == nil: list.head = entry
-
-proc contains*(list: TLinkedList, data: string): bool =
-  var it = list.head
-  while it != nil:
-    if PStrEntry(it).data == data:
-      return true
-    it = it.next
+# This module is deprecated, don't use it.
+# TODO Remove this
 
-proc newStrEntry(data: string): PStrEntry =
-  new(result)
-  result.data = data
-
-proc appendStr*(list: var TLinkedList, data: string) =
-  append(list, newStrEntry(data))
-
-proc includeStr*(list: var TLinkedList, data: string): bool =
-  if contains(list, data): return true
-  appendStr(list, data)       # else: add to list
-
-proc prepend*(list: var TLinkedList, entry: PListEntry) =
-  inc(list.counter)
-  entry.prev = nil
-  entry.next = list.head
-  if list.head != nil:
-    assert(list.head.prev == nil)
-    list.head.prev = entry
-  list.head = entry
-  if list.tail == nil: list.tail = entry
-
-proc prependStr*(list: var TLinkedList, data: string) =
-  prepend(list, newStrEntry(data))
+import os
 
-proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
-  assert(pos != nil)
-  if pos == list.head:
-    prepend(list, entry)
-  else:
-    inc(list.counter)
-    entry.next = pos
-    entry.prev = pos.prev
-    if pos.prev != nil: pos.prev.next = entry
-    pos.prev = entry
+static:
+  echo "WARNING: imported deprecated module compiler/lists.nim, use seq ore lists from the standard library"
 
-proc remove*(list: var TLinkedList, entry: PListEntry) =
-  dec(list.counter)
-  if entry == list.tail:
-    list.tail = entry.prev
-  if entry == list.head:
-    list.head = entry.next
-  if entry.next != nil: entry.next.prev = entry.prev
-  if entry.prev != nil: entry.prev.next = entry.next
+proc appendStr*(list: var seq[string]; data: string) {.deprecated.} =
+  # just use system.add
+  list.add(data)
 
-proc bringToFront*(list: var TLinkedList, entry: PListEntry) =
-  when true:
-    list.remove entry
-    list.prepend entry
+proc includeStr(list: var seq[string]; data: string): bool {.deprecated.} =
+  if list.contains(data):
+    result = true
   else:
-    if entry == list.head: return
-    if entry == list.tail: list.tail = entry.prev
-    if entry.next != nil: entry.next.prev = entry.prev
-    if entry.prev != nil: entry.prev.next = entry.next
-    entry.prev = nil
-    entry.next = list.head
-    list.head = entry
-
-proc excludePath*(list: var TLinkedList, data: string) =
-  var it = list.head
-  while it != nil:
-    let nxt = it.next
-    if cmpPaths(PStrEntry(it).data, data) == 0:
-      remove(list, it)
-    it = nxt
+    result = false
+    list.add data
 
-proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
-  result = list.head
-  while result != nil:
-    if fn(result, closure): return
-    result = result.next
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 19a4da07b..089e69ff9 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -115,7 +115,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
       considerQuotedIdent(m)
     else:
       getIdent("err:" & renderTree(m))
-  result = newSym(skError, ident, getCurrOwner(), n.info)
+  result = newSym(skError, ident, getCurrOwner(c), n.info)
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
   # pretend it's imported from some unknown module to prevent cascading errors:
diff --git a/compiler/main.nim b/compiler/main.nim
index 2acb7620c..5f86e6188 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -15,7 +15,7 @@ import
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  docgen2, service, parser, modules, ccgutils, sigmatch, ropes, lists,
+  docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
   modulegraphs
 
 from magicsys import systemModule, resetSysTypes
@@ -151,7 +151,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
   # In "nim serve" scenario, each command must reset the registered passes
   clearPasses()
   gLastCmdTime = epochTime()
-  appendStr(searchPaths, options.libpath)
+  searchPaths.add(options.libpath)
   when false: # gProjectFull.len != 0:
     # current path is always looked first for modules
     prependStr(searchPaths, gProjectPath)
@@ -229,7 +229,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
       for s in definedSymbolNames(): definedSymbols.elems.add(%s)
 
       var libpaths = newJArray()
-      for dir in iterSearchPath(searchPaths): libpaths.elems.add(%dir)
+      for dir in searchPaths: libpaths.elems.add(%dir)
 
       var dumpdata = % [
         (key: "version", val: %VersionAsString),
@@ -245,7 +245,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
       for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
       msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
 
-      for it in iterSearchPath(searchPaths): msgWriteln(it)
+      for it in searchPaths: msgWriteln(it)
   of "check":
     gCmd = cmdCheck
     commandCheck(graph, cache)
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 87a35b290..c081a099a 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -40,9 +40,16 @@ type
                               # module dependencies.
     backend*: RootRef # minor hack so that a backend can extend this easily
     config*: ConfigRef
+    doStopCompile*: proc(): bool {.closure.}
+    usageSym*: PSym # for nimsuggest
+    owners*: seq[PSym]
+    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
 
 {.this: g.}
 
+proc stopCompile*(g: ModuleGraph): bool {.inline.} =
+  result = doStopCompile != nil and doStopCompile()
+
 proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
   result = ModuleGraph()
   initStrTable(result.packageSyms)
@@ -54,6 +61,8 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
     result.config = newConfigRef()
   else:
     result.config = config
+  result.owners = @[]
+  result.methods = @[]
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
@@ -61,6 +70,9 @@ proc resetAllModules*(g: ModuleGraph) =
   modules = @[]
   importStack = @[]
   inclToMod = initTable[int32, int32]()
+  usageSym = nil
+  owners = @[]
+  methods = @[]
 
 proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
   if fileIdx >= 0 and fileIdx < modules.len:
@@ -73,7 +85,7 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: int32) =
     deps.incl m.position.dependsOn(dep)
     # we compute the transitive closure later when quering the graph lazily.
     # this improve efficiency quite a lot:
-    invalidTransitiveClosure = true
+    #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) =
   discard hasKeyOrPut(inclToMod, includeFile, module)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 49e4fa184..a1ba82263 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -567,6 +567,15 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
   if optEmbedOrigSrc in gGlobalOptions or true:
     result.lines = @[]
 
+proc fileInfoKnown*(filename: string): bool =
+  var
+    canon: string
+  try:
+    canon = canonicalizePath(filename)
+  except:
+    canon = filename
+  result = filenameToIndexTbl.hasKey(canon)
+
 proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
   var
     canon: string
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 9c5c17287..e6466fc24 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,11 +9,11 @@
 
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
-import parseutils, strutils, strtabs, os, options, msgs, lists
+import parseutils, strutils, strtabs, os, options, msgs
 
 proc addPath*(path: string, info: TLineInfo) =
-  if not contains(options.searchPaths, path):
-    lists.prependStr(options.searchPaths, path)
+  if not options.searchPaths.contains(path):
+    options.searchPaths.insert(path, 0)
 
 proc versionSplitPos(s: string): int =
   result = s.len-2
@@ -61,7 +61,7 @@ iterator chosen(packages: StringTableRef): string =
 proc addNimblePath(p: string, info: TLineInfo) =
   if not contains(options.searchPaths, p):
     message(info, hintPath, p)
-    lists.prependStr(options.lazyPaths, p)
+    options.lazyPaths.insert(p, 0)
 
 proc addPathRec(dir: string, info: TLineInfo) =
   var packages = newStringTable(modeStyleInsensitive)
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 2872bdade..aca03fc16 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -10,7 +10,7 @@
 ## exposes the Nim VM to clients.
 import
   ast, modules, passes, passaux, condsyms,
-  options, nimconf, lists, sem, semdata, llstream, vm, modulegraphs, idents
+  options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents
 
 proc execute*(program: string) =
   passes.gIncludeFile = includeModule
@@ -25,7 +25,7 @@ proc execute*(program: string) =
   registerPass(semPass)
   registerPass(evalPass)
 
-  appendStr(searchPaths, options.libpath)
+  searchPaths.add options.libpath
   var graph = newModuleGraph()
   var cache = newIdentCache()
   var m = makeStdinModule(graph)
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index 4afb16912..a97d88078 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -13,7 +13,7 @@ import strutils, os, parseopt
 import compiler/[options, commands, modules, sem,
   passes, passaux, nimfix/pretty,
   msgs, nimconf,
-  extccomp, condsyms, lists,
+  extccomp, condsyms,
   modulegraphs, idents]
 
 const Usage = """
@@ -39,10 +39,10 @@ proc mainCommand =
   registerPass verbosePass
   registerPass semPass
   gCmd = cmdPretty
-  appendStr(searchPaths, options.libpath)
+  searchPaths.add options.libpath
   if gProjectFull.len != 0:
     # current path is always looked first for modules
-    prependStr(searchPaths, gProjectPath)
+    searchPaths.insert(gProjectPath, 0)
 
   compileProject(newModuleGraph(), newIdentCache())
   pretty.overwriteFiles()
diff --git a/compiler/options.nim b/compiler/options.nim
index 2295bbf93..6281980ff 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  os, lists, strutils, strtabs, osproc, sets
+  os, strutils, strtabs, osproc, sets
 
 const
   hasTinyCBackend* = defined(tinyc)
@@ -100,7 +100,7 @@ type
 
   IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
-    ideHighlight, ideOutline
+    ideHighlight, ideOutline, ideKnown
 
   ConfigRef* = ref object ## eventually all global configuration should be moved here
     cppDefines*: HashSet[string]
@@ -129,7 +129,8 @@ var
   gExitcode*: int8
   gCmd*: TCommands = cmdNone  # the command
   gSelectedGC* = gcRefc       # the selected GC
-  searchPaths*, lazyPaths*: TLinkedList
+  searchPaths*: seq[string] = @[]
+  lazyPaths*: seq[string]   = @[]
   outFile*: string = ""
   docSeeSrcUrl*: string = ""  # if empty, no seeSrc will be generated. \
   # The string uses the formatting variables `path` and `line`.
@@ -267,9 +268,7 @@ proc removeTrailingDirSep*(path: string): string =
 
 proc disableNimblePath*() =
   gNoNimblePath = true
-  lazyPaths.head = nil
-  lazyPaths.tail = nil
-  lazyPaths.counter = 0
+  lazyPaths.setLen(0)
 
 include packagehandling
 
@@ -336,27 +335,22 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
   result = joinPath(subdir, tail)
   #echo "completeGeneratedFilePath(", f, ") = ", result
 
-iterator iterSearchPath*(searchPaths: TLinkedList): string =
-  var it = PStrEntry(searchPaths.head)
-  while it != nil:
-    yield it.data
-    it = PStrEntry(it.next)
-
 proc rawFindFile(f: string): string =
-  for it in iterSearchPath(searchPaths):
+  for it in searchPaths:
     result = joinPath(it, f)
     if existsFile(result):
       return result.canonicalizePath
   result = ""
 
 proc rawFindFile2(f: string): string =
-  var it = PStrEntry(lazyPaths.head)
-  while it != nil:
-    result = joinPath(it.data, f)
+  for i, it in lazyPaths:
+    result = joinPath(it, f)
     if existsFile(result):
-      bringToFront(lazyPaths, it)
+      # bring to front
+      for j in countDown(i,1):
+        swap(lazyPaths[j], lazyPaths[j-1])
+      
       return result.canonicalizePath
-    it = PStrEntry(it.next)
   result = ""
 
 template patchModule() {.dirty.} =
@@ -442,6 +436,7 @@ proc parseIdeCmd*(s: string): IdeCmd =
   of "mod": ideMod
   of "highlight": ideHighlight
   of "outline": ideOutline
+  of "known": ideKnown
   else: ideNone
 
 proc `$`*(c: IdeCmd): string =
@@ -456,3 +451,4 @@ proc `$`*(c: IdeCmd): string =
   of ideNone: "none"
   of ideHighlight: "highlight"
   of ideOutline: "outline"
+  of ideKnown: "known"
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index eeaf12953..2065d5893 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -29,21 +29,3 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode =
     message(n.info, hintProcessing, $idgen.gFrontendId)
 
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
-
-proc cleanUp(c: PPassContext, n: PNode): PNode =
-  result = n
-  # we cannot clean up if dead code elimination is activated
-  if optDeadCodeElim in gGlobalOptions or n == nil: return
-  case n.kind
-  of nkStmtList:
-    for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
-  of nkProcDef, nkMethodDef:
-    if n.sons[namePos].kind == nkSym:
-      var s = n.sons[namePos].sym
-      if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
-        s.ast.sons[bodyPos] = ast.emptyNode # free the memory
-  else:
-    discard
-
-const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
-
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 3cc15147e..7966ee88d 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -11,7 +11,7 @@
 # `TPass` interface.
 
 import
-  strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
+  strutils, options, ast, astalgo, llstream, msgs, platform, os,
   condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
   nimsets, syntaxes, times, rodread, idgen, modulegraphs
 
@@ -24,7 +24,7 @@ type
   TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.}
   TPassOpenCached* =
     proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.}
-  TPassClose* = proc (p: PPassContext, n: PNode): PNode {.nimcall.}
+  TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
   TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
 
   TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
@@ -94,7 +94,7 @@ proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache;
                 m: TPassData): TPassData =
   var c = p.open(g, module, cache)
   result.input = p.process(c, m.input)
-  result.closeOutput = if p.close != nil: p.close(c, m.closeOutput)
+  result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
                        else: m.closeOutput
 
 proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
@@ -121,10 +121,10 @@ proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym,
     else:
       a[i] = nil
 
-proc closePasses(a: var TPassContextArray) =
+proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
   for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m)
+    if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m)
     a[i] = nil                # free the memory here
 
 proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
@@ -142,11 +142,11 @@ proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
   for i in countup(0, gPassesLen - 1):
     if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
 
-proc closePassesCached(a: var TPassContextArray) =
+proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
   for i in countup(0, gPassesLen - 1):
     if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
-      m = gPasses[i].close(a[i], m)
+      m = gPasses[i].close(graph, a[i], m)
     a[i] = nil                # free the memory here
 
 proc resolveMod(module, relativeTo: string): int32 =
@@ -171,6 +171,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
 
 proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
                     rd: PRodReader; cache: IdentCache): bool {.discardable.} =
+  if graph.stopCompile(): return true
   var
     p: TParsers
     a: TPassContextArray
@@ -198,6 +199,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
         processImplicits implicitIncludes, nkIncludeStmt, a, module
 
       while true:
+        if graph.stopCompile(): break
         var n = parseTopLevelStmt(p)
         if n.kind == nkEmpty: break
         if sfNoForward in module.flags:
@@ -213,12 +215,14 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
         elif not processTopLevelStmt(n, a): break
       closeParsers(p)
       if s.kind != llsStdIn: break
-    closePasses(a)
+    closePasses(graph, a)
     # id synchronization point for more consistent code generation:
     idSynchronizationPoint(1000)
   else:
     openPassesCached(graph, a, module, rd)
     var n = loadInitSection(rd)
-    for i in countup(0, sonsLen(n) - 1): processTopLevelStmtCached(n.sons[i], a)
-    closePassesCached(a)
+    for i in countup(0, sonsLen(n) - 1):
+      if graph.stopCompile(): break
+      processTopLevelStmtCached(n.sons[i], a)
+    closePassesCached(graph, a)
   result = true
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 09b8d3305..859fe2a81 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -289,7 +289,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
         # constraint not fulfilled:
         if not ok: return nil
 
-  markUsed(n.info, s)
+  markUsed(n.info, s, c.graph.usageSym)
   if ctx.subMatch:
     assert m.len == 3
     m.sons[1] = result
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim
index 8a3f67dd4..338e7bcac 100644
--- a/compiler/plugins/locals/locals.nim
+++ b/compiler/plugins/locals/locals.nim
@@ -28,7 +28,7 @@ proc semLocals(c: PContext, n: PNode): PNode =
           it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
             {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
 
-        var field = newSym(skField, it.name, getCurrOwner(), n.info)
+        var field = newSym(skField, it.name, getCurrOwner(c), n.info)
         field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
         field.position = counter
         inc(counter)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 04dbd3612..4476dab72 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -11,7 +11,7 @@
 
 import
   os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
-  wordrecg, ropes, options, strutils, lists, extccomp, math, magicsys, trees,
+  wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
   rodread, types, lookups
 
 const
@@ -25,19 +25,19 @@ const
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
     wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
     wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
-    wOverride, wConstructor, wExportNims}
+    wOverride, wConstructor, wExportNims, wUsed}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas+{wBase}-{wImportCpp}
   templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
-    wDelegator, wExportNims}
+    wDelegator, wExportNims, wUsed}
   macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
     wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
     wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
-    wExportNims}
+    wExportNims, wUsed}
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
     wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
-    wTags, wLocks, wGcSafe, wExportNims}
+    wTags, wLocks, wGcSafe, wExportNims, wUsed}
   exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
     wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
@@ -55,16 +55,16 @@ const
     wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
     wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
     wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
-    wBorrow, wGcSafe, wExportNims, wPartial}
+    wBorrow, wGcSafe, wExportNims, wPartial, wUsed}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
-    wImportCpp, wImportObjC, wError, wGuard, wBitsize}
+    wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
     wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
     wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
-    wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims}
+    wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
     wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
-    wIntDefine, wStrDefine}
+    wIntDefine, wStrDefine, wUsed}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
                       wThread, wRaises, wLocks, wTags, wGcSafe}
@@ -218,20 +218,19 @@ proc processCallConv(c: PContext, n: PNode) =
     var sw = whichKeyword(n.sons[1].ident)
     case sw
     of FirstCallConv..LastCallConv:
-      POptionEntry(c.optionStack.tail).defaultCC = wordToCallConv(sw)
+      c.optionStack[^1].defaultCC = wordToCallConv(sw)
     else: localError(n.info, errCallConvExpected)
   else:
     localError(n.info, errCallConvExpected)
 
 proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
-  var it = PLib(c.libs.head)
-  while it != nil:
-    if it.kind == kind:
-      if trees.exprStructuralEquivalent(it.path, path): return it
-    it = PLib(it.next)
+  for it in c.libs:
+    if it.kind == kind and trees.exprStructuralEquivalent(it.path, path):
+      return it
+      
   result = newLib(kind)
   result.path = path
-  append(c.libs, result)
+  c.libs.add result
   if path.kind in {nkStrLit..nkTripleStrLit}:
     result.isOverriden = options.isDynlibOverride(path.strVal)
 
@@ -254,7 +253,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
   if (sym == nil) or (sym.kind == skModule):
     let lib = getLib(c, libDynamic, expectDynlibNode(c, n))
     if not lib.isOverriden:
-      POptionEntry(c.optionStack.tail).dynlib = lib
+      c.optionStack[^1].dynlib = lib
   else:
     if n.kind == nkExprColonExpr:
       var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
@@ -350,12 +349,12 @@ proc processPush(c: PContext, n: PNode, start: int) =
   if n.sons[start-1].kind == nkExprColonExpr:
     localError(n.info, errGenerated, "':' after 'push' not supported")
   var x = newOptionEntry()
-  var y = POptionEntry(c.optionStack.tail)
+  var y = c.optionStack[^1]
   x.options = gOptions
   x.defaultCC = y.defaultCC
   x.dynlib = y.dynlib
   x.notes = gNotes
-  append(c.optionStack, x)
+  c.optionStack.add(x)
   for i in countup(start, sonsLen(n) - 1):
     if processOption(c, n.sons[i]):
       # simply store it somewhere:
@@ -365,12 +364,12 @@ proc processPush(c: PContext, n: PNode, start: int) =
     #localError(n.info, errOptionExpected)
 
 proc processPop(c: PContext, n: PNode) =
-  if c.optionStack.counter <= 1:
+  if c.optionStack.len <= 1:
     localError(n.info, errAtPopWithoutPush)
   else:
-    gOptions = POptionEntry(c.optionStack.tail).options
-    gNotes = POptionEntry(c.optionStack.tail).notes
-    remove(c.optionStack, c.optionStack.tail)
+    gOptions = c.optionStack[^1].options
+    gNotes = c.optionStack[^1].notes
+    c.optionStack.setLen(c.optionStack.len - 1)
 
 proc processDefine(c: PContext, n: PNode) =
   if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
@@ -582,7 +581,13 @@ proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
   if it.kind != nkExprColonExpr:
     invalidPragma(it)
   else:
-    if it[1].kind != nkNilLit:
+    case it[1].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      if it[1].strVal == "unknown":
+        result = UnknownLockLevel
+      else:
+        localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
+    else:
       let x = expectIntLit(c, it)
       if x < 0 or x > MaxLockLevel:
         localError(it[1].info, "integer must be within 0.." & $MaxLockLevel)
@@ -961,14 +966,17 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         sym.magic = mIntDefine
       of wStrDefine:
         sym.magic = mStrDefine
+      of wUsed:
+        noVal(it)
+        if sym == nil: invalidPragma(it)
+        else: sym.flags.incl sfUsed
       else: invalidPragma(it)
     else: invalidPragma(it)
 
 proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
                       validPragmas: TSpecialWords) =
   if sym != nil and sym.kind != skModule:
-    var it = POptionEntry(c.optionStack.head)
-    while it != nil:
+    for it in c.optionStack:
       let o = it.otherPragmas
       if not o.isNil:
         pushInfoContext(n.info)
@@ -976,11 +984,10 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
           if singlePragma(c, sym, o, i, validPragmas):
             internalError(n.info, "implicitPragmas")
         popInfoContext()
-      it = it.next.POptionEntry
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
       localError(n.info, errDynlibRequiresExportc)
-    var lib = POptionEntry(c.optionStack.tail).dynlib
+    var lib = c.optionStack[^1].dynlib
     if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
         sfImportc in sym.flags and lib != nil:
       incl(sym.loc.flags, lfDynamicLib)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 1602988dd..5ce8414d6 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -10,7 +10,7 @@
 # This module implements the renderer of the standard Nim representation.
 
 import
-  lexer, options, idents, strutils, ast, msgs, lists
+  lexer, options, idents, strutils, ast, msgs
 
 type
   TRenderFlag* = enum
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index 4ce9e616a..d61d817dd 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -641,7 +641,7 @@ proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   rawAddInterfaceSym(w, module)
   result = w
 
-proc myClose(c: PPassContext, n: PNode): PNode =
+proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
   result = process(c, n)
   var w = PRodWriter(c)
   writeRod(w)
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 9e94f1c19..0c31eadbe 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -12,7 +12,7 @@
 
 import
   ast, modules, idents, passes, passaux, condsyms,
-  options, nimconf, lists, sem, semdata, llstream, vm, vmdef, commands, msgs,
+  options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
   os, times, osproc, wordrecg, strtabs, modulegraphs
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
@@ -153,7 +153,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
   registerPass(semPass)
   registerPass(evalPass)
 
-  appendStr(searchPaths, options.libpath)
+  searchPaths.add(options.libpath)
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index dbf237635..682e9d0ca 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -10,7 +10,7 @@
 # This module implements the semantic checking pass.
 
 import
-  ast, strutils, hashes, lists, options, lexer, astalgo, trees, treetab,
+  ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
@@ -166,7 +166,7 @@ proc commonType*(x, y: PType): PType =
         result.addSonSkipIntLit(r)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
+  result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
   proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii
@@ -178,12 +178,17 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     if result.kind != kind:
       localError(n.info, "cannot use symbol of kind '" &
                  $result.kind & "' as a '" & $kind & "'")
+    if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
+      # declarative context, so produce a fresh gensym:
+      result = copySym(result)
+      result.ast = n.sym.ast
+      put(c.p, n.sym, result)
     # 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
-    result.owner = getCurrOwner()
+    result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
+    result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
 
@@ -371,7 +376,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode =
   pushInfoContext(nOrig.info)
 
-  markUsed(n.info, sym)
+  markUsed(n.info, sym, c.graph.usageSym)
   styleCheckUse(n.info, sym)
   if sym == c.p.owner:
     globalError(n.info, errRecursiveDependencyX, sym.name.s)
@@ -429,7 +434,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
   c.instTypeBoundOp = sigmatch.instTypeBoundOp
 
   pushProcCon(c, module)
-  pushOwner(c.module)
+  pushOwner(c, c.module)
   c.importTable = openScope(c)
   c.importTable.addSym(module) # a module knows itself
   if sfSystemModule in module.flags:
@@ -445,7 +450,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
 
 proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext =
   result = myOpen(graph, module, rd.cache)
-  for m in items(rd.methods): methodDef(m, true)
+  for m in items(rd.methods): methodDef(graph, m, true)
 
 proc isImportSystemStmt(n: PNode): bool =
   if magicsys.systemModule == nil: return false
@@ -497,7 +502,7 @@ proc recoverContext(c: PContext) =
   # faster than wrapping every stack operation in a 'try finally' block and
   # requires far less code.
   c.currentScope = c.topLevelScope
-  while getCurrOwner().kind != skModule: popOwner()
+  while getCurrOwner(c).kind != skModule: popOwner(c)
   while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
 
 proc myProcess(context: PPassContext, n: PNode): PNode =
@@ -518,7 +523,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
       else: result = ast.emptyNode
       #if gCmd == cmdIdeTools: findSuggest(c, n)
 
-proc myClose(context: PPassContext, n: PNode): PNode =
+proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   var c = PContext(context)
   closeScope(c)         # close module's scope
   rawCloseScope(c)      # imported symbols; don't check for unused ones!
@@ -528,7 +533,7 @@ proc myClose(context: PPassContext, n: PNode): PNode =
   addCodeForGenerics(c, result)
   if c.module.ast != nil:
     result.add(c.module.ast)
-  popOwner()
+  popOwner(c)
   popProcCon(c)
 
 const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index c4dc13624..70765d087 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -105,7 +105,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDestructor:
     let op = t.destructor
     if op != nil:
-      markUsed(c.info, op)
+      markUsed(c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
       body.add newDestructorCall(op, x)
       result = true
@@ -123,14 +123,14 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
         op = t.assignment
         if op == nil:
           op = liftBody(c.c, t, c.info)
-      markUsed(c.info, op)
+      markUsed(c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
       body.add newAsgnCall(c.c, op, x, y)
       result = true
   of attachedDeepCopy:
     let op = t.deepCopy
     if op != nil:
-      markUsed(c.info, op)
+      markUsed(c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
       body.add newDeepCopyCall(op, x, y)
       result = true
@@ -248,6 +248,7 @@ proc addParam(procType: PType; param: PSym) =
 proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
   var a: TLiftCtx
   a.info = info
+  a.c = c
   let body = newNodeI(nkStmtList, info)
   result = newSym(skProc, getIdent":lifted=", typ.owner, info)
   a.fn = result
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index ae7bab89d..fe73d60e6 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -335,7 +335,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
 proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
-  markUsed(n.sons[0].info, finalCallee)
+  markUsed(n.sons[0].info, finalCallee, c.graph.usageSym)
   styleCheckUse(n.sons[0].info, finalCallee)
   assert finalCallee.ast != nil
   if x.hasFauxMatch:
@@ -411,7 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
     let tm = typeRel(m, formal, arg, true)
     if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
-  markUsed(n.info, s)
+  markUsed(n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
   result = newSymNode(newInst, n.info)
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 20fd1d9be..77d461007 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -10,15 +10,14 @@
 ## This module contains the data structures for the semantic checking phase.
 
 import
-  strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
+  strutils, intsets, options, lexer, ast, astalgo, trees, treetab,
   wordrecg,
   ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
   magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef,
   modulegraphs
 
 type
-  TOptionEntry* = object of lists.TListEntry # entries to put on a
-                                             # stack for pragma parsing
+  TOptionEntry* = object      # entries to put on a stack for pragma parsing
     options*: TOptions
     defaultCC*: TCallingConvention
     dynlib*: PLib
@@ -39,6 +38,7 @@ type
     next*: PProcCon           # used for stacking procedure contexts
     wasForwarded*: bool       # whether the current proc has a separate header
     bracketExpr*: PNode       # current bracket expression (for ^ support)
+    mapping*: TIdTable
 
   TInstantiationPair* = object
     genericSym*: PSym
@@ -78,10 +78,10 @@ type
     inGenericInst*: int        # > 0 if we are instantiating a generic
     converters*: TSymSeq       # sequence of converters
     patterns*: TSymSeq         # sequence of pattern matchers
-    optionStack*: TLinkedList
+    optionStack*: seq[POptionEntry]
     symMapping*: TIdTable      # every gensym'ed symbol needs to be mapped
                                # to some new symbol in a generic instantiation
-    libs*: TLinkedList         # all libs used by this module
+    libs*: seq[PLib]           # all libs used by this module
     semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
     semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}): PNode {.nimcall.}
@@ -124,29 +124,55 @@ proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
            else: 0
 
-var gOwners*: seq[PSym] = @[]
-
-proc getCurrOwner*(): PSym =
+proc getCurrOwner*(c: PContext): PSym =
   # owner stack (used for initializing the
   # owner field of syms)
   # the documentation comment always gets
   # assigned to the current owner
-  # BUGFIX: global array is needed!
-  result = gOwners[high(gOwners)]
+  result = c.graph.owners[^1]
 
-proc pushOwner*(owner: PSym) =
-  add(gOwners, owner)
+proc pushOwner*(c: PContext; owner: PSym) =
+  add(c.graph.owners, owner)
 
-proc popOwner*() =
-  var length = len(gOwners)
-  if length > 0: setLen(gOwners, length - 1)
+proc popOwner*(c: PContext) =
+  var length = len(c.graph.owners)
+  if length > 0: setLen(c.graph.owners, length - 1)
   else: internalError("popOwner")
 
 proc lastOptionEntry*(c: PContext): POptionEntry =
-  result = POptionEntry(c.optionStack.tail)
+  result = c.optionStack[^1]
 
 proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
+proc put*(p: PProcCon; key, val: PSym) =
+  if p.mapping.data == nil: initIdTable(p.mapping)
+  #echo "put into table ", key.info
+  p.mapping.idTablePut(key, val)
+
+proc get*(p: PProcCon; key: PSym): PSym =
+  if p.mapping.data == nil: return nil
+  result = PSym(p.mapping.idTableGet(key))
+
+proc getGenSym*(c: PContext; s: PSym): PSym =
+  if sfGenSym notin s.flags: return s
+  var it = c.p
+  while it != nil:
+    result = get(it, s)
+    if result != nil:
+      #echo "got from table ", result.name.s, " ", result.info
+      return result
+    it = it.next
+  result = s
+
+proc considerGenSyms*(c: PContext; n: PNode) =
+  if n.kind == nkSym:
+    let s = getGenSym(c, n.sym)
+    if n.sym != s:
+      n.sym = s
+  else:
+    for i in 0..<n.safeLen:
+      considerGenSyms(c, n.sons[i])
+
 proc newOptionEntry*(): POptionEntry =
   new(result)
   result.options = gOptions
@@ -157,9 +183,9 @@ proc newOptionEntry*(): POptionEntry =
 proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext =
   new(result)
   result.ambiguousSymbols = initIntSet()
-  initLinkedList(result.optionStack)
-  initLinkedList(result.libs)
-  append(result.optionStack, newOptionEntry())
+  result.optionStack = @[]
+  result.libs = @[]
+  result.optionStack.add(newOptionEntry())
   result.module = module
   result.friendModules = @[module]
   result.converters = @[]
@@ -196,7 +222,7 @@ proc addToLib*(lib: PLib, sym: PSym) =
   sym.annex = lib
 
 proc newTypeS*(kind: TTypeKind, c: PContext): PType =
-  result = newType(kind, getCurrOwner())
+  result = newType(kind, getCurrOwner(c))
 
 proc makePtrType*(c: PContext, baseType: PType): PType =
   result = newTypeS(tyPtr, c)
@@ -215,7 +241,7 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
   let typedesc = makeTypeDesc(c, typ)
-  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(), info).linkTo(typedesc)
+  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info).linkTo(typedesc)
   return newSymNode(sym, info)
 
 proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
@@ -225,7 +251,7 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
 
 proc newTypeWithSons*(c: PContext, kind: TTypeKind,
                       sons: seq[PType]): PType =
-  result = newType(kind, getCurrOwner())
+  result = newType(kind, getCurrOwner(c))
   result.sons = sons
 
 proc makeStaticExpr*(c: PContext, n: PNode): PNode =
@@ -296,7 +322,7 @@ proc errorNode*(c: PContext, n: PNode): PNode =
 
 proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
   dest.kind = kind
-  dest.owner = getCurrOwner()
+  dest.owner = getCurrOwner(c)
   dest.size = - 1
 
 proc makeRangeType*(c: PContext; first, last: BiggestInt;
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index a8873bbe2..b09404b39 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -51,7 +51,7 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
       let destructableT = instantiateDestructor(c, t.sons[i])
       if destructableT != nil:
         n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
-            useSym(destructableT.destructor),
+            useSym(destructableT.destructor, c.graph.usageSym),
             n.sons[paramsPos][1][0]]))
 
 proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
@@ -60,8 +60,8 @@ proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
   let destructableT = instantiateDestructor(c, field.typ)
   if destructableT != nil:
     result = newNode(nkCall, field.info, @[
-      useSym(destructableT.destructor),
-      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
+      useSym(destructableT.destructor, c.graph.usageSym),
+      newNode(nkDotExpr, field.info, @[holder, useSym(field, c.graph.usageSym)])])
 
 proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
   var nonTrivialFields = 0
@@ -181,7 +181,8 @@ proc createDestructorCall(c: PContext, s: PSym): PNode =
   let destructableT = instantiateDestructor(c, varTyp)
   if destructableT != nil:
     let call = semStmt(c, newNode(nkCall, s.info, @[
-      useSym(destructableT.destructor), useSym(s)]))
+      useSym(destructableT.destructor, c.graph.usageSym),
+      useSym(s, c.graph.usageSym)]))
     result = newNode(nkDefer, s.info, @[call])
 
 proc insertDestructors(c: PContext,
@@ -233,8 +234,8 @@ proc insertDestructors(c: PContext,
       tryStmt.addSon(
         newNode(nkFinally, info, @[
           semStmt(c, newNode(nkCall, info, @[
-            useSym(destructableT.destructor),
-            useSym(varId.sym)]))]))
+            useSym(destructableT.destructor, c.graph.usageSym),
+            useSym(varId.sym, c.graph.usageSym)]))]))
 
       result.outer = newNodeI(nkStmtList, info)
       varSection.sons.setLen(j+1)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 57674735a..6db7724f9 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -12,10 +12,10 @@
 
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
-  markUsed(n.info, s)
+  markUsed(n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
-  result = evalTemplate(n, s, getCurrOwner(), efFromHlo in flags)
+  result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
   popInfoContext()
 
@@ -209,7 +209,7 @@ proc semConv(c: PContext, n: PNode): PNode =
       let it = op.sons[i]
       let status = checkConvertible(c, result.typ, it.typ)
       if status in {convOK, convNotNeedeed}:
-        markUsed(n.info, it.sym)
+        markUsed(n.info, it.sym, c.graph.usageSym)
         styleCheckUse(n.info, it.sym)
         markIndirect(c, it.sym)
         return it
@@ -922,10 +922,11 @@ proc readTypeParameter(c: PContext, typ: PType,
         return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
   #echo "came here: returned nil"
 
-proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
+proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
+  let s = getGenSym(c, sym)
   case s.kind
   of skConst:
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
     of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
@@ -949,20 +950,20 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       result = newSymNode(s, n.info)
   of skMacro:
     if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
-      markUsed(n.info, s)
+      markUsed(n.info, s, c.graph.usageSym)
       styleCheckUse(n.info, s)
       result = newSymNode(s, n.info)
     else:
       result = semMacroExpr(c, n, n, s, flags)
   of skTemplate:
     if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0:
-      markUsed(n.info, s)
+      markUsed(n.info, s, c.graph.usageSym)
       styleCheckUse(n.info, s)
       result = newSymNode(s, n.info)
     else:
       result = semTemplateExpr(c, n, s, flags)
   of skParam:
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     if s.typ.kind == tyStatic and s.typ.n != nil:
       # XXX see the hack in sigmatch.nim ...
@@ -984,7 +985,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     if s.magic == mNimvm:
       localError(n.info, "illegal context for 'nimvm' magic")
 
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
     # We cannot check for access to outer vars for example because it's still
@@ -1002,7 +1003,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       n.typ = s.typ
       return n
   of skType:
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     if s.typ.kind == tyStatic and s.typ.n != nil:
       return s.typ.n
@@ -1024,7 +1025,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
           if f != nil and fieldVisible(c, f):
             # is the access to a public field or in the same module or in a friend?
             doAssert f == s
-            markUsed(n.info, f)
+            markUsed(n.info, f, c.graph.usageSym)
             styleCheckUse(n.info, f)
             result = newNodeIT(nkDotExpr, n.info, f.typ)
             result.add makeDeref(newSymNode(p.selfSym))
@@ -1037,11 +1038,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
           if ty.sons[0] == nil: break
           ty = skipTypes(ty.sons[0], skipPtrs)
     # old code, not sure if it's live code:
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
   else:
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
     result = newSymNode(s, n.info)
 
@@ -1061,7 +1062,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym: result = semSym(c, n, s, flags)
     else:
-      markUsed(n.sons[1].info, s)
+      markUsed(n.sons[1].info, s, c.graph.usageSym)
       result = semSym(c, n, s, flags)
     styleCheckUse(n.sons[1].info, s)
     return
@@ -1086,18 +1087,18 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         result = newSymNode(f)
         result.info = n.info
         result.typ = ty
-        markUsed(n.info, f)
+        markUsed(n.info, f, c.graph.usageSym)
         styleCheckUse(n.info, f)
         return
     of tyTypeParamsHolders:
       return readTypeParameter(c, ty, i, n.info)
     of tyObject, tyTuple:
       if ty.n != nil and ty.n.kind == nkRecList:
-        for field in ty.n:
-          if field.sym.name == i:
-            n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
-            n.typ.n = copyTree(n)
-            return n
+        let field = lookupInRecord(ty.n, i)
+        if field != nil:
+          n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
+          n.typ.n = copyTree(n)
+          return n
     else:
       # echo "TYPE FIELD ACCESS"
       # debug ty
@@ -1119,7 +1120,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if f != nil:
       if fieldVisible(c, f):
         # is the access to a public field or in the same module or in a friend?
-        markUsed(n.sons[1].info, f)
+        markUsed(n.sons[1].info, f, c.graph.usageSym)
         styleCheckUse(n.sons[1].info, f)
         n.sons[0] = makeDeref(n.sons[0])
         n.sons[1] = newSymNode(f) # we now have the correct field
@@ -1133,7 +1134,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   elif ty.kind == tyTuple and ty.n != nil:
     f = getSymFromList(ty.n, i)
     if f != nil:
-      markUsed(n.sons[1].info, f)
+      markUsed(n.sons[1].info, f, c.graph.usageSym)
       styleCheckUse(n.sons[1].info, f)
       n.sons[0] = makeDeref(n.sons[0])
       n.sons[1] = newSymNode(f)
@@ -1580,9 +1581,8 @@ proc getMagicSym(magic: TMagic): PSym =
   result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
   result.magic = magic
 
-proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo,
-                owner = getCurrOwner()): PSym =
-  result = newSym(kind, c.cache.idAnon, owner, info)
+proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
+  result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info)
   result.flags = {sfGenSym}
 
 proc semExpandToAst(c: PContext, n: PNode): PNode =
@@ -1591,7 +1591,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
   if expandedSym.kind == skError: return n
 
   macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
-  markUsed(n.info, expandedSym)
+  markUsed(n.info, expandedSym, c.graph.usageSym)
   styleCheckUse(n.info, expandedSym)
 
   for i in countup(1, macroCall.len-1):
@@ -1682,7 +1682,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # open a scope for temporary symbol inclusions:
   let oldScope = c.currentScope
   openScope(c)
-  let oldOwnerLen = len(gOwners)
+  let oldOwnerLen = len(c.graph.owners)
   let oldGenerics = c.generics
   let oldErrorOutputs = errorOutputs
   errorOutputs = {}
@@ -1708,7 +1708,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.inGenericInst = oldInGenericInst
   c.p = oldProcCon
   msgs.setInfoContextLen(oldContextLen)
-  setLen(gOwners, oldOwnerLen)
+  setLen(c.graph.owners, oldOwnerLen)
   c.currentScope = oldScope
   errorOutputs = oldErrorOutputs
   msgs.gErrorCounter = oldErrorCount
@@ -2106,8 +2106,8 @@ proc semBlock(c: PContext, n: PNode): PNode =
     var labl = newSymG(skLabel, n.sons[0], c)
     if sfGenSym notin labl.flags:
       addDecl(c, labl)
-      n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0].info, labl)
+    n.sons[0] = newSymNode(labl, n.sons[0].info)
+    suggestSym(n.sons[0].info, labl, c.graph.usageSym)
     styleCheckDef(labl)
   n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 9d8cea862..5ac1ad11a 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -108,7 +108,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
   if trueSymbol == nil:
     localError(n.info, errSystemNeeds, "true")
-    trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
+    trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(tyBool)
 
   result.sons[0] = newSymNode(trueSymbol, n.info)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 05e398c9a..58239d23e 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -11,7 +11,7 @@
 # and evaluation phase
 
 import
-  strutils, lists, options, ast, astalgo, trees, treetab, nimsets, times,
+  strutils, options, ast, astalgo, trees, treetab, nimsets, times,
   nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
   commands, magicsys, saturate
 
@@ -628,7 +628,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
     of {skProc, skMethod}:
       result = n
     of skType:
-      result = newSymNodeTypeDesc(s, n.info)
+      # XXX gensym'ed symbols can come here and cannot be resolved. This is
+      # dirty, but correct.
+      if s.typ != nil:
+        result = newSymNodeTypeDesc(s, n.info)
     of skGenericParam:
       if s.typ.kind == tyStatic:
         if s.typ.n != nil:
@@ -656,7 +659,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
           localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
                      "sizeof")
           result = nil
-        elif skipTypes(a.typ, typedescInst).kind in
+        elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
              IntegralTypes+NilableTypes+{tySet}:
           #{tyArray,tyObject,tyTuple}:
           result = newIntNodeT(getSize(a.typ), n)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index bc80c41ad..9c05ab2f9 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -174,7 +174,11 @@ proc semGenericStmt(c: PContext, n: PNode,
     # XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
     var dummy: bool
     result = fuzzyLookup(c, n, flags, ctx, dummy)
-  of nkEmpty, nkSym..nkNilLit:
+  of nkSym:
+    let a = n.sym
+    let b = getGenSym(c, a)
+    if b != a: n.sym = b
+  of nkEmpty, succ(nkSym)..nkNilLit:
     # see tests/compile/tgensymgeneric.nim:
     # We need to open the gensym'ed symbol again so that the instantiation
     # creates a fresh copy; but this is wrong the very first reason for gensym
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 9c57be023..82d27cf10 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -61,7 +61,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
     if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
       continue
     let symKind = if q.typ.kind == tyStatic: skConst else: skType
-    var s = newSym(symKind, q.name, getCurrOwner(), q.info)
+    var s = newSym(symKind, q.name, getCurrOwner(c), q.info)
     s.flags = s.flags + {sfUsed, sfFromGeneric}
     var t = PType(idTableGet(pt, q.typ))
     if t == nil:
@@ -106,15 +106,18 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
   #if n.kind == nkSym and sfGenSym in n.sym.flags:
   #  if n.sym.owner != orig:
   #    echo "symbol ", n.sym.name.s, " orig ", orig, " owner ", n.sym.owner
-  if n.kind == nkSym and {sfGenSym, sfFromGeneric} * n.sym.flags == {sfGenSym}: # and
+  if n.kind == nkSym and sfGenSym in n.sym.flags: # and
     #  (n.sym.owner == orig or n.sym.owner.kind in {skPackage}):
     let s = n.sym
     var x = PSym(idTableGet(symMap, s))
-    if x == nil:
+    if x != nil:
+      n.sym = x
+    elif s.owner.kind == skPackage:
+      #echo "copied this ", s.name.s
       x = copySym(s, false)
       x.owner = owner
       idTablePut(symMap, s, x)
-    n.sym = x
+      n.sym = x
   else:
     for i in 0 .. <safeLen(n): freshGenSyms(n.sons[i], owner, orig, symMap)
 
@@ -252,7 +255,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   incl(result.flags, sfFromGeneric)
   result.owner = fn
   result.ast = n
-  pushOwner(result)
+  pushOwner(c, result)
 
   openScope(c)
   let gp = n.sons[genericParamsPos]
@@ -301,7 +304,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popProcCon(c)
   popInfoContext()
   closeScope(c)           # close scope for parameters
-  popOwner()
+  popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
   dec(c.instCounter)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index e72172c81..5eed1e702 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -107,7 +107,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
   if t.sonsLen > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
-    result = evalTypeTrait(n[0], t, getCurrOwner())
+    result = evalTypeTrait(n[0], t, getCurrOwner(c))
   else:
     # a typedesc variable, pass unmodified to evals
     result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 25d4b3c74..c28dbf82f 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -30,12 +30,13 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
       of nkIdent: s = lookUp(c, n.sons[0])
       of nkSym: s = n.sons[0].sym
       else: illFormedAst(n)
+      s = getGenSym(c, s)
       if s.kind == skLabel and s.owner.id == c.p.owner.id:
         var x = newSymNode(s)
         x.info = n.info
         incl(s.flags, sfUsed)
         n.sons[0] = x
-        suggestSym(x.info, s)
+        suggestSym(x.info, s, c.graph.usageSym)
         styleCheckUse(x.info, s)
       else:
         localError(n.info, errInvalidControlFlowX, s.name.s)
@@ -366,11 +367,13 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
   if isTopLevel(c):
     result = semIdentWithPragma(c, kind, n, {sfExported})
     incl(result.flags, sfGlobal)
+    #if kind in {skVar, skLet}:
+    #  echo "global variable here ", n.info, " ", result.name.s
   else:
     result = semIdentWithPragma(c, kind, n, {})
     if result.owner.kind == skModule:
       incl(result.flags, sfGlobal)
-  suggestSym(n.info, result)
+  suggestSym(n.info, result, c.graph.usageSym)
   styleCheckDef(result)
 
 proc checkNilable(v: PSym) =
@@ -621,7 +624,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
   if iter.kind != tyTuple or length == 3:
     if length == 3:
       var v = symForVar(c, n.sons[0])
-      if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
+      if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
       # BUGFIX: don't use `iter` here as that would strip away
       # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
       # for an example:
@@ -635,7 +638,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
   else:
     for i in countup(0, length - 3):
       var v = symForVar(c, n.sons[i])
-      if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal)
+      if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
       v.typ = iter.sons[i]
       n.sons[i] = newSymNode(v)
       if sfGenSym notin v.flags and not isDiscardUnderscore(v):
@@ -745,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       # We have a generic type declaration here. In generic types,
       # symbol lookup needs to be done here.
       openScope(c)
-      pushOwner(s)
+      pushOwner(c, s)
       if s.magic == mNone: s.typ.kind = tyGenericBody
       # XXX for generic type aliases this is not correct! We need the
       # underlying Id really:
@@ -769,11 +772,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         body.sym = s
         body.size = -1 # could not be computed properly
         s.typ.sons[sonsLen(s.typ) - 1] = body
-      popOwner()
+      popOwner(c)
       closeScope(c)
     elif a.sons[2].kind != nkEmpty:
       # process the type's body:
-      pushOwner(s)
+      pushOwner(c, s)
       var t = semTypeNode(c, a.sons[2], s.typ)
       if s.typ == nil:
         s.typ = t
@@ -782,7 +785,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         assignType(s.typ, t)
         #debug s.typ
       s.ast = a
-      popOwner()
+      popOwner(c)
     let aa = a.sons[2]
     if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
        aa.sons[0].kind == nkObjectTy:
@@ -793,7 +796,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       internalAssert st.lastSon.sym == nil
       incl st.flags, tfRefsAnonObj
       let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"),
-                              getCurrOwner(), s.info)
+                              getCurrOwner(c), s.info)
       obj.typ = st.lastSon
       st.lastSon.sym = obj
 
@@ -927,7 +930,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
 
 proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
   if t != nil:
-    var s = newSym(skResult, getIdent"result", getCurrOwner(), info)
+    var s = newSym(skResult, getIdent"result", getCurrOwner(c), info)
     s.typ = t
     incl(s.flags, sfUsed)
     addParamOrResult(c, s, owner)
@@ -1002,12 +1005,12 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkSonsLen(n, bodyPos + 1)
   var s: PSym
   if n[namePos].kind != nkSym:
-    s = newSym(skProc, c.cache.idAnon, getCurrOwner(), n.info)
+    s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info)
     s.ast = n
     n.sons[namePos] = newSymNode(s)
   else:
     s = n[namePos].sym
-  pushOwner(s)
+  pushOwner(c, s)
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
@@ -1047,7 +1050,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     localError(n.info, errImplOfXexpected, s.name.s)
   closeScope(c)           # close scope for parameters
-  popOwner()
+  popOwner(c)
   result.typ = s.typ
 
 proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
@@ -1081,7 +1084,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
                  params[i].sym.name.s)
     #params[i].sym.owner = s
   openScope(c)
-  pushOwner(s)
+  pushOwner(c, s)
   addParams(c, params, skProc)
   pushProcCon(c, s)
   addResult(c, n.typ.sons[0], n.info, skProc)
@@ -1089,7 +1092,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
   n.sons[bodyPos] = transformBody(c.module, semBody, s)
   popProcCon(c)
-  popOwner()
+  popOwner(c)
   closeScope(c)
 
   # alternative variant (not quite working):
@@ -1177,11 +1180,58 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       localError(n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
+proc cursorInProcAux(n: PNode): bool =
+  if inCheckpoint(n.info) != cpNone: return true
+  for i in 0..<n.safeLen:
+    if cursorInProcAux(n[i]): return true
+
+proc cursorInProc(n: PNode): bool =
+  if n.info.fileIndex == gTrackPos.fileIndex:
+    result = cursorInProcAux(n)
+
 type
   TProcCompilationSteps = enum
     stepRegisterSymbol,
     stepDetermineType,
 
+import compilerlog
+
+proc hasObjParam(s: PSym): bool =
+  var t = s.typ
+  for col in countup(1, sonsLen(t)-1):
+    if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
+      return true
+
+proc finishMethod(c: PContext, s: PSym) =
+  if hasObjParam(s):
+    methodDef(c.graph, s, false)
+
+proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
+  if isGenericRoutine(s):
+    let tt = s.typ
+    var foundObj = false
+    # we start at 1 for now so that tparsecombnum continues to compile.
+    # XXX Revisit this problem later.
+    for col in countup(1, sonsLen(tt)-1):
+      let t = tt.sons[col]
+      if t != nil and t.kind == tyGenericInvocation:
+        var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst,
+                                      tyGenericInvocation, tyGenericBody,
+                                      tyAlias})
+        if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len:
+          foundObj = true
+          x.methods.safeAdd((col,s))
+    if not foundObj:
+      message(n.info, warnDeprecated, "generic method not attachable to object type")
+  else:
+    # why check for the body? bug #2400 has none. Checking for sfForward makes
+    # no sense either.
+    # and result.sons[bodyPos].kind != nkEmpty:
+    if hasObjParam(s):
+      methodDef(c.graph, s, fromCache=false)
+    else:
+      localError(n.info, errXNeedsParamObjectType, "method")
+
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
                 validPragmas: TSpecialWords,
                 phase = stepRegisterSymbol): PNode =
@@ -1196,7 +1246,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     assert phase == stepRegisterSymbol
 
     if n[namePos].kind == nkEmpty:
-      s = newSym(kind, c.cache.idAnon, getCurrOwner(), n.info)
+      s = newSym(kind, c.cache.idAnon, getCurrOwner(c), n.info)
       incl(s.flags, sfUsed)
       isAnon = true
     else:
@@ -1213,7 +1263,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         return
   else:
     s = n[namePos].sym
-    s.owner = getCurrOwner()
+    s.owner = getCurrOwner(c)
     typeIsDetermined = s.typ == nil
     s.ast = n
     #s.scope = c.currentScope
@@ -1222,7 +1272,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   # where the proc was declared
   let oldScope = c.currentScope
   #c.currentScope = s.scope
-  pushOwner(s)
+  pushOwner(c, s)
   openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty:
@@ -1288,8 +1338,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     if importantComments() and not isNil(proto.ast.comment):
       n.comment = proto.ast.comment
     proto.ast = n             # needed for code generation
-    popOwner()
-    pushOwner(s)
+    popOwner(c)
+    pushOwner(c, s)
   s.options = gOptions
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if s.name.s[0] in {'.', '('}:
@@ -1303,30 +1353,40 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     # Macros and Templates can have generic parameters, but they are
     # only used for overload resolution (there is no instantiation of
     # the symbol, so we must process the body now)
-    pushProcCon(c, s)
-    if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
-      if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
-
-      c.p.wasForwarded = proto != nil
-      maybeAddResult(c, s, n)
-      if lfDynamicLib notin s.loc.flags:
-        # no semantic checking for importc:
-        let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-        # unfortunately we cannot skip this step when in 'system.compiles'
-        # context as it may even be evaluated in 'system.compiles':
-        n.sons[bodyPos] = transformBody(c.module, semBody, s)
+    if not usePseudoGenerics and gIdeCmd in {ideSug, ideCon} and not
+        cursorInProc(n.sons[bodyPos]):
+      discard "speed up nimsuggest"
+      if s.kind == skMethod: semMethodPrototype(c, s, n)
     else:
-      if s.typ.sons[0] != nil and kind != skIterator:
-        addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
-      openScope(c)
-      n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
-      closeScope(c)
-      fixupInstantiatedSymbols(c, s)
-    if sfImportc in s.flags:
-      # so we just ignore the body after semantic checking for importc:
-      n.sons[bodyPos] = ast.emptyNode
-    popProcCon(c)
+      pushProcCon(c, s)
+      if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
+        if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
+
+        c.p.wasForwarded = proto != nil
+        maybeAddResult(c, s, n)
+        if s.kind == skMethod: semMethodPrototype(c, s, n)
+
+        if lfDynamicLib notin s.loc.flags:
+          # no semantic checking for importc:
+          let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
+          # unfortunately we cannot skip this step when in 'system.compiles'
+          # context as it may even be evaluated in 'system.compiles':
+          n.sons[bodyPos] = transformBody(c.module, semBody, s)
+      else:
+        if s.typ.sons[0] != nil and kind != skIterator:
+          addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
+
+        openScope(c)
+        n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
+        closeScope(c)
+        fixupInstantiatedSymbols(c, s)
+        if s.kind == skMethod: semMethodPrototype(c, s, n)
+      if sfImportc in s.flags:
+        # so we just ignore the body after semantic checking for importc:
+        n.sons[bodyPos] = ast.emptyNode
+      popProcCon(c)
   else:
+    if s.kind == skMethod: semMethodPrototype(c, s, n)
     if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s)
     if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone:
       incl(s.flags, sfForward)
@@ -1334,7 +1394,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   sideEffectsCheck(c, s)
   closeScope(c)           # close scope for parameters
   # c.currentScope = oldScope
-  popOwner()
+  popOwner(c)
   if n.sons[patternPos].kind != nkEmpty:
     c.patterns.add(s)
   if isAnon: result.typ = s.typ
@@ -1345,6 +1405,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
   #if s.magic != mNone: return
+  #if s.ast.isNil: return
   discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
 
 proc semIterator(c: PContext, n: PNode): PNode =
@@ -1352,7 +1413,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
   let isAnon = n[namePos].kind == nkEmpty
   if n[namePos].kind == nkSym:
     # gensym'ed iterators might need to become closure iterators:
-    n[namePos].sym.owner = getCurrOwner()
+    n[namePos].sym.owner = getCurrOwner(c)
     n[namePos].sym.kind = skIterator
   result = semProcAux(c, n, skIterator, iteratorPragmas)
   var s = result.sons[namePos].sym
@@ -1379,46 +1440,22 @@ proc semIterator(c: PContext, n: PNode): PNode =
 proc semProc(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skProc, procPragmas)
 
-proc hasObjParam(s: PSym): bool =
-  var t = s.typ
-  for col in countup(1, sonsLen(t)-1):
-    if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
-      return true
-
-proc finishMethod(c: PContext, s: PSym) =
-  if hasObjParam(s):
-    methodDef(s, false)
-
 proc semMethod(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
-  # macros can transform methods to nothing:
+  # macros can transform converters to nothing:
   if namePos >= result.safeLen: return result
   var s = result.sons[namePos].sym
-  if isGenericRoutine(s):
-    let tt = s.typ
-    var foundObj = false
-    # we start at 1 for now so that tparsecombnum continues to compile.
-    # XXX Revisit this problem later.
-    for col in countup(1, sonsLen(tt)-1):
-      let t = tt.sons[col]
-      if t != nil and t.kind == tyGenericInvocation:
-        var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst,
-                                      tyGenericInvocation, tyGenericBody,
-                                      tyAlias})
-        if x.kind == tyObject and t.len-1 == result.sons[genericParamsPos].len:
-          foundObj = true
-          x.methods.safeAdd((col,s))
-    if not foundObj:
-      message(n.info, warnDeprecated, "generic method not attachable to object type")
-  else:
-    # why check for the body? bug #2400 has none. Checking for sfForward makes
-    # no sense either.
-    # and result.sons[bodyPos].kind != nkEmpty:
-    if hasObjParam(s):
-      methodDef(s, fromCache=false)
-    else:
-      localError(n.info, errXNeedsParamObjectType, "method")
+  # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric
+  # test case):
+  let disp = getDispatcher(s)
+  # auto return type?
+  if disp != nil and disp.typ.sons[0] != nil and disp.typ.sons[0].kind == tyExpr:
+    let ret = s.typ.sons[0]
+    disp.typ.sons[0] = ret
+    if disp.ast[resultPos].kind == nkSym:
+      if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode
+      else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter")
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index a69fe477b..d7134402f 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -60,7 +60,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
     result = newSymNode(s, n.info)
-    markUsed(n.info, s)
+    markUsed(n.info, s, c.graph.usageSym)
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
@@ -172,7 +172,7 @@ proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
   result = newSym(kind, considerQuotedIdent(n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
-  if c.scopeN == 0: incl(result.flags, sfFromGeneric)
+  #if c.scopeN == 0: incl(result.flags, sfFromGeneric)
 
 proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
   # locals default to 'gensym':
@@ -384,11 +384,13 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     checkSonsLen(n, 2)
     openScope(c)
     if n.sons[0].kind != nkEmpty:
-      # labels are always 'gensym'ed:
-      let s = newGenSym(skLabel, n.sons[0], c)
-      addPrelimDecl(c.c, s)
-      styleCheckDef(s)
-      n.sons[0] = newSymNode(s, n.sons[0].info)
+      addLocalDecl(c, n.sons[0], skLabel)
+      when false:
+        # labels are always 'gensym'ed:
+        let s = newGenSym(skLabel, n.sons[0], c)
+        addPrelimDecl(c.c, s)
+        styleCheckDef(s)
+        n.sons[0] = newSymNode(s, n.sons[0].info)
     n.sons[1] = semTemplBody(c, n.sons[1])
     closeScope(c)
   of nkTryStmt:
@@ -559,7 +561,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   styleCheckDef(s)
   # check parameter list:
   #s.scope = c.currentScope
-  pushOwner(s)
+  pushOwner(c, s)
   openScope(c)
   n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
   if n.sons[pragmasPos].kind != nkEmpty:
@@ -613,7 +615,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   # only parameters are resolved, no type checking is performed
   semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
   closeScope(c)
-  popOwner()
+  popOwner(c)
   s.ast = n
   result = n
   if n.sons[bodyPos].kind == nkEmpty:
@@ -758,7 +760,7 @@ proc semPattern(c: PContext, n: PNode): PNode =
   ctx.toMixin = initIntSet()
   ctx.toInject = initIntSet()
   ctx.c = c
-  ctx.owner = getCurrOwner()
+  ctx.owner = getCurrOwner(c)
   result = flattenStmts(semPatternBody(ctx, n))
   if result.kind in {nkStmtList, nkStmtListExpr}:
     if result.len == 1:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 17c065b49..97af2c5cc 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -297,7 +297,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
 
 proc semTypeIdent(c: PContext, n: PNode): PSym =
   if n.kind == nkSym:
-    result = n.sym
+    result = getGenSym(c, n.sym)
   else:
     when defined(nimfix):
       result = pickSym(c, n, skType)
@@ -306,7 +306,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
     else:
       result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
     if result != nil:
-      markUsed(n.info, result)
+      markUsed(n.info, result, c.graph.usageSym)
       styleCheckUse(n.info, result)
       if result.kind == skParam and result.typ.kind == tyTypeDesc:
         # This is a typedesc param. is it already bound?
@@ -608,7 +608,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     let rec = rectype.sym
     for i in countup(0, sonsLen(n)-3):
       var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
-      suggestSym(n.sons[i].info, f)
+      suggestSym(n.sons[i].info, f, c.graph.usageSym)
       f.typ = typ
       f.position = pos
       if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
@@ -750,7 +750,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
         return genericParams.sons[i].typ
 
     let owner = if typeClass.sym != nil: typeClass.sym
-                else: getCurrOwner()
+                else: getCurrOwner(c)
     var s = newSym(skType, finalTypId, owner, info)
     if typId == nil: s.flags.incl(sfAnon)
     s.linkTo(typeClass)
@@ -850,7 +850,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
 
   of tyGenericInst:
     if paramType.lastSon.kind == tyUserTypeClass:
-      var cp = copyType(paramType, getCurrOwner(), false)
+      var cp = copyType(paramType, getCurrOwner(c), false)
       cp.kind = tyUserTypeClassInst
       return addImplicitGeneric(cp)
 
@@ -876,10 +876,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       result = liftingWalk(expanded, true)
 
   of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
-    result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
+    result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true))
 
   of tyGenericParam:
-    markUsed(info, paramType.sym)
+    markUsed(info, paramType.sym, c.graph.usageSym)
     styleCheckUse(info, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
@@ -1267,7 +1267,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mExpr:
       result = semTypeNode(c, n.sons[0], nil)
       if result != nil:
-        result = copyType(result, getCurrOwner(), false)
+        result = copyType(result, getCurrOwner(c), false)
         for i in countup(1, n.len - 1):
           result.rawAddSon(semTypeNode(c, n.sons[i], nil))
     of mDistinct:
@@ -1319,8 +1319,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
           prev.id = s.typ.id
         result = prev
   of nkSym:
-    if n.sym.kind == skType and n.sym.typ != nil:
-      var t = n.sym.typ
+    let s = getGenSym(c, n.sym)
+    if s.kind == skType and s.typ != nil:
+      var t = s.typ
       let alias = maybeAliasType(c, t, prev)
       if alias != nil:
         result = alias
@@ -1329,10 +1330,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         assignType(prev, t)
         result = prev
-      markUsed(n.info, n.sym)
+      markUsed(n.info, n.sym, c.graph.usageSym)
       styleCheckUse(n.info, n.sym)
     else:
-      if n.sym.kind != skError: localError(n.info, errTypeExpected)
+      if s.kind != skError: localError(n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
   of nkObjectTy: result = semObjectNode(c, n, prev)
   of nkTupleTy: result = semTuple(c, n, prev)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 90253a691..f2caab41f 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -73,7 +73,7 @@ type
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
 
-proc markUsed*(info: TLineInfo, s: PSym)
+proc markUsed*(info: TLineInfo, s: PSym; usageSym: var PSym)
 
 template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
 
@@ -543,7 +543,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       else:
         return isNone
     when useEffectSystem:
-      if not compatibleEffects(f, a): return isNone
+      if compatibleEffects(f, a) != efCompat: return isNone
 
   of tyNil:
     result = f.allowsNil
@@ -1291,7 +1291,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
     if fdest in {isEqual, isGeneric}:
-      markUsed(arg.info, c.converters[i])
+      markUsed(arg.info, c.converters[i], c.graph.usageSym)
       var s = newSymNode(c.converters[i])
       s.typ = c.converters[i].typ
       s.info = arg.info
@@ -1551,7 +1551,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
       else: result = nil
     else:
       # only one valid interpretation found:
-      markUsed(arg.info, arg.sons[best].sym)
+      markUsed(arg.info, arg.sons[best].sym, m.c.graph.usageSym)
       styleCheckUse(arg.info, arg.sons[best].sym)
       result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
                                   argOrig)
@@ -1583,12 +1583,14 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
     result = c.semOperand(c, a, flags)
   else:
     result = a
+    considerGenSyms(c, result)
 
 proc prepareOperand(c: PContext; a: PNode): PNode =
   if a.typ.isNil:
     result = c.semOperand(c, a, {efDetermineType})
   else:
     result = a
+    considerGenSyms(c, result)
 
 proc prepareNamedParam(a: PNode) =
   if a.sons[0].kind != nkIdent:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 66876b9b5..9ae427583 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -28,7 +28,7 @@ type
     column*: int                 # Starts at 0
     doc*: string           # Not escaped (yet)
     symkind*: TSymKind
-    forth*: string               # XXX TODO object on symkind
+    forth*: string               # type
     quality*: range[0..100]   # matching quality
     isGlobal*: bool # is a global variable
     tokenLen*: int
@@ -88,7 +88,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo;
     when not defined(noDocgen):
       result.doc = s.extractDocComment
 
-proc `$`(suggest: Suggest): string =
+proc `$`*(suggest: Suggest): string =
   result = $suggest.section
   result.add(sep)
   if suggest.section == ideHighlight:
@@ -131,7 +131,7 @@ proc suggestResult(s: Suggest) =
   if not isNil(suggestionResultHook):
     suggestionResultHook(s)
   else:
-    suggestWriteln($(s))
+    suggestWriteln($s)
 
 proc filterSym(s: PSym): bool {.inline.} =
   result = s.kind != skModule
@@ -297,10 +297,10 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
       suggestOperations(c, n, typ, outputs)
 
 type
-  TCheckPointResult = enum
+  TCheckPointResult* = enum
     cpNone, cpFuzzy, cpExact
 
-proc inCheckpoint(current: TLineInfo): TCheckPointResult =
+proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
   if current.fileIndex == gTrackPos.fileIndex:
     if current.line == gTrackPos.line and
         abs(current.col-gTrackPos.col) < 4:
@@ -353,10 +353,10 @@ when defined(nimsuggest):
     s.allUsages.add(info)
 
 var
-  usageSym*: PSym
+  #usageSym*: PSym
   lastLineInfo*: TLineInfo
 
-proc findUsages(info: TLineInfo; s: PSym) =
+proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) =
   if suggestVersion < 2:
     if usageSym == nil and isTracked(info, s.name.s.len):
       usageSym = s
@@ -385,7 +385,7 @@ proc ensureIdx[T](x: var T, y: int) =
 proc ensureSeq[T](x: var seq[T]) =
   if x == nil: newSeq(x, 0)
 
-proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} =
+proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
   when defined(nimsuggest):
     if suggestVersion == 2:
@@ -395,20 +395,20 @@ proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} =
         s.addNoDup(info)
 
     if gIdeCmd == ideUse:
-      findUsages(info, s)
+      findUsages(info, s, usageSym)
     elif gIdeCmd == ideDef:
       findDefinition(info, s)
     elif gIdeCmd == ideDus and s != nil:
       if isTracked(info, s.name.s.len):
         suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100))
-      findUsages(info, s)
+      findUsages(info, s, usageSym)
     elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
       suggestResult(symToSuggest(s, isLocal=false, $ideHighlight, info, 100))
     elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
         isDecl:
       suggestResult(symToSuggest(s, isLocal=false, $ideOutline, info, 100))
 
-proc markUsed(info: TLineInfo; s: PSym) =
+proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
   incl(s.flags, sfUsed)
   if s.kind == skEnumField and s.owner != nil:
     incl(s.owner.flags, sfUsed)
@@ -416,11 +416,11 @@ proc markUsed(info: TLineInfo; s: PSym) =
     if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s)
     if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
   when defined(nimsuggest):
-    suggestSym(info, s, false)
+    suggestSym(info, s, usageSym, false)
 
-proc useSym*(sym: PSym): PNode =
+proc useSym*(sym: PSym; usageSym: var PSym): PNode =
   result = newSymNode(sym)
-  markUsed(result.info, sym)
+  markUsed(result.info, sym, usageSym)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
   # use only for idetools support!
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 13c6dd8fe..0c53c0cbf 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -19,7 +19,7 @@
 # * transforms 'defer' into a 'try finally' statement
 
 import
-  intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
+  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
   idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
   lambdalifting, sempass2, lowerings, lookups
 
@@ -694,9 +694,10 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
     # bugfix: check after 'transformSons' if it's still a method call:
     # use the dispatcher for the call:
     if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
-      let t = lastSon(s.sons[0].sym.ast)
-      if t.kind != nkSym or sfDispatcher notin t.sym.flags:
-        methodDef(s.sons[0].sym, false)
+      when false:
+        let t = lastSon(s.sons[0].sym.ast)
+        if t.kind != nkSym or sfDispatcher notin t.sym.flags:
+          methodDef(s.sons[0].sym, false)
       result = methodCall(s).PTransNode
     else:
       result = s.PTransNode
@@ -869,7 +870,12 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     else:
       result = transformSons(c, n)
   of nkIdentDefs, nkConstDef:
-    result = transformSons(c, n)
+    when true:
+      result = transformSons(c, n)
+    else:
+      result = n.PTransNode
+      let L = n.len-1
+      result[L] = transform(c, n.sons[L])
     # XXX comment handling really sucks:
     if importantComments():
       PNode(result).comment = n.comment
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 7208f7d7b..424fba14c 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -38,7 +38,7 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
         # don't go nuts here: same symbol as string is enough:
         result = a.sym.name.id == b.sym.name.id
     of nkIdent: result = a.ident.id == b.ident.id
-    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
+    of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkEmpty, nkNilLit, nkType: result = true
@@ -62,7 +62,7 @@ proc sameTree*(a, b: PNode): bool =
       # don't go nuts here: same symbol as string is enough:
       result = a.sym.name.id == b.sym.name.id
     of nkIdent: result = a.ident.id == b.ident.id
-    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
+    of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkEmpty, nkNilLit, nkType: result = true
diff --git a/compiler/types.nim b/compiler/types.nim
index d5ec2972a..b237753ae 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1377,7 +1377,16 @@ proc compatibleEffectsAux(se, re: PNode): bool =
       return false
   result = true
 
-proc compatibleEffects*(formal, actual: PType): bool =
+type
+  EffectsCompat* = enum
+    efCompat
+    efRaisesDiffer
+    efRaisesUnknown
+    efTagsDiffer
+    efTagsUnknown
+    efLockLevelsDiffer
+
+proc compatibleEffects*(formal, actual: PType): EffectsCompat =
   # for proc type compatibility checking:
   assert formal.kind == tyProc and actual.kind == tyProc
   internalAssert formal.n.sons[0].kind == nkEffectList
@@ -1393,18 +1402,21 @@ proc compatibleEffects*(formal, actual: PType): bool =
     # 'r.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler'
     if not isNil(se) and se.kind != nkArgList:
       # spec requires some exception or tag, but we don't know anything:
-      if real.len == 0: return false
-      result = compatibleEffectsAux(se, real.sons[exceptionEffects])
-      if not result: return
+      if real.len == 0: return efRaisesUnknown
+      let res = compatibleEffectsAux(se, real.sons[exceptionEffects])
+      if not res: return efRaisesDiffer
 
     let st = spec.sons[tagEffects]
     if not isNil(st) and st.kind != nkArgList:
       # spec requires some exception or tag, but we don't know anything:
-      if real.len == 0: return false
-      result = compatibleEffectsAux(st, real.sons[tagEffects])
-      if not result: return
-  result = formal.lockLevel.ord < 0 or
-      actual.lockLevel.ord <= formal.lockLevel.ord
+      if real.len == 0: return efTagsUnknown
+      let res = compatibleEffectsAux(st, real.sons[tagEffects])
+      if not res: return efTagsDiffer
+  if formal.lockLevel.ord < 0 or
+      actual.lockLevel.ord <= formal.lockLevel.ord:
+    result = efCompat
+  else:
+    result = efLockLevelsDiffer
 
 proc isCompileTimeOnly*(t: PType): bool {.inline.} =
   result = t.kind in {tyTypeDesc, tyStatic}
@@ -1508,6 +1520,21 @@ proc typeMismatch*(n: PNode, formal, actual: PType) =
     let named = typeToString(formal)
     let desc = typeToString(formal, preferDesc)
     let x = if named == desc: named else: named & " = " & desc
-    localError(n.info, errGenerated, msgKindToString(errTypeMismatch) &
-        typeToString(actual) & ") " &
-        `%`(msgKindToString(errButExpectedX), [x]))
+    var msg = msgKindToString(errTypeMismatch) &
+              typeToString(actual) & ") " &
+              msgKindToString(errButExpectedX) % [x]
+
+    if formal.kind == tyProc and actual.kind == tyProc:
+      case compatibleEffects(formal, actual)
+      of efCompat: discard
+      of efRaisesDiffer:
+        msg.add "\n.raise effects differ"
+      of efRaisesUnknown:
+        msg.add "\n.raise effect is 'can raise any'"
+      of efTagsDiffer:
+        msg.add "\n.tag effects differ"
+      of efTagsUnknown:
+        msg.add "\n.tag effect is 'any tag allowed'"
+      of efLockLevelsDiffer:
+        msg.add "\nlock levels differ"
+    localError(n.info, errGenerated, msg)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 5d4784281..6a9545193 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1556,7 +1556,10 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
     result = n
   oldErrorCount = msgs.gErrorCounter
 
-const evalPass* = makePass(myOpen, nil, myProcess, myProcess)
+proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
+  myProcess(c, n)
+
+const evalPass* = makePass(myOpen, nil, myProcess, myClose)
 
 proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index cf66b6358..6072bd64c 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -45,7 +45,7 @@ type
     wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit,
     wAlign, wNodecl, wPure, wSideeffect, wHeader,
     wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
-    wCompilerproc, wProcVar, wBase,
+    wCompilerproc, wProcVar, wBase, wUsed,
     wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
     wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
     wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
@@ -131,7 +131,7 @@ const
     "incompletestruct",
     "requiresinit", "align", "nodecl", "pure", "sideeffect",
     "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
-    "compilerproc", "procvar", "base",
+    "compilerproc", "procvar", "base", "used",
     "fatal", "error", "warning", "hint", "line",
     "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
     "link", "compile", "linksys", "deprecated", "varargs",