summary refs log tree commit diff stats
path: root/lib/core
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-09-11 17:27:47 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-09-11 17:27:47 +0200
commitf7d1902043c1bc70ba0bb159a3e8c71b78947ad7 (patch)
tree96adf0fef3b9f1af4b318c1b639797dcca185874 /lib/core
parent0495e6cf3a1cf0f5f71622a8408d24fbc27642a0 (diff)
parentaf94946517d4e07e91b5c5ca21d58645f6da86c4 (diff)
downloadNim-f7d1902043c1bc70ba0bb159a3e8c71b78947ad7.tar.gz
fixes merge conflicts
Diffstat (limited to 'lib/core')
-rw-r--r--lib/core/allocators.nim41
-rw-r--r--lib/core/macrocache.nim47
-rw-r--r--lib/core/macros.nim470
-rw-r--r--lib/core/seqs.nim246
-rw-r--r--lib/core/strs.nim233
5 files changed, 665 insertions, 372 deletions
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/macrocache.nim b/lib/core/macrocache.nim
new file mode 100644
index 000000000..bd48b5bd4
--- /dev/null
+++ b/lib/core/macrocache.nim
@@ -0,0 +1,47 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides an API for macros that need to collect compile
+## time information across module boundaries in global variables.
+## Starting with version 0.19 of Nim this is not directly supported anymore
+## as it breaks incremental compilations.
+## Instead the API here needs to be used. See XXX (wikipedia page) for a
+## theoretical foundation behind this.
+
+type
+  CacheSeq* = distinct string
+  CacheTable* = distinct string
+  CacheCounter* = distinct string
+
+proc value*(c: CacheCounter): int {.magic: "NccValue".}
+proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
+
+proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
+proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
+proc len*(s: CacheSeq): int {.magic: "NcsLen".}
+proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
+
+iterator items*(s: CacheSeq): NimNode =
+  for i in 0 ..< len(s): yield s[i]
+
+proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
+  ## 'key' has to be unique!
+
+proc len*(t: CacheTable): int {.magic: "NctLen".}
+proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
+
+proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
+proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
+
+iterator pairs*(t: CacheTable): (string, NimNode) =
+  var h = 0
+  while hasNext(t, h):
+    let (a, b, h2) = next(t, h)
+    yield (a, b)
+    h = h2
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index f2a39f43b..aec766068 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -8,12 +8,16 @@
 #
 
 include "system/inclrtl"
+include "system/helpers"
 
 ## This module contains the interface to the compiler's abstract syntax
 ## tree (`AST`:idx:). Macros operate on this tree.
 
 ## .. include:: ../../doc/astspec.txt
 
+# If you look for the implementation of the magic symbol
+# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
+
 type
   NimNodeKind* = enum
     nnkNone, nnkEmpty, nnkIdent, nnkSym,
@@ -76,7 +80,8 @@ type
     nnkGotoState,
     nnkState,
     nnkBreakState,
-    nnkFuncDef
+    nnkFuncDef,
+    nnkTupleConstr
 
   NimNodeKinds* = set[NimNodeKind]
   NimTypeKind* = enum  # some types are no longer used, see ast.nim
@@ -118,13 +123,10 @@ type
     ## use ``ident"abc"``.
 
   NimSymObj = object # hidden
-  NimSym* = ref NimSymObj
+  NimSym* {.deprecated.} = ref NimSymObj
     ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
     ## *ident*.
 
-{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
-    TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
-    TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
 
 const
   nnkLiterals* = {nnkCharLit..nnkNilLit}
@@ -134,25 +136,23 @@ const
 
 proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
   ## constructs an identifier from the string `s`
-  ## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead.
+  ## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead.
 
-proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
+proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
   ## constructs an identifier from the string `s`
+  ## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead.
 
-proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
-  ## converts a Nim identifier to a string
-
-proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
-  ## converts a Nim symbol to a string
-
-proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
+proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.}
   ## compares two Nim identifiers
+  ## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead.
 
 proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
   ## compares two Nim nodes
 
-proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.}
+proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
   ## compares two Nim symbols
+  ## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
+
 
 proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
   ## compares two Nim nodes' types. Return true if the types are the same,
@@ -195,8 +195,53 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
 proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
 
 proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
-proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.}
-proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.}
+
+proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} =
+  ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
+
+proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.}
+  ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
+
+proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
+
+when defined(nimSymKind):
+  proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
+  proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
+  proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
+    ## retrieve the implementation of `symbol`. `symbol` can be a
+    ## routine or a const.
+
+  proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.}
+    ## converts a Nim identifier to a string
+    ## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
+
+  proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.}
+    ## converts a Nim symbol to a string
+    ## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
+
+else: # bootstrapping substitute
+  proc getImpl*(symbol: NimNode): NimNode =
+    symbol.symbol.getImpl
+
+  proc strValOld(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
+
+  proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
+
+  proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
+
+  proc strVal*(n: NimNode): string =
+    if n.kind == nnkIdent:
+      $n.ident
+    elif n.kind == nnkSym:
+      $n.symbol
+    else:
+      n.strValOld
+
+when defined(nimHasSymOwnerInMacro):
+  proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
+    ## accepts node of kind nnkSym and returns its owner's symbol.
+    ## result is also mnde of kind nnkSym if owner exists otherwise
+    ## nnkNilLit is returned
 
 proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
   ## with 'getType' you can access the node's `type`:idx:. A Nim type is
@@ -214,26 +259,65 @@ proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
 
 proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
   ## Returns the type kind of the node 'n' that should represent a type, that
-  ## means the node should have been obtained via `getType`.
-
-proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Like getType except it includes generic parameters for a specific instance
+  ## means the node should have been obtained via ``getType``.
+
+proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
+  ## Returns the `type`:idx: of a node in a form matching the way the
+  ## type instance was declared in the code.
+  runnableExamples:
+    type
+      Vec[N: static[int], T] = object
+        arr: array[N, T]
+      Vec4[T] = Vec[4, T]
+      Vec4f = Vec4[float32]
+    var a: Vec4f
+    var b: Vec4[float32]
+    var c: Vec[4, float32]
+    macro dumpTypeInst(x: typed): untyped =
+      newLit(x.getTypeInst.repr)
+    doAssert(dumpTypeInst(a) == "Vec4f")
+    doAssert(dumpTypeInst(b) == "Vec4[float32]")
+    doAssert(dumpTypeInst(c) == "Vec[4, float32]")
 
 proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Like getType except it includes generic parameters for a specific instance
-
-proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Like getType except it includes generic parameters for the implementation
+  ## Version of ``getTypeInst`` which takes a ``typedesc``.
+
+proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
+  ## Returns the `type`:idx: of a node in a form matching the implementation
+  ## of the type.  Any intermediate aliases are expanded to arrive at the final
+  ## type implementation.  You can instead use ``getImpl`` on a symbol if you
+  ## want to find the intermediate aliases.
+  runnableExamples:
+    type
+      Vec[N: static[int], T] = object
+        arr: array[N, T]
+      Vec4[T] = Vec[4, T]
+      Vec4f = Vec4[float32]
+    var a: Vec4f
+    var b: Vec4[float32]
+    var c: Vec[4, float32]
+    macro dumpTypeImpl(x: typed): untyped =
+      newLit(x.getTypeImpl.repr)
+    let t = """
+object
+  arr: array[0 .. 3, float32]
+"""
+    doAssert(dumpTypeImpl(a) == t)
+    doAssert(dumpTypeImpl(b) == t)
+    doAssert(dumpTypeImpl(c) == t)
 
 proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Like getType except it includes generic parameters for the implementation
-
-proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
+  ## Version of ``getTypeImpl`` which takes a ``typedesc``.
 
 proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
 proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
-proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect.}
-proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
+
+proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.}
+  ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead.
+
+proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.}
+  ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead.
+
 #proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
 # this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
 # can easily do:
@@ -255,18 +339,13 @@ proc newNimNode*(kind: NimNodeKind,
 proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
 proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
 
-proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect.} =
-  ## retrieve the implementation of a symbol `s`. `s` can be a routine or a
-  ## const.
-  discard
-
 proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
   ## writes an error message at compile time
 
-proc warning*(msg: string) {.magic: "NWarning", benign.}
+proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
   ## writes a warning message at compile time
 
-proc hint*(msg: string) {.magic: "NHint", benign.}
+proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
   ## writes a hint message at compile time
 
 proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
@@ -294,11 +373,9 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
   result = newNimNode(nnkIdent)
   result.ident = i
 
-proc newIdentNode*(i: string): NimNode {.compileTime.} =
-  ## creates an identifier node from `i`
-  result = newNimNode(nnkIdent)
-  result.ident = toNimIdent i
-
+proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.}
+  ## creates an identifier node from `i`. It is simply an alias for
+  ## ``ident(string)``. Use that, it's shorter.
 
 type
   BindSymRule* = enum    ## specifies how ``bindSym`` behaves
@@ -312,24 +389,34 @@ type
 
 {.deprecated: [TBindSymRule: BindSymRule].}
 
-proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {.
+proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
               magic: "NBindSym", noSideEffect.}
   ## creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
+  ## if `ident` is a NimNode, it must have nkIdent kind.
   ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
   ## returned even if the symbol is not ambiguous.
+  ##
+  ## experimental feature:
+  ## use {.experimental: "dynamicBindSym".} to activate it
+  ## if called from template / regular code, `ident` and `rule` must be
+  ## constant expression / literal value.
+  ## if called from macros / compile time procs / static blocks,
+  ## `ident` and `rule` can be VM computed value.
 
 proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
   magic: "NGenSym", noSideEffect.}
   ## generates a fresh symbol that is guaranteed to be unique. The symbol
   ## needs to occur in a declaration context.
 
-proc callsite*(): NimNode {.magic: "NCallSite", benign.}
+proc callsite*(): NimNode {.magic: "NCallSite", benign,
+  deprecated: "use varargs[untyped] in the macro prototype instead".}
   ## returns the AST of the invocation expression that invoked this macro.
+  ## **Deprecated since version 0.18.1**.
 
 proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
   ## converts the AST `n` to the concrete Nim code and wraps that
@@ -342,7 +429,8 @@ type
     line*,column*: int
 
 proc `$`*(arg: Lineinfo): string =
-  result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
+  # BUG: without `result = `, gives compile error
+  result = lineInfoToString(arg.filename, arg.line, arg.column)
 
 #proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
   ## returns the position the node appears in the original source file
@@ -352,7 +440,11 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 
+proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
+  ## copy lineinfo from info node
+
 proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
+  ## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
   result.filename = n.getFile
   result.line = n.getLine
   result.column = n.getColumn
@@ -464,9 +556,11 @@ proc newCall*(theProc: NimNode,
   result.add(args)
 
 proc newCall*(theProc: NimIdent,
-              args: varargs[NimNode]): NimNode {.compileTime.} =
+              args: varargs[NimNode]): NimNode {.compileTime, deprecated.} =
   ## produces a new call node. `theProc` is the proc that is called with
   ## the arguments ``args[0..]``.
+  ## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``,
+  ## or ``newCall(NimNode, ...)`` instead.
   result = newNimNode(nnkCall)
   result.add(newIdentNode(theProc))
   result.add(args)
@@ -594,17 +688,30 @@ proc newLit*(s: string): NimNode {.compileTime.} =
   result = newNimNode(nnkStrLit)
   result.strVal = s
 
-proc nestList*(theProc: NimIdent,
-               x: NimNode): NimNode {.compileTime.} =
-  ## nests the list `x` into a tree of call expressions:
-  ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``.
+proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
+  ## nests the list `pack` into a tree of call expressions:
+  ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
+  ## This is also known as fold expression.
+  if pack.len < 1:
+    error("`nestList` expects a node with at least 1 child")
+  result = pack[^1]
+  for i in countdown(pack.len - 2, 0):
+    result = newCall(op, pack[i], result)
+
+proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} =
+  ## nests the list `pack` into a tree of call expressions:
+  ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
+  ## This is also known as fold expression.
+  result = init
+  for i in countdown(pack.len - 1, 0):
+    result = newCall(op, pack[i], result)
+
+proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} =
+  ## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead.
   var L = x.len
   result = newCall(theProc, x[L-2], x[L-1])
   for i in countdown(L-3, 0):
-    # XXX the 'copyNimTree' here is necessary due to a bug in the evaluation
-    # engine that would otherwise create an endless loop here. :-(
-    # This could easily user code and so should be fixed in evals.nim somehow.
-    result = newCall(theProc, x[i], copyNimTree(result))
+    result = newCall(theProc, x[i], result)
 
 proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
   ## Convert the AST `n` to a human-readable tree-like string.
@@ -616,13 +723,11 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
     res.add(($n.kind).substr(3))
 
     case n.kind
-    of nnkEmpty: discard # same as nil node in this representation
-    of nnkNilLit: res.add(" nil")
+    of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
     of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
-    of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal)
-    of nnkIdent: res.add(" ident\"" & $n.ident & '"')
-    of nnkSym: res.add(" \"" & $n.symbol & '"')
+    of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
+      res.add(" " & $n.strVal.newLit.repr)
     of nnkNone: assert false
     else:
       for j in 0..n.len-1:
@@ -641,13 +746,11 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
   add(result, "(")
 
   case n.kind
-  of nnkEmpty: discard # same as nil node in this representation
-  of nnkNilLit: add(result, "nil")
+  of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
   of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
   of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
-  of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
-  of nnkIdent: add(result, "ident\"" & $n.ident & '"')
-  of nnkSym: add(result, $n.symbol)
+  of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
+    add(result, n.strVal.newLit.repr)
   of nnkNone: assert false
   else:
     if n.len > 0:
@@ -678,54 +781,27 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
   ## See also `repr`, `treeRepr`, and `lispRepr`.
 
   const
-    NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone}
+    NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
     LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
 
-  proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} =
-    ## Functions copied from strutils
-    proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} =
-      const
-        HexChars = "0123456789ABCDEF"
-      var
-        t = x
-      result = newString(len)
-      for j in countdown(len-1, 0):
-        result[j] = HexChars[int(t and 0xF)]
-        t = t shr 4
-        # handle negative overflow
-        if t == 0 and x < 0: t = -1
-
-    result = newStringOfCap(s.len + s.len shr 2)
-    result.add(prefix)
-    for c in items(s):
-      case c
-      of '\0'..'\31', '\128'..'\255':
-        add(result, "\\x")
-        add(result, toHex(ord(c), 2))
-      of '\\': add(result, "\\\\")
-      of '\'': add(result, "\\'")
-      of '\"': add(result, "\\\"")
-      else: add(result, c)
-    add(result, suffix)
-
   proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
     for i in 0..level-1: res.add "  "
     if n.kind in NodeKinds:
       res.add("new" & ($n.kind).substr(3) & "Node(")
     elif n.kind in LitKinds:
       res.add("newLit(")
+    elif n.kind == nnkNilLit:
+      res.add("newNilLit()")
     else:
       res.add($n.kind)
 
     case n.kind
-    of nnkEmpty: discard
-    of nnkNilLit: res.add("nil")
+    of nnkEmpty, nnkNilLit: discard
     of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
     of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
     of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
-    of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
-    of nnkIdent: res.add(($n.ident).escape())
-    of nnkSym: res.add(($n.symbol).escape())
+    of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
+      res.add(n.strVal.newLit.repr)
     of nnkNone: assert false
     else:
       res.add(".newTree(")
@@ -769,11 +845,10 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
   ## See `dumpTree`.
 
 macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
-  ## Deprecated.
+  ## Deprecated. Use `dumpTree` instead.
 
 macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr
-  ## Deprecated.
-
+  ## Deprecated. Use `dumpLisp` instead.
 
 proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} =
   ## Create a new empty node
@@ -1019,28 +1094,21 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
 
 proc basename*(a: NimNode): NimNode {.compiletime, benign.}
 
-
 proc `$`*(node: NimNode): string {.compileTime.} =
   ## Get the string of an identifier node
   case node.kind
-  of nnkIdent:
-    result = $node.ident
   of nnkPostfix:
-    result = $node.basename.ident & "*"
-  of nnkStrLit..nnkTripleStrLit:
+    result = node.basename.strVal & "*"
+  of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
     result = node.strVal
-  of nnkSym:
-    result = $node.symbol
   of nnkOpenSymChoice, nnkClosedSymChoice:
     result = $node[0]
   of nnkAccQuoted:
     result = $node[0]
-  of nnkCommentStmt:
-    result = node.strVal
   else:
     badNodeKind node.kind, "$"
 
-proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
+proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
   ## Create a new ident node from a string
 
 iterator items*(n: NimNode): NimNode {.inline.} =
@@ -1131,40 +1199,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} =
   ## An alias for copyNimTree().
   return node.copyNimTree()
 
-proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
-  proc toLower(c: char): char {.inline.} =
-    if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
-    else: result = c
-  var i = 0
-  var j = 0
-  # first char is case sensitive
-  if a[0] != b[0]: return 1
-  while true:
-    while a[i] == '_': inc(i)
-    while b[j] == '_': inc(j) # BUGFIX: typo
-    var aa = toLower(a[i])
-    var bb = toLower(b[j])
-    result = ord(aa) - ord(bb)
-    if result != 0 or aa == '\0': break
-    inc(i)
-    inc(j)
-
-proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
-  ## Check if two idents are identical.
-
-proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
-  ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
-  ## is the same as ``s``. Note that this is the preferred way to check! Most
-  ## other ways like ``node.ident`` are much more error-prone, unfortunately.
-  case node.kind
-  of nnkIdent:
-    result = node.ident == toNimIdent s
-  of nnkSym:
-    result = eqIdent($node.symbol, s)
-  of nnkOpenSymChoice, nnkClosedSymChoice:
-    result = eqIdent($node[0], s)
-  else:
-    result = false
+when defined(nimVmEqIdent):
+  proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
+    ## Style insensitive comparison.
+
+  proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
+    ## Style insensitive comparison.
+    ## ``a`` can be an identifier or a symbol.
+
+  proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
+    ## Style insensitive comparison.
+    ## ``b`` can be an identifier or a symbol.
+
+  proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
+    ## Style insensitive comparison.
+    ## ``a`` and ``b`` can be an identifier or a symbol.
+
+else:
+  # this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
+  proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
+    proc toLower(c: char): char {.inline.} =
+      if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
+      else: result = c
+    var i = 0
+    var j = 0
+    # first char is case sensitive
+    if a[0] != b[0]: return 1
+    while true:
+      while a[i] == '_': inc(i)
+      while b[j] == '_': inc(j) # BUGFIX: typo
+      var aa = toLower(a[i])
+      var bb = toLower(b[j])
+      result = ord(aa) - ord(bb)
+      if result != 0 or aa == '\0': break
+      inc(i)
+      inc(j)
+
+
+  proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
+    ## Check if two idents are identical.
+
+  proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
+    ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
+    ## is the same as ``s``. Note that this is the preferred way to check! Most
+    ## other ways like ``node.ident`` are much more error-prone, unfortunately.
+    case node.kind
+    of nnkSym, nnkIdent:
+      result = eqIdent(node.strVal, s)
+    of nnkOpenSymChoice, nnkClosedSymChoice:
+      result = eqIdent($node[0], s)
+    else:
+      result = false
 
 proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
   ## Search nnkFormalParams for an argument.
@@ -1215,33 +1300,81 @@ macro expandMacros*(body: typed): untyped =
   echo result.toStrLit
 
 proc customPragmaNode(n: NimNode): NimNode =
-  expectKind(n, {nnkSym, nnkDotExpr})
-  if n.kind == nnkSym:
-    let sym = n.symbol.getImpl()
-    sym.expectRoutine()
-    result = sym.pragma
-  elif n.kind == nnkDotExpr:
-    let typDef = getImpl(getTypeInst(n[0]).symbol)
-    typDef.expectKind(nnkTypeDef)
-    typDef[2].expectKind(nnkObjectTy)
-    let recList = typDef[2][2]
-    for identDefs in recList:
-      for i in 0 .. identDefs.len - 3:
-        if identDefs[i].kind == nnkPragmaExpr and
-           identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]:
-          return identDefs[i][1]
+  expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
+  let
+    typ = n.getTypeInst()
+
+  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
+    return typ[1][1]
+  elif typ.typeKind == ntyTypeDesc:
+    let impl = typ[1].getImpl()
+    if impl[0].kind == nnkPragmaExpr:
+      return impl[0][1]
+    else:
+      return impl[0] # handle types which don't have macro at all
+
+  if n.kind == nnkSym: # either an variable or a proc
+    let impl = n.getImpl()
+    if impl.kind in RoutineNodes:
+      return impl.pragma
+    else:
+      return typ.getImpl()[0][1]
+
+  if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
+    let name = (if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
+    var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]))
+    while typDef != nil:
+      typDef.expectKind(nnkTypeDef)
+      typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
+      let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy}
+      if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
+        typDef = getImpl(typDef[2][0])
+      else: # object definition, maybe an object directly defined as a ref type
+        let
+          obj = (if isRef: typDef[2][0] else: typDef[2])
+        var identDefsStack = newSeq[NimNode](obj[2].len)
+        for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
+        while identDefsStack.len > 0:
+          var identDefs = identDefsStack.pop()
+          if identDefs.kind == nnkRecCase:
+            identDefsStack.add(identDefs[0])
+            for i in 1..<identDefs.len:
+              # if it is and empty branch, skip
+              if identDefs[i][0].kind == nnkNilLit: continue
+              if identDefs[i][1].kind == nnkIdentDefs:
+                identDefsStack.add(identDefs[i][1])
+              else: # nnkRecList
+                for j in 0..<identDefs[i][1].len:
+                  identDefsStack.add(identDefs[i][1][j])
+
+          else:
+            for i in 0 .. identDefs.len - 3:
+              if identDefs[i].kind == nnkPragmaExpr and
+                identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $name:
+                return identDefs[i][1]
+
+        if obj[1].kind == nnkOfInherit: # explore the parent object
+          typDef = getImpl(obj[1][0])
+        else:
+          typDef = nil
 
 macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
   ## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
-  ## has custom pragma `cp`.
+  ## (if checking a field), a proc or a type has custom pragma `cp`.
+  ##
+  ## See also `getCustomPragmaVal`.
   ##
   ## .. code-block:: nim
   ##   template myAttr() {.pragma.}
   ##   type
   ##     MyObj = object
   ##       myField {.myAttr.}: int
+  ##
+  ##   proc myProc() {.myAttr.} = discard
+  ##
   ##   var o: MyObj
-  ##   assert(o.myField.hasCustomPragma(myAttr) == 0)
+  ##   assert(o.myField.hasCustomPragma(myAttr))
+  ##   assert(myProc.hasCustomPragma(myAttr))
   let pragmaNode = customPragmaNode(n)
   for p in pragmaNode:
     if (p.kind == nnkSym and p == cp) or
@@ -1251,20 +1384,25 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
 
 macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   ## Expands to value of custom pragma `cp` of expression `n` which is expected
-  ## to be `nnkDotExpr`.
+  ## to be `nnkDotExpr`, a proc or a type.
+  ##
+  ## See also `hasCustomPragma`
   ##
   ## .. code-block:: nim
   ##   template serializationKey(key: string) {.pragma.}
   ##   type
-  ##     MyObj = object
+  ##     MyObj {.serializationKey: "mo".} = object
   ##       myField {.serializationKey: "mf".}: int
   ##   var o: MyObj
   ##   assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
+  ##   assert(o.getCustomPragmaVal(serializationKey) == "mo")
+  ##   assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
   let pragmaNode = customPragmaNode(n)
   for p in pragmaNode:
     if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
       return p[1]
-  return newEmptyNode()
+
+  error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
 
 
 when not defined(booting):
@@ -1285,3 +1423,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
   result = newCall(callee)
   for i in 0 ..< args.len:
     result.add args[i]
+
+proc getProjectPath*(): string = discard
+  ## Returns the path to the currently compiling project
+
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index c32cf3690..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 '*'
-proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} =
-  seq[T](len: x.len, cap: x.len, data: x)
 
-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 1958f4974..186add52a 100644
--- a/lib/core/strs.nim
+++ b/lib/core/strs.nim
@@ -7,105 +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.} = 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
 
-proc nimStringLiteral(x: cstring; len: int): string {.core.} =
-  string(len: len, cap: len, data: x)
+template isLiteral(s): bool = s.p == nil or s.p.region == nil
 
-template frees(s) = dealloc(s.data, s.cap + 1)
+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.} =
-  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