summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim165
-rw-r--r--compiler/ccgstmts.nim8
-rw-r--r--compiler/ccgtypes.nim70
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/liftdestructors.nim253
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/semtypinst.nim2
-rw-r--r--lib/core/runtime_v2.nim78
-rw-r--r--lib/core/seqs.nim69
-rw-r--r--lib/core/strs.nim74
-rw-r--r--lib/system/chcks.nim88
-rw-r--r--lib/system/mmdisp.nim2
13 files changed, 574 insertions, 241 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 406528845..e080ca746 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1165,40 +1165,45 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
   assert refType.kind == tyRef
   let bt = refType.lastSon
   if sizeExpr.isNil:
-    sizeExpr = "sizeof($1)" %
-        [getTypeDesc(p.module, bt)]
-
-  let ti = genTypeInfo(p.module, typ, a.lode.info)
-  if bt.destructor != nil:
-    # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
-    # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
-    # convention at least:
-    if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault:
-      localError(p.module.config, a.lode.info,
-        "the destructor that is turned into a finalizer needs " &
-        "to have the 'nimcall' calling convention")
-    var f: TLoc
-    initLocExpr(p, newSymNode(bt.destructor), f)
-    addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
-
-  let args = [getTypeDesc(p.module, typ), ti, sizeExpr]
-  if a.storage == OnHeap and usesWriteBarrier(p.config):
-    if canFormAcycle(a.t):
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
+    sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
+
+  if optNimV2 in p.config.globalOptions:
+    b.r = ropecg(p.module, "($1) #nimNewObj($2)",
+        [getTypeDesc(p.module, typ), sizeExpr])
+    genAssignment(p, a, b, {})
+  else:
+    let ti = genTypeInfo(p.module, typ, a.lode.info)
+    if bt.destructor != nil:
+      # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
+      # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
+      # convention at least:
+      if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault:
+        localError(p.module.config, a.lode.info,
+          "the destructor that is turned into a finalizer needs " &
+          "to have the 'nimcall' calling convention")
+      var f: TLoc
+      initLocExpr(p, newSymNode(bt.destructor), f)
+      addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
+
+    let args = [getTypeDesc(p.module, typ), ti, sizeExpr]
+    if a.storage == OnHeap and usesWriteBarrier(p.config):
+      if canFormAcycle(a.t):
+        linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
+      else:
+        linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
+      if p.config.selectedGC == gcGo:
+        # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
+        # implement the write barrier
+        b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
+        linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc)
+      else:
+        # use newObjRC1 as an optimization
+        b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
+        linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
     else:
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
-    if p.config.selectedGC == gcGo:
-      # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
-      # implement the write barrier
       b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
-      linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc)
-    else:
-      # use newObjRC1 as an optimization
-      b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
-      linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
-  else:
-    b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
-    genAssignment(p, a, b, {})  # set the object type:
+      genAssignment(p, a, b, {})
+  # set the object type:
   genObjectInit(p, cpsStmts, bt, a, false)
 
 proc genNew(p: BProc, e: PNode) =
@@ -1433,22 +1438,26 @@ proc genNewFinalize(p: BProc, e: PNode) =
   gcUsage(p.config, e)
 
 proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
-  # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
-  # have to call it here first:
-  let ti = genTypeInfo(p.module, dest, info)
-  if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
-                               tfObjHasKids notin dest.flags):
-    result = "$1.m_type == $2" % [a, ti]
-  else:
-    discard cgsym(p.module, "TNimType")
-    inc p.module.labels
-    let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
-    addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
-    result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
-  when false:
-    # former version:
+  if optNimV2 in p.config.globalOptions:
     result = ropecg(p.module, "#isObj($1.m_type, $2)",
-                  a, genTypeInfo(p.module, dest, info))
+      a, genTypeInfo2Name(p.module, dest))
+  else:
+    # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
+    # have to call it here first:
+    let ti = genTypeInfo(p.module, dest, info)
+    if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
+                                tfObjHasKids notin dest.flags):
+      result = "$1.m_type == $2" % [a, ti]
+    else:
+      discard cgsym(p.module, "TNimType")
+      inc p.module.labels
+      let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
+      addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
+      result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
+    when false:
+      # former version:
+      result = ropecg(p.module, "#isObj($1.m_type, $2)",
+                    a, genTypeInfo(p.module, dest, info))
 
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
   var a: TLoc
@@ -1925,11 +1934,57 @@ proc genWasMoved(p: BProc; n: PNode) =
   #  addrLoc(p.config, a), getTypeDesc(p.module, a.t))
 
 proc genMove(p: BProc; n: PNode; d: var TLoc) =
-  if d.k == locNone: getTemp(p, n.typ, d)
   var a: TLoc
   initLocExpr(p, n[1].skipAddr, a)
-  genAssignment(p, d, a, {})
-  resetLoc(p, a)
+  if n.len == 4:
+    # generated by liftdestructors:
+    var src, destroyCall: TLoc
+    initLocExpr(p, n[2], src)
+    initLocExpr(p, n[3], destroyCall)
+    linefmt(p, cpsStmts, "if ($1.len && $1.p != $2.p) { $3; }$n" &
+      "$1.len = $2.len; $1.p = $2.p;$n",
+      [rdLoc(a), rdLoc(src), rdLoc(destroyCall)])
+  else:
+    if d.k == locNone: getTemp(p, n.typ, d)
+    genAssignment(p, d, a, {})
+    resetLoc(p, a)
+
+proc genDestroy(p: BProc; n: PNode) =
+  if optNimV2 in p.config.globalOptions:
+    let t = n[1].typ.skipTypes(abstractInst)
+    case t.kind
+    of tyString:
+      var a: TLoc
+      initLocExpr(p, n[1].skipAddr, a)
+      linefmt(p, cpsStmts, "if ($1.len && $1.region) {$n" &
+        " $1.region->dealloc($1.region, $1.p, $1.p->cap + 1 + sizeof(NI) + sizeof(void*)); }$n",
+        [rdLoc(a)])
+    of tySequence:
+      var a: TLoc
+      initLocExpr(p, n[1].skipAddr, a)
+      linefmt(p, cpsStmts, "if ($1.len && $1.region) {$n" &
+        " $1.region->dealloc($1.region, $1.p, ($1.p->cap * sizeof($2)) + sizeof(NI) + sizeof(void*)); }$n",
+        [rdLoc(a), getTypeDesc(p.module, t.lastSon)])
+    else: discard "nothing to do"
+  else:
+    discard "ignore calls to the default destructor"
+
+proc genDispose(p: BProc; n: PNode) =
+  when false:
+    let elemType = n[1].typ.skipTypes(abstractVarInst).lastSon
+
+    var a: TLoc
+    initLocExpr(p, n[1].skipAddr, a)
+
+    if isFinal(elemType):
+      if elemType.destructor != nil:
+        var destroyCall = newNodeI(nkCall, n.info)
+        genStmts(p, destroyCall)
+      lineCg(p, cpsStmts, "#nimRawDispose($#)", rdLoc(a))
+    else:
+      # ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
+      # destructor, but it uses the runtime type. Afterwards the memory is freed:
+      lineCg(p, cpsStmts, "#nimDestroyAndDispose($#)", rdLoc(a))
 
 proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   case op
@@ -2090,7 +2145,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mDotDot, mEqCString: genCall(p, e, d)
   of mWasMoved: genWasMoved(p, e)
   of mMove: genMove(p, e, d)
-  of mDestroy: discard "ignore calls to the default destructor"
+  of mDestroy: genDestroy(p, e)
   of mSlice:
     localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
       " 'toOpenArray' is only valid within a call expression")
@@ -2250,12 +2305,16 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
       while t.kind == tyObject and t.sons[0] != nil:
         add(r, ".Sup")
         t = skipTypes(t.sons[0], skipPtrs)
+    let checkFor = if optNimV2 in p.config.globalOptions:
+                     genTypeInfo2Name(p.module, dest)
+                   else:
+                     genTypeInfo(p.module, dest, n.info)
     if nilCheck != nil:
       linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
-              nilCheck, r, genTypeInfo(p.module, dest, n.info))
+              nilCheck, r, checkFor)
     else:
       linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
-              r, genTypeInfo(p.module, dest, n.info))
+              r, checkFor)
   if n.sons[0].typ.kind != tyObject:
     putIntoDest(p, d, n,
                 "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 6dc10db3d..e2790f4f6 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1039,8 +1039,12 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
         let isObjFormat = if not p.module.compileToCpp:
           "#isObj(#getCurrentException()->Sup.m_type, $1)"
           else: "#isObj(#getCurrentException()->m_type, $1)"
-        appcg(p.module, orExpr, isObjFormat,
-              [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
+
+        let checkFor = if optNimV2 in p.config.globalOptions:
+          genTypeInfo2Name(p.module, t[i][j].typ)
+        else:
+          genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
+        appcg(p.module, orExpr, isObjFormat, [checkFor])
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
       if not quirkyExceptions:
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 063c02df9..ed051170f 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -980,7 +980,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
     discard cgsym(m, "nimTypeRoot")
     addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
          [nameHcr])
-  
+
   if m.hcrOn:
     addf(m.s[cfsVars], "static TNimType* $1;$n", [name])
     addf(m.hcrCreateTypeInfosProc, "\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
@@ -1204,6 +1204,60 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
   addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
      [result, s.loc.r])
 
+proc declareNimType(m: BModule, str: Rope, ownerModule: PSym) =
+  if m.hcrOn:
+    addf(m.s[cfsVars], "static TNimType* $1;$n", [str])
+    addf(m.s[cfsTypeInit1], "\t$1 = (TNimType*)hcrGetGlobal($2, \"$1\");$n",
+          [str, getModuleDllPath(m, ownerModule)])
+  else:
+    addf(m.s[cfsVars], "extern TNimType $1;$n", [str])
+
+proc genTypeInfo2Name(m: BModule; t: PType): Rope =
+  var res = "|"
+  var it = t
+  while it != nil:
+    it = it.skipTypes(skipPtrs)
+    if it.sym != nil:
+      var m = t.sym.owner
+      while m != nil and m.kind != skModule: m = m.owner
+      if m == nil or sfSystemModule in m.flags:
+        # produce short names for system types:
+        res.add it.sym.name.s
+      else:
+        var p = m.owner
+        if p != nil and p.kind == skPackage:
+          res.add p.name.s & "."
+        res.add m.name.s & "."
+        res.add it.sym.name.s
+    else:
+      res.add $hashType(it)
+    res.add "|"
+    it = it.sons[0]
+  result = makeCString(res)
+
+proc genObjectInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
+  assert t.kind == tyObject
+  if incompleteType(t):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+                      typeToString(t))
+
+  var d: Rope
+  if t.destructor != nil:
+    # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
+    # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
+    # convention at least:
+    if t.destructor.typ == nil or t.destructor.typ.callConv != ccDefault:
+      localError(m.config, info,
+        "the destructor that is turned into a finalizer needs " &
+        "to have the 'nimcall' calling convention")
+    genProc(m, t.destructor)
+    d = t.destructor.loc.r
+  else:
+    d = rope("NIM_NIL")
+
+  addf(m.s[cfsTypeInit3], "$1.destructor = $2; $1.size = sizeof($3); $1.name = $4;$n", [
+    name, d, getTypeDesc(m, t), genTypeInfo2Name(m, t)])
+
 proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
@@ -1215,14 +1269,6 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
   if result != nil:
     return prefixTI.rope & result & ")".rope
 
-  proc declareNimType(m: BModule, str: Rope, ownerModule: PSym) =
-    if m.hcrOn:
-      addf(m.s[cfsVars], "static TNimType* $1;$n", [str])
-      addf(m.s[cfsTypeInit1], "\t$1 = (TNimType*)hcrGetGlobal($2, \"$1\");$n",
-           [str, getModuleDllPath(m, ownerModule)])
-    else:
-      addf(m.s[cfsVars], "extern TNimType $1;$n", [str])
-
   let marker = m.g.typeInfoMarker.getOrDefault(sig)
   if marker.str != nil:
     discard cgsym(m, "TNimType")
@@ -1278,7 +1324,11 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
   of tyArray: genArrayInfo(m, t, result, info)
   of tySet: genSetInfo(m, t, result, info)
   of tyEnum: genEnumInfo(m, t, result, info)
-  of tyObject: genObjectInfo(m, t, origType, result, info)
+  of tyObject:
+    if optNimV2 in m.config.globalOptions:
+      genObjectInfoV2(m, t, origType, result, info)
+    else:
+      genObjectInfo(m, t, origType, result, info)
   of tyTuple:
     # if t.n != nil: genObjectInfo(m, t, result)
     # else:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 4a10a4d2b..a7c1895f6 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -293,7 +293,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
     includeHeader(p.module, "<new>")
     linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
 
-  if optNimV2 in p.config.globalOptions: return
+  #if optNimV2 in p.config.globalOptions: return
   case analyseObjectWithTypeField(t)
   of frNone:
     discard
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 404be3df3..ee16702a2 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -740,6 +740,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     incl(conf.features, destructor)
     incl(conf.globalOptions, optNimV2)
     defineSymbol(conf.symbols, "nimV2")
+    conf.selectedGC = gcDestructors
+    defineSymbol(conf.symbols, "gcdestructors")
   of "stylecheck":
     case arg.normalize
     of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 6b36dd6fb..aea244a78 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -113,9 +113,13 @@ proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(op, y))
 
+proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
+  result = optNimV2 in c.graph.config.globalOptions and
+    (tfHasGCedMem in t.flags or t.isGCedMem)
+
 proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
                         field: PSym): bool =
-  if tfHasAsgn in t.flags:
+  if tfHasAsgn in t.flags or useNoGc(c, t):
     var op: PSym
     if sameType(t, c.asgnForType):
       # generate recursive call:
@@ -136,15 +140,28 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     body.add newAsgnCall(c.graph, op, x, y)
     result = true
 
-proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
-  case c.kind
-  of attachedDestructor:
-    let op = t.destructor
-    if op != nil:
+proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
+  let op = t.destructor
+  if op != nil:
+    markUsed(c.graph.config, c.info, op, c.graph.usageSym)
+    onUse(c.info, op)
+    body.add destructorCall(c.graph, op, x)
+    result = true
+  elif useNoGc(c, t):
+    if sameType(t, c.asgnForType) and c.kind == attachedDestructor:
+      let op = c.fn
       markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
       body.add destructorCall(c.graph, op, x)
       result = true
+    else:
+      internalError(c.graph.config, c.info,
+        "type-bound operator could not be resolved")
+
+proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
+  case c.kind
+  of attachedDestructor:
+    result = addDestructorCall(c, t, body, x)
   of attachedAsgn:
     result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
   of attachedSink:
@@ -185,12 +202,15 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
   result = newNodeI(nkWhileStmt, c.info, 2)
-  let cmp = genBuiltin(c.graph, mLeI, "<=", i)
-  cmp.add genHigh(c.graph, dest)
+  let cmp = genBuiltin(c.graph, mLtI, "<", i)
+  cmp.add genLen(c.graph, dest)
   cmp.typ = getSysType(c.graph, c.info, tyBool)
   result.sons[0] = cmp
   result.sons[1] = newNodeI(nkStmtList, c.info)
 
+proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
+  result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
+
 proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
   let incCall = genBuiltin(c.graph, mInc, "inc", i)
   incCall.add lowerings.newIntLit(c.graph, c.info, 1)
@@ -203,42 +223,166 @@ proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
   lenCall.typ = getSysType(g, x.info, tyInt)
   result.add lenCall
 
+proc setLenCall(g: ModuleGraph; x, y: PNode): PNode =
+  let lenCall = genBuiltin(g, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(g, x.info, tyInt)
+  result = genBuiltin(g, mSetLengthSeq, "setLen", genAddr(g, x))
+  result.add lenCall
+
+proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  let i = declareCounter(c, body, firstOrd(c.graph.config, t))
+  let whileLoop = genWhileLoop(c, i, x)
+  let elemType = t.lastSon
+  liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
+                                              y.at(i, elemType))
+  addIncStmt(c, whileLoop.sons[1], i)
+  body.add whileLoop
+
+proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedAsgn, attachedDeepCopy:
+    # we generate:
+    # setLen(dest, y.len)
+    # var i = 0
+    # while i < y.len: dest[i] = y[i]; inc(i)
+    # This is usually more efficient than a destroy/create pair.
+    body.add setLenCall(c.graph, x, y)
+    forallElements(c, t, body, x, y)
+  of attachedSink:
+    let moveCall = genBuiltin(c.graph, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c.graph, t.destructor, x)
+    body.add moveCall
+    when false:
+      # we generate:
+      #  if a.len != 0 and a.p != b.p:
+      #    `=destroy`(x)
+      #  a.len = b.len
+      #  a.p = b.p
+      # Note: '@' is either '.' or '->'.
+      body.add genIf(c, genVerbatim("dest@len != 0 && dest@p != src.p", c.info),
+        destructorCall(c.graph, t.destructor, x))
+      body.add genVerbatim("dest@len=src.len; dest@p=src.p;", c.info)
+  of attachedDestructor:
+    # destroy all elements:
+    forallElements(c, t, body, x, y)
+    body.add genBuiltin(c.graph, mDestroy, "destroy", x)
+    when false:
+      var deallocStmt = genVerbatim("dest@region->dealloc(dest@region, dest@p, " &
+          "(dest@p->cap * sizeof($)) + sizeof(NI) + sizeof(void*)); dest@len = 0;", c.info)
+      deallocStmt.typ = t.lastSon
+      body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
+
+proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  seqOp(c, t, body, x, y)
+
+proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedSink:
+    # we 'nil' y out afterwards so we *need* to take over its reference
+    # count value:
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedAsgn:
+    body.add callCodegenProc(c.graph, "nimIncWeakRef", c.info, y)
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
+  of attachedDeepCopy: assert(false, "cannot happen")
+
+proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var actions = newNodeI(nkStmtList, c.info)
+
+  let elemType = t.lastSon
+  #liftBodyAux(c, elemType, actions, genDeref(x), genDeref(y))
+  #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
+
+  if isFinal(elemType):
+    discard addDestructorCall(c, elemType, actions, x)
+    actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
+  else:
+    discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), x)
+    actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
+
+  case c.kind
+  of attachedSink, attachedAsgn:
+    body.add genIf(c, x, actions)
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, x, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+
+proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  if c.kind == attachedDeepCopy:
+    # a big problem is that we don't know the enviroment's type here, so we
+    # have to go through some indirection; we delegate this to the codegen:
+    let call = newNodeI(nkCall, c.info, 2)
+    call.typ = t
+    call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
+    call.sons[1] = y
+    body.add newAsgnStmt(x, call)
+  elif optNimV2 in c.graph.config.globalOptions:
+    case c.kind
+    of attachedSink, attachedAsgn: discard
+    of attachedDestructor: discard
+    of attachedDeepCopy: assert(false, "cannot happen")
+
+proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  discard "to implement"
+
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
   of tyNone, tyEmpty, tyVoid: discard
   of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
-      tyPtr, tyRef, tyOpt, tyUncheckedArray:
+      tyPtr, tyOpt, tyUncheckedArray:
     defaultOp(c, t, body, x, y)
+  of tyRef:
+    if optNimV2 in c.graph.config.globalOptions:
+      weakrefOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyProc:
+    if t.callConv == ccClosure:
+      closureOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyOwned:
+    let base = t.skipTypes(abstractInstOwned)
+    if optNimV2 in c.graph.config.globalOptions:
+      case base.kind
+      of tyRef:
+        ownedRefOp(c, base, body, x, y)
+        return
+      of tyProc:
+        if base.callConv == ccClosure:
+          ownedClosureOp(c, base, body, x, y)
+          return
+      else: discard
+    defaultOp(c, base, body, x, y)
   of tyArray:
-    if tfHasAsgn in t.flags:
-      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
-      let whileLoop = genWhileLoop(c, i, x)
-      let elemType = t.lastSon
-      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
-                                                  y.at(i, elemType))
-      addIncStmt(c, whileLoop.sons[1], i)
-      body.add whileLoop
+    if tfHasAsgn in t.flags or useNoGc(c, t):
+      forallElements(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
   of tySequence:
-    # note that tfHasAsgn is propagated so we need the check on
-    # 'selectedGC' here to determine if we have the new runtime.
-    if c.graph.config.selectedGC == gcDestructors:
+    if useNoGc(c, t):
+      seqOp(c, t, body, x, y)
+    elif c.graph.config.selectedGC == gcDestructors:
+      # note that tfHasAsgn is propagated so we need the check on
+      # 'selectedGC' here to determine if we have the new runtime.
       discard considerOverloadedOp(c, t, body, x, y)
     elif tfHasAsgn in t.flags:
       if c.kind != attachedDestructor:
         body.add newSeqCall(c.graph, x, y)
-      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
-      let whileLoop = genWhileLoop(c, i, x)
-      let elemType = t.lastSon
-      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
-                                                  y.at(i, elemType))
-      addIncStmt(c, whileLoop.sons[1], i)
-      body.add whileLoop
+      forallElements(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
   of tyString:
-    if tfHasAsgn in t.flags:
+    if useNoGc(c, t):
+      strOp(c, t, body, x, y)
+    elif tfHasAsgn in t.flags:
       discard considerOverloadedOp(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
@@ -250,17 +394,6 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
   of tyTuple:
     liftBodyTup(c, t, body, x, y)
-  of tyProc:
-    if t.callConv != ccClosure or c.kind != attachedDeepCopy:
-      defaultOp(c, t, body, x, y)
-    else:
-      # a big problem is that we don't know the enviroment's type here, so we
-      # have to go through some indirection; we delegate this to the codegen:
-      let call = newNodeI(nkCall, c.info, 2)
-      call.typ = t
-      call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
-      call.sons[1] = y
-      body.add newAsgnStmt(x, call)
   of tyVarargs, tyOpenArray:
     localError(c.graph.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
@@ -269,7 +402,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
      tyTypeDesc, tyGenericInvocation, tyForward:
     internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
   of tyOrdinal, tyRange, tyInferred,
-     tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink, tyOwned:
+     tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
     liftBodyAux(c, lastSon(t), body, x, y)
 
 proc newProcType(info: TLineInfo; owner: PSym): PType =
@@ -290,26 +423,26 @@ proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; inf
   assert typ.kind == tyDistinct
   let baseType = typ[0]
   case kind
-    of attachedAsgn:
-      if baseType.assignment == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.assignment = baseType.assignment
-      result = typ.assignment
-    of attachedSink:
-      if baseType.sink == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.sink = baseType.sink
-      result = typ.sink
-    of attachedDeepCopy:
-      if baseType.deepCopy == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.deepCopy = baseType.deepCopy
-      result = typ.deepCopy
-    of attachedDestructor:
-      if baseType.destructor == nil:
-        discard liftBody(g, baseType, kind, info)
-      typ.destructor = baseType.destructor
-      result = typ.destructor
+  of attachedAsgn:
+    if baseType.assignment == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.assignment = baseType.assignment
+    result = typ.assignment
+  of attachedSink:
+    if baseType.sink == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.sink = baseType.sink
+    result = typ.sink
+  of attachedDeepCopy:
+    if baseType.deepCopy == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.deepCopy = baseType.deepCopy
+    result = typ.deepCopy
+  of attachedDestructor:
+    if baseType.destructor == nil:
+      discard liftBody(g, baseType, kind, info)
+    typ.destructor = baseType.destructor
+    result = typ.destructor
 
 proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 0fabd5531..52b236883 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1600,7 +1600,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mSet: result = semSet(c, n, prev)
     of mOrdinal: result = semOrdinal(c, n, prev)
     of mSeq:
-      if c.config.selectedGc == gcDestructors:
+      if c.config.selectedGc == gcDestructors and optNimV2 notin c.config.globalOptions:
         let s = c.graph.sysTypes[tySequence]
         assert s != nil
         assert prev == nil
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index e3380c0e3..152a75ee3 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -593,7 +593,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
       of tySequence:
         if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and
-            result[0].kind != tyEmpty:
+            result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
           let s = cl.c.graph.sysTypes[tySequence]
           var old = copyType(s, s.owner, keepId=false)
           # Remove the 'T' parameter from tySequence:
diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim
new file mode 100644
index 000000000..8a9068c37
--- /dev/null
+++ b/lib/core/runtime_v2.nim
@@ -0,0 +1,78 @@
+#[
+In this new runtime we simply the object layouts a bit: The runtime type
+information is only accessed for the objects that have it and it's always
+at offset 0 then. The ``ref`` object header is independent from the
+runtime type and only contains a reference count.
+
+Object subtyping is checked via the generated 'name'. This should have
+comparable overhead to the old pointer chasing approach but has the benefit
+that it works across DLL boundaries.
+
+The generated name is a concatenation of the object names in the hierarchy
+so that a subtype check becomes a substring check. For example::
+
+  type
+    ObjectA = object of RootObj
+    ObjectB = object of ObjectA
+
+ObjectA's ``name`` is "|ObjectA|RootObj|".
+ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
+
+Now to check for ``x of ObjectB`` we need to check
+for ``x.typ.name.hasSubstring("|ObjectB|")``. In the actual implementation,
+however, we could also use a
+hash of ``package & "." & module & "." & name`` to save space.
+
+]#
+
+type
+  TNimNode {.compilerProc.} = object # to keep the code generator simple
+  TNimType {.compilerProc.} = object
+    destructor: proc (p: pointer) {.nimcall, benign.}
+    size: int
+    name: cstring
+  PNimType = ptr TNimType
+
+  ObjHeader = object
+    rc: int # the object header is now a single RC field.
+            # we could remove it in non-debug builds but this seems
+            # unwise.
+
+proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
+  proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
+
+  result = strstr(obj.name, subclass) != nil
+
+proc chckObj(obj: PNimType, subclass: cstring) {.compilerproc.} =
+  # checks if obj is of type subclass:
+  if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")
+
+template `+!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) +% s)
+
+template `-!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) -% s)
+
+template head(p: pointer): ptr ObjHeader =
+  cast[ptr ObjHeader](cast[int](p) -% sizeof(ObjHeader))
+
+proc nimNewObj(size: int): pointer {.compilerRtl.} =
+  result = alloc0(size + sizeof(ObjHeader)) +! sizeof(ObjHeader)
+  # XXX Respect   defined(useMalloc)  here!
+
+proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
+  dec head(p).rc
+
+proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
+  inc head(p).rc
+
+proc nimRawDispose(p: pointer) {.compilerRtl.} =
+  if head(p).rc != 0:
+    cstderr.rawWrite "[FATAL] dangling references exist\n"
+    quit 1
+  dealloc(p -! sizeof(ObjHeader))
+
+proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
+  let d = cast[ptr PNimType](p)[].destructor
+  if d != nil: d(p)
+  nimRawDispose(p)
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index fd46b96eb..08c10962b 100644
--- a/lib/core/seqs.nim
+++ b/lib/core/seqs.nim
@@ -44,43 +44,44 @@ This means the check for whether ``s.p`` needs to be freed should
 be ``s.len == 0`` even though that feels slightly more awkward.
 ]#
 
-proc `=destroy`[T](s: var seq[T]) =
-  var x = cast[ptr NimSeqV2[T]](addr s)
-  var p = x.p
-  if p != nil:
+when not defined(nimV2):
+  proc `=destroy`[T](s: var seq[T]) =
+    var x = cast[ptr NimSeqV2[T]](addr s)
+    var p = x.p
+    if p != nil:
+      mixin `=destroy`
+      when not supportsCopyMem(T):
+        for i in 0..<x.len: `=destroy`(p.data[i])
+      if p.region != nil:
+        p.region.dealloc(p.region, p, payloadSize(p.cap))
+      x.p = nil
+      x.len = 0
+
+  proc `=`[T](x: var seq[T]; y: seq[T]) =
     mixin `=destroy`
-    when not supportsCopyMem(T):
-      for i in 0..<x.len: `=destroy`(p.data[i])
-    if p.region != nil:
-      p.region.dealloc(p.region, p, payloadSize(p.cap))
-    x.p = nil
-    x.len = 0
-
-proc `=`[T](x: var seq[T]; y: seq[T]) =
-  mixin `=destroy`
-  var a = cast[ptr NimSeqV2[T]](addr x)
-  var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
-
-  if a.p == b.p: return
-  `=destroy`(x)
-  a.len = b.len
-  if b.p != nil:
-    a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
-    when supportsCopyMem(T):
-      if a.len > 0:
-        copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
-    else:
-      for i in 0..<a.len:
-        a.p.data[i] = b.p.data[i]
+    var a = cast[ptr NimSeqV2[T]](addr x)
+    var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
 
-proc `=sink`[T](x: var seq[T]; y: seq[T]) =
-  mixin `=destroy`
-  var a = cast[ptr NimSeqV2[T]](addr x)
-  var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
-  if a.p != nil and a.p != b.p:
+    if a.p == b.p: return
     `=destroy`(x)
-  a.len = b.len
-  a.p = b.p
+    a.len = b.len
+    if b.p != nil:
+      a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
+      when supportsCopyMem(T):
+        if a.len > 0:
+          copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T))
+      else:
+        for i in 0..<a.len:
+          a.p.data[i] = b.p.data[i]
+
+  proc `=sink`[T](x: var seq[T]; y: seq[T]) =
+    mixin `=destroy`
+    var a = cast[ptr NimSeqV2[T]](addr x)
+    var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
+    if a.p != nil and a.p != b.p:
+      `=destroy`(x)
+    a.len = b.len
+    a.p = b.p
 
 
 type
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
index 406efe5a1..540765de3 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -41,43 +41,45 @@ template isLiteral(s): bool = s.p == nil or s.p.region == nil
 
 template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator)
 
-template frees(s) =
-  if not isLiteral(s):
-    s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
-
-proc `=destroy`(s: var string) =
-  var a = cast[ptr NimStringV2](addr s)
-  frees(a)
-  a.len = 0
-  a.p = nil
-
-proc `=sink`(x: var string, y: string) =
-  var a = cast[ptr NimStringV2](addr x)
-  var b = cast[ptr NimStringV2](unsafeAddr y)
-  # we hope this is optimized away for not yet alive objects:
-  if unlikely(a.p == b.p): return
-  frees(a)
-  a.len = b.len
-  a.p = b.p
-
-proc `=`(x: var string, y: string) =
-  var a = cast[ptr NimStringV2](addr x)
-  var b = cast[ptr NimStringV2](unsafeAddr y)
-  if unlikely(a.p == b.p): return
-  frees(a)
-  a.len = b.len
-  if isLiteral(b):
-    # we can shallow copy literals:
+when not defined(nimV2):
+
+  template frees(s) =
+    if not isLiteral(s):
+      s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap))
+
+  proc `=destroy`(s: var string) =
+    var a = cast[ptr NimStringV2](addr s)
+    frees(a)
+    a.len = 0
+    a.p = nil
+
+  proc `=sink`(x: var string, y: string) =
+    var a = cast[ptr NimStringV2](addr x)
+    var b = cast[ptr NimStringV2](unsafeAddr y)
+    # we hope this is optimized away for not yet alive objects:
+    if unlikely(a.p == b.p): return
+    frees(a)
+    a.len = b.len
     a.p = b.p
-  else:
-    let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator()
-    # we have to allocate the 'cap' here, consider
-    # 'let y = newStringOfCap(); var x = y'
-    # on the other hand... These get turned into moves now.
-    a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
-    a.p.region = region
-    a.p.cap = b.len
-    copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
+
+  proc `=`(x: var string, y: string) =
+    var a = cast[ptr NimStringV2](addr x)
+    var b = cast[ptr NimStringV2](unsafeAddr y)
+    if unlikely(a.p == b.p): return
+    frees(a)
+    a.len = b.len
+    if isLiteral(b):
+      # we can shallow copy literals:
+      a.p = b.p
+    else:
+      let region = if a.p != nil and a.p.region != nil: a.p.region else: getLocalAllocator()
+      # we have to allocate the 'cap' here, consider
+      # 'let y = newStringOfCap(); var x = y'
+      # on the other hand... These get turned into moves now.
+      a.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(b.len)))
+      a.p.region = region
+      a.p.cap = b.len
+      copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
 
 proc resize(old: int): int {.inline.} =
   if old <= 0: result = 4
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 8821e32f8..d108e09f1 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,46 +63,48 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
   if p == nil:
     sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
 
-proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return # optimized fast path
-  while x != subclass:
-    if x == nil:
-      sysFatal(ObjectConversionError, "invalid object conversion")
-    x = x.base
-
-proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
-  if a != b:
-    sysFatal(ObjectAssignmentError, "invalid object assignment")
-
-type ObjCheckCache = array[0..1, PNimType]
-
-proc isObjSlowPath(obj, subclass: PNimType;
-                   cache: var ObjCheckCache): bool {.noinline.} =
-  # checks if obj is of type subclass:
-  var x = obj.base
-  while x != subclass:
-    if x == nil:
-      cache[0] = obj
-      return false
-    x = x.base
-  cache[1] = obj
-  return true
-
-proc isObjWithCache(obj, subclass: PNimType;
-                    cache: var ObjCheckCache): bool {.compilerProc, inline.} =
-  if obj == subclass: return true
-  if obj.base == subclass: return true
-  if cache[0] == obj: return false
-  if cache[1] == obj: return true
-  return isObjSlowPath(obj, subclass, cache)
-
-proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return true # optimized fast path
-  while x != subclass:
-    if x == nil: return false
-    x = x.base
-  return true
+when not defined(nimV2):
+
+  proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return # optimized fast path
+    while x != subclass:
+      if x == nil:
+        sysFatal(ObjectConversionError, "invalid object conversion")
+      x = x.base
+
+  proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
+    if a != b:
+      sysFatal(ObjectAssignmentError, "invalid object assignment")
+
+  type ObjCheckCache = array[0..1, PNimType]
+
+  proc isObjSlowPath(obj, subclass: PNimType;
+                    cache: var ObjCheckCache): bool {.noinline.} =
+    # checks if obj is of type subclass:
+    var x = obj.base
+    while x != subclass:
+      if x == nil:
+        cache[0] = obj
+        return false
+      x = x.base
+    cache[1] = obj
+    return true
+
+  proc isObjWithCache(obj, subclass: PNimType;
+                      cache: var ObjCheckCache): bool {.compilerProc, inline.} =
+    if obj == subclass: return true
+    if obj.base == subclass: return true
+    if cache[0] == obj: return false
+    if cache[1] == obj: return true
+    return isObjSlowPath(obj, subclass, cache)
+
+  proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return true # optimized fast path
+    while x != subclass:
+      if x == nil: return false
+      x = x.base
+    return true
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 9284f07d2..a2cddc472 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -496,6 +496,8 @@ else:
   elif defined(gcRegions):
     # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
     include "system/gc_regions"
+  elif defined(nimV2):
+    include "core/runtime_v2"
   elif defined(gcMarkAndSweep) or defined(gcDestructors):
     # XXX use 'compileOption' here
     include "system/gc_ms"