summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim9
-rw-r--r--compiler/ccgcalls.nim10
-rw-r--r--compiler/ccgexprs.nim179
-rw-r--r--compiler/ccgliterals.nim24
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/ccgtrav.nim23
-rw-r--r--compiler/ccgtypes.nim53
-rw-r--r--compiler/cgen.nim25
-rw-r--r--compiler/commands.nim9
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/destroyer.nim31
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/semasgn.nim28
-rw-r--r--compiler/semstmts.nim15
-rw-r--r--compiler/semtypes.nim35
-rw-r--r--compiler/transf.nim8
-rw-r--r--compiler/types.nim15
-rw-r--r--doc/nep1.rst76
-rw-r--r--lib/core/allocators.nim41
-rw-r--r--lib/core/seqs.nim242
-rw-r--r--lib/core/strs.nim230
-rw-r--r--lib/system.nim192
-rw-r--r--lib/system/assign.nim5
-rw-r--r--lib/system/cgprocs.nim1
-rw-r--r--lib/system/excpt.nim9
-rw-r--r--lib/system/gc_ms.nim96
-rw-r--r--lib/system/hti.nim2
-rw-r--r--lib/system/jssys.nim3
-rw-r--r--lib/system/mmdisp.nim125
-rw-r--r--lib/system/repr.nim18
-rw-r--r--lib/system/strmantle.nim298
-rw-r--r--lib/system/sysio.nim2
-rw-r--r--lib/system/sysstr.nim296
-rw-r--r--lib/system/widestrs.nim3
-rw-r--r--tests/destructor/tcustomseqs.nim7
35 files changed, 1254 insertions, 865 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 01e70ce75..a722f63f6 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -627,6 +627,7 @@ type
     mIsPartOf, mAstToStr, mParallel,
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
+    mMove, mWasMoved,
     mReset,
     mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
@@ -1087,9 +1088,9 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   result.id = getID()
   when debugIds:
     registerId(result)
-  #if result.id == 93289:
+  #if result.id == 77131:
   #  writeStacktrace()
-  #  MessageOut(name.s & " has id: " & toString(result.id))
+  #  echo name.s
 
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
@@ -1267,14 +1268,14 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
   new(result)
   result.kind = kind
   result.owner = owner
-  result.size = - 1
+  result.size = -1
   result.align = 2            # default alignment
   result.id = getID()
   result.lockLevel = UnspecifiedLockLevel
   when debugIds:
     registerId(result)
   when false:
-    if result.id == 205734:
+    if result.id == 76426:
       echo "KNID ", kind
       writeStackTrace()
 
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 2621574a6..83461350b 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -124,15 +124,19 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
     of tyString, tySequence:
       if skipTypes(n.typ, abstractInst).kind == tyVar and
             not compileToCpp(p.module):
-        result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)]
+        var t: TLoc
+        t.r = "(*$1)" % [a.rdLoc]
+        result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
       else:
-        result = "$1$3, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)]
+        result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
     of tyArray:
       result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
     of tyPtr, tyRef:
       case lastSon(a.t).kind
       of tyString, tySequence:
-        result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)]
+        var t: TLoc
+        t.r = "(*$1)" % [a.rdLoc]
+        result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenExpr(p, t), dataField(p)]
       of tyArray:
         result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
       else:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index b30d216f2..65cae8866 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -165,7 +165,7 @@ proc canMove(n: PNode): bool =
   #  result = false
 
 proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
-  if dest.storage == OnStack or not usesNativeGC(p.config):
+  if dest.storage == OnStack or not usesWriteBarrier(p.config):
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   elif dest.storage == OnHeap:
     # location is on heap
@@ -255,9 +255,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # tfShallow flag for the built-in string type too! So we check only
   # here for this flag, where it is reasonably safe to do so
   # (for objects, etc.):
-  if needToCopy notin flags or
+  if p.config.selectedGC == gcDestructors:
+    linefmt(p, cpsStmts,
+        "$1.len = $2.len; $1.p = $2.p;$n",
+        rdLoc(dest), rdLoc(src))
+  elif needToCopy notin flags or
       tfShallow in skipTypes(dest.t, abstractVarRange).flags:
-    if dest.storage == OnStack or not usesNativeGC(p.config):
+    if dest.storage == OnStack or not usesWriteBarrier(p.config):
       linefmt(p, cpsStmts,
            "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
            addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
@@ -280,17 +284,21 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyRef:
     genRefAssign(p, dest, src, flags)
   of tySequence:
-    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
+    if p.config.selectedGC == gcDestructors:
+      genGenericAsgn(p, dest, src, flags)
+    elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
       genRefAssign(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
               addrLoc(p.config, dest), rdLoc(src),
               genTypeInfo(p.module, dest.t, dest.lode.info))
   of tyString:
-    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
+    if p.config.selectedGC == gcDestructors:
+      genGenericAsgn(p, dest, src, flags)
+    elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
       genRefAssign(p, dest, src, flags)
     else:
-      if dest.storage == OnStack or not usesNativeGC(p.config):
+      if dest.storage == OnStack or not usesWriteBarrier(p.config):
         linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
       elif dest.storage == OnHeap:
         # we use a temporary to care for the dreaded self assignment:
@@ -453,6 +461,13 @@ proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   initLocExpr(p, e.sons[2], b)
   lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))
 
+proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+  var a, b: TLoc
+  if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr")
+  initLocExpr(p, e.sons[1], a)
+  initLocExpr(p, e.sons[2], b)
+  lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b))
+
 proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
   if d.k != locNone: internalError(p.config, e.info, "unaryStmt")
@@ -889,8 +904,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
   of tySequence, tyString:
     linefmt(p, cpsStmts,
       "if ($2-$1 != -1 && " &
-      "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n",
-      rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p))
+      "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n",
+      rdLoc(a), rdLoc(b), lenExpr(p, arr))
   else: discard
 
 proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
@@ -914,12 +929,12 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   if optBoundsCheck in p.options:
     if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
       linefmt(p, cpsStmts,
-              "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
-              rdLoc(b), rdLoc(a), lenField(p))
+              "if ((NU)($1) > (NU)$2) #raiseIndexError();$n",
+              rdLoc(b), lenExpr(p, a))
     else:
       linefmt(p, cpsStmts,
-              "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
-              rdLoc(b), rdLoc(a), lenField(p))
+              "if ((NU)($1) >= (NU)$2) #raiseIndexError();$n",
+              rdLoc(b), lenExpr(p, a))
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
     a.r = ropecg(p.module, "(*$1)", a.r)
@@ -1010,6 +1025,12 @@ proc genEcho(p: BProc, n: PNode) =
 proc gcUsage(conf: ConfigRef; n: PNode) =
   if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
 
+proc strLoc(p: BProc; d: TLoc): Rope =
+  if p.config.selectedGc == gcDestructors:
+    result = addrLoc(p.config, d)
+  else:
+    result = rdLoc(d)
+
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nim code>
   #   s = 'Hello ' & name & ', how do you feel?' & 'z'
@@ -1037,13 +1058,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e.sons[i + 1], a)
     if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar:
       inc(L)
-      add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a)))
+      add(appends, ropecg(p.module, "#appendChar($1, $2);$n", strLoc(p, tmp), rdLoc(a)))
     else:
       if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 1].strVal))
       else:
-        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
-      add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
+        add(lens, lenExpr(p, a))
+        add(lens, " + ")
+      add(appends, ropecg(p.module, "#appendString($1, $2);$n", strLoc(p, tmp), rdLoc(a)))
   linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
   add(p.s(cpsStmts), appends)
   if d.k == locNone:
@@ -1076,19 +1098,24 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar:
       inc(L)
       add(appends, ropecg(p.module, "#appendChar($1, $2);$n",
-                        rdLoc(dest), rdLoc(a)))
+                        strLoc(p, dest), rdLoc(a)))
     else:
       if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 2].strVal))
       else:
-        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
+        add(lens, lenExpr(p, a))
+        add(lens, " + ")
       add(appends, ropecg(p.module, "#appendString($1, $2);$n",
-                        rdLoc(dest), rdLoc(a)))
-  initLoc(call, locCall, e, OnHeap)
-  call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
-  genAssignment(p, dest, call, {})
+                        strLoc(p, dest), rdLoc(a)))
+  if p.config.selectedGC == gcDestructors:
+    linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
+            addrLoc(p.config, dest), lens, rope(L))
+  else:
+    initLoc(call, locCall, e, OnHeap)
+    call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
+    genAssignment(p, dest, call, {})
+    gcUsage(p.config, e)
   add(p.s(cpsStmts), appends)
-  gcUsage(p.config, e)
 
 proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   # seq &= x  -->
@@ -1151,7 +1178,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
     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 usesNativeGC(p.config):
+  if a.storage == OnHeap and usesWriteBarrier(p.config):
     # use newObjRC1 as an optimization
     if canFormAcycle(a.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
@@ -1182,7 +1209,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
               genTypeInfo(p.module, seqtype, dest.lode.info), length]
   var call: TLoc
   initLoc(call, locExpr, dest.lode, OnHeap)
-  if dest.storage == OnHeap and usesNativeGC(p.config):
+  if dest.storage == OnHeap and usesWriteBarrier(p.config):
     if canFormAcycle(dest.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
@@ -1201,10 +1228,16 @@ proc genNewSeq(p: BProc, e: PNode) =
   var a, b: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  let lenIsZero = optNilSeqs notin p.options and
-    e[2].kind == nkIntLit and e[2].intVal == 0
-  genNewSeqAux(p, a, b.rdLoc, lenIsZero)
-  gcUsage(p.config, e)
+  if p.config.selectedGC == gcDestructors:
+    let seqtype = skipTypes(e.sons[1].typ, abstractVarRange)
+    linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
+      a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
+      getSeqPayloadType(p.module, seqtype))
+  else:
+    let lenIsZero = optNilSeqs notin p.options and
+      e[2].kind == nkIntLit and e[2].intVal == 0
+    genNewSeqAux(p, a, b.rdLoc, lenIsZero)
+    gcUsage(p.config, e)
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
   let seqtype = skipTypes(e.typ, abstractVarRange)
@@ -1448,7 +1481,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
       putIntoDest(p, b, e,
-                  "$1$3, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p), dataField(p)], a.storage)
+                  "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage)
     of tyArray:
       putIntoDest(p, b, e,
                   "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
@@ -1492,28 +1525,19 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
     else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
   of tyString:
-    if not p.module.compileToCpp:
-      if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
-      else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
-    else:
-      if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
-      else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
+    var a: TLoc
+    initLocExpr(p, e.sons[1], a)
+    var x = lenExpr(p, a)
+    if op == mHigh: x = "($1-1)" % [x]
+    putIntoDest(p, d, e, x)
   of tySequence:
+    # we go through a temporary here because people write bullshit code.
     var a, tmp: TLoc
     initLocExpr(p, e[1], a)
     getIntTemp(p, tmp)
-    var frmt: FormatStr
-    if not p.module.compileToCpp:
-      if op == mHigh:
-        frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n"
-      else:
-        frmt = "$1 = ($2 ? $2->Sup.len : 0);$n"
-    else:
-      if op == mHigh:
-        frmt = "$1 = ($2 ? ($2->len-1) : -1);$n"
-      else:
-        frmt = "$1 = ($2 ? $2->len : 0);$n"
-    lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
+    var x = lenExpr(p, a)
+    if op == mHigh: x = "($1-1)" % [x]
+    lineCg(p, cpsStmts, "$1 = $2;$n", tmp.r, x)
     putIntoDest(p, d, e, tmp.r)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
@@ -1522,6 +1546,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   else: internalError(p.config, e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
+  if p.config.selectedGc == gcDestructors:
+    genCall(p, e, d)
+    return
   var a, b, call: TLoc
   assert(d.k == locNone)
   var x = e.sons[1]
@@ -1542,16 +1569,19 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   gcUsage(p.config, e)
 
 proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
-  var a, b, call: TLoc
-  if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
+  if p.config.selectedGc == gcDestructors:
+    binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
+  else:
+    var a, b, call: TLoc
+    if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
+    initLocExpr(p, e.sons[1], a)
+    initLocExpr(p, e.sons[2], b)
 
-  initLoc(call, locCall, e, OnHeap)
-  call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
-      rdLoc(a), rdLoc(b)])
-  genAssignment(p, a, call, {})
-  gcUsage(p.config, e)
+    initLoc(call, locCall, e, OnHeap)
+    call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
+        rdLoc(a), rdLoc(b)])
+    genAssignment(p, a, call, {})
+    gcUsage(p.config, e)
 
 proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   # swap(a, b) -->
@@ -1803,11 +1833,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
   if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
     initLocExpr(p, e.sons[2], x)
     putIntoDest(p, d, e,
-      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      ropecg(p.module, "($1 == 0)", lenExpr(p, x)))
   elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
     initLocExpr(p, e.sons[1], x)
     putIntoDest(p, d, e,
-      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      ropecg(p.module, "($1 == 0)", lenExpr(p, x)))
   else:
     binaryExpr(p, e, d, "#eqStrings($1, $2)")
 
@@ -1868,14 +1898,21 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
 
   of mConStrStr: genStrConcat(p, e, d)
   of mAppendStrCh:
-    var dest, b, call: TLoc
-    initLoc(call, locCall, e, OnHeap)
-    initLocExpr(p, e.sons[1], dest)
-    initLocExpr(p, e.sons[2], b)
-    call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
-    genAssignment(p, dest, call, {})
+    if p.config.selectedGC == gcDestructors:
+      binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n")
+    else:
+      var dest, b, call: TLoc
+      initLoc(call, locCall, e, OnHeap)
+      initLocExpr(p, e.sons[1], dest)
+      initLocExpr(p, e.sons[2], b)
+      call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
+      genAssignment(p, dest, call, {})
   of mAppendStrStr: genStrAppend(p, e, d)
-  of mAppendSeqElem: genSeqElemAppend(p, e, d)
+  of mAppendSeqElem:
+    if p.config.selectedGc == gcDestructors:
+      genCall(p, e, d)
+    else:
+      genSeqElemAppend(p, e, d)
   of mEqStr: genStrEquals(p, e, d)
   of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)")
   of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)")
@@ -1924,8 +1961,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
      mInSet:
     genSetOp(p, e, d, op)
-  of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
-      mParseBiggestFloat:
+  of mCopyStr, mCopyStrLast:
+    genCall(p, e, d)
+  of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
     var opr = e.sons[0].sym
     if lfNoDecl notin opr.loc.flags:
       discard cgsym(p.module, $opr.loc.r)
@@ -2522,6 +2560,13 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
       result = genConstSimpleList(p, n)
   of nkObjConstr:
     result = genConstObjConstr(p, n)
+  of nkStrLit..nkTripleStrLit:
+    if p.config.selectedGc == gcDestructors:
+      result = genStringLiteralV2Const(p.module, n)
+    else:
+      var d: TLoc
+      initLocExpr(p, n, d)
+      result = rdLoc(d)
   else:
     var d: TLoc
     initLocExpr(p, n, d)
diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim
index cfe71375e..34677ec06 100644
--- a/compiler/ccgliterals.nim
+++ b/compiler/ccgliterals.nim
@@ -53,20 +53,36 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope =
 
 proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope =
   result = getTempName(m)
-  addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n",
-       [result, rope(len(s)+1), makeCString(s)])
+  addf(m.s[cfsData], "static const struct {$n" &
+       "  NI cap; void* allocator; NIM_CHAR data[$2];$n" &
+       "} $1 = { $2, NIM_NIL, $3 };$n",
+       [result, rope(len(s)), makeCString(s)])
 
 proc genStringLiteralV2(m: BModule; n: PNode): Rope =
   let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
   if id == m.labels:
+    discard cgsym(m, "NimStrPayload")
+    discard cgsym(m, "NimStringV2")
     # string literal not found in the cache:
     let pureLit = genStringLiteralDataOnlyV2(m, n.strVal)
     result = getTempName(m)
-    addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n",
-        [result, rope(len(n.strVal)+1), pureLit])
+    addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
+          [result, rope(len(n.strVal)), pureLit])
   else:
     result = m.tmpBase & rope(id)
 
+proc genStringLiteralV2Const(m: BModule; n: PNode): Rope =
+  let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
+  var pureLit: Rope
+  if id == m.labels:
+    discard cgsym(m, "NimStrPayload")
+    discard cgsym(m, "NimStringV2")
+    # string literal not found in the cache:
+    pureLit = genStringLiteralDataOnlyV2(m, n.strVal)
+  else:
+    pureLit = m.tmpBase & rope(id)
+  result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit]
+
 # ------ Version selector ---------------------------------------------------
 
 proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index a7a2b3fee..69e6558bb 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -16,7 +16,7 @@ const
     # above X strings a hash-switch for strings is generated
 
 proc registerGcRoot(p: BProc, v: PSym) =
-  if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
+  if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and
       containsGarbageCollectedRef(v.loc.t):
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 349cf2707..c69bb2c80 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -7,8 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Generates traversal procs for the C backend. Traversal procs are only an
-## optimization; the GC works without them too.
+## Generates traversal procs for the C backend.
 
 # included from cgen.nim
 
@@ -61,6 +60,7 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
   else:
     result = accessor
 
+proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType)
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
   if typ == nil: return
 
@@ -93,8 +93,18 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
     let typ = getUniqueType(typ)
     for i in countup(0, sonsLen(typ) - 1):
       genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i])
-  of tyRef, tyString, tySequence:
+  of tyRef:
     lineCg(p, cpsStmts, c.visitorFrmt, accessor)
+  of tySequence:
+    if tfHasAsgn notin typ.flags:
+      lineCg(p, cpsStmts, c.visitorFrmt, accessor)
+    elif containsGarbageCollectedRef(typ.lastSon):
+      # destructor based seqs are themselves not traced but their data is, if
+      # they contain a GC'ed type:
+      genTraverseProcSeq(c, accessor, typ)
+  of tyString:
+    if tfHasAsgn notin typ.flags:
+      lineCg(p, cpsStmts, c.visitorFrmt, accessor)
   of tyProc:
     if typ.callConv == ccClosure:
       lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor))
@@ -107,8 +117,11 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var i: TLoc
   getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
   let oldCode = p.s(cpsStmts)
-  lineF(p, cpsStmts, "for ($1 = 0; $1 < ($2 ? $2->$3 : 0); $1++) {$n",
-      [i.r, accessor, lenField(c.p)])
+  var a: TLoc
+  a.r = accessor
+
+  lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
+      [i.r, lenExpr(c.p, a)])
   let oldLen = p.s(cpsStmts).len
   genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.sons[0])
   if p.s(cpsStmts).len == oldLen:
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index a16255f6e..59fbfc3e1 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -282,7 +282,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
   of tyString:
     case detectStrVersion(m)
     of 2:
-      discard cgsym(m, "string")
+      discard cgsym(m, "NimStringV2")
       result = typeNameOrLiteral(m, typ, "NimStringV2")
     else:
       discard cgsym(m, "NimStringDesc")
@@ -324,6 +324,10 @@ proc getForwardStructFormat(m: BModule): string =
   if m.compileToCpp: result = "$1 $2;$n"
   else: result = "typedef $1 $2 $2;$n"
 
+proc seqStar(m: BModule): string =
+  if m.config.selectedGC == gcDestructors: result = ""
+  else: result = "*"
+
 proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
   result = cacheGetType(m.forwTypeCache, sig)
   if result != nil: return
@@ -355,8 +359,11 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
       result = getTypeForward(m, t, hashType(t))
       pushType(m, t)
   of tySequence:
-    result = getTypeForward(m, t, hashType(t)) & "*"
-    pushType(m, t)
+    if m.config.selectedGC == gcDestructors:
+      result = getTypeDescAux(m, t, check)
+    else:
+      result = getTypeForward(m, t, hashType(t)) & seqStar(m)
+      pushType(m, t)
   else:
     result = getTypeDescAux(m, t, check)
 
@@ -487,7 +494,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
       if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
         addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
             [getTypeDescAux(m, fieldType.elemType, check), sname])
-      elif fieldType.kind in {tySequence, tyOpt}:
+      elif fieldType.kind == tySequence and m.config.selectedGC != gcDestructors:
         # we need to use a weak dependency here for trecursive_table.
         addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
       elif field.bitsize != 0:
@@ -601,6 +608,15 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
       result = if result.kind == tyGenericInst: result.sons[1]
                else: result.elemType
 
+proc getSeqPayloadType(m: BModule; t: PType): Rope =
+  result = getTypeForward(m, t, hashType(t)) & "_Content"
+  when false:
+    var check = initIntSet()
+    # XXX remove this duplication:
+    appcg(m, m.s[cfsSeqTypes],
+      "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE]; };$n",
+      [getTypeDescAux(m, t.sons[0], check), result])
+
 proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
   # returns only the type's name
   var t = origTyp.skipTypes(irrelevantForBackend)
@@ -641,7 +657,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     of tySequence:
       # no restriction! We have a forward declaration for structs
       let name = getTypeForward(m, et, hashType et)
-      result = name & "*" & star
+      result = name & seqStar(m) & star
       m.typeCache[sig] = result
       pushType(m, et)
     else:
@@ -705,20 +721,29 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
             [structOrUnion(t), result])
       m.forwTypeCache[sig] = result
     assert(cacheGetType(m.typeCache, sig) == nil)
-    m.typeCache[sig] = result & "*"
+    m.typeCache[sig] = result & seqStar(m)
     if not isImportedType(t):
       if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
         const
           cppSeq = "struct $2 : #TGenericSeq {$n"
           cSeq = "struct $2 {$n" &
                  "  #TGenericSeq Sup;$n"
-        appcg(m, m.s[cfsSeqTypes],
-            (if m.compileToCpp: cppSeq else: cSeq) &
-            "  $1 data[SEQ_DECL_SIZE];$n" &
+        if m.config.selectedGC == gcDestructors:
+          appcg(m, m.s[cfsTypes],
+            "typedef struct{ NI cap;void* allocator;$1 data[SEQ_DECL_SIZE];}$2_Content;$n" &
+            "struct $2 {$n" &
+            "  NI len; $2_Content* p;$n" &
             "};$n", [getTypeDescAux(m, t.sons[0], check), result])
+        else:
+          appcg(m, m.s[cfsSeqTypes],
+              (if m.compileToCpp: cppSeq else: cSeq) &
+              "  $1 data[SEQ_DECL_SIZE];$n" &
+              "};$n", [getTypeDescAux(m, t.sons[0], check), result])
+      elif m.config.selectedGC == gcDestructors:
+        internalError(m.config, "cannot map the empty seq type to a C type")
       else:
         result = rope("TGenericSeq")
-    add(result, "*")
+    add(result, seqStar(m))
   of tyArray:
     var n: BiggestInt = lengthOrd(m.config, t)
     if n <= 0: n = 1   # make an array of at least one element
@@ -1177,7 +1202,13 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
     else:
       let x = fakeClosureType(m, t.owner)
       genTupleInfo(m, x, x, result, info)
-  of tySequence, tyRef, tyOptAsRef:
+  of tySequence:
+    if tfHasAsgn notin t.flags:
+      genTypeInfoAux(m, t, t, result, info)
+      if m.config.selectedGC >= gcMarkAndSweep:
+        let markerProc = genTraverseProc(m, origType, sig)
+        addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
+  of tyRef, tyOptAsRef:
     genTypeInfoAux(m, t, t, result, info)
     if m.config.selectedGC >= gcMarkAndSweep:
       let markerProc = genTraverseProc(m, origType, sig)
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 01a930de6..2cb431ff9 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -230,22 +230,31 @@ proc getTempName(m: BModule): Rope =
   result = m.tmpBase & rope(m.labels)
   inc m.labels
 
+proc rdLoc(a: TLoc): Rope =
+  # 'read' location (deref if indirect)
+  result = a.r
+  if lfIndirect in a.flags: result = "(*$1)" % [result]
+
 proc lenField(p: BProc): Rope =
   result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
 
+proc lenExpr(p: BProc; a: TLoc): Rope =
+  if p.config.selectedGc == gcDestructors:
+    result = rdLoc(a) & ".len"
+  else:
+    result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
+
 proc dataField(p: BProc): Rope =
-  result = rope"->data"
+  if p.config.selectedGc == gcDestructors:
+    result = rope".p->data"
+  else:
+    result = rope"->data"
 
 include ccgliterals
 include ccgtypes
 
 # ------------------------------ Manager of temporaries ------------------
 
-proc rdLoc(a: TLoc): Rope =
-  # 'read' location (deref if indirect)
-  result = a.r
-  if lfIndirect in a.flags: result = "(*$1)" % [result]
-
 proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
   result = a.r
   if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
@@ -325,7 +334,9 @@ proc resetLoc(p: BProc, loc: var TLoc) =
 
 proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
   let typ = loc.t
-  if not isComplexValueType(typ):
+  if p.config.selectedGc == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}:
+    linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", rdLoc(loc))
+  elif not isComplexValueType(typ):
     linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
       getTypeDesc(p.module, typ))
   else:
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 1e5384f16..f7c8cf9f2 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -215,7 +215,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
     of "refc":         result = conf.selectedGC == gcRefc
     of "v2":           result = conf.selectedGC == gcV2
     of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
-    of "generational": result = conf.selectedGC == gcGenerational
+    of "generational": result = false
+    of "destructors":  result = conf.selectedGC == gcDestructors
     of "go":           result = conf.selectedGC == gcGo
     of "none":         result = conf.selectedGC == gcNone
     of "stack", "regions": result = conf.selectedGC == gcRegions
@@ -436,9 +437,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     of "markandsweep":
       conf.selectedGC = gcMarkAndSweep
       defineSymbol(conf.symbols, "gcmarkandsweep")
-    of "generational":
-      conf.selectedGC = gcGenerational
-      defineSymbol(conf.symbols, "gcgenerational")
+    of "destructors":
+      conf.selectedGC = gcDestructors
+      defineSymbol(conf.symbols, "gcdestructors")
     of "go":
       conf.selectedGC = gcGo
       defineSymbol(conf.symbols, "gogc")
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index ba1c42a74..0cf264ac3 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -74,6 +74,7 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimNoZeroTerminator")
   defineSymbol("nimNotNil")
   defineSymbol("nimVmExportFixed")
+  defineSymbol("nimNewRuntime")
   defineSymbol("nimIncrSeqV3")
   defineSymbol("nimAshr")
   defineSymbol("nimNoNilSeqs")
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 0395728c2..bd735560a 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -100,12 +100,12 @@ Rule      Pattern                 Transformed into
                                   finally: `=destroy`(x)
 1.2       var x: sink T; stmts    var x: sink T; stmts; ensureEmpty(x)
 2         x = f()                 `=sink`(x, f())
-3         x = lastReadOf z        `=sink`(x, z)
+3         x = lastReadOf z        `=sink`(x, z); wasMoved(z)
 4.1       y = sinkParam           `=sink`(y, sinkParam)
 4.2       x = y                   `=`(x, y) # a copy
 5.1       f_sink(g())             f_sink(g())
 5.2       f_sink(y)               f_sink(copy y); # copy unless we can see it's the last read
-5.3       f_sink(move y)          f_sink(y); reset(y) # explicit moves empties 'y'
+5.3       f_sink(move y)          f_sink(y); wasMoved(y) # explicit moves empties 'y'
 5.4       f_noSink(g())           var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp)
 
 Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
@@ -258,8 +258,10 @@ proc registerDropBit(c: var Con; s: PSym) =
   c.toDropBit[s.id] = result
   # generate:
   #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
-  c.destroys.add newTree(nkIfStmt,
-    newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s)))
+  let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  if t.destructor != nil:
+    c.destroys.add newTree(nkIfStmt,
+      newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s)))
 
 proc p(n: PNode; c: var Con): PNode
 
@@ -282,6 +284,11 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode =
     newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
   result.add n
 
+proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode =
+  result = newNodeI(nkCall, n.info)
+  result.add(newSymNode(createMagic(c.graph, magicname, m)))
+  result.add n
+
 proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
   if ri.kind in constrExprs:
     result = genSink(c, ri.typ, dest)
@@ -290,8 +297,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
     recurse(ri, ri2)
     result.add ri2
   elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
-    result = genSink(c, ri.typ, dest)
-    result.add p(ri, c)
+    # Rule 3: `=sink`(x, z); wasMoved(z)
+    var snk = genSink(c, ri.typ, dest)
+    snk.add p(ri, c)
+    result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
   elif ri.kind == nkSym and isSinkParam(ri.sym):
     result = genSink(c, ri.typ, dest)
     result.add destructiveMoveSink(ri, c)
@@ -313,11 +322,9 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
     result.add newTree(nkAsgn, tmp, p(n, c))
   result.add tmp
 
-proc genReset(n: PNode; c: var Con): PNode =
-  result = newNodeI(nkCall, n.info)
-  result.add(newSymNode(createMagic(c.graph, "reset", mReset)))
-  # The mReset builtin does not take the address:
-  result.add n
+proc genWasMoved(n: PNode; c: var Con): PNode =
+  # The mWasMoved builtin does not take the address.
+  result = genMagicCall(n, c, "wasMoved", mWasMoved)
 
 proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   # generate: (let tmp = v; reset(v); tmp)
@@ -334,7 +341,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   add(v, vpart)
 
   result.add v
-  result.add genReset(n, c)
+  result.add genWasMoved(n, c)
   result.add tempAsNode
 
 proc p(n: PNode; c: var Con): PNode =
diff --git a/compiler/options.nim b/compiler/options.nim
index 1873d9d5b..04b14c65f 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -104,8 +104,8 @@ type
     cmdJsonScript             # compile a .json build file
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
-    gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
-    gcV2, gcGenerational
+    gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors,
+    gcRefc, gcV2
 
   IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
@@ -373,7 +373,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
     else: discard
 
 proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
-proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
+proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
 
 template compilationCachePresent*(conf: ConfigRef): untyped =
   conf.symbolFiles in {v2Sf, writeOnlySf}
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index a05ef7a28..8b2e20efc 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -197,13 +197,10 @@ 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, tyString, tyRef, tyOpt:
+      tyPtr, tyRef, tyOpt:
     defaultOp(c, t, body, x, y)
-  of tyArray, tySequence:
+  of tyArray:
     if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}:
-      if t.kind == tySequence:
-        # XXX add 'nil' handling here
-        body.add newSeqCall(c.c, x, y)
       let i = declareCounter(c, body, firstOrd(c.c.config, t))
       let whileLoop = genWhileLoop(c, i, x)
       let elemType = t.lastSon
@@ -213,6 +210,27 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       body.add whileLoop
     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.c.config.selectedGC == gcDestructors:
+      discard considerOverloadedOp(c, t, body, x, y)
+    elif tfHasAsgn in t.flags:
+      body.add newSeqCall(c.c, x, y)
+      let i = declareCounter(c, body, firstOrd(c.c.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
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyString:
+    if tfHasAsgn in t.flags:
+      discard considerOverloadedOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
   of tyObject, tyDistinct:
     if not considerOverloadedOp(c, t, body, x, y):
       if t.sons[0] != nil:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 170ac799e..3a1278137 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1056,8 +1056,8 @@ proc checkForMetaFields(c: PContext; n: PNode) =
     case t.kind
     of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef,
        tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
-      let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst})
-      for i in start ..< t.sons.len:
+      let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
+      for i in start ..< t.len:
         checkMeta(t.sons[i])
     else:
       checkMeta(t)
@@ -1376,7 +1376,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
         elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
         else: break
-      if obj.kind in {tyObject, tyDistinct}:
+      if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
         if obj.destructor.isNil:
           obj.destructor = s
         else:
@@ -1398,7 +1398,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         if t.kind == tyGenericBody: t = t.lastSon
         elif t.kind == tyGenericInvocation: t = t.sons[0]
         else: break
-      if t.kind in {tyObject, tyDistinct, tyEnum}:
+      if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
         if t.deepCopy.isNil: t.deepCopy = s
         else:
           localError(c.config, n.info, errGenerated,
@@ -1427,7 +1427,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         elif objB.kind in {tyGenericInvocation, tyGenericInst}:
           objB = objB.sons[0]
         else: break
-      if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
+      if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
         let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
         if opr[].isNil:
           opr[] = s
@@ -1592,7 +1592,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
         localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
           ("'" & proto.name.s & "' from " & c.config$proto.info))
-    if sfForward notin proto.flags:
+    if sfForward notin proto.flags and proto.magic == mNone:
       wrongRedefinition(c, n.info, proto.name.s)
     excl(proto.flags, sfForward)
     closeScope(c)         # close scope with wrong parameter symbols
@@ -1658,7 +1658,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         openScope(c)
         n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
         closeScope(c)
-        fixupInstantiatedSymbols(c, s)
+        if s.magic == mNone:
+          fixupInstantiatedSymbols(c, s)
         if s.kind == skMethod: semMethodPrototype(c, s, n)
       if sfImportc in s.flags:
         # so we just ignore the body after semantic checking for importc:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 99f2cf20d..1669a7707 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -145,9 +145,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, errXExpectsOneTypeParam % "set")
     addSonSkipIntLit(result, errorType(c))
 
-proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
-                  prev: PType): PType =
-  result = newOrPrevType(kind, prev, c)
+proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
   if sonsLen(n) == 2:
     var base = semTypeNode(c, n.sons[1], nil)
     if base.kind == tyVoid:
@@ -157,6 +155,11 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
     localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
     addSonSkipIntLit(result, errorType(c))
 
+proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
+                  prev: PType): PType =
+  result = newOrPrevType(kind, prev, c)
+  semContainerArg(c, n, kindStr, result)
+
 proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyVarargs, prev, c)
   if sonsLen(n) == 2 or sonsLen(n) == 3:
@@ -1507,7 +1510,24 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mRange: result = semRange(c, n, prev)
     of mSet: result = semSet(c, n, prev)
     of mOrdinal: result = semOrdinal(c, n, prev)
-    of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
+    of mSeq:
+      if c.config.selectedGc == gcDestructors:
+        let s = c.graph.sysTypes[tySequence]
+        assert s != nil
+        assert prev == nil
+        result = copyType(s, s.owner, keepId=false)
+        # XXX figure out why this has children already...
+        result.sons.setLen 0
+        result.n = nil
+        if c.config.selectedGc == gcDestructors:
+          result.flags = {tfHasAsgn}
+        else:
+          result.flags = {}
+        semContainerArg(c, n, "seq", result)
+      else:
+        result = semContainer(c, n, tySequence, "seq", prev)
+        if c.config.selectedGc == gcDestructors:
+          incl result.flags, tfHasAsgn
     of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
     of mVarargs: result = semVarargs(c, n, prev)
     of mTypeDesc, mTypeTy:
@@ -1687,6 +1707,9 @@ proc processMagicType(c: PContext, m: PSym) =
   of mString:
     setMagicType(c.config, m, tyString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
+    when false:
+      if c.config.selectedGc == gcDestructors:
+        incl m.typ.flags, tfHasAsgn
   of mCstring:
     setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
@@ -1726,6 +1749,10 @@ proc processMagicType(c: PContext, m: PSym) =
     setMagicType(c.config, m, tySet, 0)
   of mSeq:
     setMagicType(c.config, m, tySequence, 0)
+    if c.config.selectedGc == gcDestructors:
+      incl m.typ.flags, tfHasAsgn
+    assert c.graph.sysTypes[tySequence] == nil
+    c.graph.sysTypes[tySequence] = m.typ
   of mOpt:
     setMagicType(c.config, m, tyOpt, 0)
   of mOrdinal:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 3a276dc38..84297aa6a 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -1050,8 +1050,8 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
     when useEffectSystem: trackTopLevelStmt(g, module, result)
     #if n.info ?? "temp.nim":
     #  echo renderTree(result, {renderIds})
-    if c.needsDestroyPass:
-      result = injectDestructorCalls(g, module, result)
+    #if c.needsDestroyPass:
+    #  result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
 
 proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode =
@@ -1063,6 +1063,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode =
     liftDefer(c, result)
     # expressions are not to be injected with destructor calls as that
     # the list of top level statements needs to be collected before.
-    if c.needsDestroyPass:
-      result = injectDestructorCalls(g, module, result)
+    #if c.needsDestroyPass:
+    #  result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
diff --git a/compiler/types.nim b/compiler/types.nim
index 80624502c..674819bc5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -278,6 +278,8 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
 proc isGCRef(t: PType): bool =
   result = t.kind in GcTypeKinds or
     (t.kind == tyProc and t.callConv == ccClosure)
+  if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags:
+    result = false
 
 proc containsGarbageCollectedRef*(typ: PType): bool =
   # returns true if typ contains a reference, sequence or string (all the
@@ -1339,14 +1341,23 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
     if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
     else: result = conf.target.ptrSize
     a = conf.target.ptrSize
-  of tyString, tyNil:
+  of tyString:
+    if tfHasAsgn in typ.flags:
+      result = conf.target.ptrSize * 2
+    else:
+      result = conf.target.ptrSize
+  of tyNil:
     result = conf.target.ptrSize
     a = result
   of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
     let base = typ.lastSon
     if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
       result = szIllegalRecursion
-    else: result = conf.target.ptrSize
+    else:
+      if typ.kind == tySequence and tfHasAsgn in typ.flags:
+        result = conf.target.ptrSize * 2
+      else:
+        result = conf.target.ptrSize
     a = result
   of tyArray:
     let elemSize = computeSizeAux(conf, typ.sons[1], a)
diff --git a/doc/nep1.rst b/doc/nep1.rst
index c4d445681..1ef8c3c24 100644
--- a/doc/nep1.rst
+++ b/doc/nep1.rst
@@ -60,7 +60,7 @@ Spacing and Whitespace Conventions
 
 
 Naming Conventions
--------------------------
+------------------
 
 Note: While the rules outlined below are the *current* naming conventions,
 these conventions have not always been in place. Previously, the naming
@@ -147,6 +147,80 @@ changed in the future.
   an in-place version should get an ``-In`` suffix (``replaceIn`` for this example).
 
 
+The stdlib API is designed to be **easy to use** and consistent. Ease of use is
+measured by the number of calls to achieve a concrete high level action. The
+ultimate goal is that the programmer can *guess* a name.
+
+The library uses a simple naming scheme that makes use of common abbreviations
+to keep the names short but meaningful.
+
+
+-------------------     ------------   --------------------------------------
+English word            To use         Notes
+-------------------     ------------   --------------------------------------
+initialize              initT          ``init`` is used to create a
+                                       value type ``T``
+new                     newP           ``new`` is used to create a
+                                       reference type ``P``
+find                    find           should return the position where
+                                       something was found; for a bool result
+                                       use ``contains``
+contains                contains       often short for ``find() >= 0``
+append                  add            use ``add`` instead of ``append``
+compare                 cmp            should return an int with the
+                                       ``< 0`` ``== 0`` or ``> 0`` semantics;
+                                       for a bool result use ``sameXYZ``
+put                     put, ``[]=``   consider overloading ``[]=`` for put
+get                     get, ``[]``    consider overloading ``[]`` for get;
+                                       consider to not use ``get`` as a
+                                       prefix: ``len`` instead of ``getLen``
+length                  len            also used for *number of elements*
+size                    size, len      size should refer to a byte size
+capacity                cap
+memory                  mem            implies a low-level operation
+items                   items          default iterator over a collection
+pairs                   pairs          iterator over (key, value) pairs
+delete                  delete, del    del is supposed to be faster than
+                                       delete, because it does not keep
+                                       the order; delete keeps the order
+remove                  delete, del    inconsistent right now
+include                 incl
+exclude                 excl
+command                 cmd
+execute                 exec
+environment             env
+variable                var
+value                   value, val     val is preferred, inconsistent right
+                                       now
+executable              exe
+directory               dir
+path                    path           path is the string "/usr/bin" (for
+                                       example), dir is the content of
+                                       "/usr/bin"; inconsistent right now
+extension               ext
+separator               sep
+column                  col, column    col is preferred, inconsistent right
+                                       now
+application             app
+configuration           cfg
+message                 msg
+argument                arg
+object                  obj
+parameter               param
+operator                opr
+procedure               proc
+function                func
+coordinate              coord
+rectangle               rect
+point                   point
+symbol                  sym
+literal                 lit
+string                  str
+identifier              ident
+indentation             indent
+-------------------     ------------   --------------------------------------
+
+
 Coding Conventions
 ------------------
 
diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim
index 62f5e9756..f652f0d85 100644
--- a/lib/core/allocators.nim
+++ b/lib/core/allocators.nim
@@ -8,28 +8,41 @@
 #
 
 type
+  AllocatorFlag* {.pure.} = enum  ## flags describing the properties of the allocator
+    ThreadLocal ## the allocator is thread local only.
+    ZerosMem    ## the allocator always zeros the memory on an allocation
   Allocator* = ptr object {.inheritable.}
     alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
     dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
     realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
+    deallocAll*: proc (a: Allocator) {.nimcall.}
+    flags*: set[AllocatorFlag]
 
 var
-  currentAllocator {.threadvar.}: Allocator
+  localAllocator {.threadvar.}: Allocator
+  sharedAllocator: Allocator
 
-proc getCurrentAllocator*(): Allocator =
-  result = currentAllocator
+proc getLocalAllocator*(): Allocator =
+  result = localAllocator
 
-proc setCurrentAllocator*(a: Allocator) =
-  currentAllocator = a
+proc setLocalAllocator*(a: Allocator) =
+  localAllocator = a
 
-proc alloc*(size: int; alignment: int = 8): pointer =
-  let a = getCurrentAllocator()
-  result = a.alloc(a, size, alignment)
+proc getSharedAllocator*(): Allocator =
+  result = sharedAllocator
 
-proc dealloc*(p: pointer; size: int) =
-  let a = getCurrentAllocator()
-  a.dealloc(a, p, size)
+proc setSharedAllocator*(a: Allocator) =
+  sharedAllocator = a
 
-proc realloc*(p: pointer; oldSize, newSize: int): pointer =
-  let a = getCurrentAllocator()
-  result = a.realloc(a, p, oldSize, newSize)
+when false:
+  proc alloc*(size: int; alignment: int = 8): pointer =
+    let a = getCurrentAllocator()
+    result = a.alloc(a, size, alignment)
+
+  proc dealloc*(p: pointer; size: int) =
+    let a = getCurrentAllocator()
+    a.dealloc(a, p, size)
+
+  proc realloc*(p: pointer; oldSize, newSize: int): pointer =
+    let a = getCurrentAllocator()
+    result = a.realloc(a, p, oldSize, newSize)
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index 02c192851..4dcf6cbbb 100644
--- a/lib/core/seqs.nim
+++ b/lib/core/seqs.nim
@@ -7,133 +7,163 @@
 #    distribution, for details about the copyright.
 #
 
-import allocators, typetraits
+
+import typetraits
+# strs already imported allocators for us.
 
 ## Default seq implementation used by Nim's core.
 type
-  seq*[T] = object
-    len, cap: int
-    data: ptr UncheckedArray[T]
+  NimSeqPayload {.core.}[T] = object
+    cap: int
+    region: Allocator
+    data: UncheckedArray[T]
+
+  NimSeqV2*[T] = object
+    len: int
+    p: ptr NimSeqPayload[T]
 
 const nimSeqVersion {.core.} = 2
 
-template frees(s) = dealloc(s.data, s.cap * sizeof(T))
+template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator)
 
 # XXX make code memory safe for overflows in '*'
 
-when defined(nimHasTrace):
-  proc `=trace`[T](s: seq[T]; a: Allocator) =
-    for i in 0 ..< s.len: `=trace`(s.data[i], a)
+when false:
+  # this is currently not part of Nim's type bound operators and so it's
+  # built into the tracing proc generation just like before.
+  proc `=trace`[T](s: NimSeqV2[T]) =
+    for i in 0 ..< s.len: `=trace`(s.data[i])
 
-proc `=destroy`[T](x: var seq[T]) =
-  if x.data != nil:
+proc `=destroy`[T](s: var seq[T]) =
+  var x = cast[ptr NimSeqV2[T]](addr s)
+  var p = x.p
+  if p != nil:
     when not supportsCopyMem(T):
-      for i in 0..<x.len: `=destroy`(x[i])
-    frees(x)
-    x.data = nil
+      for i in 0..<x.len: `=destroy`(p.data[i])
+    p.region.dealloc(p.region, p, payloadSize(p.cap))
+    x.p = nil
     x.len = 0
-    x.cap = 0
 
-proc `=`[T](a: var seq[T]; b: seq[T]) =
-  if a.data == b.data: return
-  if a.data != nil:
-    frees(a)
-    a.data = nil
+proc `=`[T](x: var seq[T]; y: seq[T]) =
+  var a = cast[ptr NimSeqV2[T]](addr x)
+  var b = cast[ptr NimSeqV2[T]](unsafeAddr y)
+
+  if a.p == b.p: return
+  `=destroy`(a)
   a.len = b.len
-  a.cap = b.cap
-  if b.data != nil:
-    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+  if b.p != nil:
+    a.p = cast[type(a.p)](alloc(payloadSize(a.len)))
     when supportsCopyMem(T):
-      copyMem(a.data, b.data, a.cap * sizeof(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.data[i] = b.data[i]
+        a.p.data[i] = b.p.data[i]
 
-proc `=sink`[T](a: var seq[T]; b: seq[T]) =
-  if a.data != nil and a.data != b.data:
-    frees(a)
+proc `=sink`[T](x: var seq[T]; y: seq[T]) =
+  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`(a)
   a.len = b.len
-  a.cap = b.cap
-  a.data = b.data
-
-proc resize[T](s: var seq[T]) =
-  let old = s.cap
-  if old == 0: s.cap = 8
-  else: s.cap = (s.cap * 3) shr 1
-  s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
-
-proc reserveSlot[T](x: var seq[T]): ptr T =
-  if x.len >= x.cap: resize(x)
-  result = addr(x.data[x.len])
-  inc x.len
-
-template add*[T](x: var seq[T]; y: T) =
-  reserveSlot(x)[] = y
-
-proc shrink*[T](x: var seq[T]; newLen: int) =
-  assert newLen <= x.len
-  assert newLen >= 0
+  a.p = b.p
+
+when false:
+  proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
+  proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
+      compilerRtl.}
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
+
+
+type
+  PayloadBase = object
+    cap: int
+    region: Allocator
+
+proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
+  # we have to use type erasure here as Nim does not support generic
+  # compilerProcs. Oh well, this will all be inlined anyway.
+  if cap <= 0:
+    let region = getLocalAllocator()
+    var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator)))
+    p.region = region
+    p.cap = cap
+    result = p
+  else:
+    result = nil
+
+proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} =
+  if len+addlen <= len:
+    result = p
+  elif p == nil:
+    result = newSeqPayload(len+addlen, elemSize)
+  else:
+    # Note: this means we cannot support things that have internal pointers as
+    # they get reallocated here. This needs to be documented clearly.
+    var p = cast[ptr PayloadBase](p)
+    let region = if p.region == nil: getLocalAllocator() else: p.region
+    let cap = max(resize(p.cap), len+addlen)
+    var q = cast[ptr PayloadBase](region.realloc(region, p,
+      sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
+      sizeof(int) + sizeof(Allocator) + elemSize * cap))
+    q.region = region
+    q.cap = cap
+    result = q
+
+proc shrink*[T](x: var seq[T]; newLen: Natural) =
+  sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
   when not supportsCopyMem(T):
     for i in countdown(x.len - 1, newLen - 1):
-      `=destroy`(x.data[i])
-  x.len = newLen
-
-proc grow*[T](x: var seq[T]; newLen: int; value: T) =
-  if newLen <= x.len: return
-  assert newLen >= 0
-  if x.cap == 0: x.cap = newLen
-  else: x.cap = max(newLen, (x.cap * 3) shr 1)
-  x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
-  for i in x.len..<newLen:
+      `=destroy`(x[i])
+
+  cast[ptr NimSeqV2[T]](addr x).len = newLen
+
+proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
+  let oldLen = x.len
+  if newLen <= oldLen: return
+  var xu = cast[ptr NimSeqV2[T]](addr x)
+
+  xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))
+  xu.len = newLen
+  for i in oldLen .. newLen-1:
     x.data[i] = value
-  x.len = newLen
-
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
-
-proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
-  if newlen < x.len: shrink(x, newLen)
-  else: grow(x, newLen, default(T))
-
-template `[]`*[T](x: seq[T]; i: Natural): T =
-  assert i < x.len
-  x.data[i]
-
-template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
-  assert i < x.len
-  x.data[i] = y
-
-proc `@`*[T](elems: openArray[T]): seq[T] =
-  result.cap = elems.len
-  result.len = elems.len
-  result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
-  when supportsCopyMem(T):
-    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
+
+proc setLen[T](s: var seq[T], newlen: Natural) =
+  if newlen < s.len:
+    shrink(s, newLen)
   else:
-    for i in 0..<result.len:
-      result.data[i] = elems[i]
-
-proc len*[T](x: seq[T]): int {.inline.} = x.len
-
-proc `$`*[T](x: seq[T]): string =
-  result = "@["
-  var firstElement = true
-  for i in 0..<x.len:
-    let
-      value = x.data[i]
-    if firstElement:
-      firstElement = false
-    else:
-      result.add(", ")
-
-    when compiles(value.isNil):
-      # this branch should not be necessary
-      if value.isNil:
-        result.add "nil"
-      else:
-        result.addQuoted(value)
+    var v: T # get the default value of 'v'
+    grow(s, newLen, v)
+
+when false:
+  proc resize[T](s: var NimSeqV2[T]) =
+    let old = s.cap
+    if old == 0: s.cap = 8
+    else: s.cap = (s.cap * 3) shr 1
+    s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
+
+  proc reserveSlot[T](x: var NimSeqV2[T]): ptr T =
+    if x.len >= x.cap: resize(x)
+    result = addr(x.data[x.len])
+    inc x.len
+
+  template add*[T](x: var NimSeqV2[T]; y: T) =
+    reserveSlot(x)[] = y
+
+  template `[]`*[T](x: NimSeqV2[T]; i: Natural): T =
+    assert i < x.len
+    x.data[i]
+
+  template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) =
+    assert i < x.len
+    x.data[i] = y
+
+  proc `@`*[T](elems: openArray[T]): NimSeqV2[T] =
+    result.cap = elems.len
+    result.len = elems.len
+    result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
+    when supportsCopyMem(T):
+      copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
     else:
-      result.addQuoted(value)
-
-  result.add("]")
+      for i in 0..<result.len:
+        result.data[i] = elems[i]
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
index ff38aef1d..186add52a 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -7,104 +7,166 @@
 #    distribution, for details about the copyright.
 #
 
-## Default string implementation used by Nim's core.
+## Default new string implementation used by Nim's core.
+
+when false:
+  # these are to be implemented or changed in the code generator.
+
+  #proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
+  # seems to be unused.
+  proc copyDeepString(src: NimString): NimString {.inline.}
+  # ----------------- sequences ----------------------------------------------
+
+  proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
+  proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
+      compilerRtl.}
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
+  proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.}
 
 import allocators
 
 type
-  string {.core, exportc: "NimStringV2".} = object
-    len, cap: int
-    data: ptr UncheckedArray[char]
+  NimStrPayload {.core.} = object
+    cap: int
+    region: Allocator
+    data: UncheckedArray[char]
+
+  NimStringV2 {.core.} = object
+    len: int
+    p: ptr NimStrPayload ## can be nil if len == 0.
 
 const nimStrVersion {.core.} = 2
 
-template frees(s) = dealloc(s.data, s.cap + 1)
+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) =
-  if s.data != nil:
-    frees(s)
-    s.data = nil
-    s.len = 0
-    s.cap = 0
+  var a = cast[ptr NimStringV2](addr s)
+  frees(a)
+  a.len = 0
+  a.p = nil
+
+template lose(a) =
+  frees(a)
 
-proc `=sink`(a: var string, b: string) =
+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 a.data != nil and a.data != b.data:
-    frees(a)
+  if unlikely(a.p == b.p): return
+  lose(a)
   a.len = b.len
-  a.cap = b.cap
-  a.data = b.data
+  a.p = b.p
 
-proc `=`(a: var string; b: string) =
-  if a.data != nil and a.data != b.data:
-    frees(a)
-    a.data = nil
+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
+  lose(a)
   a.len = b.len
-  a.cap = b.cap
-  if b.data != nil:
-    a.data = cast[type(a.data)](alloc(a.cap + 1))
-    copyMem(a.data, b.data, a.cap+1)
-
-proc resize(s: var string) =
-  let old = s.cap
-  if old == 0: s.cap = 8
-  else: s.cap = (s.cap * 3) shr 1
-  s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
-
-proc add*(s: var string; c: char) =
-  if s.len >= s.cap: resize(s)
-  s.data[s.len] = c
-  s.data[s.len+1] = '\0'
+  if isLiteral(b):
+    # we can shallow copy literals:
+    a.p = b.p
+  else:
+    let region = if 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
+  elif old < 65536: result = old * 2
+  else: result = old * 3 div 2 # for large arrays * 3/2 is better
+
+proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
+  if isLiteral(s):
+    let oldP = s.p
+    # can't mutate a literal, so we need a fresh copy here:
+    let region = getLocalAllocator()
+    s.p = cast[ptr NimStrPayload](region.alloc(region, contentSize(s.len + addlen)))
+    s.p.region = region
+    s.p.cap = s.len + addlen
+    if s.len > 0:
+      # we are about to append, so there is no need to copy the \0 terminator:
+      copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len)
+  elif s.len + addlen > s.p.cap:
+    let cap = max(s.len + addlen, resize(s.p.cap))
+    s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p,
+      oldSize = contentSize(s.p.cap),
+      newSize = contentSize(cap)))
+    s.p.cap = cap
+
+proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
+  prepareAdd(s, 1)
+  s.p.data[s.len] = c
+  s.p.data[s.len+1] = '\0'
   inc s.len
 
-proc ensure(s: var string; newLen: int) =
-  let old = s.cap
-  if newLen >= old:
-    s.cap = max((old * 3) shr 1, newLen)
-    if s.cap > 0:
-      s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
-
-proc add*(s: var string; y: string) =
-  if y.len != 0:
-    let newLen = s.len + y.len
-    ensure(s, newLen)
-    copyMem(addr s.data[len], y.data, y.data.len + 1)
+proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
+  if len <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    let region = getLocalAllocator()
+    var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
+    p.region = region
+    p.cap = len
+    if len > 0:
+      # we are about to append, so there is no need to copy the \0 terminator:
+      copyMem(unsafeAddr p.data[0], str, len)
+    result = NimStringV2(len: 0, p: p)
+
+proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
+  if str == nil: toNimStr(str, 0)
+  else: toNimStr(str, str.len)
+
+proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} =
+  if s.len == 0: result = cstring""
+  else: result = cstring(unsafeAddr s.p.data)
+
+proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
+  if src.len > 0:
+    # also copy the \0 terminator:
+    copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
+
+proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
+  dest.p.data[dest.len] = c
+  dest.p.data[dest.len+1] = '\0'
+  inc dest.len
+
+proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
+  # this is also 'system.newStringOfCap'.
+  if space <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    let region = getLocalAllocator()
+    var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space)))
+    p.region = region
+    p.cap = space
+    result = NimStringV2(len: 0, p: p)
+
+proc mnewString(len: int): NimStringV2 {.compilerProc.} =
+  if len <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    let region = getLocalAllocator()
+    var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len)))
+    p.region = region
+    p.cap = len
+    result = NimStringV2(len: len, p: p)
+
+proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
+  if newLen > s.len:
+    prepareAdd(s, newLen - s.len)
+  else:
     s.len = newLen
-
-proc len*(s: string): int {.inline.} = s.len
-
-proc newString*(len: int): string =
-  result.len = len
-  result.cap = len
-  if len > 0:
-    result.data = alloc0(len+1)
-
-converter toCString(x: string): cstring {.core, inline.} =
-  if x.len == 0: cstring"" else: cast[cstring](x.data)
-
-proc newStringOfCap*(cap: int): string =
-  result.len = 0
-  result.cap = cap
-  if cap > 0:
-    result.data = alloc(cap+1)
-
-proc `&`*(a, b: string): string =
-  let sum = a.len + b.len
-  result = newStringOfCap(sum)
-  result.len = sum
-  copyMem(addr result.data[0], a.data, a.len)
-  copyMem(addr result.data[a.len], b.data, b.len)
-  if sum > 0:
-    result.data[sum] = '\0'
-
-proc concat(x: openArray[string]): string {.core.} =
-  ## used be the code generator to optimize 'x & y & z ...'
-  var sum = 0
-  for i in 0 ..< x.len: inc(sum, x[i].len)
-  result = newStringOfCap(sum)
-  sum = 0
-  for i in 0 ..< x.len:
-    let L = x[i].len
-    copyMem(addr result.data[sum], x[i].data, L)
-    inc(sum, L)
-
+    # this also only works because the destructor
+    # looks at s.p and not s.len
diff --git a/lib/system.nim b/lib/system.nim
index d61924a5b..52ed524be 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -211,6 +211,7 @@ proc new*(T: typedesc): auto =
   new(r)
   return r
 
+const ThisIsSystem = true
 
 proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## leaked implementation detail. Do not use.
@@ -229,6 +230,17 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
   ## resets an object `obj` to its initial (binary zero) value. This needs to
   ## be called before any possible `object branch transition`:idx:.
 
+when defined(nimNewRuntime):
+  proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} =
+    ## resets an object `obj` to its initial (binary zero) value to signify
+    ## it was "moved" and to signify its destructor should do nothing and
+    ## ideally be optimized away.
+    discard
+
+  proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} =
+    result = x
+    wasMoved(x)
+
 type
   range*{.magic: "Range".}[T] ## Generic type to construct range types.
   array*{.magic: "Array".}[I, T]  ## Generic type to construct
@@ -413,7 +425,7 @@ include "system/inclrtl"
 const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \
   ## "fake variables" like 'var EBADF {.importc.}: cint'.
 
-when not defined(JS):
+when not defined(JS) and not defined(gcDestructors):
   type
     TGenericSeq {.compilerproc, pure, inheritable.} = object
       len, reserved: int
@@ -426,8 +438,9 @@ when not defined(JS):
     NimString = ptr NimStringDesc
 
 when not defined(JS) and not defined(nimscript):
-  template space(s: PGenericSeq): int {.dirty.} =
-    s.reserved and not (seqShallowFlag or strlitFlag)
+  when not defined(gcDestructors):
+    template space(s: PGenericSeq): int {.dirty.} =
+      s.reserved and not (seqShallowFlag or strlitFlag)
   include "system/hti"
 
 type
@@ -692,7 +705,8 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {.
   ## ``cap``.
   discard
 
-when not defined(JS):
+when not defined(JS) and not defined(gcDestructors):
+  # XXX enable this for --gc:destructors
   proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] =
     ## creates a new sequence of type ``seq[T]`` with length ``len``.
     ##
@@ -1481,11 +1495,17 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n
 
 when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
   include "system/cgprocs"
-when not defined(JS) and not defined(nimscript) and hasAlloc:
+when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
   proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
 
-proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
-proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
+when defined(gcDestructors):
+  proc add*[T](x: var seq[T], y: sink T) {.magic: "AppendSeqElem", noSideEffect.} =
+    let xl = x.len
+    setLen(x, xl + 1)
+    x[xl] = y
+else:
+  proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
+proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
   ## Generic proc for adding a data item `y` to a container `x`.
   ## For containers that have an order, `add` means *append*. New generic
   ## containers should also call their adding proc `add` for consistency.
@@ -1681,17 +1701,6 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
 # In case of an unhandled exeption the exit handlers should
 # not be called explicitly! The user may decide to do this manually though.
 
-proc substr*(s: string, first = 0): string {.
-  magic: "CopyStr", importc: "copyStr", noSideEffect.}
-proc substr*(s: string, first, last: int): string {.
-  magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.}
-  ## copies a slice of `s` into a new string and returns this new
-  ## string. The bounds `first` and `last` denote the indices of
-  ## the first and last characters that shall be copied. If ``last``
-  ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
-  ## is used instead: This means ``substr`` can also be used to `cut`:idx:
-  ## or `limit`:idx: a string's length.
-
 when not defined(nimscript) and not defined(JS):
   proc zeroMem*(p: pointer, size: Natural) {.inline, benign.}
     ## overwrites the contents of the memory at ``p`` with the value 0.
@@ -2820,6 +2829,58 @@ else:
     if x < 0: -x else: x
 {.pop.}
 
+when not defined(JS):
+  proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
+  proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
+
+template likely*(val: bool): bool =
+  ## Hints the optimizer that `val` is likely going to be true.
+  ##
+  ## You can use this template to decorate a branch condition. On certain
+  ## platforms this can help the processor predict better which branch is
+  ## going to be run. Example:
+  ##
+  ## .. code-block:: nim
+  ##   for value in inputValues:
+  ##     if likely(value <= 100):
+  ##       process(value)
+  ##     else:
+  ##       echo "Value too big!"
+  ##
+  ## On backends without branch prediction (JS and the nimscript VM), this
+  ## template will not affect code execution.
+  when nimvm:
+    val
+  else:
+    when defined(JS):
+      val
+    else:
+      likely_proc(val)
+
+template unlikely*(val: bool): bool =
+  ## Hints the optimizer that `val` is likely going to be false.
+  ##
+  ## You can use this proc to decorate a branch condition. On certain
+  ## platforms this can help the processor predict better which branch is
+  ## going to be run. Example:
+  ##
+  ## .. code-block:: nim
+  ##   for value in inputValues:
+  ##     if unlikely(value > 100):
+  ##       echo "Value too big!"
+  ##     else:
+  ##       process(value)
+  ##
+  ## On backends without branch prediction (JS and the nimscript VM), this
+  ## template will not affect code execution.
+  when nimvm:
+    val
+  else:
+    when defined(JS):
+      val
+    else:
+      unlikely_proc(val)
+
 type
   FileSeekPos* = enum ## Position relative to which seek should happen
                       # The values are ordered so that they match with stdio
@@ -2853,10 +2914,11 @@ when not defined(JS): #and not defined(nimscript):
       when declared(nimGC_setStackBottom):
         nimGC_setStackBottom(locals)
 
-    {.push profiler: off.}
-    var
-      strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
-    {.pop.}
+    when not defined(gcDestructors):
+      {.push profiler: off.}
+      var
+        strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
+      {.pop.}
 
 
   # ----------------- IO Part ------------------------------------------------
@@ -3242,8 +3304,14 @@ when not defined(JS): #and not defined(nimscript):
     when hasAlloc: include "system/mmdisp"
     {.pop.}
     {.push stack_trace: off, profiler:off.}
-    when hasAlloc: include "system/sysstr"
+    when hasAlloc:
+      when defined(gcDestructors):
+        include "core/strs"
+        include "core/seqs"
+      else:
+        include "system/sysstr"
     {.pop.}
+    when hasAlloc: include "system/strmantle"
 
     when hostOS != "standalone": include "system/sysio"
     when hasThreadSupport:
@@ -3288,8 +3356,9 @@ when not defined(JS): #and not defined(nimscript):
       while f.readLine(res): yield res
 
   when not defined(nimscript) and hasAlloc:
-    include "system/assign"
-    include "system/repr"
+    when not defined(gcDestructors):
+      include "system/assign"
+      include "system/repr"
 
   when hostOS != "standalone" and not defined(nimscript):
     proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} =
@@ -3396,58 +3465,6 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
 {.pop.} # checks
 {.pop.} # hints
 
-when not defined(JS):
-  proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
-  proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
-
-template likely*(val: bool): bool =
-  ## Hints the optimizer that `val` is likely going to be true.
-  ##
-  ## You can use this template to decorate a branch condition. On certain
-  ## platforms this can help the processor predict better which branch is
-  ## going to be run. Example:
-  ##
-  ## .. code-block:: nim
-  ##   for value in inputValues:
-  ##     if likely(value <= 100):
-  ##       process(value)
-  ##     else:
-  ##       echo "Value too big!"
-  ##
-  ## On backends without branch prediction (JS and the nimscript VM), this
-  ## template will not affect code execution.
-  when nimvm:
-    val
-  else:
-    when defined(JS):
-      val
-    else:
-      likely_proc(val)
-
-template unlikely*(val: bool): bool =
-  ## Hints the optimizer that `val` is likely going to be false.
-  ##
-  ## You can use this proc to decorate a branch condition. On certain
-  ## platforms this can help the processor predict better which branch is
-  ## going to be run. Example:
-  ##
-  ## .. code-block:: nim
-  ##   for value in inputValues:
-  ##     if unlikely(value > 100):
-  ##       echo "Value too big!"
-  ##     else:
-  ##       process(value)
-  ##
-  ## On backends without branch prediction (JS and the nimscript VM), this
-  ## template will not affect code execution.
-  when nimvm:
-    val
-  else:
-    when defined(JS):
-      val
-    else:
-      unlikely_proc(val)
-
 proc `/`*(x, y: int): float {.inline, noSideEffect.} =
   ## integer division that results in a float.
   result = toFloat(x) / toFloat(y)
@@ -3827,7 +3844,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
   ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not
   ## perform deep copies of `s`. This is only useful for optimization
   ## purposes.
-  when not defined(JS) and not defined(nimscript):
+  when not defined(JS) and not defined(nimscript) and not defined(gcDestructors):
     var s = cast[PGenericSeq](s)
     # string literals cannot become 'shallow':
     if (s.reserved and strlitFlag) == 0:
@@ -4011,7 +4028,9 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ##   # -> B is 1
   discard
 
-when hasAlloc and not defined(nimscript) and not defined(JS):
+when hasAlloc and not defined(nimscript) and not defined(JS) and
+    not defined(gcDestructors):
+  # XXX how to implement 'deepCopy' is an open problem.
   proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
     ## performs a deep copy of `y` and copies it into `x`.
     ## This is also used by the code generator
@@ -4092,6 +4111,21 @@ template once*(body: untyped): untyped =
 
 {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
 
+proc substr*(s: string, first, last: int): string =
+  let L = max(min(last, high(s)) - first + 1, 0)
+  result = newString(L)
+  for i in 0 .. L-1:
+    result[i] = s[i+first]
+
+proc substr*(s: string, first = 0): string =
+  ## copies a slice of `s` into a new string and returns this new
+  ## string. The bounds `first` and `last` denote the indices of
+  ## the first and last characters that shall be copied. If ``last``
+  ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
+  ## is used instead: This means ``substr`` can also be used to `cut`:idx:
+  ## or `limit`:idx: a string's length.
+  result = substr(s, first, high(s))
+
 when defined(nimconfig):
   include "system/nimscript"
 
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index 16b56aba7..2b74e6682 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -202,11 +202,6 @@ proc objectInit(dest: pointer, typ: PNimType) =
 
 # ---------------------- assign zero -----------------------------------------
 
-proc nimDestroyRange[T](r: T) {.compilerProc.} =
-  # internal proc used for destroying sequences and arrays
-  mixin `=destroy`
-  for i in countup(0, r.len - 1): `=destroy`(r[i])
-
 proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, benign.}
 proc genericResetAux(dest: pointer, n: ptr TNimNode) =
   var d = cast[ByteAddress](dest)
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index 660c68116..72219c2b7 100644
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -12,7 +12,6 @@
 type
   LibHandle = pointer       # private type
   ProcAddr = pointer        # library loading and loading of procs:
-{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
 
 proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
 proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index f25da0ad8..7b4979cfa 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -432,10 +432,11 @@ proc getStackTrace(e: ref Exception): string =
   else:
     result = ""
 
-proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
-  ## Returns the attached stack trace to the exception ``e`` as
-  ## a ``seq``. This is not yet available for the JS backend.
-  shallowCopy(result, e.trace)
+when not defined(gcDestructors):
+  proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
+    ## Returns the attached stack trace to the exception ``e`` as
+    ## a ``seq``. This is not yet available for the JS backend.
+    shallowCopy(result, e.trace)
 
 when defined(nimRequiresNimFrame):
   proc stackOverflow() {.noinline.} =
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index 75f9c6749..96221b175 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -264,12 +264,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
     of tyRef, tyOptAsRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
-      var s = cast[PGenericSeq](d)
-      if s != nil:
-        for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
-            GenericSeqSize), cell.typ.base, op)
+      when not defined(gcDestructors):
+        var d = cast[ByteAddress](cellToUsr(cell))
+        var s = cast[PGenericSeq](d)
+        if s != nil:
+          for i in 0..s.len-1:
+            forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
+              GenericSeqSize), cell.typ.base, op)
     else: discard
 
 proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
@@ -310,53 +311,54 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(typ, size, gch)
   when defined(memProfiler): nimProfile(size)
 
-proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  # `newObj` already uses locks, so no need for them here.
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(typ, size, gch)
   zeroMem(result, size)
   when defined(memProfiler): nimProfile(size)
 
-proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
-proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  acquire(gch)
-  collectCT(gch, newsize + sizeof(Cell))
-  var ol = usrToCell(old)
-  sysAssert(ol.typ != nil, "growObj: 1")
-  gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
-
-  var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize = 1
-  if ol.typ.kind != tyString: elemSize = ol.typ.base.size
-  incTypeSize ol.typ, newsize
-
-  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
-  copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
-          newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  when withBitvectors: incl(gch.allocated, res)
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  release(gch)
-  result = cellToUsr(res)
-  when defined(memProfiler): nimProfile(newsize-oldsize)
+when not defined(gcDestructors):
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
+    # `newObj` already uses locks, so no need for them here.
+    let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+    result = newObj(typ, size)
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).reserved = len
+    when defined(memProfiler): nimProfile(size)
+
+  proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
+    let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+    result = newObj(typ, size)
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).reserved = len
+    when defined(memProfiler): nimProfile(size)
+
+  proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
+    acquire(gch)
+    collectCT(gch, newsize + sizeof(Cell))
+    var ol = usrToCell(old)
+    sysAssert(ol.typ != nil, "growObj: 1")
+    gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
+
+    var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
+    var elemSize = 1
+    if ol.typ.kind != tyString: elemSize = ol.typ.base.size
+    incTypeSize ol.typ, newsize
+
+    var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
+    copyMem(res, ol, oldsize + sizeof(Cell))
+    zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
+            newsize-oldsize)
+    sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
+    when withBitvectors: incl(gch.allocated, res)
+    when useCellIds:
+      inc gch.idGenerator
+      res.id = gch.idGenerator
+    release(gch)
+    result = cellToUsr(res)
+    when defined(memProfiler): nimProfile(newsize-oldsize)
 
-proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
-  result = growObj(old, newsize, gch)
+  proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
+    result = growObj(old, newsize, gch)
 
 {.push profiler:off.}
 
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 45b1d1cd3..bb3769ac4 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-when declared(NimString):
+when declared(ThisIsSystem):
   # we are in system module:
   {.pragma: codegenType, compilerproc.}
 else:
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 8d4a2e482..152b48c24 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -31,8 +31,6 @@ type
 
   JSRef = ref RootObj # Fake type.
 
-{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
-
 var
   framePtr {.importc, nodecl, volatile.}: PCallFrame
   excHandler {.importc, nodecl, volatile.}: int = 0
@@ -506,7 +504,6 @@ proc chckNilDisp(p: pointer) {.compilerproc.} =
   if p == nil:
     sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
 
-type NimString = string # hack for hti.nim
 include "system/hti"
 
 proc isFatPointer(ti: PNimType): bool =
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index b33ca93f2..e7e14b948 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -175,8 +175,7 @@ when defined(boehmgc):
     dest[] = src
 
   type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
+    MemRegion = object
 
   proc alloc(r: var MemRegion, size: int): pointer =
     result = boehmAlloc(size)
@@ -215,60 +214,58 @@ elif defined(gogc):
     goNumSizeClasses = 67
 
   type
-    cbool {.importc: "_Bool", nodecl.} = bool
-
     goMStats_inner_struct = object
-        size: uint32
-        nmalloc: uint64
-        nfree: uint64
+      size: uint32
+      nmalloc: uint64
+      nfree: uint64
 
     goMStats = object
-        # General statistics.
-        alloc: uint64            # bytes allocated and still in use
-        total_alloc: uint64      # bytes allocated (even if freed)
-        sys: uint64              # bytes obtained from system (should be sum of xxx_sys below, no locking, approximate)
-        nlookup: uint64          # number of pointer lookups
-        nmalloc: uint64          # number of mallocs
-        nfree: uint64            # number of frees
-        # Statistics about malloc heap.
-        # protected by mheap.Lock
-        heap_alloc: uint64       # bytes allocated and still in use
-        heap_sys: uint64         # bytes obtained from system
-        heap_idle: uint64        # bytes in idle spans
-        heap_inuse: uint64       # bytes in non-idle spans
-        heap_released: uint64    # bytes released to the OS
-        heap_objects: uint64 # total number of allocated objects
-        # Statistics about allocation of low-level fixed-size structures.
-        # Protected by FixAlloc locks.
-        stacks_inuse: uint64     # bootstrap stacks
-        stacks_sys: uint64
-        mspan_inuse: uint64      # MSpan structures
-        mspan_sys: uint64
-        mcache_inuse: uint64     # MCache structures
-        mcache_sys: uint64
-        buckhash_sys: uint64     # profiling bucket hash table
-        gc_sys: uint64
-        other_sys: uint64
-        # Statistics about garbage collector.
-        # Protected by mheap or stopping the world during GC.
-        next_gc: uint64          # next GC (in heap_alloc time)
-        last_gc: uint64          # last GC (in absolute time)
-        pause_total_ns: uint64
-        pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths
-        pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
-        numgc: uint32
-        numforcedgc: uint32      # number of user-forced GCs
-        gc_cpu_fraction: float64 # fraction of CPU time used by GC
-        enablegc: cbool
-        debuggc: cbool
-        # Statistics about allocation size classes.
-        by_size: array[goNumSizeClasses, goMStats_inner_struct]
-        # Statistics below here are not exported to MemStats directly.
-        tinyallocs: uint64       # number of tiny allocations that didn't cause actual allocation; not exported to go directly
-        gc_trigger: uint64
-        heap_live: uint64
-        heap_scan: uint64
-        heap_marked: uint64
+      # General statistics.
+      alloc: uint64            # bytes allocated and still in use
+      total_alloc: uint64      # bytes allocated (even if freed)
+      sys: uint64              # bytes obtained from system (should be sum of xxx_sys below, no locking, approximate)
+      nlookup: uint64          # number of pointer lookups
+      nmalloc: uint64          # number of mallocs
+      nfree: uint64            # number of frees
+      # Statistics about malloc heap.
+      # protected by mheap.Lock
+      heap_alloc: uint64       # bytes allocated and still in use
+      heap_sys: uint64         # bytes obtained from system
+      heap_idle: uint64        # bytes in idle spans
+      heap_inuse: uint64       # bytes in non-idle spans
+      heap_released: uint64    # bytes released to the OS
+      heap_objects: uint64 # total number of allocated objects
+      # Statistics about allocation of low-level fixed-size structures.
+      # Protected by FixAlloc locks.
+      stacks_inuse: uint64     # bootstrap stacks
+      stacks_sys: uint64
+      mspan_inuse: uint64      # MSpan structures
+      mspan_sys: uint64
+      mcache_inuse: uint64     # MCache structures
+      mcache_sys: uint64
+      buckhash_sys: uint64     # profiling bucket hash table
+      gc_sys: uint64
+      other_sys: uint64
+      # Statistics about garbage collector.
+      # Protected by mheap or stopping the world during GC.
+      next_gc: uint64          # next GC (in heap_alloc time)
+      last_gc: uint64          # last GC (in absolute time)
+      pause_total_ns: uint64
+      pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths
+      pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
+      numgc: uint32
+      numforcedgc: uint32      # number of user-forced GCs
+      gc_cpu_fraction: float64 # fraction of CPU time used by GC
+      enablegc: bool
+      debuggc: bool
+      # Statistics about allocation size classes.
+      by_size: array[goNumSizeClasses, goMStats_inner_struct]
+      # Statistics below here are not exported to MemStats directly.
+      tinyallocs: uint64       # number of tiny allocations that didn't cause actual allocation; not exported to go directly
+      gc_trigger: uint64
+      heap_live: uint64
+      heap_scan: uint64
+      heap_marked: uint64
 
   proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl,
     importc: "runtime_ReadMemStats",
@@ -341,9 +338,12 @@ elif defined(gogc):
     proc getOccupiedSharedMem(): int = discard
 
   const goFlagNoZero: uint32 = 1 shl 3
-  proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
+  proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.
+    importc: "runtime_mallocgc", dynlib: goLib.}
 
-  proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
+  proc goSetFinalizer(obj: pointer, f: pointer) {.
+    importc: "set_finalizer", codegenDecl: "$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3",
+    dynlib: goLib.}
 
   proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
     result = goRuntimeMallocGC(roundup(size, sizeof(pointer)).uint, 0.uint, 0.uint32)
@@ -369,8 +369,7 @@ elif defined(gogc):
 
   proc growObj(old: pointer, newsize: int): pointer =
     # the Go GC doesn't have a realloc
-    var
-      oldsize = cast[PGenericSeq](old).len * cast[PGenericSeq](old).elemSize + GenericSeqSize
+    let oldsize = cast[PGenericSeq](old).len * cast[PGenericSeq](old).elemSize + GenericSeqSize
     result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
     copyMem(result, old, oldsize)
     zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
@@ -386,8 +385,7 @@ elif defined(gogc):
     dest[] = src
 
   type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
+    MemRegion = object
 
   proc alloc(r: var MemRegion, size: int): pointer =
     result = alloc(size)
@@ -477,8 +475,7 @@ elif defined(nogc) and defined(useMalloc):
     dest[] = src
 
   type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
+    MemRegion = object
 
   proc alloc(r: var MemRegion, size: int): pointer =
     result = alloc(size)
@@ -551,15 +548,13 @@ else:
   elif defined(gcRegions):
     # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
     include "system/gc_regions"
-  elif defined(gcMarkAndSweep):
+  elif defined(gcMarkAndSweep) or defined(gcDestructors):
     # XXX use 'compileOption' here
     include "system/gc_ms"
-  elif defined(gcGenerational):
-    include "system/gc"
   else:
     include "system/gc"
 
-when not declared(nimNewSeqOfCap):
+when not declared(nimNewSeqOfCap) and not defined(gcDestructors):
   proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
     when defined(gcRegions):
       let s = mulInt(cap, typ.base.size)  # newStr already adds GenericSeqSize
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 982b07467..45acae7f1 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -25,24 +25,6 @@ proc reprPointer(x: pointer): string {.compilerproc.} =
     discard c_sprintf(buf, "%p", x)
     return $buf
 
-proc `$`(x: uint64): string =
-  if x == 0:
-    result = "0"
-  else:
-    result = newString(60)
-    var i = 0
-    var n = x
-    while n != 0:
-      let nn = n div 10'u64
-      result[i] = char(n - 10'u64 * nn + ord('0'))
-      inc i
-      n = nn
-    result.setLen i
-
-    let half = i div 2
-    # Reverse
-    for t in 0 .. half-1: swap(result[t], result[i-t-1])
-
 proc reprStrAux(result: var string, s: cstring; len: int) =
   if cast[pointer](s) == nil:
     add result, "nil"
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
new file mode 100644
index 000000000..ceaecb4f9
--- /dev/null
+++ b/lib/system/strmantle.nim
@@ -0,0 +1,298 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Compilerprocs for strings that do not depend on the string implementation.
+
+proc cmpStrings(a, b: string): int {.inline, compilerProc.} =
+  let alen = a.len
+  let blen = b.len
+  let minlen = min(alen, blen)
+  if minlen > 0:
+    result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], minlen.csize)
+    if result == 0:
+      result = alen - blen
+  else:
+    result = alen - blen
+
+proc eqStrings(a, b: string): bool {.inline, compilerProc.} =
+  let alen = a.len
+  let blen = b.len
+  if alen == blen:
+    if alen == 0: return true
+    return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen)
+
+proc hashString(s: string): int {.compilerproc.} =
+  # the compiler needs exactly the same hash function!
+  # this used to be used for efficient generation of string case statements
+  var h = 0
+  for i in 0..len(s)-1:
+    h = h +% ord(s[i])
+    h = h +% h shl 10
+    h = h xor (h shr 6)
+  h = h +% h shl 3
+  h = h xor (h shr 11)
+  h = h +% h shl 15
+  result = h
+
+proc add*(result: var string; x: int64) =
+  let base = result.len
+  setLen(result, base + sizeof(x)*4)
+  var i = 0
+  var y = x
+  while true:
+    var d = y div 10
+    result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
+    inc(i)
+    y = d
+    if y == 0: break
+  if x < 0:
+    result[base+i] = '-'
+    inc(i)
+  setLen(result, base+i)
+  # mirror the string:
+  for j in 0..i div 2 - 1:
+    swap(result[base+j], result[base+i-j-1])
+
+proc nimIntToStr(x: int): string {.compilerRtl.} =
+  result = newStringOfCap(sizeof(x)*4)
+  result.add x
+
+proc add*(result: var string; x: float) =
+  when nimvm:
+    result.add $x
+  else:
+    var buf: array[0..64, char]
+    when defined(nimNoArrayToCstringConversion):
+      var n: int = c_sprintf(addr buf, "%.16g", x)
+    else:
+      var n: int = c_sprintf(buf, "%.16g", x)
+    var hasDot = false
+    for i in 0..n-1:
+      if buf[i] == ',':
+        buf[i] = '.'
+        hasDot = true
+      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+        hasDot = true
+    if not hasDot:
+      buf[n] = '.'
+      buf[n+1] = '0'
+      buf[n+2] = '\0'
+    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
+    # of '-1.#IND' are produced.
+    # We want to get rid of these here:
+    if buf[n-1] in {'n', 'N', 'D', 'd'}:
+      result.add "nan"
+    elif buf[n-1] == 'F':
+      if buf[0] == '-':
+        result.add "-inf"
+      else:
+        result.add "inf"
+    else:
+      var i = 0
+      while buf[i] != '\0':
+        result.add buf[i]
+        inc i
+
+proc nimFloatToStr(f: float): string {.compilerproc.} =
+  result = newStringOfCap(8)
+  result.add f
+
+proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
+  importc: "strtod", header: "<stdlib.h>", noSideEffect.}
+
+const
+  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
+  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+              1e20, 1e21, 1e22]
+
+proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
+                          start = 0): int {.compilerProc.} =
+  # This routine attempt to parse float that can parsed quickly.
+  # ie whose integer part can fit inside a 53bits integer.
+  # their real exponent must also be <= 22. If the float doesn't follow
+  # these restrictions, transform the float into this form:
+  #  INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
+  # This avoid the problems of decimal character portability.
+  # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+  var
+    i = start
+    sign = 1.0
+    kdigits, fdigits = 0
+    exponent: int
+    integer: uint64
+    frac_exponent = 0
+    exp_sign = 1
+    first_digit = -1
+    has_sign = false
+
+  # Sign?
+  if s[i] == '+' or s[i] == '-':
+    has_sign = true
+    if s[i] == '-':
+      sign = -1.0
+    inc(i)
+
+  # NaN?
+  if s[i] == 'N' or s[i] == 'n':
+    if s[i+1] == 'A' or s[i+1] == 'a':
+      if s[i+2] == 'N' or s[i+2] == 'n':
+        if s[i+3] notin IdentChars:
+          number = NaN
+          return i+3 - start
+    return 0
+
+  # Inf?
+  if s[i] == 'I' or s[i] == 'i':
+    if s[i+1] == 'N' or s[i+1] == 'n':
+      if s[i+2] == 'F' or s[i+2] == 'f':
+        if s[i+3] notin IdentChars:
+          number = Inf*sign
+          return i+3 - start
+    return 0
+
+  if s[i] in {'0'..'9'}:
+    first_digit = (s[i].ord - '0'.ord)
+  # Integer part?
+  while s[i] in {'0'..'9'}:
+    inc(kdigits)
+    integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
+    inc(i)
+    while s[i] == '_': inc(i)
+
+  # Fractional part?
+  if s[i] == '.':
+    inc(i)
+    # if no integer part, Skip leading zeros
+    if kdigits <= 0:
+      while s[i] == '0':
+        inc(frac_exponent)
+        inc(i)
+        while s[i] == '_': inc(i)
+
+    if first_digit == -1 and s[i] in {'0'..'9'}:
+      first_digit = (s[i].ord - '0'.ord)
+    # get fractional part
+    while s[i] in {'0'..'9'}:
+      inc(fdigits)
+      inc(frac_exponent)
+      integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
+      inc(i)
+      while s[i] == '_': inc(i)
+
+  # if has no digits: return error
+  if kdigits + fdigits <= 0 and
+     (i == start or # no char consumed (empty string).
+     (i == start + 1 and has_sign)): # or only '+' or '-
+    return 0
+
+  if s[i] in {'e', 'E'}:
+    inc(i)
+    if s[i] == '+' or s[i] == '-':
+      if s[i] == '-':
+        exp_sign = -1
+
+      inc(i)
+    if s[i] notin {'0'..'9'}:
+      return 0
+    while s[i] in {'0'..'9'}:
+      exponent = exponent * 10 + (ord(s[i]) - ord('0'))
+      inc(i)
+      while s[i] == '_': inc(i) # underscores are allowed and ignored
+
+  var real_exponent = exp_sign*exponent - frac_exponent
+  let exp_negative = real_exponent < 0
+  var abs_exponent = abs(real_exponent)
+
+  # if exponent greater than can be represented: +/- zero or infinity
+  if abs_exponent > 999:
+    if exp_negative:
+      number = 0.0*sign
+    else:
+      number = Inf*sign
+    return i - start
+
+  # if integer is representable in 53 bits:  fast path
+  # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
+  let digits = kdigits + fdigits
+  if digits <= 15 or (digits <= 16 and first_digit <= 8):
+    # max float power of ten with set bits above the 53th bit is 10^22
+    if abs_exponent <= 22:
+      if exp_negative:
+        number = sign * integer.float / powtens[abs_exponent]
+      else:
+        number = sign * integer.float * powtens[abs_exponent]
+      return i - start
+
+    # if exponent is greater try to fit extra exponent above 22 by multiplying
+    # integer part is there is space left.
+    let slop = 15 - kdigits - fdigits
+    if  abs_exponent <= 22 + slop and not exp_negative:
+      number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop]
+      return i - start
+
+  # if failed: slow path with strtod.
+  var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
+  var ti = 0
+  let maxlen = t.high - "e+000".len # reserve enough space for exponent
+
+  result = i - start
+  i = start
+  # re-parse without error checking, any error should be handled by the code above.
+  if s[i] == '.': i.inc
+  while s[i] in {'0'..'9','+','-'}:
+    if ti < maxlen:
+      t[ti] = s[i]; inc(ti)
+    inc(i)
+    while s[i] in {'.', '_'}: # skip underscore and decimal point
+      inc(i)
+
+  # insert exponent
+  t[ti] = 'E'; inc(ti)
+  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
+  inc(ti, 3)
+
+  # insert adjusted exponent
+  t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
+  t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
+  t[ti-3] = ('0'.ord + abs_exponent mod 10).char
+
+  when defined(nimNoArrayToCstringConversion):
+    number = c_strtod(addr t, nil)
+  else:
+    number = c_strtod(t, nil)
+
+proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
+  result = newStringOfCap(sizeof(x)*4)
+  result.add x
+
+proc nimBoolToStr(x: bool): string {.compilerRtl.} =
+  return if x: "true" else: "false"
+
+proc nimCharToStr(x: char): string {.compilerRtl.} =
+  result = newString(1)
+  result[0] = x
+
+proc `$`(x: uint64): string =
+  if x == 0:
+    result = "0"
+  else:
+    result = newString(60)
+    var i = 0
+    var n = x
+    while n != 0:
+      let nn = n div 10'u64
+      result[i] = char(n - 10'u64 * nn + ord('0'))
+      inc i
+      n = nn
+    result.setLen i
+
+    let half = i div 2
+    # Reverse
+    for t in 0 .. half-1: swap(result[t], result[i-t-1])
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 7a10849dd..3168e4cb2 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -148,7 +148,7 @@ proc readLine(f: File, line: var TaintedString): bool =
   if line.string.isNil:
     line = TaintedString(newStringOfCap(80))
   else:
-    when not defined(nimscript):
+    when not defined(nimscript) and not defined(gcDestructors):
       sp = cint(cast[PGenericSeq](line.string).space)
     line.string.setLen(sp)
   while true:
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 0e690d832..6438a0541 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -20,37 +20,6 @@ proc resize(old: int): int {.inline.} =
   elif old < 65536: result = old * 2
   else: result = old * 3 div 2 # for large arrays * 3/2 is better
 
-proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
-  if a == b: return 0
-  when defined(nimNoNil):
-    let alen = if a == nil: 0 else: a.len
-    let blen = if b == nil: 0 else: b.len
-  else:
-    if a == nil: return -1
-    if b == nil: return 1
-    let alen = a.len
-    let blen = b.len
-  let minlen = min(alen, blen)
-  if minlen > 0:
-    result = nimCmpMem(addr a.data, addr b.data, minlen.csize)
-    if result == 0:
-      result = alen - blen
-  else:
-    result = alen - blen
-
-proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
-  if a == b: return true
-  when defined(nimNoNil):
-    let alen = if a == nil: 0 else: a.len
-    let blen = if b == nil: 0 else: b.len
-  else:
-    if a == nil or b == nil: return false
-    let alen = a.len
-    let blen = b.len
-  if alen == blen:
-    if alen == 0: return true
-    return equalMem(addr(a.data), addr(b.data), alen)
-
 when declared(allocAtomic):
   template allocStr(size: untyped): untyped =
     cast[NimString](allocAtomic(size))
@@ -94,6 +63,8 @@ proc mnewString(len: int): NimString {.compilerProc.} =
   result.len = len
 
 proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
   let start = max(start, 0)
   if s == nil: return nil
   let len = min(last, s.len-1) - start + 1
@@ -105,14 +76,16 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
   else:
     result = rawNewString(len)
 
-proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} =
-  if s == nil or s.len == 0: result = cstring""
-  else: result = cstring(addr s.data)
-
 proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
   if s == nil: return nil
   result = copyStrLast(s, start, s.len-1)
 
+proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} =
+  if s == nil or s.len == 0: result = cstring""
+  else: result = cstring(addr s.data)
+
 proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
   result = rawNewStringNoInit(len)
   result.len = len
@@ -164,19 +137,6 @@ proc copyDeepString(src: NimString): NimString {.inline.} =
     result.len = src.len
     copyMem(addr(result.data), addr(src.data), src.len + 1)
 
-proc hashString(s: string): int {.compilerproc.} =
-  # the compiler needs exactly the same hash function!
-  # this used to be used for efficient generation of string case statements
-  var h = 0
-  for i in 0..len(s)-1:
-    h = h +% ord(s[i])
-    h = h +% h shl 10
-    h = h xor (h shr 6)
-  h = h +% h shl 3
-  h = h xor (h shr 11)
-  h = h +% h shl 15
-  result = h
-
 proc addChar(s: NimString, c: char): NimString =
   # is compilerproc!
   if s == nil:
@@ -403,243 +363,3 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
       result.len = newLen
     else:
       result = setLengthSeq(s, typ.base.size, newLen)
-
-# --------------- other string routines ----------------------------------
-proc add*(result: var string; x: int64) =
-  let base = result.len
-  setLen(result, base + sizeof(x)*4)
-  var i = 0
-  var y = x
-  while true:
-    var d = y div 10
-    result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
-    inc(i)
-    y = d
-    if y == 0: break
-  if x < 0:
-    result[base+i] = '-'
-    inc(i)
-  setLen(result, base+i)
-  # mirror the string:
-  for j in 0..i div 2 - 1:
-    swap(result[base+j], result[base+i-j-1])
-
-proc nimIntToStr(x: int): string {.compilerRtl.} =
-  result = newStringOfCap(sizeof(x)*4)
-  result.add x
-
-proc add*(result: var string; x: float) =
-  when nimvm:
-    result.add $x
-  else:
-    var buf: array[0..64, char]
-    when defined(nimNoArrayToCstringConversion):
-      var n: int = c_sprintf(addr buf, "%.16g", x)
-    else:
-      var n: int = c_sprintf(buf, "%.16g", x)
-    var hasDot = false
-    for i in 0..n-1:
-      if buf[i] == ',':
-        buf[i] = '.'
-        hasDot = true
-      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
-        hasDot = true
-    if not hasDot:
-      buf[n] = '.'
-      buf[n+1] = '0'
-      buf[n+2] = '\0'
-    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
-    # of '-1.#IND' are produced.
-    # We want to get rid of these here:
-    if buf[n-1] in {'n', 'N', 'D', 'd'}:
-      result.add "nan"
-    elif buf[n-1] == 'F':
-      if buf[0] == '-':
-        result.add "-inf"
-      else:
-        result.add "inf"
-    else:
-      var i = 0
-      while buf[i] != '\0':
-        result.add buf[i]
-        inc i
-
-proc nimFloatToStr(f: float): string {.compilerproc.} =
-  result = newStringOfCap(8)
-  result.add f
-
-proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
-  importc: "strtod", header: "<stdlib.h>", noSideEffect.}
-
-const
-  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
-  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
-              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-              1e20, 1e21, 1e22]
-
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
-                          start = 0): int {.compilerProc.} =
-  # This routine attempt to parse float that can parsed quickly.
-  # ie whose integer part can fit inside a 53bits integer.
-  # their real exponent must also be <= 22. If the float doesn't follow
-  # these restrictions, transform the float into this form:
-  #  INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
-  # This avoid the problems of decimal character portability.
-  # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
-  var
-    i = start
-    sign = 1.0
-    kdigits, fdigits = 0
-    exponent: int
-    integer: uint64
-    frac_exponent = 0
-    exp_sign = 1
-    first_digit = -1
-    has_sign = false
-
-  # Sign?
-  if s[i] == '+' or s[i] == '-':
-    has_sign = true
-    if s[i] == '-':
-      sign = -1.0
-    inc(i)
-
-  # NaN?
-  if s[i] == 'N' or s[i] == 'n':
-    if s[i+1] == 'A' or s[i+1] == 'a':
-      if s[i+2] == 'N' or s[i+2] == 'n':
-        if s[i+3] notin IdentChars:
-          number = NaN
-          return i+3 - start
-    return 0
-
-  # Inf?
-  if s[i] == 'I' or s[i] == 'i':
-    if s[i+1] == 'N' or s[i+1] == 'n':
-      if s[i+2] == 'F' or s[i+2] == 'f':
-        if s[i+3] notin IdentChars:
-          number = Inf*sign
-          return i+3 - start
-    return 0
-
-  if s[i] in {'0'..'9'}:
-    first_digit = (s[i].ord - '0'.ord)
-  # Integer part?
-  while s[i] in {'0'..'9'}:
-    inc(kdigits)
-    integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
-    inc(i)
-    while s[i] == '_': inc(i)
-
-  # Fractional part?
-  if s[i] == '.':
-    inc(i)
-    # if no integer part, Skip leading zeros
-    if kdigits <= 0:
-      while s[i] == '0':
-        inc(frac_exponent)
-        inc(i)
-        while s[i] == '_': inc(i)
-
-    if first_digit == -1 and s[i] in {'0'..'9'}:
-      first_digit = (s[i].ord - '0'.ord)
-    # get fractional part
-    while s[i] in {'0'..'9'}:
-      inc(fdigits)
-      inc(frac_exponent)
-      integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
-      inc(i)
-      while s[i] == '_': inc(i)
-
-  # if has no digits: return error
-  if kdigits + fdigits <= 0 and
-     (i == start or # no char consumed (empty string).
-     (i == start + 1 and has_sign)): # or only '+' or '-
-    return 0
-
-  if s[i] in {'e', 'E'}:
-    inc(i)
-    if s[i] == '+' or s[i] == '-':
-      if s[i] == '-':
-        exp_sign = -1
-
-      inc(i)
-    if s[i] notin {'0'..'9'}:
-      return 0
-    while s[i] in {'0'..'9'}:
-      exponent = exponent * 10 + (ord(s[i]) - ord('0'))
-      inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
-
-  var real_exponent = exp_sign*exponent - frac_exponent
-  let exp_negative = real_exponent < 0
-  var abs_exponent = abs(real_exponent)
-
-  # if exponent greater than can be represented: +/- zero or infinity
-  if abs_exponent > 999:
-    if exp_negative:
-      number = 0.0*sign
-    else:
-      number = Inf*sign
-    return i - start
-
-  # if integer is representable in 53 bits:  fast path
-  # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
-  let digits = kdigits + fdigits
-  if digits <= 15 or (digits <= 16 and first_digit <= 8):
-    # max float power of ten with set bits above the 53th bit is 10^22
-    if abs_exponent <= 22:
-      if exp_negative:
-        number = sign * integer.float / powtens[abs_exponent]
-      else:
-        number = sign * integer.float * powtens[abs_exponent]
-      return i - start
-
-    # if exponent is greater try to fit extra exponent above 22 by multiplying
-    # integer part is there is space left.
-    let slop = 15 - kdigits - fdigits
-    if  abs_exponent <= 22 + slop and not exp_negative:
-      number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop]
-      return i - start
-
-  # if failed: slow path with strtod.
-  var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
-  var ti = 0
-  let maxlen = t.high - "e+000".len # reserve enough space for exponent
-
-  result = i - start
-  i = start
-  # re-parse without error checking, any error should be handled by the code above.
-  if s[i] == '.': i.inc
-  while s[i] in {'0'..'9','+','-'}:
-    if ti < maxlen:
-      t[ti] = s[i]; inc(ti)
-    inc(i)
-    while s[i] in {'.', '_'}: # skip underscore and decimal point
-      inc(i)
-
-  # insert exponent
-  t[ti] = 'E'; inc(ti)
-  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
-  inc(ti, 3)
-
-  # insert adjusted exponent
-  t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
-  t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
-  t[ti-3] = ('0'.ord + abs_exponent mod 10).char
-
-  when defined(nimNoArrayToCstringConversion):
-    number = c_strtod(addr t, nil)
-  else:
-    number = c_strtod(t, nil)
-
-proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
-  result = newStringOfCap(sizeof(x)*4)
-  result.add x
-
-proc nimBoolToStr(x: bool): string {.compilerRtl.} =
-  return if x: "true" else: "false"
-
-proc nimCharToStr(x: char): string {.compilerRtl.} =
-  result = newString(1)
-  result[0] = x
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
index a8b28c279..85e5e1462 100644
--- a/lib/system/widestrs.nim
+++ b/lib/system/widestrs.nim
@@ -10,13 +10,12 @@
 # Nim support for C/C++'s `wide strings`:idx:. This is part of the system
 # module! Do not import it directly!
 
-when not declared(NimString):
+when not declared(ThisIsSystem):
   {.error: "You must not import this module explicitly".}
 
 type
   Utf16Char* = distinct int16
   WideCString* = ref UncheckedArray[Utf16Char]
-{.deprecated: [TUtf16Char: Utf16Char].}
 
 proc len*(w: WideCString): int =
   ## returns the length of a widestring. This traverses the whole string to
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
index 97d7c07b6..83df0053f 100644
--- a/tests/destructor/tcustomseqs.nim
+++ b/tests/destructor/tcustomseqs.nim
@@ -43,9 +43,10 @@ proc `=destroy`*[T](x: var myseq[T]) =
 proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
   if a.data == b.data: return
   if a.data != nil:
-    dealloc(a.data)
-    inc deallocCount
-    a.data = nil
+    `=destroy`(a)
+    #dealloc(a.data)
+    #inc deallocCount
+    #a.data = nil
   a.len = b.len
   a.cap = b.cap
   if b.data != nil: