summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-10-25 08:50:47 +0100
committerGitHub <noreply@github.com>2020-10-25 08:50:47 +0100
commit226595515c25785eaf078834dfb6f0ac337a5278 (patch)
treea534966892d7712baf77b4d4f340fdbe42c0f3c8
parent7252a50fefb844d97ad4630c1db00063380ff3c4 (diff)
downloadNim-226595515c25785eaf078834dfb6f0ac337a5278.tar.gz
explicit ID generation for easier IC (#15559)
* refactoring: idents don't need inheritance
* refactoring: adding an IdGenerator (part 1)
* refactoring: adding an IdGenerator (part 2)
* refactoring: adding an IdGenerator (part 3)
* refactoring: adding an IdGenerator (part 4)
* refactoring: adding an IdGenerator (part 5)
* refactoring: adding an IdGenerator (part 5)
* IdGenerator must be a ref type; hello world works again
* make bootstrapping work again
* progress: add back the 'exactReplica' ideas
* added back the missing exactReplica hacks
* make tcompilerapi work again
* make important packages green
* attempt to fix the build for 32 bit machines (probably need a better solution here)
-rw-r--r--compiler/ast.nim93
-rw-r--r--compiler/ccgexprs.nim20
-rw-r--r--compiler/ccgmerge.nim9
-rw-r--r--compiler/ccgstmts.nim4
-rw-r--r--compiler/ccgtypes.nim14
-rw-r--r--compiler/cgen.nim11
-rw-r--r--compiler/cgmeth.nim12
-rw-r--r--compiler/closureiters.nim35
-rw-r--r--compiler/cmdlinehelper.nim2
-rw-r--r--compiler/depends.nim2
-rw-r--r--compiler/docgen2.nim4
-rw-r--r--compiler/enumtostr.nim20
-rw-r--r--compiler/evaltempl.nim5
-rw-r--r--compiler/guards.nim5
-rw-r--r--compiler/hlo.nim4
-rw-r--r--compiler/idents.nim7
-rw-r--r--compiler/idgen.nim59
-rw-r--r--compiler/importer.nim2
-rw-r--r--compiler/injectdestructors.nim27
-rw-r--r--compiler/isolation_check.nim2
-rw-r--r--compiler/jsgen.nim18
-rw-r--r--compiler/lambdalifting.nim138
-rw-r--r--compiler/liftdestructors.nim99
-rw-r--r--compiler/liftlocals.nim8
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/lowerings.nim61
-rw-r--r--compiler/magicsys.nim30
-rw-r--r--compiler/main.nim17
-rw-r--r--compiler/modulegraphs.nim7
-rw-r--r--compiler/modules.nim21
-rw-r--r--compiler/nimconf.nim21
-rw-r--r--compiler/nimeval.nim14
-rw-r--r--compiler/passaux.nim8
-rw-r--r--compiler/passes.nim32
-rw-r--r--compiler/plugins/itersgen.nim6
-rw-r--r--compiler/plugins/locals.nim4
-rw-r--r--compiler/pragmas.nim8
-rw-r--r--compiler/rod.nim4
-rw-r--r--compiler/rodimpl.nim4
-rw-r--r--compiler/scriptconfig.nim10
-rw-r--r--compiler/sem.nim44
-rw-r--r--compiler/semcall.nim4
-rw-r--r--compiler/semdata.nim35
-rw-r--r--compiler/semexprs.nim54
-rw-r--r--compiler/semfields.nim2
-rw-r--r--compiler/semfold.nim131
-rw-r--r--compiler/semgnrc.nim10
-rw-r--r--compiler/seminst.nim18
-rw-r--r--compiler/semmagic.nim38
-rw-r--r--compiler/semparallel.nim30
-rw-r--r--compiler/sempass2.nim18
-rw-r--r--compiler/semstmts.nim44
-rw-r--r--compiler/semtempl.nim6
-rw-r--r--compiler/semtypes.nim84
-rw-r--r--compiler/semtypinst.nim19
-rw-r--r--compiler/sigmatch.nim18
-rw-r--r--compiler/sinkparameter_inference.nim12
-rw-r--r--compiler/spawn.nim107
-rw-r--r--compiler/transf.nim62
-rw-r--r--compiler/types.nim12
-rw-r--r--compiler/vm.nim68
-rw-r--r--compiler/vmdef.nim7
-rw-r--r--compiler/vmdeps.nim51
-rw-r--r--compiler/vmgen.nim6
-rw-r--r--compiler/vmmarshal.nim23
-rw-r--r--compiler/vmops.nim2
-rw-r--r--lib/system/iterators.nim2
67 files changed, 853 insertions, 903 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index d81ccf33b..a9793fb21 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,7 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  lineinfos, hashes, options, ropes, idents, idgen, int128
+  lineinfos, hashes, options, ropes, idents, int128
 from strutils import toLowerAscii
 
 export int128
@@ -717,6 +717,14 @@ const
     mInSet, mRepr}
 
 type
+  ItemId* = object
+    module*: int32
+    item*: int32
+
+  TIdObj* = object of RootObj
+    itemId*: ItemId
+  PIdObj* = ref TIdObj
+
   PNode* = ref TNode
   TNodeSeq* = seq[PNode]
   PType* = ref TType
@@ -926,7 +934,7 @@ type
     loc*: TLoc
     typeInst*: PType          # for generic instantiations the tyGenericInst that led to this
                               # type.
-    uniqueId*: int            # due to a design mistake, we need to keep the real ID here as it
+    uniqueId*: ItemId         # due to a design mistake, we need to keep the real ID here as it
                               # required by the --incremental:on mode.
 
   TPair* = object
@@ -1060,6 +1068,35 @@ proc getnimblePkg*(a: PSym): PSym =
     else:
       assert false, $result.kind
 
+const
+  moduleShift = when defined(cpu32): 20 else: 24
+
+template id*(a: PIdObj): int =
+  let x = a
+  (x.itemId.module.int shl moduleShift) + x.itemId.item.int
+
+type
+  IdGenerator* = ref ItemId # unfortunately, we really need the 'shared mutable' aspect here.
+
+const
+  PackageModuleId* = -3'i32
+
+proc idGeneratorFromModule*(m: PSym): IdGenerator =
+  assert m.kind == skModule
+  result = IdGenerator(module: m.itemId.module, item: m.itemId.item)
+
+proc nextId*(x: IdGenerator): ItemId {.inline.} =
+  inc x.item
+  result = x[]
+
+when false:
+  proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
+    assert dest.ItemId.module == src.ItemId.module
+    if dest.ItemId.item > src.ItemId.item:
+      echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module
+    assert dest.ItemId.item <= src.ItemId.item
+    dest = src
+
 proc getnimblePkgId*(a: PSym): int =
   let b = a.getnimblePkg
   result = if b == nil: -1 else: b.id
@@ -1164,13 +1201,11 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[
 template previouslyInferred*(t: PType): PType =
   if t.sons.len > 1: t.lastSon else: nil
 
-proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
+proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
              info: TLineInfo; options: TOptions = {}): PSym =
   # generates a symbol and initializes the hash field too
-  result = PSym(name: name, kind: symKind, flags: {}, info: info, id: getID(),
+  result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
                 options: options, owner: owner, offset: defaultOffset)
-  when debugIds:
-    registerId(result)
 
 proc astdef*(s: PSym): PNode =
   # get only the definition (initializer) portion of the ast
@@ -1344,13 +1379,11 @@ proc `$`*(s: PSym): string =
   else:
     result = "<nil>"
 
-proc newType*(kind: TTypeKind, owner: PSym): PType =
-  let id = getID()
+proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
   result = PType(kind: kind, owner: owner, size: defaultSize,
-                 align: defaultAlignment, id: id, uniqueId: id,
-                 lockLevel: UnspecifiedLockLevel)
-  when debugIds:
-    registerId(result)
+                 align: defaultAlignment, itemId: id,
+                 lockLevel: UnspecifiedLockLevel,
+                 uniqueId: id)
   when false:
     if result.id == 76426:
       echo "KNID ", kind
@@ -1392,22 +1425,18 @@ proc assignType*(dest, src: PType) =
   newSons(dest, src.len)
   for i in 0..<src.len: dest[i] = src[i]
 
-proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
-  result = newType(t.kind, owner)
+proc copyType*(t: PType, id: ItemId, owner: PSym): PType =
+  result = newType(t.kind, id, owner)
   assignType(result, t)
-  if keepId:
-    result.id = t.id
-  else:
-    when debugIds: registerId(result)
   result.sym = t.sym          # backend-info should not be copied
 
-proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)
+proc exactReplica*(t: PType): PType =
+  result = copyType(t, t.itemId, t.owner)
 
-proc copySym*(s: PSym): PSym =
-  result = newSym(s.kind, s.name, s.owner, s.info, s.options)
+proc copySym*(s: PSym; id: ItemId): PSym =
+  result = newSym(s.kind, s.name, id, s.owner, s.info, s.options)
   #result.ast = nil            # BUGFIX; was: s.ast which made problems
   result.typ = s.typ
-  when debugIds: registerId(result)
   result.flags = s.flags
   result.magic = s.magic
   if s.kind == skModule:
@@ -1421,12 +1450,12 @@ proc copySym*(s: PSym): PSym =
     result.bitsize = s.bitsize
     result.alignment = s.alignment
 
-proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
+proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
                         options: TOptions): PSym =
-  result = newSym(s.kind, newIdent, s.owner, info, options)
+  result = newSym(s.kind, newIdent, id, s.owner, info, options)
   # keep ID!
   result.ast = s.ast
-  result.id = s.id
+  #result.id = s.id # XXX figure out what to do with the ID.
   result.flags = s.flags
   system.shallowCopy(result.tab, s.tab)
   result.options = s.options
@@ -1576,7 +1605,7 @@ proc transitionNoneToSym*(n: PNode) =
 
 template transitionSymKindCommon*(k: TSymKind) =
   let obj {.inject.} = s[]
-  s[] = TSym(kind: k, id: obj.id, magic: obj.magic, typ: obj.typ, name: obj.name,
+  s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
              info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
              options: obj.options, position: obj.position, offset: obj.offset,
              loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
@@ -1797,19 +1826,19 @@ proc skipStmtList*(n: PNode): PNode =
   else:
     result = n
 
-proc toVar*(typ: PType; kind: TTypeKind): PType =
+proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
   ## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
   result = typ
   if typ.kind != kind:
-    result = newType(kind, typ.owner)
+    result = newType(kind, nextId(idgen), typ.owner)
     rawAddSon(result, typ)
 
-proc toRef*(typ: PType): PType =
+proc toRef*(typ: PType; idgen: IdGenerator): PType =
   ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
   if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
-    result = newType(tyRef, typ.owner)
+    result = newType(tyRef, nextId(idgen), typ.owner)
     rawAddSon(result, typ)
 
 proc toObject*(typ: PType): PType =
@@ -1888,8 +1917,8 @@ proc isSinkParam*(s: PSym): bool {.inline.} =
 proc isSinkType*(t: PType): bool {.inline.} =
   t.kind == tySink or tfHasOwned in t.flags
 
-proc newProcType*(info: TLineInfo; owner: PSym): PType =
-  result = newType(tyProc, owner)
+proc newProcType*(info: TLineInfo; id: ItemId; owner: PSym): PType =
+  result = newType(tyProc, id, owner)
   result.n = newNodeI(nkFormalParams, info)
   rawAddSon(result, nil) # return type
   # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 5c897e1a4..b6caec760 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1765,20 +1765,20 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
   else: internalError(p.config, e.info, "genArrayLen()")
 
-proc makePtrType(baseType: PType): PType =
-  result = newType(tyPtr, baseType.owner)
-  addSonSkipIntLit(result, baseType)
+proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, nextId idgen, baseType.owner)
+  addSonSkipIntLit(result, baseType, idgen)
 
-proc makeAddr(n: PNode): PNode =
+proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkHiddenAddr:
     result = n
   else:
     result = newTree(nkHiddenAddr, n)
-    result.typ = makePtrType(n.typ)
+    result.typ = makePtrType(n.typ, idgen)
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   if optSeqDestructors in p.config.globalOptions:
-    e[1] = makeAddr(e[1])
+    e[1] = makeAddr(e[1], p.module.idgen)
     genCall(p, e, d)
     return
   var a, b, call: TLoc
@@ -2240,7 +2240,7 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
       toStrProc = p
       break
   if toStrProc == nil:
-    toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph)
+    toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen)
     t.methods.add((ToStringProcSlot, toStrProc))
   var n = copyTree(e)
   n[0] = newSymNode(toStrProc)
@@ -2294,7 +2294,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mAppendStrStr: genStrAppend(p, e, d)
   of mAppendSeqElem:
     if optSeqDestructors in p.config.globalOptions:
-      e[1] = makeAddr(e[1])
+      e[1] = makeAddr(e[1], p.module.idgen)
       genCall(p, e, d)
     else:
       genSeqElemAppend(p, e, d)
@@ -2393,13 +2393,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     when defined(leanCompiler):
       quit "compiler built without support for the 'spawn' statement"
     else:
-      let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
+      let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil)
       expr(p, n, d)
   of mParallel:
     when defined(leanCompiler):
       quit "compiler built without support for the 'parallel' statement"
     else:
-      let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e)
+      let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
       expr(p, n, d)
   of mDeepCopy:
     if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index a87d5b609..40833a64e 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -180,10 +180,6 @@ proc readKey(L: var TBaseLexer, result: var string) =
   if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
   L.bufpos = pos + 1 # skip ':'
 
-proc newFakeType(id: int): PType =
-  new(result)
-  result.id = id
-
 proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
   if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
   inc L.bufpos
@@ -192,10 +188,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
     var key = decodeStr(L.buf, L.bufpos)
     if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
     inc L.bufpos
-    var value = decodeStr(L.buf, L.bufpos)
-    # XXX implement me
-    when false:
-      idTablePut(result, newFakeType(key), value.rope)
+    discard decodeStr(L.buf, L.bufpos)
   inc L.bufpos
 
 proc readIntSet(L: var TBaseLexer, result: var IntSet) =
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 1282cf11f..aaed37508 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -66,7 +66,7 @@ proc genVarTuple(p: BProc, n: PNode) =
   # if we have a something that's been captured, use the lowering instead:
   for i in 0..<n.len-2:
     if n[i].kind != nkSym:
-      genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc))
+      genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.module.idgen, p.prc))
       return
 
   # check only the first son
@@ -1525,7 +1525,7 @@ proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLo
       theProc = p
       break
   if theProc == nil:
-    theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph)
+    theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
     t.methods.add((ObjDiscMappingProcSlot, theProc))
   var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
   call.add newSymNode(theProc)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 6c82a140b..e3f832d68 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -1266,10 +1266,10 @@ proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
 
 proc fakeClosureType(m: BModule; owner: PSym): PType =
   # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
-  result = newType(tyTuple, owner)
-  result.rawAddSon(newType(tyPointer, owner))
-  var r = newType(tyRef, owner)
-  let obj = createObj(m.g.graph, owner, owner.info, final=false)
+  result = newType(tyTuple, nextId m.idgen, owner)
+  result.rawAddSon(newType(tyPointer, nextId m.idgen, owner))
+  var r = newType(tyRef, nextId m.idgen, owner)
+  let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false)
   r.rawAddSon(obj)
   result.rawAddSon(r)
 
@@ -1395,9 +1395,9 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
   result = prefixTI.rope & result & ")".rope
 
 proc openArrayToTuple(m: BModule; t: PType): PType =
-  result = newType(tyTuple, t.owner)
-  let p = newType(tyPtr, t.owner)
-  let a = newType(tyUncheckedArray, t.owner)
+  result = newType(tyTuple, nextId m.idgen, t.owner)
+  let p = newType(tyPtr, nextId m.idgen, t.owner)
+  let a = newType(tyUncheckedArray, nextId m.idgen, t.owner)
   a.add t.lastSon
   p.add a
   result.add p
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 5c60a3cf3..f0120c435 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -996,9 +996,9 @@ proc genProcAux(m: BModule, prc: PSym) =
   var returnStmt: Rope = nil
   assert(prc.ast != nil)
 
-  var procBody = transformBody(m.g.graph, prc, cache = false)
+  var procBody = transformBody(m.g.graph, m.idgen, prc, cache = false)
   if sfInjectDestructors in prc.flags:
-    procBody = injectDestructorCalls(m.g.graph, prc, procBody)
+    procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
 
   if sfPure notin prc.flags and prc.typ[0] != nil:
     if resultPos >= prc.ast.len:
@@ -1844,9 +1844,10 @@ template injectG() {.dirty.} =
 when not defined(nimHasSinkInference):
   {.pragma: nosinks.}
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} =
+proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
   injectG()
   result = newModule(g, module, graph.config)
+  result.idgen = idgen
   if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
     let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
             else: graph.config.projectFull
@@ -1920,9 +1921,9 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   m.initProc.options = initProcOptions(m)
   #softRnl = if optLineDir in m.config.options: noRnl else: rnl
   # XXX replicate this logic!
-  var transformedN = transformStmt(m.g.graph, m.module, n)
+  var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n)
   if sfInjectDestructors in m.module.flags:
-    transformedN = injectDestructorCalls(m.g.graph, m.module, transformedN)
+    transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
 
   if m.hcrOn:
     addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 9b871a898..a0c16f2ed 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -107,11 +107,11 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
       s.ast[resultPos] = newNodeI(nkEmpty, s.info)
     s.ast[dispatcherPos] = dispatcher
 
-proc createDispatcher(s: PSym): PSym =
-  var disp = copySym(s)
+proc createDispatcher(s: PSym; idgen: IdGenerator): PSym =
+  var disp = copySym(s, nextId(idgen))
   incl(disp.flags, sfDispatcher)
   excl(disp.flags, sfExported)
-  disp.typ = copyType(disp.typ, disp.typ.owner, false)
+  disp.typ = copyType(disp.typ, nextId(idgen), disp.typ.owner)
   # we can't inline the dispatcher itself (for now):
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
   disp.ast = copyTree(s.ast)
@@ -119,7 +119,7 @@ proc createDispatcher(s: PSym): PSym =
   disp.loc.r = nil
   if s.typ[0] != nil:
     if disp.ast.len > resultPos:
-      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym)
+      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextId(idgen))
     else:
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
@@ -157,7 +157,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
       if disp.typ.lockLevel < meth.typ.lockLevel:
         disp.typ.lockLevel = meth.typ.lockLevel
 
-proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
+proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) =
   var witness: PSym
   for i in 0..<g.methods.len:
     let disp = g.methods[i].dispatcher
@@ -177,7 +177,7 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
     of Invalid:
       if witness.isNil: witness = g.methods[i].methods[0]
   # create a new dispatcher:
-  g.methods.add((methods: @[s], dispatcher: createDispatcher(s)))
+  g.methods.add((methods: @[s], dispatcher: createDispatcher(s, idgen)))
   #echo "adding ", s.info
   #if fromCache:
   #  internalError(s.info, "no method dispatcher found")
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 567bb3b91..48088a609 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -153,6 +153,7 @@ type
     curExcHandlingState: int # Negative for except, positive for finally
     nearestFinally: int # Index of the nearest finally block. For try/except it
                     # is their finally. For finally it is parent finally. Otherwise -1
+    idgen: IdGenerator
 
 const
   nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
@@ -176,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
   ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
 
 proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
-  result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info)
+  result = newSym(skVar, getIdent(ctx.g.cache, name), nextId(ctx.idgen), ctx.fn, ctx.fn.info)
   result.typ = typ
   assert(not typ.isNil)
 
@@ -189,7 +190,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
   else:
     let envParam = getEnvParam(ctx.fn)
     # let obj = envParam.typ.lastSon
-    result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache)
+    result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache, ctx.idgen)
 
 proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   if ctx.stateVarSym.isNil:
@@ -804,7 +805,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
   cmp.typ = ctx.g.getSysType(info, tyBool)
 
   let asgn = newTree(nkFastAsgn,
-    newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
+    newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
     ctx.newTmpResultAccess())
 
   let retStmt = newTree(nkReturnStmt, asgn)
@@ -1044,7 +1045,7 @@ proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
       if n[0][0].kind != nkEmpty:
         var a = newNodeI(nkAsgn, n[0][0].info)
         var retVal = n[0][0] #liftCapturedVars(n[0], owner, d, c)
-        a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn))
+        a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen))
         a.add retVal
         retStmt.add(a)
       else:
@@ -1116,10 +1117,10 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
     for i in 0..<n.len:
       n[i] = ctx.skipThroughEmptyStates(n[i])
 
-proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
-  result = newType(tyArray, owner)
+proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType =
+  result = newType(tyArray, nextId(idgen), owner)
 
-  let rng = newType(tyRange, owner)
+  let rng = newType(tyRange, nextId(idgen), owner)
   rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
   rng.rawAddSon(t)
 
@@ -1128,7 +1129,7 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
 
 proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
   result = newNodeI(nkBracket, ctx.fn.info)
-  result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.fn)
+  result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
 
   for i in ctx.exceptionTable:
     let elem = newIntNode(nkIntLit, i)
@@ -1289,10 +1290,12 @@ type
     finallys: seq[PNode]
     config: ConfigRef
     blocks: seq[(PNode, int)]
+    idgen: IdGenerator
   FreshVarsContext = object
     tab: Table[int, PSym]
     config: ConfigRef
     info: TLineInfo
+    idgen: IdGenerator
 
 proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
   case n.kind
@@ -1311,7 +1314,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
         let idefs = copyNode(it)
         for v in 0..it.len-3:
           if it[v].kind == nkSym:
-            let x = copySym(it[v].sym)
+            let x = copySym(it[v].sym, nextId(c.idgen))
             c.tab[it[v].sym.id] = x
             idefs.add newSymNode(x)
           else:
@@ -1371,27 +1374,29 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
       if fin >= 0:
         result = newNodeI(nkStmtList, n.info)
         for i in countdown(c.finallys.high, fin):
-          var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info)
+          var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
           result.add freshVars(preprocess(c, c.finallys[i]), vars)
+          c.idgen = vars.idgen
         result.add n
   of nkSkip: discard
   else:
     for i in 0 ..< n.len:
       result[i] = preprocess(c, n[i])
 
-proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
+proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
   var ctx: Ctx
   ctx.g = g
   ctx.fn = fn
+  ctx.idgen = idgen
 
   if getEnvParam(fn).isNil:
     # Lambda lifting was not done yet. Use temporary :state sym, which will
     # be handled specially by lambda lifting. Local temp vars (if needed)
     # should follow the same logic.
-    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
-    ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
-  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
-  var pc = PreprocessContext(finallys: @[], config: g.config)
+    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextId(idgen), fn, fn.info)
+    ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
+  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextId(idgen), fn, fn.info)
+  var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
   var n = preprocess(pc, n.toStmtList)
   #echo "transformed into ", n
   #var n = n.toStmtList
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
index 5fb8ab471..a415afce9 100644
--- a/compiler/cmdlinehelper.nim
+++ b/compiler/cmdlinehelper.nim
@@ -59,7 +59,7 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
                                    graph: ModuleGraph): bool =
   if self.suggestMode:
     conf.command = "nimsuggest"
-  loadConfigs(DefaultConfig, cache, conf) # load all config files
+  loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
 
   if not self.suggestMode:
     let scriptFile = conf.projectFull.changeFileExt("nims")
diff --git a/compiler/depends.nim b/compiler/depends.nim
index cc385da4b..7225b6b47 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -54,7 +54,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
 when not defined(nimHasSinkInference):
   {.pragma: nosinks.}
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} =
+proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
   var g: PGen
   new(g)
   g.module = module
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 9cb40a7fc..cfbb33156 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -68,10 +68,10 @@ template myOpenImpl(ext: untyped) {.dirty.} =
   g.doc = d
   result = g
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   myOpenImpl(HtmlExt)
 
-proc myOpenJson(graph: ModuleGraph; module: PSym): PPassContext =
+proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   myOpenImpl(JsonExt)
 
 const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
index 3eb703ade..3274462d7 100644
--- a/compiler/enumtostr.nim
+++ b/compiler/enumtostr.nim
@@ -1,16 +1,16 @@
 
 import ast, idents, lineinfos, modulegraphs, magicsys
 
-proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
-  result = newSym(skProc, getIdent(g.cache, "$"), t.owner, info)
+proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
+  result = newSym(skProc, getIdent(g.cache, "$"), nextId idgen, t.owner, info)
 
-  let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
+  let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info)
   dest.typ = t
 
-  let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info)
   res.typ = getSysType(g, info, tyString)
 
-  result.typ = newType(tyProc, t.owner)
+  result.typ = newType(tyProc, nextId idgen, t.owner)
   result.typ.n = newNodeI(nkFormalParams, info)
   rawAddSon(result.typ, res.typ)
   result.typ.n.add newNodeI(nkEffectList, info)
@@ -62,16 +62,16 @@ proc searchObjCase(t: PType; field: PSym): PNode =
     result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
   doAssert result != nil
 
-proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph): PSym =
-  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), t.owner, info)
+proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
+  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextId idgen, t.owner, info)
 
-  let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
+  let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info)
   dest.typ = field.typ
 
-  let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info)
   res.typ = getSysType(g, info, tyUInt8)
 
-  result.typ = newType(tyProc, t.owner)
+  result.typ = newType(tyProc, nextId idgen, t.owner)
   result.typ.n = newNodeI(nkFormalParams, info)
   rawAddSon(result.typ, res.typ)
   result.typ.n.add newNodeI(nkEffectList, info)
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index c1024e2fe..218a597d8 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -22,6 +22,7 @@ type
     config: ConfigRef
     ic: IdentCache
     instID: int
+    idgen: IdGenerator
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
@@ -48,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
         internalAssert c.config, sfGenSym in s.flags or s.kind == skType
         var x = PSym(idTableGet(c.mapping, s))
         if x == nil:
-          x = copySym(s)
+          x = copySym(s, nextId(c.idgen))
           # sem'check needs to set the owner properly later, see bug #9476
           x.owner = nil # c.genSymOwner
           #if x.kind == skParam and x.owner.kind == skModule:
@@ -168,6 +169,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
 proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
                    conf: ConfigRef;
                    ic: IdentCache; instID: ref int;
+                   idgen: IdGenerator;
                    fromHlo=false): PNode =
   inc(conf.evalTemplateCounter)
   if conf.evalTemplateCounter > evalTemplateLimit:
@@ -183,6 +185,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
   ctx.ic = ic
   initIdTable(ctx.mapping)
   ctx.instID = instID[]
+  ctx.idgen = idgen
 
   let body = tmpl.getBody
   #echo "instantion of ", renderTree(body, {renderIds})
diff --git a/compiler/guards.nim b/compiler/guards.nim
index a6ca44978..4f07df201 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -993,8 +993,9 @@ proc addFactLt*(m: var TModel; a, b: PNode) =
   addFactLe(m, a, bb)
 
 proc settype(n: PNode): PType =
-  result = newType(tySet, n.typ.owner)
-  addSonSkipIntLit(result, n.typ)
+  result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
+  var idgen: IdGenerator
+  addSonSkipIntLit(result, n.typ, idgen)
 
 proc buildOf(it, loc: PNode; o: Operators): PNode =
   var s = newNodeI(nkCurly, it.info, it.len-1)
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index bb69a1477..af54cabbb 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -86,9 +86,9 @@ proc hlo(c: PContext, n: PNode): PNode =
       else:
         result = fitNode(c, n.typ, result, n.info)
       # optimization has been applied so check again:
-      result = commonOptimizations(c.graph, c.module, result)
+      result = commonOptimizations(c.graph, c.idgen, c.module, result)
       result = hlo(c, result)
-      result = commonOptimizations(c.graph, c.module, result)
+      result = commonOptimizations(c.graph, c.idgen, c.module, result)
 
 proc hloBody(c: PContext, n: PNode): PNode =
   # fast exit:
diff --git a/compiler/idents.nim b/compiler/idents.nim
index a035974f3..b621503ec 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -15,12 +15,9 @@ import
   hashes, wordrecg
 
 type
-  TIdObj* = object of RootObj
-    id*: int # unique id; use this for comparisons and not the pointers
-
-  PIdObj* = ref TIdObj
   PIdent* = ref TIdent
-  TIdent*{.acyclic.} = object of TIdObj
+  TIdent*{.acyclic.} = object
+    id*: int # unique id; use this for comparisons and not the pointers
     s*: string
     next*: PIdent             # for hash-table chaining
     h*: Hash                 # hash value of s
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
deleted file mode 100644
index 7d49e33e3..000000000
--- a/compiler/idgen.nim
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module contains a simple persistent id generator.
-
-import idents, strutils, options, pathutils
-
-var gFrontEndId*: int
-
-const
-  debugIds* = false
-
-when debugIds:
-  import intsets
-
-  var usedIds = initIntSet()
-
-proc registerID*(id: PIdObj) =
-  when debugIds:
-    if id.id == -1 or containsOrIncl(usedIds, id.id):
-      internalError("ID already used: " & $id.id)
-
-proc getID*(): int {.inline.} =
-  result = gFrontEndId
-  inc(gFrontEndId)
-
-proc setId*(id: int) {.inline.} =
-  gFrontEndId = max(gFrontEndId, id + 1)
-
-proc idSynchronizationPoint*(idRange: int) =
-  gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
-
-proc toGid(conf: ConfigRef; f: AbsoluteFile): string =
-  # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
-  # will cause strange bugs if multiple projects are in the same folder, so
-  # we simply use a project independent name:
-  result = options.completeGeneratedFilePath(conf, AbsoluteFile"nim.gid").string
-
-proc saveMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
-  var f = open(toGid(conf, project), fmWrite)
-  f.writeLine($gFrontEndId)
-  f.close()
-
-proc loadMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
-  var f: File
-  if open(f, toGid(conf, project), fmRead):
-    var line = newStringOfCap(20)
-    if f.readLine(line):
-      var frontEndId = parseInt(line)
-      if f.readLine(line):
-        # var backEndId = parseInt(line)
-        gFrontEndId = max(gFrontEndId, frontEndId)
-    f.close()
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 34e76d7d9..6722eab15 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -136,7 +136,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
     localError(c.config, n.info, "module alias must be an identifier")
   elif n[1].ident.id != realModule.name.id:
     # some misguided guy will write 'import abc.foo as foo' ...
-    result = createModuleAlias(realModule, n[1].ident, realModule.info,
+    result = createModuleAlias(realModule, nextId c.idgen, n[1].ident, realModule.info,
                                c.config.options)
 
 proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index ea90299e2..0b893ede8 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -40,6 +40,7 @@ type
     inLoop, inSpawn: int
     uninit: IntSet # set of uninit'ed vars
     uninitComputed: bool
+    idgen: IdGenerator
 
   ProcessMode = enum
     normal
@@ -61,7 +62,7 @@ template dbg(body) =
       body
 
 proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
-  let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info)
+  let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextId c.idgen, c.owner, info)
   sym.typ = typ
   s.vars.add(sym)
   result = newSymNode(sym)
@@ -250,16 +251,16 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
   m.add c.owner.name.s
   localError(c.graph.config, ri.info, errGenerated, m)
 
-proc makePtrType(c: Con, baseType: PType): PType =
-  result = newType(tyPtr, c.owner)
-  addSonSkipIntLit(result, baseType)
+proc makePtrType(c: var Con, baseType: PType): PType =
+  result = newType(tyPtr, nextId c.idgen, c.owner)
+  addSonSkipIntLit(result, baseType, c.idgen)
 
-proc genOp(c: Con; op: PSym; dest: PNode): PNode =
+proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
   let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
   addrExp.add(dest)
   result = newTree(nkCall, newSymNode(op), addrExp)
 
-proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
+proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
   var op = t.attachedOps[kind]
   if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
     # give up and find the canonical type instead:
@@ -280,7 +281,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
   if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
   c.genOp(op, dest)
 
-proc genDestroy(c: Con; dest: PNode): PNode =
+proc genDestroy(c: var Con; dest: PNode): PNode =
   let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
   result = c.genOp(t, attachedDestructor, dest, nil)
 
@@ -310,7 +311,7 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
       # and copyMem(dest, source). This is efficient.
       result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
 
-proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode =
+proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode =
   let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
   result = c.genOp(t, attachedAsgn, dest, ri)
 
@@ -346,7 +347,7 @@ It is best to factor out piece of object that needs custom destructor into separ
       return
 
     # generate: if le != tmp: `=destroy`(le)
-    let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info)
+    let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
     let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
     cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
     cond.add le
@@ -377,7 +378,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
   else:
     result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
+    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextId c.idgen, c.owner, n.info)
     temp.typ = n.typ
     var v = newNodeI(nkLetSection, n.info)
     let tempAsNode = newSymNode(temp)
@@ -799,7 +800,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       for it in n:
         var ri = it[^1]
         if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
-          let x = lowerTupleUnpacking(c.graph, it, c.owner)
+          let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
           result.add p(x, c, s, consumed)
         elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ) and not isCursor(it[0], c):
           for j in 0..<it.len-2:
@@ -1010,10 +1011,10 @@ proc injectDefaultCalls(n: PNode, c: var Con) =
     for i in 0..<n.safeLen:
       injectDefaultCalls(n[i], c)
 
-proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
+proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
   if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
     return n
-  var c = Con(owner: owner, graph: g, g: constructCfg(owner, n))
+  var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen)
   dbg:
     echo "\n### ", owner.name.s, ":\nCFG:"
     echoCfg(c.g)
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim
index f3b0c4919..8c40dd4ad 100644
--- a/compiler/isolation_check.nim
+++ b/compiler/isolation_check.nim
@@ -11,7 +11,7 @@
 ## https://github.com/nim-lang/RFCs/issues/244 for more details.
 
 import
-  ast, types, renderer, idents, intsets
+  ast, types, renderer, intsets
 
 proc canAlias(arg, ret: PType; marker: var IntSet): bool
 
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index bea5225e6..4d913aa87 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -602,12 +602,12 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
   of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
   of mShrI: applyFormat("", "")
-  of mShlI: 
+  of mShlI:
     if n[1].typ.size <= 4:
       applyFormat("($1 << $2)", "($1 << $2)")
     else:
       applyFormat("($1 * Math.pow(2,$2))", "($1 * Math.pow(2,$2))")
-  of mAshrI: 
+  of mAshrI:
     if n[1].typ.size <= 4:
       applyFormat("($1 >> $2)", "($1 >> $2)")
     else:
@@ -1804,7 +1804,7 @@ proc genVarStmt(p: PProc, n: PNode) =
     var a = n[i]
     if a.kind != nkCommentStmt:
       if a.kind == nkVarTuple:
-        let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc)
+        let unpacked = lowerTupleUnpacking(p.module.graph, a, p.module.idgen, p.prc)
         genStmt(p, unpacked)
       else:
         assert(a.kind == nkIdentDefs)
@@ -2316,9 +2316,9 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
     else:
       returnStmt = "return $#;$n" % [a.res]
 
-  var transformedBody = transformBody(oldProc.module.graph, prc, cache = false)
+  var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, cache = false)
   if sfInjectDestructors in prc.flags:
-    transformedBody = injectDestructorCalls(oldProc.module.graph, prc, transformedBody)
+    transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
 
   p.nested: genStmt(p, transformedBody)
 
@@ -2607,9 +2607,9 @@ proc genModule(p: PProc, n: PNode) =
     p.body.add(frameCreate(p,
         makeJSString("module " & p.module.module.name.s),
         makeJSString(toFilenameOption(p.config, p.module.module.info.fileIndex, foStacktrace))))
-  var transformedN = transformStmt(p.module.graph, p.module.module, n)
+  var transformedN = transformStmt(p.module.graph, p.module.idgen, p.module.module, n)
   if sfInjectDestructors in p.module.module.flags:
-    transformedN = injectDestructorCalls(p.module.graph, p.module.module, transformedN)
+    transformedN = injectDestructorCalls(p.module.graph, p.module.idgen, p.module.module, transformedN)
   if p.config.hcrOn and n.kind == nkStmtList:
     let moduleSym = p.module.module
     var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" &
@@ -2686,7 +2686,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
       writeFile(outFile.string & ".map", $(%map))
     discard writeRopeIfNotEqual(code, outFile)
 
-proc myOpen(graph: ModuleGraph; s: PSym): PPassContext =
+
+proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
   result = newModule(graph, s)
+  result.idgen = idgen
 
 const JSgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index c23ca52cb..b552d9455 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -126,32 +126,32 @@ proc newCall(a: PSym, b: PNode): PNode =
   result.add newSymNode(a)
   result.add b
 
-proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
+proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType =
   var n = newNodeI(nkRange, iter.info)
   n.add newIntNode(nkIntLit, -1)
   n.add newIntNode(nkIntLit, 0)
-  result = newType(tyRange, iter)
+  result = newType(tyRange, nextId(idgen), iter)
   result.n = n
   var intType = nilOrSysInt(g)
-  if intType.isNil: intType = newType(tyInt, iter)
+  if intType.isNil: intType = newType(tyInt, nextId(idgen), iter)
   rawAddSon(result, intType)
 
-proc createStateField(g: ModuleGraph; iter: PSym): PSym =
-  result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info)
-  result.typ = createClosureIterStateType(g, iter)
+proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
+  result = newSym(skField, getIdent(g.cache, ":state"), nextId(idgen), iter, iter.info)
+  result.typ = createClosureIterStateType(g, iter, idgen)
 
-proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
+proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
   # YYY meh, just add the state field for every closure for now, it's too
   # hard to figure out if it comes from a closure iterator:
-  result = createObj(g, owner, info, final=false)
-  rawAddField(result, createStateField(g, owner))
+  result = createObj(g, idgen, owner, info, final=false)
+  rawAddField(result, createStateField(g, owner, idgen))
 
-proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym =
+proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
   if resultPos < iter.ast.len:
     result = iter.ast[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {})
+    result = newSym(skResult, getIdent(g.cache, ":result"), nextId(idgen), iter, iter.info, {})
     result.typ = iter.typ[0]
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
@@ -209,7 +209,7 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   result[0] = le
   result[1] = ri
 
-proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
+proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
   result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
   if env == nil:
@@ -219,7 +219,7 @@ proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode
       localError(g.config, info, "internal error: taking closure of closure")
     result.add(env)
   #if isClosureIterator(result.typ):
-  createTypeBoundOps(g, nil, result.typ, info)
+  createTypeBoundOps(g, nil, result.typ, info, idgen)
   if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
     prc.flags.incl sfInjectDestructors
 
@@ -237,13 +237,13 @@ proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
   let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
   result = conf.backend == backendJs and not isCompileTime
 
-proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; owner: PSym) =
-  createTypeBoundOps(g, nil, refType.lastSon, info)
-  createTypeBoundOps(g, nil, refType, info)
+proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
+  createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
+  createTypeBoundOps(g, nil, refType, info, idgen)
   if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
     owner.flags.incl sfInjectDestructors
 
-proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
   if liftingHarmful(g.config, owner): return n
   let iter = n.sym
@@ -255,10 +255,10 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   var env: PNode
   if owner.isIterator:
     let it = getHiddenParam(g, owner)
-    addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache)
+    addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
-    let e = newSym(skLet, iter.name, owner, n.info)
+    let e = newSym(skLet, iter.name, nextId(idgen), owner, n.info)
     e.typ = hp.typ
     e.flags = hp.flags
     env = newSymNode(e)
@@ -267,13 +267,13 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
     result.add(v)
   # add 'new' statement:
   result.add newCall(getSysSym(g, n.info, "internalNew"), env)
-  createTypeBoundOpsLL(g, env.typ, n.info, owner)
-  result.add makeClosure(g, iter, env, n.info)
+  createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
+  result.add makeClosure(g, idgen, iter, env, n.info)
 
-proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
+proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
-  addField(obj, s, g.cache)
+  addField(obj, s, g.cache, idgen)
 
   var access = newSymNode(envParam)
   assert obj.kind == tyObject
@@ -305,13 +305,15 @@ type
     ownerToType: Table[int, PType]
     somethingToDo: bool
     graph: ModuleGraph
+    idgen: IdGenerator
 
-proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
+proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
   result.processed = initIntSet()
   result.capturedVars = initIntSet()
   result.ownerToType = initTable[int, PType]()
   result.processed.incl(fn.id)
   result.graph = g
+  result.idgen = idgen
 
 discard """
 proc outer =
@@ -327,15 +329,15 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
                         info: TLineInfo): PType =
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
-    result = newType(tyRef, owner)
-    let obj = createEnvObj(c.graph, owner, info)
+    result = newType(tyRef, nextId(c.idgen), owner)
+    let obj = createEnvObj(c.graph, c.idgen, owner, info)
     rawAddSon(result, obj)
     c.ownerToType[owner.id] = result
 
-proc asOwnedRef(c: DetectionPass; t: PType): PType =
+proc asOwnedRef(c: var DetectionPass; t: PType): PType =
   if optOwnedRefs in c.graph.config.globalOptions:
     assert t.kind == tyRef
-    result = newType(tyOwned, t.owner)
+    result = newType(tyOwned, nextId(c.idgen), t.owner)
     result.flags.incl tfHasOwned
     result.rawAddSon t
   else:
@@ -344,7 +346,7 @@ proc asOwnedRef(c: DetectionPass; t: PType): PType =
 proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
                           info: TLineInfo): PType =
   var r = c.getEnvTypeForOwner(owner, info)
-  result = newType(tyPtr, owner)
+  result = newType(tyPtr, nextId(c.idgen), owner)
   rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
 
 proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
@@ -372,7 +374,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
       if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
         localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
   else:
-    let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
+    let result = newSym(skField, upIdent, nextId(c.idgen), obj.owner, obj.owner.info)
     result.typ = fieldType
     when false:
       if c.graph.config.selectedGC == gcDestructors:
@@ -410,7 +412,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
   let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
   let t = c.getEnvTypeForOwner(owner, info)
   if cp == nil:
-    cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info)
+    cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextId(c.idgen), fn, fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = t
     addHiddenParam(fn, cp)
@@ -433,7 +435,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
     if innerProc:
       if s.isIterator: c.somethingToDo = true
       if not c.processed.containsOrIncl(s.id):
-        let body = transformBody(c.graph, s, cache = true)
+        let body = transformBody(c.graph, c.idgen, s, cache = true)
         detectCapturedVars(body, s, c)
     let ow = s.skipGenericOwner
     if ow == owner:
@@ -446,9 +448,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
             #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
 
             if s.name.id == getIdent(c.graph.cache, ":state").id:
-              obj.n[0].sym.id = -s.id
+              obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
             else:
-              addField(obj, s, c.graph.cache)
+              addField(obj, s, c.graph.cache, c.idgen)
     # direct or indirect dependency:
     elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
       discard """
@@ -470,7 +472,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
         let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
         #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
-        addField(obj, s, c.graph.cache)
+        addField(obj, s, c.graph.cache, c.idgen)
       # create required upFields:
       var w = owner.skipGenericOwner
       if isInnerProc(w) or owner.isIterator:
@@ -535,8 +537,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
-proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNode =
-  var v = newSym(skVar, getIdent(cache, envName), owner, info)
+proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  var v = newSym(skVar, getIdent(cache, envName), nextId(idgen), owner, info)
   v.flags = {sfShadowed, sfGeneratedOp}
   v.typ = typ
   result = newSymNode(v)
@@ -548,7 +550,7 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNo
     else:
       result = newSymNode(v)
 
-proc setupEnvVar(owner: PSym; d: DetectionPass;
+proc setupEnvVar(owner: PSym; d: var DetectionPass;
                  c: var LiftingPass; info: TLineInfo): PNode =
   if owner.isIterator:
     return getHiddenParam(d.graph, owner).newSymNode
@@ -557,10 +559,10 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
       localError d.graph.config, owner.info, "internal error: could not determine closure type"
-    result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info)
+    result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
     c.envVars[owner.id] = result
     if optOwnedRefs in d.graph.config.globalOptions:
-      var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info)
+      var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextId d.idgen, owner, info)
       v.flags = {sfShadowed, sfGeneratedOp}
       v.typ = envVarType
       c.unownedEnvVars[owner.id] = newSymNode(v)
@@ -576,7 +578,7 @@ proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
       result = rawIndirectAccess(result, upField, p.info)
 
 proc rawClosureCreation(owner: PSym;
-                        d: DetectionPass; c: var LiftingPass;
+                        d: var DetectionPass; c: var LiftingPass;
                         info: TLineInfo): PNode =
   result = newNodeI(nkStmtList, owner.info)
 
@@ -602,7 +604,7 @@ proc rawClosureCreation(owner: PSym;
       let env2 = copyTree(env)
       env2.typ = unowned.typ
       result.add newAsgnStmt(unowned, env2, env.info)
-      createTypeBoundOpsLL(d.graph, unowned.typ, env.info, owner)
+      createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
 
     # add assignment statements for captured parameters:
     for i in 1..<owner.typ.n.len:
@@ -611,7 +613,7 @@ proc rawClosureCreation(owner: PSym;
         let fieldAccess = indirectAccess(env, local, env.info)
         # add ``env.param = param``
         result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
-        createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info)
+        createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
         if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
           owner.flags.incl sfInjectDestructors
 
@@ -628,28 +630,28 @@ proc rawClosureCreation(owner: PSym;
       localError(d.graph.config, env.info, "internal error: cannot create up reference")
   # we are not in the sem'check phase anymore! so pass 'nil' for the PContext
   # and hope for the best:
-  createTypeBoundOpsLL(d.graph, env.typ, owner.info, owner)
+  createTypeBoundOpsLL(d.graph, env.typ, owner.info, d.idgen, owner)
 
-proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass;
+proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
                            info: TLineInfo; res: PNode) =
   if optOwnedRefs in d.graph.config.globalOptions:
     let unowned = c.unownedEnvVars[owner.id]
     assert unowned != nil
     let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
     res.add newAsgnStmt(unowned, nilLit, info)
-    createTypeBoundOpsLL(d.graph, unowned.typ, info, owner)
+    createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
 
 proc closureCreationForIter(iter: PNode;
-                            d: DetectionPass; c: var LiftingPass): PNode =
+                            d: var DetectionPass; c: var LiftingPass): PNode =
   result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
   let owner = iter.sym.skipGenericOwner
-  var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
+  var v = newSym(skVar, getIdent(d.graph.cache, envName), nextId(d.idgen), owner, iter.info)
   incl(v.flags, sfShadowed)
   v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
   var vnode: PNode
   if owner.isIterator:
     let it = getHiddenParam(d.graph, owner)
-    addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache)
+    addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
     vnode = v.newSymNode
@@ -657,7 +659,7 @@ proc closureCreationForIter(iter: PNode;
     addVar(vs, vnode)
     result.add(vs)
   result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
-  createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, owner)
+  createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
 
   let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
   if upField != nil:
@@ -667,9 +669,9 @@ proc closureCreationForIter(iter: PNode;
                  u, iter.info))
     else:
       localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
-  result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
+  result.add makeClosure(d.graph, d.idgen, iter.sym, vnode, iter.info)
 
-proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
+proc accessViaEnvVar(n: PNode; owner: PSym; d: var DetectionPass;
                      c: var LiftingPass): PNode =
   var access = setupEnvVar(owner, d, c, n.info)
   if optOwnedRefs in d.graph.config.globalOptions:
@@ -685,21 +687,21 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
 proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
   getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}).n[0].sym
 
-proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
+proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
                       c: var LiftingPass): PNode
 
-proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
+proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
                   c: var LiftingPass): PNode =
   let s = n.sym
   if s == owner:
     # recursive calls go through (lambda, hiddenParam):
     let available = getHiddenParam(d.graph, owner)
-    result = makeClosure(d.graph, s, available.newSymNode, n.info)
+    result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
   elif s.isIterator:
     result = closureCreationForIter(n, d, c)
   elif s.skipGenericOwner == owner:
     # direct dependency, so use the outer's env variable:
-    result = makeClosure(d.graph, s, setupEnvVar(owner, d, c, n.info), n.info)
+    result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
   else:
     let available = getHiddenParam(d.graph, owner)
     let wanted = getHiddenParam(d.graph, s).typ
@@ -707,7 +709,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
     var access = newSymNode(available)
     while true:
       if access.typ == wanted:
-        return makeClosure(d.graph, s, access, n.info)
+        return makeClosure(d.graph, d.idgen, s, access, n.info)
       let obj = access.typ.skipTypes({tyOwned, tyRef, tyPtr})
       let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
       if upField == nil:
@@ -715,7 +717,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
         return n
       access = rawIndirectAccess(access, upField, n.info)
 
-proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
+proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
                       c: var LiftingPass): PNode =
   result = n
   case n.kind
@@ -727,7 +729,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         c.inContainer = 0
-        var body = transformBody(d.graph, s, cache = false)
+        var body = transformBody(d.graph, d.idgen, s, cache = false)
         body = liftCapturedVars(body, s, d, c)
         if c.envVars.getOrDefault(s.id).isNil:
           s.transformedBody = body
@@ -833,8 +835,9 @@ proc semCaptureSym*(s, owner: PSym) =
     # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
     # here
 
-proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
-  var d = initDetectionPass(g, fn)
+proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
+                     idgen: IdGenerator): PNode =
+  var d = initDetectionPass(g, fn, idgen)
   var c = initLiftingPass(fn)
   # pretend 'fn' is a closure iterator for the analysis:
   let oldKind = fn.kind
@@ -847,7 +850,8 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo
   fn.transitionRoutineSymKind(oldKind)
   fn.typ.callConv = oldCC
 
-proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
+proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
+                  idgen: IdGenerator): PNode =
   # XXX backend == backendJs does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
 
@@ -862,7 +866,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
     result = body
     tooEarly = true
   else:
-    var d = initDetectionPass(g, fn)
+    var d = initDetectionPass(g, fn, idgen)
     detectCapturedVars(body, fn, d)
     if not d.somethingToDo and fn.isIterator:
       addClosureParam(d, fn, body.info)
@@ -886,7 +890,7 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
 
 # ------------------- iterator transformation --------------------------------
 
-proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
+proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # problem ahead: the iterator could be invoked indirectly, but then
   # we don't know what environment to create here:
   #
@@ -931,7 +935,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
     let iter = op.sym
 
     let hp = getHiddenParam(g, iter)
-    env = newSym(skLet, iter.name, owner, body.info)
+    env = newSym(skLet, iter.name, nextId(idgen), owner, body.info)
     env.typ = hp.typ
     env.flags = hp.flags
 
@@ -940,7 +944,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
     result.add(v)
     # add 'new' statement:
     result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
-    createTypeBoundOpsLL(g, env.typ, body.info, owner)
+    createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
 
   elif op.kind == nkStmtListExpr:
     let closure = op.lastSon
@@ -966,7 +970,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
 
   vpart.add newNodeI(nkEmpty, body.info) # no explicit type
   if not env.isNil:
-    call[0] = makeClosure(g, call[0].sym, env.newSymNode, body.info)
+    call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
   vpart.add call
   v2.add vpart
 
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 2735fb417..30094875c 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -27,12 +27,14 @@ type
     canRaise: bool
     filterDiscriminator: PSym  # we generating destructor for case branch
     c: PContext # c can be nil, then we are called from lambdalifting!
+    idgen: IdGenerator
 
 proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym
+              info: TLineInfo; idgen: IdGenerator): PSym
 
-proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo)
+proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
+                         idgen: IdGenerator)
 
 proc at(a, i: PNode, elemType: PType): PNode =
   result = newNodeI(nkBracketExpr, a.info, 2)
@@ -73,12 +75,12 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     call.typ = t
     body.add newAsgnStmt(x, call)
 
-proc genAddr(g: ModuleGraph; x: PNode): PNode =
+proc genAddr(c: var TLiftCtx; x: PNode): PNode =
   if x.kind == nkHiddenDeref:
-    checkSonsLen(x, 1, g.config)
+    checkSonsLen(x, 1, c.g.config)
     result = x[0]
   else:
-    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
+    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ, c.idgen))
     result.add x
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
@@ -92,11 +94,11 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
 proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
   result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
 
-proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode =
+proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
   # generate: cast[ptr ObjType](cast[int](addr(x)) - offsetOf(objType.field))
   let intType = getSysType(c.g, unknownLineInfo, tyInt)
 
-  let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ))
+  let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ, c.idgen))
   addrOf.add newDeref(newSymNode(x))
   let castExpr1 = newNodeIT(nkCast, c.info, intType)
   castExpr1.add newNodeIT(nkType, c.info, intType)
@@ -113,7 +115,7 @@ proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode =
   minusExpr.typ = intType
   minusExpr.add offsetOf
 
-  let objPtr = makePtrType(objType.owner, objType)
+  let objPtr = makePtrType(objType.owner, objType, c.idgen)
   result = newNodeIT(nkCast, c.info, objPtr)
   result.add newNodeIT(nkType, c.info, objPtr)
   result.add minusExpr
@@ -121,7 +123,7 @@ proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode =
 proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
   var destroy = newNodeIT(nkCall, x.info, op.typ[0])
   destroy.add(newSymNode(op))
-  destroy.add genAddr(c.g, x)
+  destroy.add genAddr(c, x)
   if sfNeverRaises notin op.flags:
     c.canRaise = true
   if c.addMemReset:
@@ -217,7 +219,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
     # for every field (dependent on dest.kind):
     #   `=` dest.field, src.field
     # =destroy(blob)
-    var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.fn, c.info)
+    var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId c.idgen, c.fn, c.info)
     temp.typ = x.typ
     incl(temp.flags, sfFromGeneric)
     var v = newNodeI(nkVarSection, c.info)
@@ -251,7 +253,7 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
   if sfNeverRaises notin op.flags:
     c.canRaise = true
   if op.typ.sons[1].kind == tyVar:
-    result.add genAddr(c.g, x)
+    result.add genAddr(c, x)
   else:
     result.add x
   if y != nil:
@@ -311,7 +313,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     else:
       op = field
       if op == nil:
-        op = produceSym(c.g, c.c, t, c.kind, c.info)
+        op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
     if sfError in op.flags:
       incl c.fn.flags, sfError
     #else:
@@ -340,7 +342,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
       t.attachedOps[attachedDestructor] = op
 
   if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
-    op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
+    op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
     doAssert op != nil
     doAssert op == t.destructor
 
@@ -381,7 +383,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
       result = true
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.fn, c.info)
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info)
   temp.typ = getSysType(c.g, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
@@ -452,7 +454,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add genBuiltin(c.g, mDestroy, "destroy", x)
 
 proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  createTypeBoundOps(c.g, c.c, t, body.info)
+  createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
   # recursions are tricky, so we might need to forward the generated
   # operation here:
   var t = t
@@ -493,7 +495,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
   of attachedAsgn, attachedDeepCopy:
-    body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c.g, x), y)
+    body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
   of attachedSink:
     let moveCall = genBuiltin(c.g, mMove, "move", x)
     moveCall.add y
@@ -509,7 +511,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   var actions = newNodeI(nkStmtList, c.info)
   let elemType = t.lastSon
 
-  createTypeBoundOps(c.g, c.c, elemType, c.info)
+  createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
 
   if isFinal(elemType):
     addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
@@ -548,10 +550,10 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     if isFinal(elemType):
       let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
       typInfo.typ = getSysType(c.g, c.info, tyPointer)
-      body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x), typInfo, y)
+      body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
     else:
       # If the ref is polymorphic we have to account for this
-      body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x), y)
+      body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
   of attachedDispose:
     # this is crucial! dispose is like =destroy but we don't follow refs
     # as that is dealt within the cycle collector.
@@ -593,7 +595,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add genIf(c, cond, actions)
   of attachedDeepCopy: assert(false, "cannot happen")
   of attachedTrace:
-    body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv), y)
+    body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
   of attachedDispose:
     # this is crucial! dispose is like =destroy but we don't follow refs
     # as that is dealt within the cycle collector.
@@ -788,28 +790,30 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of tyOptDeprecated: doAssert false
 
 proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
-                            kind: TTypeAttachedOp; info: TLineInfo): PSym =
+                            kind: TTypeAttachedOp; info: TLineInfo;
+                            idgen: IdGenerator): PSym =
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   if baseType.attachedOps[kind] == nil:
-    discard produceSym(g, c, baseType, kind, info)
+    discard produceSym(g, c, baseType, kind, info, idgen)
   typ.attachedOps[kind] = baseType.attachedOps[kind]
   result = typ.attachedOps[kind]
 
 proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym =
+              info: TLineInfo; idgen: IdGenerator): PSym =
 
   let procname = getIdent(g.cache, AttachedOpToStr[kind])
-  result = newSym(skProc, procname, owner, info)
-  let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
-  let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), result, info)
-  dest.typ = makeVarType(typ.owner, typ)
+  result = newSym(skProc, procname, nextId(idgen), owner, info)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), nextId(idgen), result, info)
+  let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
+                   nextId(idgen), result, info)
+  dest.typ = makeVarType(typ.owner, typ, idgen)
   if kind == attachedTrace:
     src.typ = getSysType(g, info, tyPointer)
   else:
     src.typ = typ
 
-  result.typ = newProcType(info, owner)
+  result.typ = newProcType(info, nextId(idgen), owner)
   result.typ.addParam dest
   if kind notin {attachedDestructor, attachedDispose}:
     result.typ.addParam src
@@ -825,15 +829,15 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
 
 
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym =
+              info: TLineInfo; idgen: IdGenerator): PSym =
   if typ.kind == tyDistinct:
-    return produceSymDistinctType(g, c, typ, kind, info)
+    return produceSymDistinctType(g, c, typ, kind, info, idgen)
 
   result = typ.attachedOps[kind]
   if result == nil:
-    result = symPrototype(g, typ, typ.owner, kind, info)
+    result = symPrototype(g, typ, typ.owner, kind, info, idgen)
 
-  var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType:typ)
+  var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType:typ, idgen: idgen)
   a.fn = result
 
   let dest = result.typ.n[1].sym
@@ -866,18 +870,19 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
   if not a.canRaise: incl result.flags, sfNeverRaises
 
 
-proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, info: TLineInfo): PSym =
+proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
+                                        info: TLineInfo; idgen: IdGenerator): PSym =
   assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
-  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info)
-  var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ)
+  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
+  var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen)
   a.fn = result
   a.asgnForType = typ
   a.filterDiscriminator = field
   a.addMemReset = true
   let discrimantDest = result.typ.n[1].sym
 
-  let dst = newSym(skVar, getIdent(g.cache, "dest"), result, info)
-  dst.typ = makePtrType(typ.owner, typ)
+  let dst = newSym(skVar, getIdent(g.cache, "dest"), nextId(idgen), result, info)
+  dst.typ = makePtrType(typ.owner, typ, idgen)
   let dstSym = newSymNode(dst)
   let d = newDeref(dstSym)
   let v = newNodeI(nkVarSection, info)
@@ -891,12 +896,12 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
 template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   discard "now a nop"
 
-proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo) =
+proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: IdGenerator) =
   if n.kind in nkCallKinds:
     if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
       let t = n[1].typ.skipTypes(abstractVar)
       if t.destructor == nil:
-        discard produceSym(g, c, t, attachedDestructor, info)
+        discard produceSym(g, c, t, attachedDestructor, info, idgen)
 
       if t.destructor != nil:
         if t.destructor.ast[genericParamsPos].kind != nkEmpty:
@@ -904,9 +909,9 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo) =
         if t.destructor.magic == mDestroy:
           internalError(g.config, info, "patching mDestroy with mDestroy?")
         n[0] = newSymNode(t.destructor)
-  for x in n: patchBody(g, c, x, info)
+  for x in n: patchBody(g, c, x, info, idgen)
 
-template inst(field, t) =
+template inst(field, t, idgen) =
   if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
     if t.typeInst != nil:
       var a: TLiftCtx
@@ -914,17 +919,19 @@ template inst(field, t) =
       a.g = g
       a.kind = k
       a.c = c
+      a.idgen = idgen
 
       field = instantiateGeneric(a, field, t, t.typeInst)
       if field.ast != nil:
-        patchBody(g, c, field.ast, info)
+        patchBody(g, c, field.ast, info, a.idgen)
     else:
       localError(g.config, info, "unresolved generic parameter")
 
 proc isTrival(s: PSym): bool {.inline.} =
   s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
 
-proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo) =
+proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
+                        idgen: IdGenerator) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'injectdestructors' pass depends on it.
@@ -959,14 +966,14 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
   for k in attachedDestructor..lastAttached:
     generics[k] = canon.attachedOps[k] != nil
     if not generics[k]:
-      canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info)
+      canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info, idgen)
 
   # we generate the destructor first so that other operators can depend on it:
   for k in attachedDestructor..lastAttached:
     if not generics[k]:
-      discard produceSym(g, c, canon, k, info)
+      discard produceSym(g, c, canon, k, info, idgen)
     else:
-      inst(canon.attachedOps[k], canon)
+      inst(canon.attachedOps[k], canon, idgen)
     if canon != orig:
       orig.attachedOps[k] = canon.attachedOps[k]
 
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 672dca5d9..0b46c73a2 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -21,13 +21,14 @@ type
     partialParam: PSym
     objType: PType
     cache: IdentCache
+    idgen: IdGenerator
 
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and
     sfGlobal notin s.flags
 
 proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
-  let field = addUniqueField(c.objType, s, c.cache)
+  let field = addUniqueField(c.objType, s, c.cache, c.idgen)
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = c.objType
   deref.add(newSymNode(c.partialParam, info))
@@ -53,7 +54,8 @@ proc lookupParam(params, dest: PNode): PSym =
     if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
       return params[i].sym
 
-proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef): PNode =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef;
+                            idgen: IdGenerator): PNode =
   let liftDest = getPragmaVal(prc.ast, wLiftLocals)
   if liftDest == nil: return n
   let partialParam = lookupParam(prc.typ.n, liftDest)
@@ -65,7 +67,7 @@ proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: Config
   if objType.kind != tyObject or tfPartial notin objType.flags:
     localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
     return n
-  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache)
+  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache, idgen: idgen)
   let w = newTree(nkStmtList, n)
   liftLocals(w, 0, c)
   result = w[0]
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 744f77cf8..0a6a0b950 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -135,7 +135,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
       considerQuotedIdent(c, m)
     else:
       getIdent(c.cache, "err:" & renderTree(m))
-  result = newSym(skError, ident, getCurrOwner(c), n.info, {})
+  result = newSym(skError, ident, nextId(c.idgen), 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/lowerings.nim b/compiler/lowerings.nim
index 2d96f8a60..40d61b779 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -66,12 +66,13 @@ proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
   result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
   result[1].add ri
 
-proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen),
+                    owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
@@ -85,12 +86,13 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
     if n[i].kind == nkSym: v.addVar(n[i], val)
     else: result.add newAsgnStmt(n[i], val)
 
-proc evalOnce*(g: ModuleGraph; value: PNode; owner: PSym): PNode =
+proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
   ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
   ## freely, multiple times. This is frequently required and such a builtin would also be
   ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
   result = newNodeIT(nkStmtListExpr, value.info, value.typ)
-  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen),
+                    owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
@@ -111,11 +113,11 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
 proc newTryFinally*(body, final: PNode): PNode =
   result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(g.cache, "_"), owner, value.info, owner.options)
+  var temp = newSym(skTemp, getIdent(g.cache, "_"), nextId(idgen), owner, value.info, owner.options)
   var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
 
@@ -130,10 +132,10 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   for i in 0..<lhs.len:
     result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
 
-proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
-  var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options)
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextId(idgen), owner, n.info, owner.options)
   temp.typ = n[1].typ
   incl(temp.flags, sfFromGeneric)
 
@@ -150,8 +152,8 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   result.add newFastAsgnStmt(n[1], n[2])
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
-proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
-  result = newType(tyObject, owner)
+proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
+  result = newType(tyObject, nextId(idgen), owner)
   if final:
     rawAddSon(result, nil)
     incl result.flags, tfFinal
@@ -159,6 +161,7 @@ proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType
     rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
   let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
+                  nextId(idgen),
                   owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
@@ -196,7 +199,7 @@ proc rawDirectAccess*(obj, field: PSym): PNode =
   result.add newSymNode(field)
   result.typ = field.typ
 
-proc lookupInRecord(n: PNode, id: int): PSym =
+proc lookupInRecord(n: PNode, id: ItemId): PSym =
   result = nil
   case n.kind
   of nkRecList:
@@ -214,16 +217,16 @@ proc lookupInRecord(n: PNode, id: int): PSym =
         if result != nil: return
       else: discard
   of nkSym:
-    if n.sym.id == -abs(id): result = n.sym
+    if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
   else: discard
 
-proc addField*(obj: PType; s: PSym; cache: IdentCache) =
+proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
-                     s.options)
-  field.id = -s.id
-  let t = skipIntLit(s.typ)
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
+                     nextId(idgen), s.owner, s.info, s.options)
+  field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+  let t = skipIntLit(s.typ, idgen)
   field.typ = t
   assert t.kind != tyTyped
   propagateToOwner(obj, t)
@@ -232,13 +235,13 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache) =
   obj.n.add newSymNode(field)
   fieldCheck()
 
-proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} =
-  result = lookupInRecord(obj.n, s.id)
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
+  result = lookupInRecord(obj.n, s.itemId)
   if result == nil:
-    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
-                       s.options)
-    field.id = -s.id
-    let t = skipIntLit(s.typ)
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextId(idgen),
+                       s.owner, s.info, s.options)
+    field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+    let t = skipIntLit(s.typ, idgen)
     field.typ = t
     assert t.kind != tyTyped
     propagateToOwner(obj, t)
@@ -248,13 +251,13 @@ proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable
 
 proc newDotExpr*(obj, b: PSym): PNode =
   result = newNodeI(nkDotExpr, obj.info)
-  let field = lookupInRecord(obj.typ.n, b.id)
+  let field = lookupInRecord(obj.typ.n, b.itemId)
   assert field != nil, b.name.s
   result.add newSymNode(obj)
   result.add newSymNode(field)
   result.typ = field.typ
 
-proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
+proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.skipTypes(abstractInst)[0]
@@ -306,7 +309,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
   var t = t
   while true:
     assert t.kind == tyObject
-    result = lookupInRecord(t.n, v.id)
+    result = lookupInRecord(t.n, v.itemId)
     if result != nil: break
     t = t[0]
     if t == nil: break
@@ -314,15 +317,15 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
 
 proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
   # returns a[].b as a node
-  result = indirectAccess(a, b.id, info)
+  result = indirectAccess(a, b.itemId, info)
 
 proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
   result = indirectAccess(newSymNode(a), b, info)
 
-proc genAddrOf*(n: PNode, typeKind = tyPtr): PNode =
+proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
   result = newNodeI(nkAddr, n.info, 1)
   result[0] = n
-  result.typ = newType(typeKind, n.typ.owner)
+  result.typ = newType(typeKind, nextId(idgen), n.typ.owner)
   result.typ.rawAddSon(n.typ)
 
 proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 604c5b537..71003371e 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -21,7 +21,7 @@ proc registerSysType*(g: ModuleGraph; t: PType) =
   if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t
 
 proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
-  result = newType(kind, g.systemModule)
+  result = newType(kind, nextId(g.idgen), g.systemModule)
   result.size = size
   result.align = size.int16
 
@@ -29,8 +29,8 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
   result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
-    result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {})
-    result.typ = newType(tyError, g.systemModule)
+    result = newSym(skError, getIdent(g.cache, name), nextId(g.idgen), g.systemModule, g.systemModule.info, {})
+    result.typ = newType(tyError, nextId(g.idgen), g.systemModule)
   if result.kind == skAlias: result = result.owner
 
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
@@ -45,8 +45,8 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy
     r = nextIdentIter(ti, g.systemModule.tab)
   if result != nil: return result
   localError(g.config, info, "system module needs: " & name)
-  result = newSym(skError, id, g.systemModule, g.systemModule.info, {})
-  result.typ = newType(tyError, g.systemModule)
+  result = newSym(skError, id, nextId(g.idgen), g.systemModule, g.systemModule.info, {})
+  result.typ = newType(tyError, nextId(g.idgen), g.systemModule)
 
 proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
   result = getSysSym(g, info, name).typ
@@ -101,12 +101,12 @@ proc getIntLitType*(g: ModuleGraph; literal: PNode): PType =
     result = g.intTypeCache[value.int]
     if result == nil:
       let ti = getSysType(g, literal.info, tyInt)
-      result = copyType(ti, ti.owner, false)
+      result = copyType(ti, nextId(g.idgen), ti.owner)
       result.n = literal
       g.intTypeCache[value.int] = result
   else:
     let ti = getSysType(g, literal.info, tyInt)
-    result = copyType(ti, ti.owner, false)
+    result = copyType(ti, nextId(g.idgen), ti.owner)
     result.n = literal
 
 proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
@@ -114,17 +114,17 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
   result = newSysType(g, tyFloat, size=8)
   result.n = literal
 
-proc skipIntLit*(t: PType): PType {.inline.} =
+proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
   if t.n != nil and t.kind in {tyInt, tyFloat}:
-    result = copyType(t, t.owner, false)
+    result = copyType(t, nextId(id), t.owner)
     result.n = nil
   else:
     result = t
 
-proc addSonSkipIntLit*(father, son: PType) =
+proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
   when not defined(nimNoNilSeqs):
     if isNil(father.sons): father.sons = @[]
-  let s = son.skipIntLit
+  let s = son.skipIntLit(id)
   father.sons.add(s)
   propagateToOwner(father, s)
 
@@ -181,13 +181,13 @@ proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
 proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
   case t.kind
   of tyInt,  tyInt8, tyInt16, tyInt32, tyInt64,
-     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64: 
+     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
     result = getSysMagic(g, info, "==", mEqI)
-  of tyEnum: 
+  of tyEnum:
     result = getSysMagic(g, info, "==", mEqEnum)
-  of tyBool: 
+  of tyBool:
     result = getSysMagic(g, info, "==", mEqB)
-  of tyRef, tyPtr, tyPointer: 
+  of tyRef, tyPtr, tyPointer:
     result = getSysMagic(g, info, "==", mEqRef)
   of tyString:
     result = getSysMagic(g, info, "==", mEqStr)
diff --git a/compiler/main.nim b/compiler/main.nim
index d85aa43dc..210933231 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -17,7 +17,7 @@ import
   condsyms, times,
   sem, idents, passes, extccomp,
   cgen, json, nversion,
-  platform, nimconf, passaux, depends, vm, idgen,
+  platform, nimconf, passaux, depends, vm,
   modules,
   modulegraphs, tables, rod, lineinfos, pathutils, vmprofiler
 
@@ -137,12 +137,8 @@ proc commandInteractive(graph: ModuleGraph) =
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
-    processModule(graph, m, llStreamOpenStdIn())
-
-const evalPasses = [verbosePass, semPass, evalPass]
-
-proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
-  carryPasses(graph, nodes, module, evalPasses)
+    var idgen = IdGenerator(module: m.itemId.module, item: m.itemId.item)
+    processModule(graph, m, idgen, llStreamOpenStdIn())
 
 proc commandScan(cache: IdentCache, config: ConfigRef) =
   var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
@@ -173,7 +169,6 @@ proc mainCommand*(graph: ModuleGraph) =
   clearPasses(graph)
   conf.lastCmdTime = epochTime()
   conf.searchPaths.add(conf.libpath)
-  setId(100)
 
   proc customizeForBackend(backend: TBackend) =
     ## Sets backend specific options but don't compile to backend yet in
@@ -219,7 +214,7 @@ proc mainCommand*(graph: ModuleGraph) =
     else:
       wantMainModule(conf)
       conf.cmd = cmdDoc
-      loadConfigs(DocConfig, cache, conf)
+      loadConfigs(DocConfig, cache, conf, graph.idgen)
       defineSymbol(conf.symbols, "nimdoc")
       body
 
@@ -277,14 +272,14 @@ proc mainCommand*(graph: ModuleGraph) =
       quit "compiler wasn't built with documentation generator"
     else:
       conf.cmd = cmdRst2html
-      loadConfigs(DocConfig, cache, conf)
+      loadConfigs(DocConfig, cache, conf, graph.idgen)
       commandRst2Html(cache, conf)
   of "rst2tex":
     when defined(leanCompiler):
       quit "compiler wasn't built with documentation generator"
     else:
       conf.cmd = cmdRst2tex
-      loadConfigs(DocTexConfig, cache, conf)
+      loadConfigs(DocTexConfig, cache, conf, graph.idgen)
       commandRst2TeX(cache, conf)
   of "jsondoc0": docLikeCmd commandJson(cache, conf)
   of "jsondoc2", "jsondoc": docLikeCmd commandDoc2(graph, true)
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index da2a222a4..9e7a487cf 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -74,11 +74,13 @@ type
     globalDestructors*: seq[PNode]
     strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
     compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
+    idgen*: IdGenerator
 
   TPassContext* = object of RootObj # the pass's context
+    idgen*: IdGenerator
   PPassContext* = ref TPassContext
 
-  TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.}
+  TPassOpen* = proc (graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nimcall.}
   TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
   TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
 
@@ -165,12 +167,13 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
   result = g.doStopCompile != nil and g.doStopCompile()
 
 proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo, {})
+  result = newSym(skProc, getIdent(g.cache, name), nextId(g.idgen), nil, unknownLineInfo, {})
   result.magic = m
   result.flags = {sfNeverRaises}
 
 proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result = ModuleGraph()
+  result.idgen = IdGenerator(module: -1'i32, item: 0'i32)
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.importDeps = initTable[FileIndex, seq[FileIndex]]()
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 8c5b86ea4..87a6bc507 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -11,7 +11,7 @@
 
 import
   ast, astalgo, magicsys, msgs, options,
-  idents, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
+  idents, lexer, passes, syntaxes, llstream, modulegraphs, rod,
   lineinfos, pathutils, tables
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
@@ -20,6 +20,8 @@ proc resetSystemArtifacts*(g: ModuleGraph) =
 template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
   getIdent(graph.cache, splitFile(filename).name)
 
+template packageId(): untyped {.dirty.} = ItemId(module: PackageModuleId, item: int32(fileIdx))
+
 proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   ## returns package symbol (skPackage) for yet to be defined module for fileIdx
   let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
@@ -31,7 +33,7 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
     pack = getIdent(graph.cache, pck2)
   var packSym = graph.packageSyms.strTableGet(pack)
   if packSym == nil:
-    packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, info)
+    packSym = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
     initStrTable(packSym.tab)
     graph.packageSyms.strTableAdd(packSym)
   else:
@@ -47,7 +49,7 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
         # to resolve the conflicts:
         let pck3 = fakePackageName(graph.config, filename)
         # this makes the new `packSym`'s owner be the original `packSym`
-        packSym = newSym(skPackage, getIdent(graph.cache, pck3), packSym, info)
+        packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info)
         initStrTable(packSym.tab)
         graph.packageSyms.strTableAdd(packSym)
   result = packSym
@@ -69,7 +71,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
   # We cannot call ``newSym`` here, because we have to circumvent the ID
   # mechanism, which we do in order to assign each module a persistent ID.
-  result = PSym(kind: skModule, id: -1, # for better error checking
+  result = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
                 name: getModuleIdent(graph, filename),
                 info: newLineInfo(fileIdx, 1, 1))
   if not isNimIdentifier(result.name.s):
@@ -82,25 +84,21 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
   result = graph.getModule(fileIdx)
   if result == nil:
     let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
-    let (r, id) = loadModuleSym(graph, fileIdx, filename)
-    result = r
+    result = loadModuleSym(graph, fileIdx, filename)
     if result == nil:
       result = newModule(graph, fileIdx)
       result.flags.incl flags
-      result.id = id
       registerModule(graph, result)
     else:
       partialInitModule(result, graph, fileIdx, filename)
-      result.id = id
-      assert result.id < 0
-    discard processModule(graph, result,
+    discard processModule(graph, result, idGeneratorFromModule(result),
       if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
   elif graph.isDirty(result):
     result.flags.excl sfDirty
     # reset module fields:
     initStrTable(result.tab)
     result.ast = nil
-    discard processModule(graph, result,
+    discard processModule(graph, result, idGeneratorFromModule(result),
       if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
     graph.markClientsDirty(fileIdx)
 
@@ -161,7 +159,6 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
 
 proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
   result = graph.newModule(fileInfoIdx(graph.config, filename))
-  result.id = getID()
   registerModule(graph, result)
 
 proc makeModule*(graph: ModuleGraph; filename: string): PSym =
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index c3591a945..3e8c4d194 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -10,7 +10,7 @@
 # This module handles the reading of the config file.
 
 import
-  llstream, commands, os, strutils, msgs, lexer,
+  llstream, commands, os, strutils, msgs, lexer, ast,
   options, idents, wordrecg, strtabs, lineinfos, pathutils, scriptconfig
 
 # ---------------- configuration file parser -----------------------------
@@ -238,7 +238,7 @@ proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile
     if not fileExists(result): result = p / RelativeDir"etc/nim" / filename
     if not fileExists(result): result = AbsoluteDir"/etc/nim" / filename
 
-proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef) =
+proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator) =
   setDefaultLibpath(conf)
 
   var configFiles = newSeq[AbsoluteFile]()
@@ -252,7 +252,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef) =
     let p = path # eval once
     if fileExists(p):
       configFiles.add(p)
-      runNimScript(cache, p, freshDefines = false, conf)
+      runNimScript(cache, p, idgen, freshDefines = false, conf)
 
   if optSkipSystemConfigFile notin conf.globalOptions:
     readConfigFile(getSystemConfigPath(conf, cfg))
@@ -291,13 +291,12 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef) =
     # delayed to here so that `hintConf` is honored
     rawMessage(conf, hintConf, filename.string)
 
-  block:
-    let scriptFile = conf.projectFull.changeFileExt("nims")
-    if conf.command != "nimsuggest":
+  let scriptFile = conf.projectFull.changeFileExt("nims")
+  if conf.command != "nimsuggest":
+    runNimScriptIfExists(scriptFile)
+  else:
+    if scriptFile != conf.projectFull:
       runNimScriptIfExists(scriptFile)
     else:
-      if scriptFile != conf.projectFull:
-        runNimScriptIfExists(scriptFile)
-      else:
-        # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
-        discard
+      # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
+      discard
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 5d4fea9f3..3ffe347c2 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -19,6 +19,7 @@ type
     mainModule: PSym
     graph: ModuleGraph
     scriptName: string
+    idgen: IdGenerator
 
 iterator exportedSymbols*(i: Interpreter): PSym =
   assert i != nil
@@ -74,7 +75,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
 
   let s = if scriptStream != nil: scriptStream
           else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
-  processModule(i.graph, i.mainModule, s)
+  processModule(i.graph, i.mainModule, i.idgen, s)
 
 proc findNimStdLib*(): string =
   ## Tries to find a path to a valid "system.nim" file.
@@ -121,14 +122,15 @@ proc createInterpreter*(scriptName: string;
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  var vm = newCtx(m, cache, graph)
+  var idgen = idGeneratorFromModule(m)
+  var vm = newCtx(m, cache, graph, idgen)
   vm.mode = emRepl
   vm.features = flags
   if registerOps:
     vm.registerAdditionalOps() # Required to register parts of stdlib modules
   graph.vm = vm
   graph.compileSystemModule()
-  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
+  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
 
 proc destroyInterpreter*(i: Interpreter) =
   ## destructor.
@@ -162,6 +164,8 @@ proc runRepl*(r: TLLRepl;
   registerPass(graph, evalPass)
   var m = graph.makeStdinModule()
   incl(m.flags, sfMainModule)
-  if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph)
+  var idgen = idGeneratorFromModule(m)
+
+  if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
   graph.compileSystemModule()
-  processModule(graph, m, llStreamOpenStdIn(r))
+  processModule(graph, m, idgen, llStreamOpenStdIn(r))
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 155b956df..715b7d676 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,7 +10,7 @@
 ## implements some little helper passes
 
 import
-  ast, passes, idents, msgs, options, idgen, lineinfos
+  ast, passes, idents, msgs, options, lineinfos
 
 from modulegraphs import ModuleGraph, PPassContext
 
@@ -18,9 +18,9 @@ type
   VerboseRef = ref object of PPassContext
     config: ConfigRef
 
-proc verboseOpen(graph: ModuleGraph; s: PSym): PPassContext =
+proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
   #MessageOut('compiling ' + s.name.s);
-  result = VerboseRef(config: graph.config)
+  result = VerboseRef(config: graph.config, idgen: idgen)
   rawMessage(graph.config, hintProcessing, s.name.s)
 
 proc verboseProcess(context: PPassContext, n: PNode): PNode =
@@ -30,6 +30,6 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode =
     # system.nim deactivates all hints, for verbosity:3 we want the processing
     # messages nonetheless, so we activate them again (but honor cmdlineNotes)
     v.config.setNote(hintProcessing)
-    message(v.config, n.info, hintProcessing, $idgen.gFrontEndId)
+    message(v.config, n.info, hintProcessing, $v.idgen[])
 
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 2d30ebbb5..f266d2a6b 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,7 +13,7 @@
 import
   options, ast, llstream, msgs,
   idents,
-  syntaxes, idgen, modulegraphs, reorder, rod,
+  syntaxes, modulegraphs, reorder, rod,
   lineinfos, pathutils
 
 type
@@ -51,31 +51,18 @@ proc registerPass*(g: ModuleGraph; p: TPass) =
   internalAssert g.config, g.passes.len < maxPasses
   g.passes.add(p)
 
-proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
-                m: TPassData): TPassData =
-  var c = p.open(g, module)
-  result.input = p.process(c, m.input)
-  result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
-                       else: m.closeOutput
-
-proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
-                  passes: openArray[TPass]) =
-  var passdata: TPassData
-  passdata.input = nodes
-  for pass in passes:
-    passdata = carryPass(g, pass, module, passdata)
-
 proc openPasses(g: ModuleGraph; a: var TPassContextArray;
-                module: PSym) =
+                module: PSym; idgen: IdGenerator) =
   for i in 0..<g.passes.len:
     if not isNil(g.passes[i].open):
-      a[i] = g.passes[i].open(g, module)
+      a[i] = g.passes[i].open(g, module, idgen)
     else: a[i] = nil
 
 proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
   for i in 0..<graph.passes.len:
-    if not isNil(graph.passes[i].close): m = graph.passes[i].close(graph, a[i], m)
+    if not isNil(graph.passes[i].close):
+      m = graph.passes[i].close(graph, a[i], m)
     a[i] = nil                # free the memory here
 
 proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool =
@@ -123,7 +110,8 @@ proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
 proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
   result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
 
-proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
+proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
+                    stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
   var
     p: Parser
@@ -135,7 +123,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
     # new module caching mechanism:
     for i in 0..<graph.passes.len:
       if not isNil(graph.passes[i].open) and not graph.passes[i].isFrontend:
-        a[i] = graph.passes[i].open(graph, module)
+        a[i] = graph.passes[i].open(graph, module, idgen)
       else:
         a[i] = nil
 
@@ -154,7 +142,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
         m = graph.passes[i].close(graph, a[i], m)
       a[i] = nil
   else:
-    openPasses(graph, a, module)
+    openPasses(graph, a, module, idgen)
     if stream == nil:
       let filename = toFullPathConsiderDirty(graph.config, fileIdx)
       s = llStreamOpen(filename, fmRead)
@@ -215,6 +203,4 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
       closeParser(p)
       if s.kind != llsStdIn: break
     closePasses(graph, a)
-    # id synchronization point for more consistent code generation:
-    idSynchronizationPoint(1000)
   result = true
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 61dda62af..a004fd4fd 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -29,10 +29,10 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
     localError(c.config, n[2].info,
         "type must be a non-generic ref|ptr to object with state field")
     return
-  let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t)
+  let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t, c.idgen)
 
-  let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
-  prc.typ = copyType(iter.sym.typ, prc, false)
+  let prc = newSym(skProc, n[3].ident, nextId c.idgen, iter.sym.owner, iter.sym.info)
+  prc.typ = copyType(iter.sym.typ, nextId c.idgen, prc)
   excl prc.typ.flags, tfCapturesEnv
   prc.typ.n.add newSymNode(getEnvParam(iter.sym))
   prc.typ.rawAddSon t
diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim
index f3ee94547..efa8b1e94 100644
--- a/compiler/plugins/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -27,13 +27,13 @@ proc semLocals*(c: PContext, n: PNode): PNode =
             {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
 
         if it.owner == owner:
-          var field = newSym(skField, it.name, owner, n.info)
+          var field = newSym(skField, it.name, nextId c.idgen, owner, n.info)
           field.typ = it.typ.skipTypes({tyVar})
           field.position = counter
           inc(counter)
 
           tupleType.n.add newSymNode(field)
-          addSonSkipIntLit(tupleType, field.typ)
+          addSonSkipIntLit(tupleType, field.typ, c.idgen)
 
           var a = newSymNode(it, result.info)
           if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 4d5240e3c..c0c9cc06a 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -121,7 +121,7 @@ proc pragmaEnsures(c: PContext, n: PNode) =
     openScope(c)
     let o = getCurrOwner(c)
     if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
-      var s = newSym(skResult, getIdent(c.cache, "result"), o, n.info)
+      var s = newSym(skResult, getIdent(c.cache, "result"), nextId(c.idgen), o, n.info)
       s.typ = o.typ.sons[0]
       incl(s.flags, sfUsed)
       addDecl(c, s)
@@ -636,7 +636,7 @@ proc processPragma(c: PContext, n: PNode, i: int) =
   elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
     invalidPragma(c, n)
 
-  var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
+  var userPragma = newSym(skTemplate, it[1].ident, nextId(c.idgen), nil, it.info, c.config.options)
   userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
   strTableAdd(c.userPragmas, userPragma)
 
@@ -717,7 +717,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
       if dest == nil or dest.kind in routineKinds:
         localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
       let src = considerQuotedIdent(c, n[0])
-      let alias = newSym(skAlias, src, dest, n[0].info, c.config.options)
+      let alias = newSym(skAlias, src, nextId(c.idgen), dest, n[0].info, c.config.options)
       incl(alias.flags, sfExported)
       if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
       addInterfaceDecl(c, alias)
@@ -739,7 +739,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # We return a dummy symbol; later passes over the type will repair it.
       # Generic instantiation needs to know about this too. But we're lazy
       # and perform the lookup on demand instead.
-      result = newSym(skUnknown, considerQuotedIdent(c, n), nil, n.info,
+      result = newSym(skUnknown, considerQuotedIdent(c, n), nextId(c.idgen), nil, n.info,
         c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
diff --git a/compiler/rod.nim b/compiler/rod.nim
index e3e568a0e..13a4a53c9 100644
--- a/compiler/rod.nim
+++ b/compiler/rod.nim
@@ -9,14 +9,14 @@
 
 ## This module implements the canonalization for the various caching mechanisms.
 
-import ast, idgen, lineinfos, incremental, modulegraphs, pathutils
+import ast, lineinfos, incremental, modulegraphs, pathutils
 
 when not nimIncremental:
   template setupModuleCache*(g: ModuleGraph) = discard
   template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard
   template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList)
 
-  proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) {.inline.} = (nil, getID())
+  proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym) {.inline.} = nil
 
   template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard
 
diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index e50ab66de..692f9c843 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -85,9 +85,9 @@ proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): in
     db.exec(sql"delete from toplevelstmts where module = ?", module[0])
     db.exec(sql"delete from statics where module = ?", module[0])
 
-proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) =
+proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): PSym =
   let id = getModuleId(g, fileIdx, fullpath)
-  result = (g.incr.r.syms.getOrDefault(abs id), id)
+  result = g.incr.r.syms.getOrDefault(abs id)
 
 proc pushType(w: var Writer, t: PType) =
   if not containsOrIncl(w.tmarks, t.uniqueId):
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index b359c9a48..ab7e5e0b5 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -27,9 +27,9 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   setResult(a, result)
 
 proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
-              graph: ModuleGraph): PEvalContext =
+              graph: ModuleGraph; idgen: IdGenerator): PEvalContext =
   # For Nimble we need to export 'setupVM'.
-  result = newCtx(module, cache, graph)
+  result = newCtx(module, cache, graph, idgen)
   result.mode = emRepl
   registerAdditionalOps(result)
   let conf = graph.config
@@ -198,6 +198,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
       setResult(a, stdin.readAll())
 
 proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
+                   idgen: IdGenerator;
                    freshDefines=true; conf: ConfigRef) =
   let oldSymbolFiles = conf.symbolFiles
   conf.symbolFiles = disabledSf
@@ -221,10 +222,11 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  graph.vm = setupVM(m, cache, scriptName.string, graph)
+  var vm = setupVM(m, cache, scriptName.string, graph, idgen)
+  graph.vm = vm
 
   graph.compileSystemModule()
-  discard graph.processModule(m, llStreamOpen(scriptName, fmRead))
+  discard graph.processModule(m, vm.idgen, llStreamOpen(scriptName, fmRead))
 
   # watch out, "newruntime" can be set within NimScript itself and then we need
   # to remember this:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 705e1b72c..f90d9e1f9 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -14,7 +14,7 @@ import
   wordrecg, ropes, msgs, idents, renderer, types, platform, math,
   magicsys, nversion, nimsets, semfold, modulepaths, importer,
   procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
-  intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
+  intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
   lowerings, plugins/active, rod, lineinfos, strtabs, int128,
   isolation_check, typeallowed
@@ -83,7 +83,7 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
   if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped:
     changeType(c, x, formal, check=true)
   result = arg
-  result = skipHiddenSubConv(result)
+  result = skipHiddenSubConv(result, c.idgen)
 
 
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
@@ -108,7 +108,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
 
 template commonTypeBegin*(): PType = PType(kind: tyUntyped)
 
-proc commonType*(x, y: PType): PType =
+proc commonType*(c: PContext; x, y: PType): PType =
   # new type relation that is used for array constructors,
   # if expressions, etc.:
   if x == nil: return x
@@ -124,8 +124,8 @@ proc commonType*(x, y: PType): PType =
     # turn any concrete typedesc into the abstract typedesc type
     if a.len == 0: result = a
     else:
-      result = newType(tyTypeDesc, a.owner)
-      rawAddSon(result, newType(tyNone, a.owner))
+      result = newType(tyTypeDesc, nextId(c.idgen), a.owner)
+      rawAddSon(result, newType(tyNone, nextId(c.idgen), a.owner))
   elif b.kind in {tyArray, tySet, tySequence} and
       a.kind == b.kind:
     # check for seq[empty] vs. seq[int]
@@ -137,7 +137,7 @@ proc commonType*(x, y: PType): PType =
       let aEmpty = isEmptyContainer(a[i])
       let bEmpty = isEmptyContainer(b[i])
       if aEmpty != bEmpty:
-        if nt.isNil: nt = copyType(a, a.owner, false)
+        if nt.isNil: nt = copyType(a, nextId(c.idgen), a.owner)
         nt[i] = if aEmpty: b[i] else: a[i]
     if not nt.isNil: result = nt
     #elif b[idx].kind == tyEmpty: return x
@@ -149,7 +149,7 @@ proc commonType*(x, y: PType): PType =
     #  type(if b: 0 else 1) == int and not range[0..1]. For now. In the long
     # run people expect ranges to work properly within a tuple.
     if not sameType(a, b):
-      result = skipTypes(a, {tyRange}).skipIntLit
+      result = skipTypes(a, {tyRange}).skipIntLit(c.idgen)
     when false:
       if a.kind != tyRange and b.kind == tyRange:
         # XXX This really needs a better solution, but a proper fix now breaks
@@ -176,8 +176,8 @@ proc commonType*(x, y: PType): PType =
       # ill-formed AST, no need for additional tyRef/tyPtr
       if k != tyNone and x.kind != tyGenericInst:
         let r = result
-        result = newType(k, r.owner)
-        result.addSonSkipIntLit(r)
+        result = newType(k, nextId(c.idgen), r.owner)
+        result.addSonSkipIntLit(r, c.idgen)
 
 proc endsInNoReturn(n: PNode): bool =
   # check if expr ends in raise exception or call of noreturn proc
@@ -187,13 +187,13 @@ proc endsInNoReturn(n: PNode): bool =
   result = it.kind in nkLastBlockStmts or
     it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
 
-proc commonType*(x: PType, y: PNode): PType =
+proc commonType*(c: PContext; x: PType, y: PNode): PType =
   # ignore exception raising branches in case/if expressions
   if endsInNoReturn(y): return x
-  commonType(x, y.typ)
+  commonType(c, x, y.typ)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
@@ -216,7 +216,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # template; we must fix it here: see #909
     result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -255,7 +255,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
 proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
   if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent(c.cache, "AnonType"), t.owner, info)
+  result = newSym(skType, getIdent(c.cache, "AnonType"), nextId c.idgen, t.owner, info)
   result.flags.incl sfAnon
   result.typ = t
 
@@ -316,7 +316,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   var e = semExprWithType(c, n)
   if e == nil: return
 
-  result = getConstExpr(c.module, e, c.graph)
+  result = getConstExpr(c.module, e, c.idgen, c.graph)
   if result != nil: return
 
   let oldErrorCount = c.config.errorCounter
@@ -327,7 +327,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
 
   try:
-    result = evalConstExpr(c.module, c.graph, e)
+    result = evalConstExpr(c.module, c.idgen, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       result = nil
     else:
@@ -348,10 +348,10 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
   if e == nil:
     localError(c.config, n.info, errConstExprExpected)
     return n
-  result = getConstExpr(c.module, e, c.graph)
+  result = getConstExpr(c.module, e, c.idgen, c.graph)
   if result == nil:
     #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
-    result = evalConstExpr(c.module, c.graph, e)
+    result = evalConstExpr(c.module, c.idgen, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       if e.info != n.info:
         pushInfoContext(c.config, n.info)
@@ -373,7 +373,7 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     result = semExprWithType(c, n, flags)
     if efPreferStatic in flags:
-      var evaluated = getConstExpr(c.module, result, c.graph)
+      var evaluated = getConstExpr(c.module, result, c.idgen, c.graph)
       if evaluated != nil: return evaluated
       evaluated = evalAtCompileTime(c, result)
       if evaluated != nil: return evaluated
@@ -399,7 +399,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   if c.config.evalTemplateCounter > evalTemplateLimit:
     globalError(c.config, s.info, "template instantiation too nested")
   c.friendModules.add(s.owner.getModule)
-  idSynchronizationPoint(5000)
   result = macroResult
   resetSemFlag result
   if s.typ[0] == nil:
@@ -469,7 +468,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-  result = evalMacroCall(c.module, c.graph, c.templInstCounter, n, nOrig, sym)
+  result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, n, result, sym, flags)
   if c.config.macrosToExpand.hasKey(sym.name.s):
@@ -502,8 +501,9 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
         n.add prc.ast
   c.lastGenericIdx = c.generics.len
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} =
+proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
   var c = newContext(graph, module)
+  c.idgen = idgen
   if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
   c.semExpr = semExpr
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 50df1085f..dd2398f87 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -688,9 +688,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
     var x: PType
     if param.typ.kind == tyVar:
       x = newTypeS(param.typ.kind, c)
-      x.addSonSkipIntLit t.baseOfDistinct
+      x.addSonSkipIntLit(t.baseOfDistinct(c.idgen), c.idgen)
     else:
-      x = t.baseOfDistinct
+      x = t.baseOfDistinct(c.idgen)
     call.add(newNodeIT(nkEmpty, fn.info, x))
   if hasDistinct:
     let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index b15f65967..8d1c25e4d 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -277,14 +277,14 @@ proc addToLib*(lib: PLib, sym: PSym) =
   sym.annex = lib
 
 proc newTypeS*(kind: TTypeKind, c: PContext): PType =
-  result = newType(kind, getCurrOwner(c))
+  result = newType(kind, nextId(c.idgen), getCurrOwner(c))
 
-proc makePtrType*(owner: PSym, baseType: PType): PType =
-  result = newType(tyPtr, owner)
-  addSonSkipIntLit(result, baseType)
+proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, nextId(idgen), owner)
+  addSonSkipIntLit(result, baseType, idgen)
 
 proc makePtrType*(c: PContext, baseType: PType): PType =
-  makePtrType(getCurrOwner(c), baseType)
+  makePtrType(getCurrOwner(c), baseType, c.idgen)
 
 proc makeTypeWithModifier*(c: PContext,
                            modifier: TTypeKind,
@@ -295,21 +295,21 @@ proc makeTypeWithModifier*(c: PContext,
     result = baseType
   else:
     result = newTypeS(modifier, c)
-    addSonSkipIntLit(result, baseType)
+    addSonSkipIntLit(result, baseType, c.idgen)
 
 proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
   if baseType.kind == kind:
     result = baseType
   else:
     result = newTypeS(kind, c)
-    addSonSkipIntLit(result, baseType)
+    addSonSkipIntLit(result, baseType, c.idgen)
 
-proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType =
+proc makeVarType*(owner: PSym, baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
   if baseType.kind == kind:
     result = baseType
   else:
-    result = newType(kind, owner)
-    addSonSkipIntLit(result, baseType)
+    result = newType(kind, nextId(idgen), owner)
+    addSonSkipIntLit(result, baseType, idgen)
 
 proc makeTypeDesc*(c: PContext, typ: PType): PType =
   if typ.kind == tyTypeDesc:
@@ -317,14 +317,14 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
   else:
     result = newTypeS(tyTypeDesc, c)
     incl result.flags, tfCheckedForDestructor
-    result.addSonSkipIntLit(typ)
+    result.addSonSkipIntLit(typ, c.idgen)
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
   let typedesc = newTypeS(tyTypeDesc, c)
   incl typedesc.flags, tfCheckedForDestructor
   internalAssert(c.config, typ != nil)
-  typedesc.addSonSkipIntLit(typ)
-  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
+  typedesc.addSonSkipIntLit(typ, c.idgen)
+  let sym = newSym(skType, c.cache.idAnon, nextId(c.idgen), getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
   return newSymNode(sym, info)
 
@@ -333,13 +333,14 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
   assert n != nil
   result.n = n
 
-proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType =
-  result = newType(kind, owner)
+proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
+                      idgen: IdGenerator): PType =
+  result = newType(kind, nextId(idgen), owner)
   result.sons = sons
 
 proc newTypeWithSons*(c: PContext, kind: TTypeKind,
                       sons: seq[PType]): PType =
-  result = newType(kind, getCurrOwner(c))
+  result = newType(kind, nextId(c.idgen), getCurrOwner(c))
   result.sons = sons
 
 proc makeStaticExpr*(c: PContext, n: PNode): PNode =
@@ -418,7 +419,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
   n.add newIntTypeNode(last, intType)
   result = newTypeS(tyRange, c)
   result.n = n
-  addSonSkipIntLit(result, intType) # basetype of range
+  addSonSkipIntLit(result, intType, c.idgen) # basetype of range
 
 proc markIndirect*(c: PContext, s: PSym) {.inline.} =
   if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index cb852c68a..2de25dadb 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -33,7 +33,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   # Note: This is n.info on purpose. It prevents template from creating an info
   # context when called from an another template
   pushInfoContext(c.config, n.info, s.detailedInfo)
-  result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, c.templInstCounter, efFromHlo in flags)
+  result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache,
+                        c.templInstCounter, c.idgen, efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
   popInfoContext(c.config)
 
@@ -384,7 +385,7 @@ proc fixupStaticType(c: PContext, n: PNode) =
   # apply this measure only in code that is enlightened to work
   # with static types.
   if n.typ.kind != tyStatic:
-    n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
+    n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ], c.idgen)
     n.typ.n = n # XXX: cycles like the one here look dangerous.
                 # Consider using `n.copyTree`
 
@@ -555,7 +556,7 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
     rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
   else:
     var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-    addSonSkipIntLit(typ, t)
+    addSonSkipIntLit(typ, t, c.idgen)
   typ[0] = makeRangeType(c, 0, n.len - 1, n.info)
   result = typ
 
@@ -605,11 +606,11 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
       let xx = semExprWithType(c, x, flags*{efAllowDestructor})
       result.add xx
-      typ = commonType(typ, xx.typ)
+      typ = commonType(c, typ, xx.typ)
       #n[i] = semExprWithType(c, x, flags*{efAllowDestructor})
       #result.add fitNode(c, typ, n[i])
       inc(lastIndex)
-    addSonSkipIntLit(result.typ, typ)
+    addSonSkipIntLit(result.typ, typ, c.idgen)
     for i in 0..<result.len:
       result[i] = fitNode(c, typ, result[i], result[i].info)
   result.typ[0] = makeRangeType(c, toInt64(firstIndex), toInt64(lastIndex), n.info,
@@ -756,14 +757,14 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     call.add(n[0])
     var allConst = true
     for i in 1..<n.len:
-      var a = getConstExpr(c.module, n[i], c.graph)
+      var a = getConstExpr(c.module, n[i], c.idgen, c.graph)
       if a == nil:
         allConst = false
         a = n[i]
         if a.kind == nkHiddenStdConv: a = a[1]
       call.add(a)
     if allConst:
-      result = semfold.getConstExpr(c.module, call, c.graph)
+      result = semfold.getConstExpr(c.module, call, c.idgen, c.graph)
       if result.isNil: result = n
       else: return result
 
@@ -800,19 +801,19 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     var call = newNodeIT(nkCall, n.info, n.typ)
     call.add(n[0])
     for i in 1..<n.len:
-      let a = getConstExpr(c.module, n[i], c.graph)
+      let a = getConstExpr(c.module, n[i], c.idgen, c.graph)
       if a == nil: return n
       call.add(a)
 
     #echo "NOW evaluating at compile time: ", call.renderTree
     if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
       if sfCompileTime in callee.flags:
-        result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
+        result = evalStaticExpr(c.module, c.idgen, c.graph, call, c.p.owner)
         if result.isNil:
           localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
         else: result = fixupTypeAfterEval(c, result, n)
       else:
-        result = evalConstExpr(c.module, c.graph, call)
+        result = evalConstExpr(c.module, c.idgen, c.graph, call)
         if result.isNil: result = n
         else: result = fixupTypeAfterEval(c, result, n)
     else:
@@ -827,7 +828,7 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
   closeScope(c)
   dec c.inStaticContext
   if a.findUnresolvedStatic != nil: return a
-  result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
+  result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
   if result.isNil:
     localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
     result = c.graph.emptyNode
@@ -1115,7 +1116,7 @@ proc readTypeParameter(c: PContext, typ: PType,
             # This seems semantically correct and then we'll be able
             # to return the section symbol directly here
             let foundType = makeTypeDesc(c, def[2].typ)
-            return newSymNode(copySym(def[0].sym).linkTo(foundType), info)
+            return newSymNode(copySym(def[0].sym, nextId c.idgen).linkTo(foundType), info)
 
       of nkConstSection:
         for def in statement:
@@ -1140,7 +1141,7 @@ proc readTypeParameter(c: PContext, typ: PType,
             return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
-          return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
+          return newSymNode(copySym(tParam.sym, nextId c.idgen).linkTo(foundTyp), info)
 
   return nil
 
@@ -1700,7 +1701,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner), {})
+      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
@@ -1930,7 +1931,7 @@ proc expectString(c: PContext, n: PNode): string =
     localError(c.config, n.info, errStringLiteralExpected)
 
 proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
-  result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info)
+  result = newSym(kind, c.cache.idAnon, nextId c.idgen, getCurrOwner(c), info)
 
 proc semExpandToAst(c: PContext, n: PNode): PNode =
   let macroCall = n[1]
@@ -2145,9 +2146,9 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = semDirectOp(c, n, flags)
 
 proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
-  result = newType(tyGenericInvocation, c.module)
-  addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ)
-  addSonSkipIntLit(result, t)
+  result = newType(tyGenericInvocation, nextId c.idgen, c.module)
+  addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen)
+  addSonSkipIntLit(result, t, c.idgen)
   result = instGenericContainer(c, info, result, allowMetaTypes = false)
 
 proc instantiateCreateFlowVarCall(c: PContext; t: PType;
@@ -2328,7 +2329,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       if whenNimvm:
         if semCheck:
           it[1] = semExpr(c, it[1])
-          typ = commonType(typ, it[1].typ)
+          typ = commonType(c, typ, it[1].typ)
         result = n # when nimvm is not elimited until codegen
       else:
         let e = forceBool(c, semConstExpr(c, it[0]))
@@ -2343,17 +2344,13 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       if result == nil or whenNimvm:
         if semCheck:
           it[0] = semExpr(c, it[0])
-          typ = commonType(typ, it[0].typ)
+          typ = commonType(c, typ, it[0].typ)
         if result == nil:
           result = it[0]
     else: illFormedAst(n, c.config)
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
   if whenNimvm: result.typ = typ
-  # The ``when`` statement implements the mechanism for platform dependent
-  # code. Thus we try to ensure here consistent ID allocation after the
-  # ``when`` statement.
-  idSynchronizationPoint(200)
 
 proc semSetConstr(c: PContext, n: PNode): PNode =
   result = newNodeI(nkCurly, n.info)
@@ -2386,7 +2383,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
     elif lengthOrd(c.config, typ) > MaxSetElements:
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    addSonSkipIntLit(result.typ, typ)
+    addSonSkipIntLit(result.typ, typ, c.idgen)
     for i in 0..<n.len:
       var m: PNode
       let info = n[i].info
@@ -2467,7 +2464,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n[i][1].typ = errorType(c)
 
     var f = newSymS(skField, n[i][0], c)
-    f.typ = skipIntLit(n[i][1].typ)
+    f.typ = skipIntLit(n[i][1].typ, c.idgen)
     f.position = i
     rawAddSon(typ, f.typ)
     typ.n.add newSymNode(f)
@@ -2481,7 +2478,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var typ = newTypeS(tyTuple, c)  # leave typ.n nil!
   for i in 0..<n.len:
     n[i] = semExprWithType(c, n[i], flags*{efAllowDestructor})
-    addSonSkipIntLit(typ, n[i].typ)
+    addSonSkipIntLit(typ, n[i].typ, c.idgen)
   result.typ = typ
 
 include semobjconstr
@@ -2608,7 +2605,8 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
     let paramPos = defExpr.sym.position + 1
 
     if call[paramPos].kind != nkSym:
-      let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.p.owner, letSection.info, c.p.owner.options)
+      let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), nextId c.idgen,
+                                 c.p.owner, letSection.info, c.p.owner.options)
       hoistedVarSym.typ = call[paramPos].typ
 
       letSection.add newTreeI(nkIdentDefs, letSection.info,
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index cea084d3a..d7563e33f 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -109,7 +109,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
-    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info)
+    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextId c.idgen, getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
   result[0] = newSymNode(trueSymbol, n.info)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index e94a15fbe..9b21c5fa3 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -17,7 +17,7 @@ import
 
 proc errorType*(g: ModuleGraph): PType =
   ## creates a type representing an error state
-  result = newType(tyError, g.owners[^1])
+  result = newType(tyError, nextId(g.idgen), g.owners[^1])
   result.flags.incl tfCheckedForDestructor
 
 proc newIntNodeT*(intVal: Int128, n: PNode; g: ModuleGraph): PNode =
@@ -42,7 +42,7 @@ proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
   result.typ = n.typ
   result.info = n.info
 
-proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
+proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   # evaluates the constant expression or returns nil if it is no constant
   # expression
 proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
@@ -113,45 +113,6 @@ proc pickIntRange(a, b: PType): PType =
 proc isIntRangeOrLit(t: PType): bool =
   result = isIntRange(t) or isIntLit(t)
 
-proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
-  let minA = min(first, last)
-  let maxA = max(first, last)
-  let lowerNode = newIntNode(nkIntLit, minA)
-  if typ.kind == tyInt and minA == maxA:
-    result = getIntLitType(g, lowerNode)
-  elif typ.kind in {tyUInt, tyUInt64}:
-    # these are not ordinal types, so you get no subrange type for these:
-    result = typ
-  else:
-    var n = newNode(nkRange)
-    n.add lowerNode
-    n.add newIntNode(nkIntLit, maxA)
-    result = newType(tyRange, typ.owner)
-    result.n = n
-    addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
-
-proc makeRangeF(typ: PType, first, last: BiggestFloat; g: ModuleGraph): PType =
-  var n = newNode(nkRange)
-  n.add newFloatNode(nkFloatLit, min(first.float, last.float))
-  n.add newFloatNode(nkFloatLit, max(first.float, last.float))
-  result = newType(tyRange, typ.owner)
-  result.n = n
-  addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
-
-proc fitLiteral(c: ConfigRef, n: PNode): PNode {.deprecated: "no substitute".} =
-  # Trim the literal value in order to make it fit in the destination type
-  if n == nil:
-    # `n` may be nil if the overflow check kicks in
-    return
-
-  doAssert n.kind in {nkIntLit, nkCharLit}
-
-  result = n
-
-  let typ = n.typ.skipTypes(abstractRange)
-  if typ.kind in tyUInt..tyUInt32:
-    result.intVal = result.intVal and castToInt64(lastOrd(c, typ))
-
 proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   # b and c may be nil
   result = nil
@@ -342,19 +303,19 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
         exprStructuralEquivalent(a, b, strictSymEquality=true))), n, g)
   else: discard
 
-proc getConstIfExpr(c: PSym, n: PNode; g: ModuleGraph): PNode =
+proc getConstIfExpr(c: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = nil
   for i in 0..<n.len:
     var it = n[i]
     if it.len == 2:
-      var e = getConstExpr(c, it[0], g)
+      var e = getConstExpr(c, it[0], idgen, g)
       if e == nil: return nil
       if getOrdValue(e) != 0:
         if result == nil:
-          result = getConstExpr(c, it[1], g)
+          result = getConstExpr(c, it[1], idgen, g)
           if result == nil: return
     elif it.len == 1:
-      if result == nil: result = getConstExpr(c, it[0], g)
+      if result == nil: result = getConstExpr(c, it[0], idgen, g)
     else: internalError(g.config, it.info, "getConstIfExpr()")
 
 proc leValueConv*(a, b: PNode): bool =
@@ -372,18 +333,18 @@ proc leValueConv*(a, b: PNode): bool =
     else: result = false # internalError(a.info, "leValueConv")
   else: result = false # internalError(a.info, "leValueConv")
 
-proc magicCall(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc magicCall(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   if n.len <= 1: return
 
   var s = n[0].sym
-  var a = getConstExpr(m, n[1], g)
+  var a = getConstExpr(m, n[1], idgen, g)
   var b, c: PNode
   if a == nil: return
   if n.len > 2:
-    b = getConstExpr(m, n[2], g)
+    b = getConstExpr(m, n[2], idgen, g)
     if b == nil: return
     if n.len > 3:
-      c = getConstExpr(m, n[3], g)
+      c = getConstExpr(m, n[3], idgen, g)
       if c == nil: return
   result = evalOp(s.magic, n, a, b, c, g)
 
@@ -451,19 +412,19 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
     result = a
     result.typ = n.typ
 
-proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   if n.kind == nkBracket:
     result = n
   else:
-    result = getConstExpr(m, n, g)
+    result = getConstExpr(m, n, idgen, g)
     if result == nil: result = n
 
-proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
-  var x = getConstExpr(m, n[0], g)
+proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  var x = getConstExpr(m, n[0], idgen, g)
   if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc:
     return
 
-  var y = getConstExpr(m, n[1], g)
+  var y = getConstExpr(m, n[1], idgen, g)
   if y == nil: return
 
   var idx = toInt64(getOrdValue(y))
@@ -486,9 +447,9 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
       localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
   else: discard
 
-proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   # a real field access; proc calls have already been transformed
-  var x = getConstExpr(m, n[0], g)
+  var x = getConstExpr(m, n[0], idgen, g)
   if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return
 
   var field = n[1].sym
@@ -504,23 +465,23 @@ proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
       return
   localError(g.config, n.info, "field not found: " & field.name.s)
 
-proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = newNodeIT(nkStrLit, n.info, n.typ)
   result.strVal = ""
   for i in 1..<n.len:
-    let a = getConstExpr(m, n[i], g)
+    let a = getConstExpr(m, n[i], idgen, g)
     if a == nil: return nil
     result.strVal.add(getStrOrChar(a))
 
-proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
+proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
   result = newSymNode(s, info)
   if s.typ.kind != tyTypeDesc:
-    result.typ = newType(tyTypeDesc, s.owner)
-    result.typ.addSonSkipIntLit(s.typ)
+    result.typ = newType(tyTypeDesc, idgen.nextId, s.owner)
+    result.typ.addSonSkipIntLit(s.typ, idgen)
   else:
     result.typ = s.typ
 
-proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = nil
   case n.kind
   of nkSym:
@@ -570,12 +531,12 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       result = n
     of skParam:
       if s.typ != nil and s.typ.kind == tyTypeDesc:
-        result = newSymNodeTypeDesc(s, n.info)
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     of skType:
       # 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)
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     of skGenericParam:
       if s.typ.kind == tyStatic:
         if s.typ.n != nil and tfUnresolved notin s.typ.flags:
@@ -584,12 +545,12 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       elif s.typ.isIntLit:
         result = s.typ.n
       else:
-        result = newSymNodeTypeDesc(s, n.info)
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     else: discard
   of nkCharLit..nkNilLit:
     result = copyNode(n)
   of nkIfExpr:
-    result = getConstIfExpr(m, n, g)
+    result = getConstIfExpr(m, n, idgen, g)
   of nkCallKinds:
     if n[0].kind != nkSym: return
     var s = n[0].sym
@@ -612,17 +573,17 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
           else:
             result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
         else:
-          var a = getArrayConstr(m, n[1], g)
+          var a = getArrayConstr(m, n[1], idgen, g)
           if a.kind == nkBracket:
             # we can optimize it away:
             result = newIntNodeT(toInt128(a.len-1), n, g)
       of mLengthOpenArray:
-        var a = getArrayConstr(m, n[1], g)
+        var a = getArrayConstr(m, n[1], idgen, g)
         if a.kind == nkBracket:
           # we can optimize it away! This fixes the bug ``len(134)``.
           result = newIntNodeT(toInt128(a.len), n, g)
         else:
-          result = magicCall(m, n, g)
+          result = magicCall(m, n, idgen, g)
       of mLengthArray:
         # It doesn't matter if the argument is const or not for mLengthArray.
         # This fixes bug #544.
@@ -636,33 +597,33 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       of mAstToStr:
         result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
       of mConStrStr:
-        result = foldConStrStr(m, n, g)
+        result = foldConStrStr(m, n, idgen, g)
       of mIs:
         # The only kind of mIs node that comes here is one depending on some
         # generic parameter and that's (hopefully) handled at instantiation time
         discard
       else:
-        result = magicCall(m, n, g)
+        result = magicCall(m, n, idgen, g)
     except OverflowDefect:
       localError(g.config, n.info, "over- or underflow")
     except DivByZeroDefect:
       localError(g.config, n.info, "division by zero")
   of nkAddr:
-    var a = getConstExpr(m, n[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a != nil:
       result = n
       n[0] = a
   of nkBracket, nkCurly:
     result = copyNode(n)
     for i, son in n.pairs:
-      var a = getConstExpr(m, son, g)
+      var a = getConstExpr(m, son, idgen, g)
       if a == nil: return nil
       result.add a
     incl(result.flags, nfAllConst)
   of nkRange:
-    var a = getConstExpr(m, n[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
-    var b = getConstExpr(m, n[1], g)
+    var b = getConstExpr(m, n[1], idgen, g)
     if b == nil: return
     result = copyNode(n)
     result.add a
@@ -681,18 +642,18 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       for i, expr in n.pairs:
         let exprNew = copyNode(expr) # nkExprColonExpr
         exprNew.add expr[0]
-        let a = getConstExpr(m, expr[1], g)
+        let a = getConstExpr(m, expr[1], idgen, g)
         if a == nil: return nil
         exprNew.add a
         result.add exprNew
     else:
       for i, expr in n.pairs:
-        let a = getConstExpr(m, expr, g)
+        let a = getConstExpr(m, expr, idgen, g)
         if a == nil: return nil
         result.add a
     incl(result.flags, nfAllConst)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    var a = getConstExpr(m, n[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
     if leValueConv(n[1], a) and leValueConv(a, n[2]):
       result = a              # a <= x and x <= b
@@ -702,31 +663,31 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
         "conversion from $1 to $2 is invalid" %
           [typeToString(n[0].typ), typeToString(n.typ)])
   of nkStringToCString, nkCStringToString:
-    var a = getConstExpr(m, n[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
     result = a
     result.typ = n.typ
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var a = getConstExpr(m, n[1], g)
+    var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
     result = foldConv(n, a, g, check=true)
   of nkCast:
-    var a = getConstExpr(m, n[1], g)
+    var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
     if n.typ != nil and n.typ.kind in NilableTypes:
       # we allow compile-time 'cast' for pointer types:
       result = a
       result.typ = n.typ
-  of nkBracketExpr: result = foldArrayAccess(m, n, g)
-  of nkDotExpr: result = foldFieldAccess(m, n, g)
+  of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
+  of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
   of nkCheckedFieldExpr:
-    result = foldFieldAccess(m, n[0], g)
+    result = foldFieldAccess(m, n[0], idgen, g)
   of nkStmtListExpr:
     var i = 0
     while i <= n.len - 2:
       if n[i].kind in {nkComesFrom, nkCommentStmt, nkEmpty}: i.inc
       else: break
     if i == n.len - 1:
-      result = getConstExpr(m, n[i], g)
+      result = getConstExpr(m, n[i], idgen, g)
   else:
     discard
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index a80b1d057..5f55772e9 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -95,7 +95,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
       else:
         result = n
     else:
-      result = newSymNodeTypeDesc(s, n.info)
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
     onUse(n.info, s)
   of skParam:
     result = n
@@ -103,7 +103,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
-      result = newSymNodeTypeDesc(s, n.info)
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
     else:
       result = n
     onUse(n.info, s)
@@ -267,13 +267,13 @@ proc semGenericStmt(c: PContext, n: PNode,
         if s.magic == mRunnableExamples:
           first = result.safeLen # see trunnableexamples.fun3
       of skGenericParam:
-        result[0] = newSymNodeTypeDesc(s, fn.info)
+        result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
         onUse(fn.info, s)
         first = 1
       of skType:
         # bad hack for generics:
         if (s.typ != nil) and (s.typ.kind != tyGenericParam):
-          result[0] = newSymNodeTypeDesc(s, fn.info)
+          result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
           onUse(fn.info, s)
           first = 1
       else:
@@ -470,7 +470,7 @@ proc semGenericStmt(c: PContext, n: PNode,
                                               flags, ctx)
     if n[paramsPos].kind != nkEmpty:
       if n[paramsPos][0].kind != nkEmpty:
-        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
+        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextId c.idgen, nil, n.info))
       n[paramsPos] = semGenericStmt(c, n[paramsPos], flags, ctx)
     n[pragmasPos] = semGenericStmt(c, n[pragmasPos], flags, ctx)
     var body: PNode
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index ffeed00d6..8f2f51ae8 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -65,7 +65,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(c), q.info)
+    var s = newSym(symKind, q.name, nextId c.idgen, getCurrOwner(c), q.info)
     s.flags.incl {sfUsed, sfFromGeneric}
     var t = PType(idTableGet(pt, q.typ))
     if t == nil:
@@ -105,7 +105,7 @@ when false:
   proc `$`(x: PSym): string =
     result = x.name.s & " " & " id " & $x.id
 
-proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
+proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable) =
   # we need to create a fresh set of gensym'ed symbols:
   #if n.kind == nkSym and sfGenSym in n.sym.flags:
   #  if n.sym.owner != orig:
@@ -118,12 +118,12 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
       n.sym = x
     elif s.owner == nil or s.owner.kind == skPackage:
       #echo "copied this ", s.name.s
-      x = copySym(s)
+      x = copySym(s, nextId c.idgen)
       x.owner = owner
       idTablePut(symMap, s, x)
       n.sym = x
   else:
-    for i in 0..<n.safeLen: freshGenSyms(n[i], owner, orig, symMap)
+    for i in 0..<n.safeLen: freshGenSyms(c, n[i], owner, orig, symMap)
 
 proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
 
@@ -144,7 +144,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
         let param = params[i].sym
         if sfGenSym in param.flags:
           idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
-    freshGenSyms(b, result, orig, symMap)
+    freshGenSyms(c, b, result, orig, symMap)
     b = semProcBody(c, b)
     result.ast[bodyPos] = hloBody(c, b)
     trackProc(c, result, result.ast[bodyPos])
@@ -200,7 +200,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
     var param: PSym
 
     template paramSym(kind): untyped =
-      newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
+      newSym(kind, genParam.sym.name, nextId c.idgen, genericTyp.sym, genParam.sym.info)
 
     if genParam.kind == tyStatic:
       param = paramSym skConst
@@ -270,7 +270,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
 
     internalAssert c.config, originalParams[i].kind == nkSym
     let oldParam = originalParams[i].sym
-    let param = copySym(oldParam)
+    let param = copySym(oldParam, nextId c.idgen)
     param.owner = prc
     param.typ = result[i]
 
@@ -314,7 +314,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
     propagateToOwner(result, result[0])
 
   eraseVoidParams(result)
-  skipIntLiteralParams(result)
+  skipIntLiteralParams(result, c.idgen)
 
   prc.typ = result
   popInfoContext(c.config)
@@ -339,7 +339,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   c.matchedConcept = nil
   let oldScope = c.currentScope
   while not isTopLevel(c): c.currentScope = c.currentScope.parent
-  result = copySym(fn)
+  result = copySym(fn, nextId c.idgen)
   incl(result.flags, sfFromGeneric)
   result.owner = fn
   result.ast = n
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index cffd58b92..5e6b4dbe2 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -118,8 +118,8 @@ proc uninstantiate(t: PType): PType =
     of tyCompositeTypeClass: uninstantiate t[1]
     else: t
 
-proc getTypeDescNode(typ: PType, sym: PSym, info: TLineInfo): PNode =
-  var resType = newType(tyTypeDesc, sym)
+proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode =
+  var resType = newType(tyTypeDesc, nextId c.idgen, sym)
   rawAddSon(resType, typ)
   result = toNode(resType, info)
 
@@ -133,7 +133,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     traitCall[2].typ.skipTypes({tyTypeDesc})
 
   template typeWithSonsResult(kind, sons): PNode =
-    newTypeWithSons(context, kind, sons).toNode(traitCall.info)
+    newTypeWithSons(context, kind, sons, c.idgen).toNode(traitCall.info)
 
   if operand.kind == tyGenericParam or (traitCall.len > 2 and operand2.kind == tyGenericParam):
     return traitCall  ## too early to evaluate
@@ -160,19 +160,19 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     result.info = traitCall.info
   of "arity":
     result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
-    result.typ = newType(tyInt, context)
+    result.typ = newType(tyInt, nextId c.idgen, context)
     result.info = traitCall.info
   of "genericHead":
     var arg = operand
     case arg.kind
     of tyGenericInst:
-      result = getTypeDescNode(arg.base, operand.owner, traitCall.info)
+      result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
     # of tySequence: # this doesn't work
     #   var resType = newType(tySequence, operand.owner)
     #   result = toNode(resType, traitCall.info) # doesn't work yet
     else:
       localError(c.config, traitCall.info, "expected generic type, got: type $2 of kind $1" % [arg.kind.toHumanStr, typeToString(operand)])
-      result = newType(tyError, context).toNode(traitCall.info)
+      result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info)
   of "stripGenericParams":
     result = uninstantiate(operand).toNode(traitCall.info)
   of "supportsCopyMem":
@@ -194,11 +194,11 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
       while arg.kind == tyDistinct:
         arg = arg.base
         arg = arg.skipTypes(skippedTypes + {tyGenericInst})
-      result = getTypeDescNode(arg, operand.owner, traitCall.info)
+      result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
     else:
       localError(c.config, traitCall.info,
         "distinctBase expects a distinct type as argument. The given type was " & typeToString(operand))
-      result = newType(tyError, context).toNode(traitCall.info)
+      result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info)
   else:
     localError(c.config, traitCall.info, "unknown trait: " & s)
     result = newNodeI(nkEmpty, traitCall.info)
@@ -286,7 +286,7 @@ proc semDynamicBindSym(c: PContext, n: PNode): PNode =
     return semBindSym(c, n)
 
   if c.graph.vm.isNil:
-    setupGlobalCtx(c.module, c.graph)
+    setupGlobalCtx(c.module, c.graph, c.idgen)
 
   let
     vm = PCtx c.graph.vm
@@ -370,7 +370,7 @@ proc semUnown(c: PContext; n: PNode): PNode =
         elems[i] = unownedType(c, t[i])
         if elems[i] != t[i]: someChange = true
       if someChange:
-        result = newType(tyTuple, t.owner)
+        result = newType(tyTuple, nextId c.idgen, t.owner)
         # we have to use 'rawAddSon' here so that type flags are
         # properly computed:
         for e in elems: result.rawAddSon(e)
@@ -381,7 +381,7 @@ proc semUnown(c: PContext; n: PNode): PNode =
        tyGenericInst, tyAlias:
       let b = unownedType(c, t[^1])
       if b != t[^1]:
-        result = copyType(t, t.owner, keepId = false)
+        result = copyType(t, nextId c.idgen, t.owner)
         result[^1] = b
         result.flags.excl tfHasOwned
       else:
@@ -399,7 +399,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
   # Replace nkDerefExpr by nkHiddenDeref
   # nkDeref is for 'ref T':  x[].field
   # nkHiddenDeref is for 'var T': x<hidden deref [] here>.field
-  proc transform(procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
+  proc transform(c: PContext; procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
     result = shallowCopy(n)
     if sameTypeOrNil(n.typ, old):
       result.typ = fresh
@@ -407,26 +407,26 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
       if n.sym == oldParam:
         result.sym = newParam
       elif n.sym.owner == orig:
-        result.sym = copySym(n.sym)
+        result.sym = copySym(n.sym, nextId c.idgen)
         result.sym.owner = procSym
     for i in 0 ..< safeLen(n):
-      result[i] = transform(procSym, n[i], old, fresh, oldParam, newParam)
+      result[i] = transform(c, procSym, n[i], old, fresh, oldParam, newParam)
     #if n.kind == nkDerefExpr and sameType(n[0].typ, old):
     #  result =
 
-  result = copySym(orig)
+  result = copySym(orig, nextId c.idgen)
   result.info = info
   result.flags.incl sfFromGeneric
   result.owner = orig
   let origParamType = orig.typ[1]
-  let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs))
+  let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
   let oldParam = orig.typ.n[1].sym
-  let newParam = newSym(skParam, oldParam.name, result, result.info)
+  let newParam = newSym(skParam, oldParam.name, nextId c.idgen, result, result.info)
   newParam.typ = newParamType
   # proc body:
-  result.ast = transform(result, orig.ast, origParamType, newParamType, oldParam, newParam)
+  result.ast = transform(c, result, orig.ast, origParamType, newParamType, oldParam, newParam)
   # proc signature:
-  result.typ = newProcType(result.info, result)
+  result.typ = newProcType(result.info, nextId c.idgen, result)
   result.typ.addParam newParam
 
 proc semQuantifier(c: PContext; n: PNode): PNode =
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index 578687e6c..82fb069db 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -416,13 +416,13 @@ proc transformSlices(g: ModuleGraph; n: PNode): PNode =
   else:
     result = n
 
-proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode
-proc transformSpawnSons(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; idgen: IdGenerator;owner: PSym; n, barrier: PNode): PNode
+proc transformSpawnSons(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode =
   result = shallowCopy(n)
   for i in 0..<n.len:
-    result[i] = transformSpawn(g, owner, n[i], barrier)
+    result[i] = transformSpawn(g, idgen, owner, n[i], barrier)
 
-proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode =
   case n.kind
   of nkVarSection, nkLetSection:
     result = nil
@@ -436,25 +436,25 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
           result.add n
         let t = b[1][0].typ[0]
         if spawnResult(t, true) == srByVar:
-          result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
+          result.add wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, it[0])
           it[^1] = newNodeI(nkEmpty, it.info)
         else:
-          it[^1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
+          it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
   of nkAsgn, nkFastAsgn:
     let b = n[1]
     if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
         spawnResult(t, true) == srByVar):
       let m = transformSlices(g, b)
-      return wrapProcForSpawn(g, owner, m, b.typ, barrier, n[0])
-    result = transformSpawnSons(g, owner, n, barrier)
+      return wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, n[0])
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   of nkCallKinds:
     if getMagic(n) == mSpawn:
       result = transformSlices(g, n)
-      return wrapProcForSpawn(g, owner, result, n.typ, barrier, nil)
-    result = transformSpawnSons(g, owner, n, barrier)
+      return wrapProcForSpawn(g, idgen, owner, result, n.typ, barrier, nil)
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   elif n.safeLen > 0:
-    result = transformSpawnSons(g, owner, n, barrier)
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   else:
     result = n
 
@@ -464,7 +464,7 @@ proc checkArgs(a: var AnalysisCtx; n: PNode) =
 proc generateAliasChecks(a: AnalysisCtx; result: PNode) =
   discard "too implement"
 
-proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
+proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
   # this needs to be called after the 'for' loop elimination
 
   # first pass:
@@ -482,16 +482,16 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
-  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info)
+  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextId idgen, owner, n.info)
   temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
   varSection.addVar tempNode
 
-  let barrier = genAddrOf(tempNode)
+  let barrier = genAddrOf(tempNode, idgen)
   result = newNodeI(nkStmtList, n.info)
   generateAliasChecks(a, result)
   result.add varSection
   result.add callCodegenProc(g, "openBarrier", barrier.info, barrier)
-  result.add transformSpawn(g, owner, body, barrier)
+  result.add transformSpawn(g, idgen, owner, body, barrier)
   result.add callCodegenProc(g, "closeBarrier", barrier.info, barrier)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 352871412..92a896a38 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -92,7 +92,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
         optSeqDestructors in tracked.config.globalOptions:
       createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
 
-  createTypeBoundOps(tracked.graph, tracked.c, typ, info)
+  createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen)
   if (tfHasAsgn in typ.flags) or
       optSeqDestructors in tracked.config.globalOptions:
     tracked.owner.flags.incl sfInjectDestructors
@@ -219,7 +219,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       if reason.kind == nkSym:
         a.owner.gcUnsafetyReason = reason.sym
       else:
-        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name,
+        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextId a.c.idgen,
                                           a.owner, reason.info, {})
 
 when true:
@@ -755,7 +755,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
   if n.typ != nil:
     if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
       createTypeBoundOps(tracked, n.typ, n.info)
-  if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
+  if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) != nil:
     return
   if a.kind == nkCast and a[1].typ.kind == tyProc:
     a = a[1]
@@ -829,7 +829,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
       case op[i].kind
       of tySink:
         createTypeBoundOps(tracked,  op[i][0], n.info)
-        checkForSink(tracked.config, tracked.owner, n[i])
+        checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i])
       of tyVar:
         tracked.hasDangerousAssign = true
       #of tyOut:
@@ -947,7 +947,7 @@ proc track(tracked: PEffects, n: PNode) =
     if tracked.owner.kind != skMacro:
       createTypeBoundOps(tracked, n[0].typ, n.info)
     if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym):
-      checkForSink(tracked.config, tracked.owner, n[1])
+      checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[1])
       if not tracked.hasDangerousAssign and n[0].kind != nkSym:
         tracked.hasDangerousAssign = true
   of nkVarSection, nkLetSection:
@@ -1054,9 +1054,9 @@ proc track(tracked: PEffects, n: PNode) =
       if x.kind == nkExprColonExpr:
         if x[0].kind == nkSym:
           notNilCheck(tracked, x[1], x[0].sym.typ)
-        checkForSink(tracked.config, tracked.owner, x[1])
+        checkForSink(tracked.config, tracked.c.idgen, tracked.owner, x[1])
       else:
-        checkForSink(tracked.config, tracked.owner, x)
+        checkForSink(tracked.config, tracked.c.idgen, tracked.owner, x)
     setLen(tracked.guards.s, oldFacts)
     if tracked.owner.kind != skMacro:
       # XXX n.typ can be nil in runnableExamples, we need to do something about it.
@@ -1071,7 +1071,7 @@ proc track(tracked: PEffects, n: PNode) =
           createTypeBoundOps(tracked, n[i][0].typ, n.info)
         else:
           createTypeBoundOps(tracked, n[i].typ, n.info)
-      checkForSink(tracked.config, tracked.owner, n[i])
+      checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i])
   of nkPragmaBlock:
     let pragmaList = n[0]
     var bc = createBlockContext(tracked)
@@ -1122,7 +1122,7 @@ proc track(tracked: PEffects, n: PNode) =
   of nkBracket:
     for i in 0..<n.safeLen:
       track(tracked, n[i])
-      checkForSink(tracked.config, tracked.owner, n[i])
+      checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i])
     if tracked.owner.kind != skMacro:
       createTypeBoundOps(tracked, n.typ, n.info)
   of nkBracketExpr:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index b5e69c135..2a058eee2 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -160,12 +160,12 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
       openScope(c)
       it[0] = forceBool(c, semExprWithType(c, it[0]))
       it[1] = semExprBranch(c, it[1], flags)
-      typ = commonType(typ, it[1])
+      typ = commonType(c, typ, it[1])
       closeScope(c)
     elif it.len == 1:
       hasElse = true
       it[0] = semExprBranchScope(c, it[0])
-      typ = commonType(typ, it[0])
+      typ = commonType(c, typ, it[0])
     else: illFormedAst(it, c.config)
   if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
       (not hasElse and efInTypeof notin flags):
@@ -203,7 +203,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
 
   var typ = commonTypeBegin
   n[0] = semExprBranchScope(c, n[0])
-  typ = commonType(typ, n[0].typ)
+  typ = commonType(c, typ, n[0].typ)
 
   var last = n.len - 1
   var catchAllExcepts = 0
@@ -223,7 +223,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
         let isImported = semExceptBranchType(a[0][1])
         let symbol = newSymG(skLet, a[0][2], c)
         symbol.typ = if isImported: a[0][1].typ
-                     else: a[0][1].typ.toRef()
+                     else: a[0][1].typ.toRef(c.idgen)
         addDecl(c, symbol)
         # Overwrite symbol in AST with the symbol in the symbol table.
         a[0][2] = newSymNode(symbol, a[0][2].info)
@@ -261,7 +261,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
 
     # last child of an nkExcept/nkFinally branch is a statement:
     a[^1] = semExprBranchScope(c, a[^1])
-    if a.kind != nkFinally: typ = commonType(typ, a[^1])
+    if a.kind != nkFinally: typ = commonType(c, typ, a[^1])
     else: dec last
     closeScope(c)
 
@@ -411,8 +411,8 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
     let y = considerQuotedIdent(c, n[1])
     let obj = x.typ.skipTypes(abstractPtrs)
     if obj.kind == tyObject and tfPartial in obj.flags:
-      let field = newSym(skField, getIdent(c.cache, y.s), obj.sym, n[1].info)
-      field.typ = skipIntLit(typ)
+      let field = newSym(skField, getIdent(c.cache, y.s), nextId c.idgen, obj.sym, n[1].info)
+      field.typ = skipIntLit(typ, c.idgen)
       field.position = obj.n.len
       obj.n.add newSymNode(field)
       n[0] = makeDeref x
@@ -513,7 +513,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           def = fitNode(c, typ, def, def.info)
           #changeType(def.skipConv, typ, check=true)
       else:
-        typ = def.typ.skipTypes({tyStatic}).skipIntLit
+        typ = def.typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
         if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
           typ = typ.lastSon
         if hasEmpty(typ):
@@ -621,7 +621,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       if sfCompileTime in v.flags:
         var x = newNodeI(result.kind, v.info)
         x.add result[i]
-        vm.setupCompileTimeVar(c.module, c.graph, x)
+        vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x)
       if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
         message(c.config, v.info, hintGlobalVar)
 
@@ -969,19 +969,19 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
       semCaseBranch(c, n, x, i, covered)
       var last = x.len-1
       x[last] = semExprBranchScope(c, x[last])
-      typ = commonType(typ, x[last])
+      typ = commonType(c, typ, x[last])
     of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2, c.config)
       openScope(c)
       x[0] = forceBool(c, semExprWithType(c, x[0]))
       x[1] = semExprBranch(c, x[1])
-      typ = commonType(typ, x[1])
+      typ = commonType(c, typ, x[1])
       closeScope(c)
     of nkElse:
       checkSonsLen(x, 1, c.config)
       x[0] = semExprBranchScope(c, x[0])
-      typ = commonType(typ, x[0])
+      typ = commonType(c, typ, x[0])
       if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse:
         message(c.config, x.info, warnUnreachableElse)
       hasElse = true
@@ -1276,7 +1276,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       internalAssert c.config, st.lastSon.sym == nil
       incl st.flags, tfRefsAnonObj
       let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
-                              getCurrOwner(c), s.info)
+                       nextId c.idgen, getCurrOwner(c), s.info)
       obj.ast = a
       if sfPure in s.flags:
         obj.flags.incl sfPure
@@ -1337,11 +1337,11 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
           if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
             if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
               assignType(s.typ, t)
-              s.typ.id = t.id
+              s.typ.itemId = t.itemId
             elif t.kind in {tyObject, tyEnum, tyDistinct}:
               assert s.typ != nil
               assignType(s.typ, t)
-              s.typ.id = t.id     # same id
+              s.typ.itemId = t.itemId     # same id
         checkConstructedType(c.config, s.info, s.typ)
         if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
           checkForMetaFields(c, s.typ.n)
@@ -1437,7 +1437,7 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
         localError(c.config, n.info, "incorrect result proc symbol")
       c.p.resultSym = n[resultPos].sym
     else:
-      var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), n.info)
+      var s = newSym(skResult, getIdent(c.cache, "result"), nextId c.idgen, getCurrOwner(c), n.info)
       s.typ = t
       incl(s.flags, sfUsed)
       c.p.resultSym = s
@@ -1535,7 +1535,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkSonsLen(n, bodyPos + 1, c.config)
   var s: PSym
   if n[namePos].kind != nkSym:
-    s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info)
+    s = newSym(skProc, c.cache.idAnon, nextId c.idgen, getCurrOwner(c), n.info)
     s.ast = n
     n[namePos] = newSymNode(s)
   else:
@@ -1800,7 +1800,7 @@ proc hasObjParam(s: PSym): bool =
 
 proc finishMethod(c: PContext, s: PSym) =
   if hasObjParam(s):
-    methodDef(c.graph, s, false)
+    methodDef(c.graph, c.idgen, s, false)
 
 proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
   if isGenericRoutine(s):
@@ -1825,7 +1825,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
     # no sense either.
     # and result[bodyPos].kind != nkEmpty:
     if hasObjParam(s):
-      methodDef(c.graph, s, fromCache=false)
+      methodDef(c.graph, c.idgen, s, fromCache=false)
     else:
       localError(c.config, n.info, "'method' needs a parameter that has an object type")
 
@@ -1843,7 +1843,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     assert phase == stepRegisterSymbol
 
     if n[namePos].kind == nkEmpty:
-      s = newSym(kind, c.cache.idAnon, getCurrOwner(c), n.info)
+      s = newSym(kind, c.cache.idAnon, nextId c.idgen, getCurrOwner(c), n.info)
       incl(s.flags, sfUsed)
       isAnon = true
     else:
@@ -1998,7 +1998,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         if s.kind == skMethod: semMethodPrototype(c, s, n)
       else:
         if (s.typ[0] != nil and kind != skIterator) or kind == skMacro:
-          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
+          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextId c.idgen, nil, n.info))
 
         openScope(c)
         n[bodyPos] = semGenericStmt(c, n[bodyPos])
@@ -2189,7 +2189,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   closeScope(c)
   dec c.inStaticContext
   n[0] = a
-  evalStaticStmt(c.module, c.graph, a, c.p.owner)
+  evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
   when false:
     # for incremental replays, keep the AST as required for replays:
     result = n
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index df832c814..03bf1af92 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -176,7 +176,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
       result[i] = onlyReplaceParams(c, n[i])
 
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c, n), nextId c.c.idgen, c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
@@ -239,12 +239,12 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
     result = symChoice(c, n, s, scOpen, isField)
   of skGenericParam:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, n.info)
+    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
   of skParam:
     result = n
   of skType:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, n.info)
+    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
   else:
     if isField and sfGenSym in s.flags: result = n
     else: result = newSymNode(s, n.info)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index f2bb82a5c..97cf7c5ec 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -51,7 +51,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
 proc newConstraint(c: PContext, k: TTypeKind): PType =
   result = newTypeS(tyBuiltInTypeClass, c)
   result.flags.incl tfCheckedForDestructor
-  result.addSonSkipIntLit(newTypeS(k, c))
+  result.addSonSkipIntLit(newTypeS(k, c), c.idgen)
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 0: return newConstraint(c, tyEnum)
@@ -156,7 +156,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tySet, prev, c)
   if n.len == 2 and n[1].kind != nkEmpty:
     var base = semTypeNode(c, n[1], nil)
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
     if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
     if base.kind != tyGenericParam:
       if not isOrdinalType(base, allowEnumWithHoles = true):
@@ -165,17 +165,17 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
         localError(c.config, n.info, errSetTooBig)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "set")
-    addSonSkipIntLit(result, errorType(c))
+    addSonSkipIntLit(result, errorType(c), c.idgen)
 
 proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
   if n.len == 2:
     var base = semTypeNode(c, n[1], nil)
     if base.kind == tyVoid:
       localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
-    addSonSkipIntLit(result, errorType(c))
+    addSonSkipIntLit(result, errorType(c), c.idgen)
 
 proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
                   prev: PType): PType =
@@ -186,12 +186,12 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyVarargs, prev, c)
   if n.len == 2 or n.len == 3:
     var base = semTypeNode(c, n[1], nil)
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
     if n.len == 3:
       result.n = newIdentNode(considerQuotedIdent(c, n[2]), n[2].info)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
-    addSonSkipIntLit(result, errorType(c))
+    addSonSkipIntLit(result, errorType(c), c.idgen)
 
 proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
   if n.len == 1:
@@ -200,14 +200,14 @@ proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
     if base.kind == tyVar:
       localError(c.config, n.info, "type 'var var' is not allowed")
       base = base[0]
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     result = newConstraint(c, kind)
 
 proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 0: return newConstraint(c, tyDistinct)
   result = newOrPrevType(tyDistinct, prev, c)
-  addSonSkipIntLit(result, semTypeNode(c, n[0], nil))
+  addSonSkipIntLit(result, semTypeNode(c, n[0], nil), c.idgen)
   if n.len > 1: result.n = n[1]
 
 proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
@@ -217,7 +217,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   result.n = newNodeI(nkRange, n.info)
   # always create a 'valid' range type, but overwrite it later
   # because 'semExprWithType' can raise an exception. See bug #6895.
-  addSonSkipIntLit(result, errorType(c))
+  addSonSkipIntLit(result, errorType(c), c.idgen)
 
   if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
     localError(c.config, n.info, "range is empty")
@@ -228,7 +228,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
 
   var rangeT: array[2, PType]
   for i in 0..1:
-    rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
+    rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
 
   let hasUnknownTypes = c.inGenericContext > 0 and
     rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
@@ -345,7 +345,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
     # bug #6682: Do not propagate initialization requirements etc for the
     # index type:
     rawAddSonNoPropagationOfTypeFlags(result, indx)
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errArrayExpectsTwoTypeParams)
     result = newOrPrevType(tyError, prev, c)
@@ -357,7 +357,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
         localError(c.config, n[1].info, errOrdinalTypeExpected)
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
     result = newOrPrevType(tyError, prev, c)
@@ -384,7 +384,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
         if result.typ.sym == nil:
           localError(c.config, n.info, errTypeExpected)
           return errorSym(c, n)
-        result = result.typ.sym.copySym
+        result = result.typ.sym.copySym(nextId c.idgen)
         result.typ = exactReplica(result.typ)
         result.typ.flags.incl tfUnresolved
 
@@ -429,7 +429,7 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, errTypeExpected)
   result = newOrPrevType(tyTuple, prev, c)
   for it in n:
-    addSonSkipIntLit(result, semTypeNode(c, it, nil))
+    addSonSkipIntLit(result, semTypeNode(c, it, nil), c.idgen)
 
 proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   var typ: PType
@@ -457,15 +457,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
         localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'")
       else:
         result.n.add newSymNode(field)
-        addSonSkipIntLit(result, typ)
+        addSonSkipIntLit(result, typ, c.idgen)
       styleCheckDef(c.config, a[j].info, field)
       onDef(field.info, field)
   if result.n.len == 0: result.n = nil
   if isTupleRecursive(result):
     localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
 
-
-
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym =
   # identifier with visibility
@@ -925,11 +923,11 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
         elif region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
               tyError, tyObject}:
           message c.config, n[i].info, errGenerated, "region needs to be an object type"
-          addSonSkipIntLit(result, region)
+          addSonSkipIntLit(result, region, c.idgen)
         else:
           message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
-          addSonSkipIntLit(result, region)
-    addSonSkipIntLit(result, t)
+          addSonSkipIntLit(result, region, c.idgen)
+    addSonSkipIntLit(result, t, c.idgen)
     if tfPartial in result.flags:
       if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
     #if not isNilable: result.flags.incl tfNotNil
@@ -963,7 +961,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   if kind == skMacro:
     let staticType = findEnforcedStaticType(param.typ)
     if staticType != nil:
-      var a = copySym(param)
+      var a = copySym(param, nextId c.idgen)
       a.typ = staticType.base
       addDecl(c, a)
       #elif param.typ != nil and param.typ.kind == tyTypeDesc:
@@ -971,7 +969,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
     else:
       # within a macro, every param has the type NimNode!
       let nn = getSysSym(c.graph, param.info, "NimNode")
-      var a = copySym(param)
+      var a = copySym(param, nextId c.idgen)
       a.typ = nn.typ
       addDecl(c, a)
   else:
@@ -1001,7 +999,7 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
 
   let owner = if typeClass.sym != nil: typeClass.sym
               else: getCurrOwner(c)
-  var s = newSym(skType, finalTypId, owner, info)
+  var s = newSym(skType, finalTypId, nextId c.idgen, owner, info)
   if sfExplain in owner.flags: s.flags.incl sfExplain
   if typId == nil: s.flags.incl(sfAnon)
   s.linkTo(typeClass)
@@ -1107,7 +1105,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
 
   of tyGenericInst:
     if paramType.lastSon.kind == tyUserTypeClass:
-      var cp = copyType(paramType, getCurrOwner(c), false)
+      var cp = copyType(paramType, nextId c.idgen, getCurrOwner(c))
       cp.kind = tyUserTypeClassInst
       return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName)
 
@@ -1144,7 +1142,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
      tyAnd, tyOr, tyNot:
     result = addImplicitGeneric(c,
-        copyType(paramType, getCurrOwner(c), false), paramTypId,
+        copyType(paramType, nextId c.idgen, getCurrOwner(c)), paramTypId,
         info, genericParams, paramName)
 
   of tyGenericParam:
@@ -1274,7 +1272,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           typ = errorType(c)
       let lifted = liftParamType(c, kind, genericParams, typ,
                                  arg.name.s, arg.info)
-      let finalType = if lifted != nil: lifted else: typ.skipIntLit
+      let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
       arg.typ = finalType
       arg.position = counter
       arg.constraint = constraint
@@ -1331,7 +1329,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
             #if r.kind != tyGenericParam:
             #echo "came here for ", typeToString(r)
             r.flags.incl tfRetType
-        r = skipIntLit(r)
+        r = skipIntLit(r, c.idgen)
         if kind == skIterator:
           # see tchainediterators
           # in cases like iterator foo(it: iterator): type(it)
@@ -1413,13 +1411,13 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     t = t.base
 
   result = newOrPrevType(tyGenericInvocation, prev, c)
-  addSonSkipIntLit(result, t)
+  addSonSkipIntLit(result, t, c.idgen)
 
   template addToResult(typ) =
     if typ.isNil:
       internalAssert c.config, false
       rawAddSon(result, typ)
-    else: addSonSkipIntLit(result, typ)
+    else: addSonSkipIntLit(result, typ, c.idgen)
 
   if t.kind == tyForward:
     for i in 1..<n.len:
@@ -1518,9 +1516,9 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
     localError(c.config, n.info, "expected type, but got: " & n.renderTree)
     result = errorType(c)
 
-proc freshType(res, prev: PType): PType {.inline.} =
+proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
   if prev.isNil:
-    result = copyType(res, res.owner, keepId=false)
+    result = copyType(res, nextId c.idgen, res.owner)
   else:
     result = res
 
@@ -1542,7 +1540,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyUserTypeClass, prev, c)
   result.flags.incl tfCheckedForDestructor
   var owner = getCurrOwner(c)
-  var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
+  var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType], c.idgen)
   result.sons = @[candidateTypeSlot]
   result.n = n
 
@@ -1575,7 +1573,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
 
     internalAssert c.config, dummyName.kind == nkIdent
     var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
-                            dummyName.ident, owner, param.info)
+                            dummyName.ident, nextId c.idgen, owner, param.info)
     dummyParam.typ = dummyType
     incl dummyParam.flags, sfUsed
     addDecl(c, dummyParam)
@@ -1713,7 +1711,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = semTypeNode(c, n[1], prev)
       if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes:
         if tfNotNil in result.flags:
-          result = freshType(result, prev)
+          result = freshType(c, result, prev)
           result.flags.excl(tfNotNil)
       else:
         localError(c.config, n.info, errGenerated, "invalid type")
@@ -1774,7 +1772,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
             # to other types such as `Option[T]`.
             result = makeTypeFromExpr(c, newTree(nkStmtListType, n.copyTree))
           of NilableTypes + {tyGenericInvocation, tyForward}:
-            result = freshType(result, prev)
+            result = freshType(c, result, prev)
             result.flags.incl(tfNotNil)
           else:
             localError(c.config, n.info, errGenerated, "invalid type")
@@ -1828,19 +1826,19 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mExpr:
       result = semTypeNode(c, n[0], nil)
       if result != nil:
-        result = copyType(result, getCurrOwner(c), false)
+        result = copyType(result, nextId c.idgen, getCurrOwner(c))
         for i in 1..<n.len:
           result.rawAddSon(semTypeNode(c, n[i], nil))
     of mDistinct:
       result = newOrPrevType(tyDistinct, prev, c)
-      addSonSkipIntLit(result, semTypeNode(c, n[1], nil))
+      addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
     of mVar:
       result = newOrPrevType(tyVar, prev, c)
       var base = semTypeNode(c, n[1], nil)
       if base.kind in {tyVar, tyLent}:
         localError(c.config, n.info, "type 'var var' is not allowed")
         base = base[0]
-      addSonSkipIntLit(result, base)
+      addSonSkipIntLit(result, base, c.idgen)
     of mRef: result = semAnyRef(c, n, tyRef, prev)
     of mPtr: result = semAnyRef(c, n, tyPtr, prev)
     of mTuple: result = semTuple(c, n, prev)
@@ -1886,7 +1884,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         # bugfix: keep the fresh id for aliases to integral types:
         if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
                              tyUInt..tyUInt64}:
-          prev.id = s.typ.id
+          prev.itemId = s.typ.itemId
         result = prev
   of nkSym:
     let s = getGenSym(c, n.sym)
@@ -1929,7 +1927,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = newTypeS(tyBuiltInTypeClass, c)
       let child = newTypeS(tyProc, c)
       child.flags.incl tfIterator
-      result.addSonSkipIntLit(child)
+      result.addSonSkipIntLit(child, c.idgen)
     else:
       result = semProcTypeWithScope(c, n, prev, skIterator)
       if result.kind == tyProc:
@@ -2060,7 +2058,7 @@ proc semGenericConstraints(c: PContext, x: PType): PType =
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
 
   template addSym(result: PNode, s: PSym): untyped =
-    if father != nil: addSonSkipIntLit(father, s.typ)
+    if father != nil: addSonSkipIntLit(father, s.typ, c.idgen)
     if sfGenSym notin s.flags: addDecl(c, s)
     result.add newSymNode(s)
 
@@ -2107,7 +2105,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
 
       for j in 0..<a.len-2:
         let finalType = if j == 0: typ
-                        else: copyType(typ, typ.owner, false)
+                        else: copyType(typ, nextId c.idgen, typ.owner)
                         # it's important the we create an unique
                         # type for each generic param. the index
                         # of the parameter will be stored in the
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 68ba93ebc..931581d12 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -279,7 +279,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
       var g: G[string]
 
   ]#
-  result = copySym(s)
+  result = copySym(s, nextId cl.c.idgen)
   incl(result.flags, sfFromGeneric)
   #idTablePut(cl.symMap, s, result)
   result.owner = s.owner
@@ -302,7 +302,12 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
 
 proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   # XXX: relying on allowMetaTypes is a kludge
-  result = copyType(t, t.owner, cl.allowMetaTypes)
+  if cl.allowMetaTypes:
+    result = t.exactReplica
+  else:
+    result = copyType(t, nextId(cl.c.idgen), t.owner)
+    #cl.typeMap.topLayer.idTablePut(result, t)
+
   if cl.allowMetaTypes: return
   result.flags.incl tfFromGeneric
   if not (t.kind in tyMetaTypes or
@@ -355,7 +360,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   else:
     header = instCopyType(cl, t)
 
-  result = newType(tyGenericInst, t[0].owner)
+  result = newType(tyGenericInst, nextId(cl.c.idgen), t[0].owner)
   result.flags = header.flags
   # be careful not to propagate unnecessary flags here (don't use rawAddSon)
   result.sons = @[header[0]]
@@ -459,11 +464,11 @@ proc eraseVoidParams*(t: PType) =
       setLen t.n.sons, pos
       break
 
-proc skipIntLiteralParams*(t: PType) =
+proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
   for i in 0..<t.len:
     let p = t[i]
     if p == nil: continue
-    let skipped = p.skipIntLit
+    let skipped = p.skipIntLit(idgen)
     if skipped != p:
       t[i] = skipped
       if i > 0: t.n[i].sym.typ = skipped
@@ -553,7 +558,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         result = n.typ
 
   of tyInt, tyFloat:
-    result = skipIntLit(t)
+    result = skipIntLit(t, cl.c.idgen)
 
   of tyTypeDesc:
     let lookup = cl.typeMap.lookup(t)
@@ -617,7 +622,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
       of tyProc:
         eraseVoidParams(result)
-        skipIntLiteralParams(result)
+        skipIntLiteralParams(result, cl.c.idgen)
 
       of tyRange:
         result[0] = result[0].skipTypes({tyStatic, tyDistinct})
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 687634e93..2cf2846c0 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -143,7 +143,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
         writeStackTrace()
     if c.c.module.name.s == "temp3":
       echo "binding ", key, " -> ", val
-  idTablePut(c.bindings, key, val.skipIntLit)
+  idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen))
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
                     binding: PNode, calleeScope = -1,
@@ -726,7 +726,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
       if alreadyBound != nil: typ = alreadyBound
 
       template paramSym(kind): untyped =
-        newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info, {})
+        newSym(kind, typeParamName, nextId(c.idgen), typeClass.sym, typeClass.sym.info, {})
 
       block addTypeParam:
         for prev in typeParams:
@@ -739,6 +739,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
         of tyStatic:
           param = paramSym skConst
           param.typ = typ.exactReplica
+          #copyType(typ, nextId(c.idgen), typ.owner)
           if typ.n == nil:
             param.typ.flags.incl tfInferrableStatic
           else:
@@ -746,6 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
         of tyUnknown:
           param = paramSym skVar
           param.typ = typ.exactReplica
+          #copyType(typ, nextId(c.idgen), typ.owner)
         else:
           param = paramSym skType
           param.typ = if typ.isMetaType:
@@ -796,7 +798,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   if ff.kind == tyUserTypeClassInst:
     result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
   else:
-    result = copyType(ff, ff.owner, true)
+    result = ff.exactReplica
+    #copyType(ff, nextId(c.idgen), ff.owner)
 
   result.n = checkedBody
 
@@ -1086,7 +1089,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
 
   template bindingRet(res) =
     if doBind:
-      let bound = aOrig.skipTypes({tyRange}).skipIntLit
+      let bound = aOrig.skipTypes({tyRange}).skipIntLit(c.c.idgen)
       put(c, f, bound)
     return res
 
@@ -1500,14 +1503,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
 
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+
     var preventHack = false
     if x.kind == tyOwned and f[0].kind != tyOwned:
       preventHack = true
       x = x.lastSon
     # XXX: This is very hacky. It should be moved back into liftTypeParam
     if x.kind in {tyGenericInst, tyArray} and
-       c.calleeSym != nil and
-       c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
+      c.calleeSym != nil and
+      c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
       let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
       #echo "inferred ", typeToString(inst), " for ", f
       return typeRel(c, inst, a, flags)
@@ -2291,7 +2295,7 @@ proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
   rawAddSon(result, makeRangeType(c, 0, 0, n.info))
   addSonSkipIntLit(result, skipTypes(n.typ,
-      {tyGenericInst, tyVar, tyLent, tyOrdinal}))
+      {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen)
 
 proc arrayConstr(c: PContext, info: TLineInfo): PType =
   result = newTypeS(tyArray, c)
diff --git a/compiler/sinkparameter_inference.nim b/compiler/sinkparameter_inference.nim
index 182e44324..841f2de8e 100644
--- a/compiler/sinkparameter_inference.nim
+++ b/compiler/sinkparameter_inference.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) =
+proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNode) =
   #[ Patterns we seek to detect:
 
     someLocation = p # ---> p: sink T
@@ -32,7 +32,7 @@ proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) =
       if sfWasForwarded notin owner.flags:
         let argType = arg.sym.typ
 
-        let sinkType = newType(tySink, owner)
+        let sinkType = newType(tySink, nextId(idgen), owner)
         sinkType.size = argType.size
         sinkType.align = argType.align
         sinkType.paddingAtEnd = argType.paddingAtEnd
@@ -52,18 +52,18 @@ proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) =
       #echo config $ arg.info, " candidate for a sink parameter here"
   of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr:
     if not isEmptyType(arg.typ):
-      checkForSink(config, owner, arg.lastSon)
+      checkForSink(config, idgen, owner, arg.lastSon)
   of nkIfStmt, nkIfExpr, nkWhen:
     for branch in arg:
       let value = branch.lastSon
       if not isEmptyType(value.typ):
-        checkForSink(config, owner, value)
+        checkForSink(config, idgen, owner, value)
   of nkCaseStmt:
     for i in 1..<arg.len:
       let value = arg[i].lastSon
       if not isEmptyType(value.typ):
-        checkForSink(config, owner, value)
+        checkForSink(config, idgen, owner, value)
   of nkTryStmt:
-    checkForSink(config, owner, arg[0])
+    checkForSink(config, idgen, owner, arg[0])
   else:
     discard "nothing to do"
diff --git a/compiler/spawn.nim b/compiler/spawn.nim
index 15bed77b0..65e19b8db 100644
--- a/compiler/spawn.nim
+++ b/compiler/spawn.nim
@@ -53,9 +53,9 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
   if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
   result = not containsGarbageCollectedRef(t)
 
-proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
+proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType;
                  v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), nextId idgen, owner, varSection.info,
                   owner.options)
   result.typ = typ
   incl(result.flags, sfFromGeneric)
@@ -111,22 +111,23 @@ stmtList:
 
 proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
                        varSection, varInit, call, barrier, fv: PNode;
+                       idgen: IdGenerator;
                        spawnKind: TSpawnResult, result: PSym) =
   var body = newNodeI(nkStmtList, f.info)
   var threadLocalBarrier: PSym
   if barrier != nil:
     var varSection2 = newNodeI(nkVarSection, barrier.info)
-    threadLocalBarrier = addLocalVar(g, varSection2, nil, result,
+    threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result,
                                      barrier.typ, barrier)
     body.add varSection2
     body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
       threadLocalBarrier.newSymNode)
   var threadLocalProm: PSym
   if spawnKind == srByVar:
-    threadLocalProm = addLocalVar(g, varSection, nil, result, fv.typ, fv)
+    threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv)
   elif fv != nil:
     internalAssert g.config, fv.typ.kind == tyGenericInst
-    threadLocalProm = addLocalVar(g, varSection, nil, result, fv.typ, fv)
+    threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv)
   body.add varSection
   body.add varInit
   if fv != nil and spawnKind != srByVar:
@@ -168,7 +169,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   params.add threadParam.newSymNode
   params.add argsParam.newSymNode
 
-  var t = newType(tyProc, threadParam.owner)
+  var t = newType(tyProc, nextId idgen, threadParam.owner)
   t.rawAddSon nil
   t.rawAddSon threadParam.typ
   t.rawAddSon argsParam.typ
@@ -184,15 +185,15 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
       exceptions = emptyNode)
   result.typ = t
 
-proc createCastExpr(argsParam: PSym; objType: PType): PNode =
+proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode =
   result = newNodeI(nkCast, argsParam.info)
   result.add newNodeI(nkEmpty, argsParam.info)
   result.add newSymNode(argsParam)
-  result.typ = newType(tyPtr, objType.owner)
+  result.typ = newType(tyPtr, nextId idgen, objType.owner)
   result.typ.rawAddSon(objType)
 
 proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
-                             owner: PSym; scratchObj: PSym,
+                             idgen: IdGenerator; owner: PSym; scratchObj: PSym,
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
@@ -210,16 +211,17 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
     #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
 
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
+    var field = newSym(skField, fieldname, nextId idgen, objType.owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field, g.cache)
+    objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
 
-    let temp = addLocalVar(g, varSection, varInit, owner, argType,
+    let temp = addLocalVar(g, varSection, varInit, idgen, owner, argType,
                            indirectAccess(castExpr, field, n.info))
     call.add(newSymNode(temp))
 
 proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
+                             idgen: IdGenerator;
                              owner: PSym; scratchObj: PSym;
                              castExpr, call,
                              varSection, varInit, result: PNode) =
@@ -237,7 +239,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
     #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
 
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
+    var field = newSym(skField, fieldname, nextId idgen, objType.owner, n.info, g.config.options)
 
     if argType.kind in {tyVarargs, tyOpenArray}:
       # important special case: we always create a zero-copy slice:
@@ -245,30 +247,30 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
       slice.typ = n.typ
       slice[0] = newSymNode(createMagic(g, "slice", mSlice))
       slice[0].typ = getSysType(g, n.info, tyInt) # fake type
-      var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
+      var fieldB = newSym(skField, tmpName, nextId idgen, objType.owner, n.info, g.config.options)
       fieldB.typ = getSysType(g, n.info, tyInt)
-      objType.addField(fieldB, g.cache)
+      objType.addField(fieldB, g.cache, idgen)
 
       if getMagic(n) == mSlice:
-        let a = genAddrOf(n[1])
+        let a = genAddrOf(n[1], idgen)
         field.typ = a.typ
-        objType.addField(field, g.cache)
+        objType.addField(field, g.cache, idgen)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
 
-        var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
+        var fieldA = newSym(skField, tmpName, nextId idgen, objType.owner, n.info, g.config.options)
         fieldA.typ = getSysType(g, n.info, tyInt)
-        objType.addField(fieldA, g.cache)
+        objType.addField(fieldA, g.cache, idgen)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
 
-        let threadLocal = addLocalVar(g, varSection,nil, owner, fieldA.typ,
+        let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldA.typ,
                                       indirectAccess(castExpr, fieldA, n.info),
                                       useShallowCopy=true)
         slice[2] = threadLocal.newSymNode
       else:
-        let a = genAddrOf(n)
+        let a = genAddrOf(n, idgen)
         field.typ = a.typ
-        objType.addField(field, g.cache)
+        objType.addField(field, g.cache, idgen)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
 
@@ -276,7 +278,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
       # the array itself does not need to go through a thread local variable:
       slice[1] = genDeref(indirectAccess(castExpr, field, n.info))
 
-      let threadLocal = addLocalVar(g, varSection,nil, owner, fieldB.typ,
+      let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldB.typ,
                                     indirectAccess(castExpr, fieldB, n.info),
                                     useShallowCopy=true)
       slice[3] = threadLocal.newSymNode
@@ -284,26 +286,26 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
     elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
         n.getRoot != nil:
       # it is more efficient to pass a pointer instead:
-      let a = genAddrOf(n)
+      let a = genAddrOf(n, idgen)
       field.typ = a.typ
-      objType.addField(field, g.cache)
+      objType.addField(field, g.cache, idgen)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-      let threadLocal = addLocalVar(g, varSection,nil, owner, field.typ,
+      let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
                                     useShallowCopy=true)
       call.add(genDeref(threadLocal.newSymNode))
     else:
       # boring case
       field.typ = argType
-      objType.addField(field, g.cache)
+      objType.addField(field, g.cache, idgen)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
       let threadLocal = addLocalVar(g, varSection, varInit,
-                                    owner, field.typ,
+                                    idgen, owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
                                     useShallowCopy=true)
       call.add(threadLocal.newSymNode)
 
-proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
+proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExpr: PNode; retType: PType;
                        barrier, dest: PNode = nil): PNode =
   # if 'barrier' != nil, then it is in a 'parallel' section and we
   # generate quite different code
@@ -330,9 +332,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   var fn = n[0]
   let
     name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper"
-    wrapperProc = newSym(skProc, getIdent(g.cache, name), owner, fn.info, g.config.options)
-    threadParam = newSym(skParam, getIdent(g.cache, "thread"), wrapperProc, n.info, g.config.options)
-    argsParam = newSym(skParam, getIdent(g.cache, "args"), wrapperProc, n.info, g.config.options)
+    wrapperProc = newSym(skProc, getIdent(g.cache, name), nextId idgen, owner, fn.info, g.config.options)
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), nextId idgen, wrapperProc, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), nextId idgen, wrapperProc, n.info, g.config.options)
 
   wrapperProc.flags.incl sfInjectDestructors
   block:
@@ -341,11 +343,11 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
     argsParam.typ = ptrType
     argsParam.position = 1
 
-  var objType = createObj(g, owner, n.info)
+  var objType = createObj(g, idgen, owner, n.info)
   incl(objType.flags, tfFinal)
-  let castExpr = createCastExpr(argsParam, objType)
+  let castExpr = createCastExpr(argsParam, objType, idgen)
 
-  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), nextId idgen, owner, n.info, g.config.options)
   block:
     scratchObj.typ = objType
     incl(scratchObj.flags, sfFromGeneric)
@@ -362,9 +364,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
                                                skFunc, skMethod, skConverter}):
     # for indirect calls we pass the function pointer in the scratchObj
     var argType = n[0].typ.skipTypes(abstractInst)
-    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fn"), nextId idgen, owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field, g.cache)
+    objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
     fn = indirectAccess(castExpr, field, n.info)
   elif fn.kind == nkSym and fn.sym.kind == skIterator:
@@ -376,49 +378,48 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   var varSection = newNodeI(nkVarSection, n.info)
   var varInit = newNodeI(nkStmtList, n.info)
   if barrier.isNil:
-    setupArgsForConcurrency(g, n, objType, wrapperProc, scratchObj, castExpr, call,
+    setupArgsForConcurrency(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call,
                             varSection, varInit, result)
   else:
-    setupArgsForParallelism(g, n, objType, wrapperProc, scratchObj, castExpr, call,
+    setupArgsForParallelism(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call,
                             varSection, varInit, result)
 
   var barrierAsExpr: PNode = nil
   if barrier != nil:
-    let typ = newType(tyPtr, owner)
+    let typ = newType(tyPtr, nextId idgen, owner)
     typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
-    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), nextId idgen, owner, n.info, g.config.options)
     field.typ = typ
-    objType.addField(field, g.cache)
+    objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
     barrierAsExpr = indirectAccess(castExpr, field, n.info)
 
   var fvField, fvAsExpr: PNode = nil
   if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), nextId idgen, owner, n.info, g.config.options)
     field.typ = retType
-    objType.addField(field, g.cache)
+    objType.addField(field, g.cache, idgen)
     fvField = newDotExpr(scratchObj, field)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     # create flowVar:
     result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
     if barrier == nil:
-      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
-        fvField)
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField)
 
   elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
-    field.typ = newType(tyPtr, objType.owner)
+    var field = newSym(skField, getIdent(g.cache, "fv"), nextId idgen, owner, n.info, g.config.options)
+    field.typ = newType(tyPtr, nextId idgen, objType.owner)
     field.typ.rawAddSon(retType)
-    objType.addField(field, g.cache)
+    objType.addField(field, g.cache, idgen)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen))
 
-  createTypeBoundOps(g, nil, objType, n.info)
+  createTypeBoundOps(g, nil, objType, n.info, idgen)
   createWrapperProc(g, fn, threadParam, argsParam,
                       varSection, varInit, call,
-                      barrierAsExpr, fvAsExpr, spawnKind, wrapperProc)
+                      barrierAsExpr, fvAsExpr, idgen, spawnKind, wrapperProc)
   result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapperProc.info,
-    wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
+    wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr)
 
   if spawnKind == srFlowVar: result.add fvField
 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 5922ca595..42a78c8e3 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -24,7 +24,7 @@ import
   lowerings, liftlocals,
   modulegraphs, lineinfos
 
-proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): PNode
 
 import closureiters, lambdalifting
 
@@ -40,7 +40,7 @@ type
                               # if we encounter the 2nd yield statement
     next: PTransCon           # for stacking
 
-  TTransfContext = object of TPassContext
+  TTransfContext = object
     module: PSym
     transCon: PTransCon      # top of a TransCon stack
     inlining: int            # > 0 if we are in inlining context (copy vars)
@@ -48,6 +48,7 @@ type
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
     deferDetected, tooEarly: bool
     graph: ModuleGraph
+    idgen: IdGenerator
   PTransf = ref TTransfContext
 
 proc newTransNode(a: PNode): PNode {.inline.} =
@@ -86,12 +87,12 @@ proc getCurrOwner(c: PTransf): PSym =
   else: result = c.module
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
-  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info)
+  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), nextId(c.idgen), getCurrOwner(c), info)
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
   if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(c.graph, r, owner)
+    result = freshVarForClosureIter(c.graph, r, c.idgen, owner)
   else:
     result = newSymNode(r)
 
@@ -111,13 +112,13 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   let s = n.sym
   if s.typ != nil and s.typ.callConv == ccClosure:
     if s.kind in routineKinds:
-      discard transformBody(c.graph, s, true)
+      discard transformBody(c.graph, c.idgen, s, true)
     if s.kind == skIterator:
       if c.tooEarly: return n
-      else: return liftIterSym(c.graph, n, getCurrOwner(c))
+      else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c))
     elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
       # top level .closure procs are still somewhat supported for 'Nake':
-      return makeClosure(c.graph, s, nil, n.info)
+      return makeClosure(c.graph, c.idgen, s, nil, n.info)
   #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
   #  echo n.info, " come heer for ", c.tooEarly
   #  if not c.tooEarly:
@@ -160,9 +161,9 @@ proc transformSym(c: PTransf, n: PNode): PNode =
 proc freshVar(c: PTransf; v: PSym): PNode =
   let owner = getCurrOwner(c)
   if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(c.graph, v, owner)
+    result = freshVarForClosureIter(c.graph, v, c.idgen, owner)
   else:
-    var newVar = copySym(v)
+    var newVar = copySym(v, nextId(c.idgen))
     incl(newVar.flags, sfFromGeneric)
     newVar.owner = owner
     result = newSymNode(newVar)
@@ -232,7 +233,7 @@ proc hasContinue(n: PNode): bool =
       if hasContinue(n[i]): return true
 
 proc newLabel(c: PTransf, n: PNode): PSym =
-  result = newSym(skLabel, nil, getCurrOwner(c), n.info)
+  result = newSym(skLabel, nil, nextId(c.idgen), getCurrOwner(c), n.info)
   result.name = getIdent(c.graph.cache, genPrefix)
 
 proc transformBlock(c: PTransf, n: PNode): PNode =
@@ -423,7 +424,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         result.typ = n.typ
       elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
-        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n[0][1]
     if m.kind == a or m.kind == b:
@@ -433,7 +434,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         result.typ = n.typ
       elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
-        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   else:
     if n[0].kind == a or n[0].kind == b:
       # addr ( deref ( x )) --> x
@@ -495,7 +496,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
   of tyOpenArray, tyVarargs:
     result = transform(c, n[1])
     #result = transformSons(c, n)
-    result.typ = takeType(n.typ, n[1].typ)
+    result.typ = takeType(n.typ, n[1].typ, c.idgen)
     #echo n.info, " came here and produced ", typeToString(result.typ),
     #   " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
   of tyCString:
@@ -629,7 +630,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
     result[1] = n
     result[1][^1] = transformLoopBody(c, n[^1])
     result[1][^2] = transform(c, n[^2])
-    result[1] = lambdalifting.liftForLoop(c.graph, result[1], getCurrOwner(c))
+    result[1] = lambdalifting.liftForLoop(c.graph, result[1], c.idgen, getCurrOwner(c))
     discard c.breakSyms.pop
     return result
 
@@ -698,7 +699,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
       stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
       idNodeTablePut(newC.mapping, formal, temp)
 
-  let body = transformBody(c.graph, iter, true)
+  let body = transformBody(c.graph, c.idgen, iter, true)
   pushInfoContext(c.graph.config, n.info)
   inc(c.inlining)
   stmtList.add(transform(c, body))
@@ -830,7 +831,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
     convNode[0] = newNodeI(nkEmpty, n.info)
     convNode[1] = excCall
-    convNode.typ = excTypeNode.typ.toRef()
+    convNode.typ = excTypeNode.typ.toRef(c.idgen)
     # -> let exc = ...
     let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
     identDefs[0] = n[0][2]
@@ -850,10 +851,10 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
   else:
     result = transformSons(c, n)
 
-proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
+proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode): PNode =
   result = n
   for i in 0..<n.safeLen:
-    result[i] = commonOptimizations(g, c, n[i])
+    result[i] = commonOptimizations(g, idgen, c, n[i])
   var op = getMergeOp(n)
   if (op != nil) and (op.magic != mNone) and (n.len >= 3):
     result = newNodeIT(nkCall, n.info, n.typ)
@@ -873,7 +874,7 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
       result.add(a)
     if result.len == 2: result = result[1]
   else:
-    var cnst = getConstExpr(c, n, g)
+    var cnst = getConstExpr(c, n, idgen, g)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
       result = cnst
@@ -1012,7 +1013,7 @@ proc transform(c: PTransf, n: PNode): PNode =
                           n.typ != nil and
                           n.typ.kind == tyPointer
   if not exprIsPointerCast:
-    var cnst = getConstExpr(c.module, result, c.graph)
+    var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
       result = cnst # do not miss an optimization
@@ -1027,12 +1028,13 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   popTransCon(c)
   incl(result.flags, nfTransf)
 
-proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf =
+proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator): PTransf =
   new(result)
   result.contSyms = @[]
   result.breakSyms = @[]
   result.module = module
   result.graph = g
+  result.idgen = idgen
 
 proc flattenStmts(n: PNode) =
   var goOn = true
@@ -1075,7 +1077,7 @@ template liftDefer(c, root) =
   if c.deferDetected:
     liftDeferAux(root)
 
-proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode =
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): PNode =
   assert prc.kind in routineKinds
 
   if prc.transformedBody != nil:
@@ -1084,14 +1086,14 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode =
     result = prc.ast[bodyPos]
   else:
     prc.transformedBody = newNode(nkEmpty) # protects from recursion
-    var c = openTransf(g, prc.getModule, "")
-    result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly)
+    var c = openTransf(g, prc.getModule, "", idgen)
+    result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly, c.idgen)
     result = processTransf(c, result, prc)
     liftDefer(c, result)
-    result = liftLocalsIfRequested(prc, result, g.cache, g.config)
+    result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen)
 
     if prc.isIterator:
-      result = g.transformClosureIterator(prc, result)
+      result = g.transformClosureIterator(c.idgen, prc, result)
 
     incl(result.flags, nfTransf)
 
@@ -1105,21 +1107,21 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode =
   #if prc.name.s == "main":
   #  echo "transformed into ", renderTree(result, {renderIds})
 
-proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
+proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "")
+    var c = openTransf(g, module, "", idgen)
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
     incl(result.flags, nfTransf)
 
-proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode =
+proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "")
+    var c = openTransf(g, module, "", idgen)
     result = processTransf(c, n, module)
     liftDefer(c, result)
     # expressions are not to be injected with destructor calls as that
diff --git a/compiler/types.nim b/compiler/types.nim
index 75e12cac4..a3a4695b0 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1296,11 +1296,11 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
 proc containsGenericType*(t: PType): bool =
   result = iterOverType(t, containsGenericTypeIter, nil)
 
-proc baseOfDistinct*(t: PType): PType =
+proc baseOfDistinct*(t: PType; idgen: IdGenerator): PType =
   if t.kind == tyDistinct:
     result = t[0]
   else:
-    result = copyType(t, t.owner, false)
+    result = copyType(t, nextId idgen, t.owner)
     var parent: PType = nil
     var it = result
     while it.kind in {tyPtr, tyRef, tyOwned}:
@@ -1438,7 +1438,7 @@ proc isEmptyContainer*(t: PType): bool =
   of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon)
   else: result = false
 
-proc takeType*(formal, arg: PType): PType =
+proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
   # param: openArray[string] = []
   # [] is an array constructor of length 0 of type string!
   if arg.kind == tyNil:
@@ -1446,7 +1446,7 @@ proc takeType*(formal, arg: PType): PType =
     result = formal
   elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
       arg.isEmptyContainer:
-    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), arg.owner, keepId=false)
+    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextId(idgen), arg.owner)
     a[ord(arg.kind == tyArray)] = formal[0]
     result = a
   elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
@@ -1454,14 +1454,14 @@ proc takeType*(formal, arg: PType): PType =
   else:
     result = arg
 
-proc skipHiddenSubConv*(n: PNode): PNode =
+proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkHiddenSubConv:
     # param: openArray[string] = []
     # [] is an array constructor of length 0 of type string!
     let formal = n.typ
     result = n[1]
     let arg = result.typ
-    let dest = takeType(formal, arg)
+    let dest = takeType(formal, arg, idgen)
     if dest == arg and formal.kind != tyUntyped:
       #echo n.info, " came here for ", formal.typeToString
       result = n
diff --git a/compiler/vm.nim b/compiler/vm.nim
index bb217c4db..185975ec3 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1105,7 +1105,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
             let ast = a.sym.ast.shallowCopy
             for i in 0..<a.sym.ast.len:
               ast[i] = a.sym.ast[i]
-            ast[bodyPos] = transformBody(c.graph, a.sym, cache=true)
+            ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, cache=true)
             ast.copyTree()
     of opcSymOwner:
       decodeB(rkNode)
@@ -1233,7 +1233,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
           macroCall.add(node)
-        var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter)
+        var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
         if a.kind == nkStmtList and a.len == 1: a = a[0]
         a.recSetFlagIsRef
         ensureKind(rkNode)
@@ -1599,9 +1599,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
         elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
-          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc])
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
       of 1:
@@ -1617,18 +1617,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getTypeInst opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
         elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
-          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc])
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
       else:
         # getTypeImpl opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
         elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
-          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc])
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
     of opcNGetSize:
@@ -1938,7 +1938,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
         internalError(c.config, c.debug[pc], "request to create symbol of invalid kind")
-      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc])
+      var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextId c.idgen, c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
       regs[ra].node.flags.incl nfIsRef
@@ -2058,7 +2058,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       inc pc
       let typ = c.types[c.code[pc].regBx - wordExcess]
-      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config))
+      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config, c.idgen))
     of opcMarshalStore:
       decodeB(rkNode)
       inc pc
@@ -2103,7 +2103,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
       "NimScript: attempt to call non-routine: " & sym.name.s)
 
 proc evalStmt*(c: PCtx, n: PNode) =
-  let n = transformExpr(c.graph, c.module, n)
+  let n = transformExpr(c.graph, c.idgen, c.module, n)
   let start = genStmt(c, n)
   # execute new instructions; this redundant opcEof check saves us lots
   # of allocations in 'execute':
@@ -2111,7 +2111,7 @@ proc evalStmt*(c: PCtx, n: PNode) =
     discard execute(c, start)
 
 proc evalExpr*(c: PCtx, n: PNode): PNode =
-  let n = transformExpr(c.graph, c.module, n)
+  let n = transformExpr(c.graph, c.idgen, c.module, n)
   let start = genExpr(c, n)
   assert c.code[start].opcode != opcEof
   result = execute(c, start)
@@ -2122,20 +2122,20 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
 
 include vmops
 
-proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
+proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
   if graph.vm.isNil:
-    graph.vm = newCtx(module, graph.cache, graph)
+    graph.vm = newCtx(module, graph.cache, graph, idgen)
     registerAdditionalOps(PCtx graph.vm)
   else:
-    refresh(PCtx graph.vm, module)
+    refresh(PCtx graph.vm, module, idgen)
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} =
+proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
   #var c = newEvalContext(module, emRepl)
   #c.features = {allowCast, allowInfiniteLoops}
   #pushStackFrame(c, newStackFrame())
 
   # XXX produce a new 'globals' environment here:
-  setupGlobalCtx(module, graph)
+  setupGlobalCtx(module, graph, idgen)
   result = PCtx graph.vm
 
 proc myProcess(c: PPassContext, n: PNode): PNode =
@@ -2149,16 +2149,16 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
   c.oldErrorCount = c.config.errorCounter
 
 proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
-  myProcess(c, n)
+  result = myProcess(c, n)
 
 const evalPass* = makePass(myOpen, myProcess, myClose)
 
-proc evalConstExprAux(module: PSym;
+proc evalConstExprAux(module: PSym; idgen: IdGenerator;
                       g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
   if g.config.errorCounter > 0: return n
-  let n = transformExpr(g, module, n)
-  setupGlobalCtx(module, g)
+  let n = transformExpr(g, idgen, module, n)
+  setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
   let oldMode = c.mode
   c.mode = mode
@@ -2173,17 +2173,17 @@ proc evalConstExprAux(module: PSym;
   if result.info.col < 0: result.info = n.info
   c.mode = oldMode
 
-proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode =
-  result = evalConstExprAux(module, g, nil, e, emConst)
+proc evalConstExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode): PNode =
+  result = evalConstExprAux(module, idgen, g, nil, e, emConst)
 
-proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode =
-  result = evalConstExprAux(module, g, prc, e, emStaticExpr)
+proc evalStaticExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym): PNode =
+  result = evalConstExprAux(module, idgen, g, prc, e, emStaticExpr)
 
-proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) =
-  discard evalConstExprAux(module, g, prc, e, emStaticStmt)
+proc evalStaticStmt*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym) =
+  discard evalConstExprAux(module, idgen, g, prc, e, emStaticStmt)
 
-proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) =
-  discard evalConstExprAux(module, g, nil, n, emStaticStmt)
+proc setupCompileTimeVar*(module: PSym; idgen: IdGenerator; g: ModuleGraph; n: PNode) =
+  discard evalConstExprAux(module, idgen, g, nil, n, emStaticStmt)
 
 proc prepareVMValue(arg: PNode): PNode =
   ## strip nkExprColonExpr from tuple values recurively. That is how
@@ -2227,14 +2227,14 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
 # to prevent endless recursion in macro instantiation
 const evalMacroLimit = 1000
 
-proc errorNode(owner: PSym, n: PNode): PNode =
+proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
   result = newNodeI(nkEmpty, n.info)
-  result.typ = newType(tyError, owner)
+  result.typ = newType(tyError, nextId idgen, owner)
   result.typ.flags.incl tfCheckedForDestructor
 
-proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int;
+proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
                     n, nOrig: PNode, sym: PSym): PNode =
-  if g.config.errorCounter > 0: return errorNode(module, n)
+  if g.config.errorCounter > 0: return errorNode(idgen, module, n)
 
   # XXX globalError() is ugly here, but I don't know a better solution for now
   inc(g.config.evalMacroCounter)
@@ -2247,7 +2247,7 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int;
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
         n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
 
-  setupGlobalCtx(module, g)
+  setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
   let oldMode = c.mode
   c.mode = emStaticStmt
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 59ffe9581..202087af7 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -282,17 +282,18 @@ type
 
   PEvalContext* = PCtx
 
-proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx =
+proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator): PCtx =
   PCtx(code: @[], debug: @[],
     globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
     prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
     comesFromHeuristic: unknownLineInfo, callbacks: @[], errorFlag: "",
-    cache: cache, config: g.config, graph: g)
+    cache: cache, config: g.config, graph: g, idgen: idgen)
 
-proc refresh*(c: PCtx, module: PSym) =
+proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
   c.module = module
   c.prc = PProc(blocks: @[])
   c.loopIterations = c.config.maxLoopIterationsVM
+  c.idgen = idgen
 
 proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
   result = c.callbacks.len
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 37e7e4b38..8083ae179 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -24,8 +24,9 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
     localError(conf, info, "cannot open file: " & file)
     result = ""
 
-proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
-  let sym = newSym(skType, getIdent(cache, name), t.owner, info)
+proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
+                 idgen: IdGenerator): PNode =
+  let sym = newSym(skType, getIdent(cache, name), nextId(idgen), t.owner, info)
   sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
@@ -35,44 +36,46 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
   result = newSymNode(s)
   result.info = info
 
-proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator;
                    inst=false; allowRecursionX=false): PNode
 
 proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
+                       idgen: IdGenerator;
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-  result.add atomicTypeX(cache, name, m, t, info)
+  result.add atomicTypeX(cache, name, m, t, info, idgen)
   for i in 0..<t.len:
     if t[i] == nil:
-      let void = atomicTypeX(cache, "void", mVoid, t, info)
-      void.typ = newType(tyVoid, t.owner)
+      let void = atomicTypeX(cache, "void", mVoid, t, info, idgen)
+      void.typ = newType(tyVoid, nextId(idgen), t.owner)
       result.add void
     else:
-      result.add mapTypeToAstX(cache, t[i], info, inst)
+      result.add mapTypeToAstX(cache, t[i], info, idgen, inst)
 
-proc objectNode(cache: IdentCache; n: PNode): PNode =
+proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkSym:
     result = newNodeI(nkIdentDefs, n.info)
     result.add n  # name
-    result.add mapTypeToAstX(cache, n.sym.typ, n.info, true, false)  # type
+    result.add mapTypeToAstX(cache, n.sym.typ, n.info, idgen, true, false)  # type
     result.add newNodeI(nkEmpty, n.info)  # no assigned value
   else:
     result = copyNode(n)
     for i in 0..<n.safeLen:
-      result.add objectNode(cache, n[i])
+      result.add objectNode(cache, n[i], idgen)
 
 proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
+                   idgen: IdGenerator;
                    inst=false; allowRecursionX=false): PNode =
   var allowRecursion = allowRecursionX
-  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info)
+  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, inst, true)
+  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
+  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
   template mapTypeToAst(t,i,info): untyped =
-    if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, inst)
+    if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, idgen, inst)
     else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
-    mapTypeToBracketX(cache, name, m, t, info, inst)
+    mapTypeToBracketX(cache, name, m, t, info, idgen, inst)
   template newNodeX(kind): untyped =
     newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
   template newIdentDefs(n,t): untyped =
@@ -134,14 +137,14 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
         for i in 1..<t.len-1:
           result.add mapTypeToAst(t[i], info)
     else:
-      result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
+      result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
   of tyGenericBody:
     if inst:
       result = mapTypeToAstR(t.lastSon, info)
     else:
       result = mapTypeToAst(t.lastSon, info)
   of tyAlias:
-    result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
+    result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
   of tyOrdinal:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
@@ -169,7 +172,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
         nn.add mapTypeToAst(t[0], info)
         result.add nn
       if t.n.len > 0:
-        result.add objectNode(cache, t.n)
+        result.add objectNode(cache, t.n, idgen)
       else:
         result.add newNodeI(nkEmpty, info)
     else:
@@ -298,15 +301,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyOwned: result = mapTypeToBracket("owned", mBuiltinType, t, info)
   of tyOptDeprecated: doAssert false
 
-proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, inst=false, allowRecursionX=true)
+proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=false, allowRecursionX=true)
 
 # the "Inst" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type declaration
-proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=false)
+proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false)
 
 # the "Impl" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type implementation
-proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=true)
+proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 44b10ea67..5ba15bb2b 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1118,7 +1118,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(d)
   of mSwap:
     unused(c, n, dest)
-    c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym))
+    c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mParseBiggestFloat:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1819,7 +1819,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a[i].kind == nkSym:
           if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
           checkCanEval(c, a[i])
-      c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
+      c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner))
     elif a[0].kind == nkSym:
       let s = a[0].sym
       checkCanEval(c, a[0])
@@ -2235,7 +2235,7 @@ proc genProc(c: PCtx; s: PSym): int =
     s.ast[miscPos] = x
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
-    let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s))
+    let body = transformBody(c.graph, c.idgen, s, cache = not isCompileTimeProc(s))
     let procStart = c.xjmp(body, opcJmp, 0)
     var p = PProc(blocks: @[], sym: s)
     let oldPrc = c.prc
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index 696183943..979e5de8a 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -136,7 +136,8 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
 proc loadAny(p: var JsonParser, t: PType,
              tab: var Table[BiggestInt, PNode];
              cache: IdentCache;
-             conf: ConfigRef): PNode =
+             conf: ConfigRef;
+             idgen: IdGenerator): PNode =
   case t.kind
   of tyNone: assert false
   of tyBool:
@@ -170,7 +171,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkBracket)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.elemType, tab, cache, conf)
+      result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tySequence:
@@ -182,7 +183,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       result = newNode(nkBracket)
       while p.kind != jsonArrayEnd and p.kind != jsonEof:
-        result.add loadAny(p, t.elemType, tab, cache, conf)
+        result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
@@ -198,7 +199,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       if i >= t.len:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
-      result.add loadAny(p, t[i], tab, cache, conf)
+      result.add loadAny(p, t[i], tab, cache, conf, idgen)
       inc i
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -219,8 +220,8 @@ proc loadAny(p: var JsonParser, t: PType,
       if pos >= result.len:
         setLen(result.sons, pos + 1)
       let fieldNode = newNode(nkExprColonExpr)
-      fieldNode.add newSymNode(newSym(skField, ident, nil, unknownLineInfo))
-      fieldNode.add loadAny(p, field.typ, tab, cache, conf)
+      fieldNode.add newSymNode(newSym(skField, ident, nextId(idgen), nil, unknownLineInfo))
+      fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen)
       result[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -229,7 +230,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkCurly)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.lastSon, tab, cache, conf)
+      result.add loadAny(p, t.lastSon, tab, cache, conf, idgen)
       next(p)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
@@ -248,7 +249,7 @@ proc loadAny(p: var JsonParser, t: PType,
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab, cache, conf)
+        result = loadAny(p, t.lastSon, tab, cache, conf, idgen)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
@@ -276,14 +277,14 @@ proc loadAny(p: var JsonParser, t: PType,
       return
     raiseParseErr(p, "float expected")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    result = loadAny(p, t.lastSon, tab, cache, conf)
+    result = loadAny(p, t.lastSon, tab, cache, conf, idgen)
   else:
     internalError conf, "cannot marshal at compile-time " & t.typeToString
 
-proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode =
+proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
   var tab = initTable[BiggestInt, PNode]()
   var p: JsonParser
   open(p, newStringStream(s), "unknown file")
   next(p)
-  result = loadAny(p, t, tab, cache, conf)
+  result = loadAny(p, t, tab, cache, conf, idgen)
   close(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index a3d4d74db..47df3d24f 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -268,7 +268,7 @@ proc registerAdditionalOps*(c: PCtx) =
         fn.typ.n[0][effectIndex] != nil:
       var list = newNodeI(nkBracket, fn.info)
       for e in fn.typ.n[0][effectIndex]:
-        list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info)
+        list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen)
       setResult(a, list)
 
   registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim
index 99ad6eef6..c8c0edbd4 100644
--- a/lib/system/iterators.nim
+++ b/lib/system/iterators.nim
@@ -82,7 +82,7 @@ iterator mitems*(a: var cstring): var char {.inline.} =
       yield a[i]
       inc(i)
 
-iterator items*(E: typedesc[enum]): E =
+iterator items*[T: enum](E: typedesc[T]): T =
   ## Iterates over the values of the enum ``E``.
   for v in low(E) .. high(E):
     yield v