summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xbuild.bat14
-rwxr-xr-xcompiler/ast.nim17
-rwxr-xr-xcompiler/c2nim/c2nim.nim3
-rwxr-xr-xcompiler/c2nim/cparse.nim8
-rwxr-xr-xcompiler/c2nim/tests/systest.c2
-rw-r--r--compiler/ccgcalls.nim6
-rwxr-xr-xcompiler/cgen.nim58
-rw-r--r--compiler/cgendata.nim2
-rwxr-xr-xcompiler/commands.nim2
-rwxr-xr-xcompiler/extccomp.nim35
-rwxr-xr-xcompiler/lookups.nim7
-rwxr-xr-xcompiler/msgs.nim6
-rwxr-xr-xcompiler/nimrod.nim12
-rwxr-xr-xcompiler/parser.nim24
-rwxr-xr-xcompiler/pragmas.nim3
-rwxr-xr-xcompiler/semgnrc.nim20
-rwxr-xr-xcompiler/semstmts.nim8
-rwxr-xr-xcompiler/semtypes.nim6
-rwxr-xr-xcompiler/sigmatch.nim3
-rwxr-xr-xcompiler/suggest.nim27
-rwxr-xr-xcompiler/types.nim2
-rwxr-xr-xdoc/lib.txt6
-rw-r--r--doc/subexes.txt58
-rwxr-xr-xkoch.nim70
-rwxr-xr-xlib/impure/zipfiles.nim25
-rwxr-xr-xlib/nimbase.h8
-rw-r--r--lib/pure/actors.nim14
-rw-r--r--lib/pure/collections/critbits.nim302
-rw-r--r--lib/pure/events.nim39
-rwxr-xr-xlib/pure/os.nim8
-rwxr-xr-xlib/pure/osproc.nim9
-rwxr-xr-xlib/pure/strutils.nim273
-rw-r--r--lib/pure/subexes.nim380
-rwxr-xr-xlib/system.nim8
-rwxr-xr-xlib/system/alloc.nim98
-rw-r--r--lib/system/avltree.nim91
-rwxr-xr-xlib/system/gc.nim25
-rwxr-xr-xlib/system/sysio.nim2
-rwxr-xr-xlib/windows/windows.nim18
-rw-r--r--tests/compile/talias.nim66
-rwxr-xr-xtests/compile/tlibs.nim2
-rwxr-xr-xtests/reject/trecinca.nim2
-rwxr-xr-xtests/reject/trecincb.nim2
-rw-r--r--tests/run/tcritbits.nim28
-rw-r--r--tests/specials.nim21
-rw-r--r--tests/system/io.nim7
-rwxr-xr-xtodo.txt12
-rwxr-xr-xweb/news.txt10
-rwxr-xr-xweb/nimrod.ini2
49 files changed, 1557 insertions, 294 deletions
diff --git a/build.bat b/build.bat
index 98eaf7f85..7a46fe5fc 100755
--- a/build.bat
+++ b/build.bat
@@ -35,6 +35,10 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/strtabs.c -o build/1_1/strtabs.o
 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/strtabs.c -o build/1_1/strtabs.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/hashes.c -o build/1_1/hashes.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/hashes.c -o build/1_1/hashes.o

+ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/tables.c -o build/1_1/tables.o

+%CC% %COMP_FLAGS% -Ibuild -c build/1_1/tables.c -o build/1_1/tables.o

+ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/math.c -o build/1_1/math.o

+%CC% %COMP_FLAGS% -Ibuild -c build/1_1/math.c -o build/1_1/math.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/nversion.c -o build/1_1/nversion.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/nversion.c -o build/1_1/nversion.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/condsyms.c -o build/1_1/condsyms.o

@@ -51,8 +55,6 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idents.c -o build/1_1/idents.o
 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idents.c -o build/1_1/idents.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/intsets.c -o build/1_1/intsets.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/intsets.c -o build/1_1/intsets.o

-ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/math.c -o build/1_1/math.o

-%CC% %COMP_FLAGS% -Ibuild -c build/1_1/math.c -o build/1_1/math.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idgen.c -o build/1_1/idgen.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idgen.c -o build/1_1/idgen.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/astalgo.c -o build/1_1/astalgo.o

@@ -109,8 +111,6 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/bitsets.c -o build/1_1/bitsets.o
 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/bitsets.c -o build/1_1/bitsets.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/semthreads.c -o build/1_1/semthreads.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/semthreads.c -o build/1_1/semthreads.o

-ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/tables.c -o build/1_1/tables.o

-%CC% %COMP_FLAGS% -Ibuild -c build/1_1/tables.c -o build/1_1/tables.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/importer.c -o build/1_1/importer.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/importer.c -o build/1_1/importer.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/lookups.c -o build/1_1/lookups.o

@@ -139,6 +139,8 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/sigmatch.c -o build/1_1/sigmatch.o
 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/sigmatch.c -o build/1_1/sigmatch.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/suggest.c -o build/1_1/suggest.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/suggest.c -o build/1_1/suggest.o

+ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/aliases.c -o build/1_1/aliases.o

+%CC% %COMP_FLAGS% -Ibuild -c build/1_1/aliases.c -o build/1_1/aliases.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/docgen.c -o build/1_1/docgen.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/docgen.c -o build/1_1/docgen.o

 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/rst.c -o build/1_1/rst.o

@@ -162,8 +164,8 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/depends.c -o build/1_1/depends.o
 ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o

 %CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o

 

-ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe  build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/intsets.o build/1_1/math.o build/1_1/idgen.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/lexer.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/parser.o build/1_1/pbraces.o build/1_1/filters.o build/1_1/renderer.o build/1_1/filter_tmpl.o build/1_1/rodread.o build/1_1/memfiles.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/semthreads.o build/1_1/tables.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/evals.o build/1_1/semfold.o build/1_1/transf.o build/1_1/cgmeth.o build/1_1/sem.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgendata.o build/1_1/ccgmerge.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/parseopt.o

-%LINKER% %LINK_FLAGS% -o bin\nimrod.exe  build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/intsets.o build/1_1/math.o build/1_1/idgen.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/lexer.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/parser.o build/1_1/pbraces.o build/1_1/filters.o build/1_1/renderer.o build/1_1/filter_tmpl.o build/1_1/rodread.o build/1_1/memfiles.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/semthreads.o build/1_1/tables.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/evals.o build/1_1/semfold.o build/1_1/transf.o build/1_1/cgmeth.o build/1_1/sem.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgendata.o build/1_1/ccgmerge.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/parseopt.o

+ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe  build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/tables.o build/1_1/math.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/intsets.o build/1_1/idgen.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/lexer.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/parser.o build/1_1/pbraces.o build/1_1/filters.o build/1_1/renderer.o build/1_1/filter_tmpl.o build/1_1/rodread.o build/1_1/memfiles.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/semthreads.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/evals.o build/1_1/semfold.o build/1_1/transf.o build/1_1/cgmeth.o build/1_1/sem.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/aliases.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgendata.o build/1_1/ccgmerge.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/parseopt.o

+%LINKER% %LINK_FLAGS% -o bin\nimrod.exe  build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/tables.o build/1_1/math.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/intsets.o build/1_1/idgen.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/lexer.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/parser.o build/1_1/pbraces.o build/1_1/filters.o build/1_1/renderer.o build/1_1/filter_tmpl.o build/1_1/rodread.o build/1_1/memfiles.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/semthreads.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/evals.o build/1_1/semfold.o build/1_1/transf.o build/1_1/cgmeth.o build/1_1/sem.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/semtypinst.o build/1_1/sigmatch.o build/1_1/suggest.o build/1_1/aliases.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgendata.o build/1_1/ccgmerge.o build/1_1/ecmasgen.o build/1_1/passaux.o build/1_1/depends.o build/1_1/parseopt.o

 

 ECHO SUCCESS

 

diff --git a/compiler/ast.nim b/compiler/ast.nim
index 419d57562..aafd52098 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -273,6 +273,7 @@ type
 
 const
   tyPureObject* = tyTuple
+  GcTypeKinds* = {tyRef, tySequence, tyString}
 
 type
   TTypeKinds* = set[TTypeKind]
@@ -771,6 +772,14 @@ proc NewType(kind: TTypeKind, owner: PSym): PType =
   #if result.id < 2000 then
   #  MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
   
+proc mergeLoc(a: var TLoc, b: TLoc) =
+  if a.k == low(a.k): a.k = b.k
+  if a.s == low(a.s): a.s = b.s
+  a.flags = a.flags + b.flags
+  if a.t == nil: a.t = b.t
+  if a.r == nil: a.r = b.r
+  if a.a == 0: a.a = b.a
+  
 proc assignType(dest, src: PType) = 
   dest.kind = src.kind
   dest.flags = src.flags
@@ -779,6 +788,14 @@ proc assignType(dest, src: PType) =
   dest.size = src.size
   dest.align = src.align
   dest.containerID = src.containerID
+  # this fixes 'type TLock = TSysLock':
+  if src.sym != nil:
+    if dest.sym != nil:
+      dest.sym.flags = dest.sym.flags + src.sym.flags
+      if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
+      mergeLoc(dest.sym.loc, src.sym.loc)
+    else:
+      dest.sym = src.sym
   newSons(dest, sonsLen(src))
   for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i]
   
diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim
index 6d7a0d6c1..e408467ef 100755
--- a/compiler/c2nim/c2nim.nim
+++ b/compiler/c2nim/c2nim.nim
@@ -44,7 +44,8 @@ proc main(infile, outfile: string, options: PParserOptions) =
   var module = parseUnit(p)
   closeParser(p)
   renderModule(module, outfile)
-  rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start)])
+  rawMessage(hintSuccessX, [$gLinesCompiled, $(getTime() - start), 
+                            formatSize(getTotalMem())])
 
 var
   infile = ""
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim
index f8f58347d..ad583b92b 100755
--- a/compiler/c2nim/cparse.nim
+++ b/compiler/c2nim/cparse.nim
@@ -94,7 +94,7 @@ proc OpenParser(p: var TParser, filename: string,
   new(p.tok)
 
 proc parMessage(p: TParser, msg: TMsgKind, arg = "") = 
-  #assert false
+  assert false
   lexMessage(p.lex, msg, arg)
 
 proc CloseParser(p: var TParser) = CloseLexer(p.lex)
@@ -930,10 +930,8 @@ proc declaration(p: var TParser): PNode =
       parMessage(p, errTokenExpected, ";")
     if sonsLen(result.sons[pragmasPos]) == 0: 
       result.sons[pragmasPos] = ast.emptyNode
-  of pxAsgn, pxSemicolon, pxComma:
-    result = parseVarDecl(p, baseTyp, rettyp, origName)
   else:
-    parMessage(p, errTokenExpected, ";")
+    result = parseVarDecl(p, baseTyp, rettyp, origName)
   assert result != nil
 
 proc createConst(name, typ, val: PNode, p: TParser): PNode =
@@ -1594,7 +1592,7 @@ proc parseSwitch(p: var TParser): PNode =
   eat(p, pxCurlyRi)
 
 proc addStmt(sl, a: PNode) = 
-  # merge type sections is possible:
+  # merge type sections if possible:
   if a.kind != nkTypeSection or sonsLen(sl) == 0 or
       lastSon(sl).kind != nkTypeSection:
     addSon(sl, a)
diff --git a/compiler/c2nim/tests/systest.c b/compiler/c2nim/tests/systest.c
index d1fbb6784..389fdfdc2 100755
--- a/compiler/c2nim/tests/systest.c
+++ b/compiler/c2nim/tests/systest.c
@@ -351,7 +351,7 @@ HWBType;
 static HWBType *
 RGB_to_HWB (RGBType RGB, HWBType * HWB)
 {
-
+  HWBType* myArray[20];
   /*
    * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is  
    * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.  
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index b28765f8f..3ea539389 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -252,7 +252,8 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
     genNamedParamCall(p, e, d)
   else:
     genPrefixCall(p, nil, e, d)
-  if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
+  when false:
+    if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
 
 proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
   if ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
@@ -262,5 +263,6 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
     genNamedParamCall(p, ri, d)
   else:
     genPrefixCall(p, le, ri, d)
-  if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
+  when false:
+    if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 16210c026..7dfc259ef 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -297,23 +297,36 @@ proc getTemp(p: BProc, t: PType, result: var TLoc) =
   initTemp(p, result)
 
 proc keepAlive(p: BProc, toKeepAlive: TLoc) =
-  if optRefcGC notin gGlobalOptions: return
-  var result: TLoc
-  inc(p.labels)
-  result.r = con("LOC", toRope(p.labels))
-  appf(p.s[cpsLocals], "volatile $1 $2;$n",
-      [getTypeDesc(p.module, toKeepAlive.t), result.r])
-  result.k = locTemp
-  result.a = -1
-  result.t = toKeepAlive.t
-  result.s = OnStack
-  result.flags = {}
-  if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType:
-    appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)])
-  else:
-    appcg(p, cpsStmts,
-         "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
-         [addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)])
+  when false:
+    # deactivated because of the huge slowdown this causes; GC will take care
+    # of interior pointers instead
+    if optRefcGC notin gGlobalOptions: return
+    var result: TLoc
+    var fid = toRope(p.gcFrameId)
+    result.r = con("GCFRAME.F", fid)
+    appf(p.gcFrameType, "  $1 F$2;$n",
+        [getTypeDesc(p.module, toKeepAlive.t), fid])
+    inc(p.gcFrameId)
+    result.k = locTemp
+    result.a = -1
+    result.t = toKeepAlive.t
+    result.s = OnStack
+    result.flags = {}
+
+    if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType:
+      appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)])
+    else:
+      appcg(p, cpsStmts,
+           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+           [addrLoc(result), addrLoc(toKeepAlive), rdLoc(result)])
+
+proc initGCFrame(p: BProc): PRope =
+  if p.gcFrameId > 0: result = ropef("struct {$1} GCFRAME;$n", p.gcFrameType)
+
+proc deinitGCFrame(p: BProc): PRope =
+  if p.gcFrameId > 0:
+    result = ropecg(p.module,
+                    "if (((NU)&GCFRAME) < 4096) #nimGCFrame(&GCFRAME);$n")
 
 proc cstringLit(p: BProc, r: var PRope, s: string): PRope = 
   if gCmd == cmdCompileToLLVM: 
@@ -370,6 +383,8 @@ proc assignLocalVar(p: BProc, s: PSym) =
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
   if sfRegister in s.flags: app(p.s[cpsLocals], " register")
+  elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
+    app(p.s[cpsLocals], " GC_GUARD")
   if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): 
     app(p.s[cpsLocals], " volatile")
   appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
@@ -428,7 +443,7 @@ include "ccgexprs.nim", "ccgstmts.nim"
 proc libCandidates(s: string, dest: var TStringSeq) = 
   var le = strutils.find(s, '(')
   var ri = strutils.find(s, ')', le+1)
-  if le >= 0 and ri > le: 
+  if le >= 0 and ri > le:
     var prefix = substr(s, 0, le - 1)
     var suffix = substr(s, ri + 1)
     for middle in split(substr(s, le + 1, ri - 1), '|'):
@@ -583,8 +598,9 @@ proc genProcAux(m: BModule, prc: PSym) =
   if sfPure in prc.flags: 
     generatedProc = ropeff("$1 {$n$2$3$4}$n", "define $1 {$n$2$3$4}$n",
         [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
-  else: 
+  else:
     generatedProc = ropeff("$1 {$n", "define $1 {$n", [header])
+    app(generatedProc, initGCFrame(p))
     if optStackTrace in prc.options: 
       getFrameDecl(p)
       app(generatedProc, p.s[cpsLocals])
@@ -608,6 +624,7 @@ proc genProcAux(m: BModule, prc: PSym) =
     app(generatedProc, p.s[cpsInit])
     app(generatedProc, p.s[cpsStmts])
     if p.beforeRetNeeded: appf(generatedProc, "BeforeRet: $n;")
+    app(generatedProc, deinitGCFrame(p))
     if optStackTrace in prc.options: app(generatedProc, deinitFrame(p))
     if (optProfiler in prc.options) and (gCmd != cmdCompileToLLVM): 
       appf(generatedProc, 
@@ -816,6 +833,8 @@ proc genInitCode(m: BModule) =
     m.FrameDeclared = true
     getFrameDecl(m.initProc)
   
+  app(prc, initGCFrame(m.initProc))
+  
   app(prc, genSectionStart(cpsLocals))
   app(prc, m.initProc.s[cpsLocals])
   app(prc, genSectionEnd(cpsLocals))
@@ -842,6 +861,7 @@ proc genInitCode(m: BModule) =
   if optStackTrace in m.initProc.options and not m.PreventStackTrace:
     app(prc, deinitFrame(m.initProc))
   app(prc, genSectionEnd(cpsStmts))
+  app(prc, deinitGCFrame(m.initProc))
   appf(prc, "}$n$n")
   # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
   # that would lead to a *nesting* of merge sections which the merger does
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 54e1f5d1a..ecbec664e 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -72,6 +72,8 @@ type
     receiveClosure*: PType    # closure record type that we get
     module*: BModule          # used to prevent excessive parameter passing
     withinLoop*: int          # > 0 if we are within a loop
+    gcFrameId*: natural       # for the GC stack marking
+    gcFrameType*: PRope       # the struct {} we put the GC markers into
   
   TTypeSeq* = seq[PType]
   TCGen = object of TPassContext # represents a C source file
diff --git a/compiler/commands.nim b/compiler/commands.nim
index f07361abe..14b943327 100755
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -207,7 +207,7 @@ proc addPathRec(dir: string, info: TLineInfo) =
 
 proc track(arg: string, info: TLineInfo) = 
   var a = arg.split(',')
-  if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLMUN")
+  if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN")
   var line, column: int
   if parseUtils.parseInt(a[1], line) <= 0:
     LocalError(info, errInvalidNumber, a[1])
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index facb22432..b7548a4b5 100755
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -21,7 +21,8 @@ type
     hasSwitchRange,           # CC allows ranges in switch statements (GNU C)
     hasComputedGoto,          # CC has computed goto (GNU C extension)
     hasCpp,                   # CC is/contains a C++ compiler
-    hasAssume                 # CC has __assume (Visual C extension)
+    hasAssume,                # CC has __assume (Visual C extension)
+    hasGcGuard                # CC supports GC_GUARD to keep stack roots
   TInfoCCProps* = set[TInfoCCProp]
   TInfoCC* = tuple[
     name: string,        # the short name of the compiler
@@ -71,7 +72,7 @@ compiler gcc:
     debug: "",
     pic: "-fPIC",
     asmStmtFrmt: "asm($1);$n",
-    props: {hasSwitchRange, hasComputedGoto, hasCpp})
+    props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard})
     
 compiler gpp:
   result = gcc()
@@ -79,11 +80,10 @@ compiler gpp:
   result.name = "gpp"
   result.compilerExe = "g++"
   result.linkerExe = "g++"  
-  
-  result.debug.add " -g " # XXX: Why is this default for g++, but not for gcc?
 
-  result.buildDll = " -mdll" # XXX: Hmm, I'm keeping this from the previos version, 
-                             # but my gcc doesn't even have such an option (is this mingw?)
+  result.buildDll = " -mdll" 
+  # XXX: Hmm, I'm keeping this from the previos version, 
+  # but my gcc doesn't even have such an option (is this mingw?)
 
 compiler llvmGcc:
   result = gcc()
@@ -422,6 +422,13 @@ proc getOptSize(c: TSystemCC): string =
   if result == "": 
     result = cc[c].optSize    # use default settings from this file
 
+proc noAbsolutePaths: bool {.inline.} =
+  # We used to check current OS != specified OS, but this makes no sense
+  # really: Cross compilation from Linux to Linux for example is entirely
+  # reasonable.
+  # `optGenMapping` is included here for niminst.
+  result = gGlobalOptions * {optGenScript, optGenMapping} != {}
+
 const 
   specialFileA = 42
   specialFileB = 42
@@ -461,7 +468,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
     add(options, ' ' & cc[c].pic)
   
   var includeCmd, compilePattern: string
-  if targetOS == platform.hostOS: 
+  if not noAbsolutePaths(): 
     # compute include paths:
     includeCmd = cc[c].includeCmd & quoteIfContainsWhite(libpath)
 
@@ -474,9 +481,8 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
     compilePattern = cc[c].compilerExe
   
   # XXX fix the grammar finally, we need multi-line if expressions:
-  var cfile = if targetOS == platform.hostOS: cfilename else: extractFileName(
-                                                                     cfilename)
-  var objfile = if not isExternal or targetOS != platform.hostOS: toObjFile(
+  var cfile = if noAbsolutePaths(): extractFileName(cfilename) else: cfilename
+  var objfile = if not isExternal or noAbsolutePaths(): toObjFile(
                       cfile) else: completeCFilePath(toObjFile(cfile))
   cfile = quoteIfContainsWhite(AddFileExt(cfile, cExt))
   objfile = quoteIfContainsWhite(objfile)
@@ -532,9 +538,10 @@ proc CallCCompiler*(projectfile: string) =
     var it = PStrEntry(toLink.head)
     var objfiles = ""
     while it != nil:
+      let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
       add(objfiles, ' ')
       add(objfiles, quoteIfContainsWhite(
-          addFileExt(it.data, cc[ccompiler].objExt)))
+          addFileExt(objFile, cc[ccompiler].objExt)))
       it = PStrEntry(it.next)
 
     if optGenStaticLib in gGlobalOptions:
@@ -545,18 +552,18 @@ proc CallCCompiler*(projectfile: string) =
       var linkerExe = getConfigVar(cc[c].name & ".linkerexe")
       if len(linkerExe) == 0: linkerExe = cc[c].linkerExe
       if targetOS == osWindows: linkerExe = addFileExt(linkerExe, "exe")
-      if platform.hostOS != targetOS: linkCmd = quoteIfContainsWhite(linkerExe)
+      if noAbsolutePaths(): linkCmd = quoteIfContainsWhite(linkerExe)
       else: linkCmd = quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe))
       if optGenGuiApp in gGlobalOptions: buildGui = cc[c].buildGui
       else: buildGui = ""
       var exefile: string
       if optGenDynLib in gGlobalOptions:
-        exefile = platform.os[targetOS].dllFrmt % [splitFile(projectFile).name]
+        exefile = platform.os[targetOS].dllFrmt % splitFile(projectFile).name
         buildDll = cc[c].buildDll
       else:
         exefile = splitFile(projectFile).name & platform.os[targetOS].exeExt
         buildDll = ""
-      if targetOS == platform.hostOS:
+      if not noAbsolutePaths():
         exefile = joinPath(splitFile(projectFile).dir, exefile)
       exefile = quoteIfContainsWhite(exefile)
       for linkedLib in items(cLinkedLibs):
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 61b8ead4c..b2ae0d843 100755
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -53,7 +53,7 @@ proc CloseScope*(tab: var TSymTab) =
   var it: TTabIter
   var s = InitTabIter(it, tab.stack[tab.tos-1])
   while s != nil:
-    if sfForward in s.flags: 
+    if sfForward in s.flags:
       LocalError(s.info, errImplOfXexpected, getSymRepr(s))
     elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: 
       # BUGFIX: check options in s!
@@ -68,7 +68,10 @@ proc AddSym*(t: var TStrTable, n: PSym) =
 proc addDecl*(c: PContext, sym: PSym) = 
   if SymTabAddUnique(c.tab, sym) == Failure: 
     LocalError(sym.info, errAttemptToRedefine, sym.Name.s)
-  
+
+proc addPrelimDecl*(c: PContext, sym: PSym) =
+  discard SymTabAddUnique(c.tab, sym)
+
 proc addDeclAt*(c: PContext, sym: PSym, at: Natural) = 
   if SymTabAddUniqueAt(c.tab, sym, at) == Failure: 
     LocalError(sym.info, errAttemptToRedefine, sym.Name.s)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index ff153c1c2..50afd47f9 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -92,7 +92,8 @@ type
     errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, 
     errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, 
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
-    errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errWrongSymbolX,
+    errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
+    errThreadvarCannotInit, errWrongSymbolX,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -321,6 +322,7 @@ const
     errXhasSideEffects: "\'$1\' can have side effects", 
     errIteratorExpected: "iterator within for loop context expected",
     errLetNeedsInit: "'let' symbol requires an initialization",
+    errThreadvarCannotInit: "a thread var cannot be initialized explicitly",
     errWrongSymbolX: "usage of \'$1\' is a user-defined error", 
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
@@ -342,7 +344,7 @@ const
     warnWriteToForeignHeap: "write to foreign heap [WriteToForeignHeap]",
     warnUser: "$1 [User]", 
     hintSuccess: "operation successful [Success]", 
-    hintSuccessX: "operation successful ($1 lines compiled; $2 sec total) [SuccessX]", 
+    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", 
     hintLineTooLong: "line too long [LineTooLong]", 
     hintXDeclaredButNotUsed: "\'$1\' is declared but not used [XDeclaredButNotUsed]", 
     hintConvToBaseNotNeeded: "conversion to base object is not needed [ConvToBaseNotNeeded]", 
diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim
index 24dbc0617..a6918ce63 100755
--- a/compiler/nimrod.nim
+++ b/compiler/nimrod.nim
@@ -61,9 +61,9 @@ proc prependCurDir(f: string): string =
   else:
     result = f
 
-proc HandleCmdLine() = 
+proc HandleCmdLine() =
   var start = epochTime()
-  if paramCount() == 0: 
+  if paramCount() == 0:
     writeCommandLineUsage()
   else:
     # Process command line arguments:
@@ -85,13 +85,15 @@ proc HandleCmdLine() =
     ProcessCmdLine(passCmd2)
     MainCommand()
     if gVerbosity >= 2: echo(GC_getStatistics())
+    #echo(GC_getStatistics())
     if msgs.gErrorCounter == 0:
       when hasTinyCBackend:
         if gCmd == cmdRun:
           tccgen.run()
-      if gCmd notin {cmdInterpret, cmdRun}: 
-        rawMessage(hintSuccessX, [$gLinesCompiled, 
-                   formatFloat(epochTime() - start, ffDecimal, 3)])
+      if gCmd notin {cmdInterpret, cmdRun}:
+        rawMessage(hintSuccessX, [$gLinesCompiled,
+                   formatFloat(epochTime() - start, ffDecimal, 3),
+                   formatSize(getTotalMem())])
       if optRun in gGlobalOptions:
         var ex = quoteIfContainsWhite(
             changeFileExt(gProjectFull, "").prependCurDir)
diff --git a/compiler/parser.nim b/compiler/parser.nim
index e3bf3a748..ab715f029 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -263,15 +263,16 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
     optInd(p, a)
   eat(p, endTok)
 
+proc newDotExpr(p: var TParser, a: PNode): PNode =
+  getTok(p)
+  optInd(p, a)
+  result = newNodeI(nkDotExpr, a.info)
+  addSon(result, a)
+  addSon(result, parseSymbol(p))
+
 proc qualifiedIdent(p: var TParser): PNode = 
   result = parseSymbol(p)     #optInd(p, result);
-  if p.tok.tokType == tkDot: 
-    getTok(p)
-    optInd(p, result)
-    var a = result
-    result = newNodeI(nkDotExpr, a.info)
-    addSon(result, a)
-    addSon(result, parseSymbol(p))
+  if p.tok.tokType == tkDot: result = newDotExpr(p, result)
 
 proc qualifiedIdentListAux(p: var TParser, endTok: TTokType, result: PNode) = 
   getTok(p)
@@ -465,13 +466,8 @@ proc primary(p: var TParser): PNode =
       result = newNodeP(nkCall, p)
       addSon(result, a)
       exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result)
-    of tkDot: 
-      var a = result
-      result = newNodeP(nkDotExpr, p)
-      addSon(result, a)
-      getTok(p)               # skip '.'
-      optInd(p, result)
-      addSon(result, parseSymbol(p))
+    of tkDot:
+      result = newDotExpr(p, result)
       result = parseGStrLit(p, result)
     of tkBracketLe: 
       result = indexExprList(p, result, nkBracketExpr, tkBracketRi)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index a291d4741..9877d4c52 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -147,9 +147,6 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
       s.magic = m
       break
   if s.magic == mNone: Message(n.info, warnUnknownMagic, v)
-  # magics don't need an implementation, so we
-  # treat them as imported, instead of modifing a lot of working code:
-  incl(s.flags, sfImportc)
 
 proc wordToCallConv(sw: TSpecialWord): TCallingConvention = 
   # this assumes that the order of special words and calling conventions is
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index a81347eda..216b5674d 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -144,13 +144,15 @@ proc semGenericStmt(c: PContext, n: PNode,
     var L = sonsLen(n)
     openScope(c.tab)
     n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, toBind)
-    for i in countup(0, L - 3): addDecl(c, newSymS(skUnknown, n.sons[i], c))
+    for i in countup(0, L - 3):
+      addPrelimDecl(c, newSymS(skUnknown, n.sons[i], c))
     n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, toBind)
     closeScope(c.tab)
   of nkBlockStmt, nkBlockExpr, nkBlockType: 
     checkSonsLen(n, 2)
     openScope(c.tab)
-    if n.sons[0].kind != nkEmpty: addDecl(c, newSymS(skUnknown, n.sons[0], c))
+    if n.sons[0].kind != nkEmpty: 
+      addPrelimDecl(c, newSymS(skUnknown, n.sons[0], c))
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, toBind)
     closeScope(c.tab)
   of nkTryStmt: 
@@ -174,7 +176,7 @@ proc semGenericStmt(c: PContext, n: PNode,
                                    toBind)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, toBind)
       for j in countup(0, L-3):
-        addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
+        addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
   of nkGenericParams: 
     for i in countup(0, sonsLen(n) - 1): 
       var a = n.sons[i]
@@ -185,14 +187,14 @@ proc semGenericStmt(c: PContext, n: PNode,
                                    toBind) 
       # do not perform symbol lookup for default expressions 
       for j in countup(0, L-3): 
-        addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
+        addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
   of nkConstSection: 
     for i in countup(0, sonsLen(n) - 1): 
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue 
       if (a.kind != nkConstDef): IllFormedAst(a)
       checkSonsLen(a, 3)
-      addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
+      addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
       a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, toBind)
       a.sons[2] = semGenericStmt(c, a.sons[2], flags, toBind)
   of nkTypeSection: 
@@ -201,7 +203,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       if a.kind == nkCommentStmt: continue 
       if (a.kind != nkTypeDef): IllFormedAst(a)
       checkSonsLen(a, 3)
-      addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
+      addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[0]), c))
     for i in countup(0, sonsLen(n) - 1): 
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue 
@@ -240,17 +242,17 @@ proc semGenericStmt(c: PContext, n: PNode,
                                    toBind)
       a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, toBind)
       for j in countup(0, L-3): 
-        addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
+        addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
   of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, 
      nkIteratorDef, nkLambda: 
     checkSonsLen(n, bodyPos + 1)
-    addDecl(c, newSymS(skUnknown, getIdentNode(n.sons[0]), c))
+    addPrelimDecl(c, newSymS(skUnknown, getIdentNode(n.sons[0]), c))
     openScope(c.tab)
     n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], 
                                               flags, toBind)
     if n.sons[paramsPos].kind != nkEmpty: 
       if n.sons[paramsPos].sons[0].kind != nkEmpty: 
-        addDecl(c, newSym(skUnknown, getIdent("result"), nil))
+        addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil))
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, toBind)
     n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, toBind)
     var body: PNode
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 29bf48c19..d8336fc94 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -269,7 +269,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       if def != nil and def.kind != nkEmpty:
         # this is only needed for the evaluation pass:
         v.ast = def
-      if a.kind != nkVarTuple: 
+        if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
+      if a.kind != nkVarTuple:
         v.typ = typ
         b = newNodeI(nkIdentDefs, a.info)
         addSon(b, newSymNode(v))
@@ -733,7 +734,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       n.sons[bodyPos] = ast.emptyNode
   else: 
     if proto != nil: LocalError(n.info, errImplOfXexpected, proto.name.s)
-    if {sfImportc, sfBorrow} * s.flags == {}: incl(s.flags, sfForward)
+    if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: 
+      incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
   closeScope(c.tab)           # close scope for parameters
@@ -800,7 +802,7 @@ proc evalInclude(c: PContext, n: PNode): PNode =
     var f = checkModuleName(n.sons[i])
     var fileIndex = f.fileInfoIdx
     if ContainsOrIncl(c.includedFiles, fileIndex): 
-      GlobalError(n.info, errRecursiveDependencyX, f)
+      GlobalError(n.info, errRecursiveDependencyX, f.shortenDir)
     addSon(result, semStmt(c, gIncludeFile(f)))
     Excl(c.includedFiles, fileIndex)
   
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 78a95c56b..b71f230eb 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -781,7 +781,11 @@ proc semGenericConstraints(c: PContext, n: PNode, result: PType) =
     semGenericConstraints(c, n.sons[1], result)
     semGenericConstraints(c, n.sons[2], result)
   else:
-    result.addSon(semTypeNode(c, n, nil))
+    var x = semTypeNode(c, n, nil)
+    if x.kind in StructuralEquivTypes and sonsLen(x) == 0:
+      x = newConstraint(c, x.kind)
+      #echo "came here for: ", typeToString(x)
+    result.addSon(x)
 
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = 
   result = copyNode(n)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index a6bde6e40..9a8f3cee1 100755
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -388,7 +388,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
   of tyGenericInst: 
     result = typeRel(mapping, lastSon(f), a)
   of tyGenericBody: 
-    result = typeRel(mapping, lastSon(f), a)
+    let ff = lastSon(f)
+    if ff != nil: result = typeRel(mapping, ff, a)
   of tyGenericInvokation: 
     assert(f.sons[0].kind == tyGenericBody)
     if a.kind == tyGenericInvokation: 
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 86d91bc1c..971ff4d32 100755
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -10,7 +10,8 @@
 ## This file implements features required for IDE support.
 
 import 
-  lexer, idents, ast, astalgo, semdata, msgs, types, sigmatch, options
+  lexer, idents, ast, astalgo, semdata, msgs, types, sigmatch, options, 
+  renderer
 
 const
   sep = '\t'
@@ -150,27 +151,25 @@ proc suggestFieldAccess(c: PContext, n: PNode) =
     else:
       suggestOperations(c, n, typ)
 
-proc findClosestDot(n: PNode): PNode = 
-  if msgs.inCheckpoint(n.info) == cpExact: 
+proc findClosestDot(n: PNode): PNode =
+  if n.kind == nkDotExpr and msgs.inCheckpoint(n.info) == cpExact:
     result = n
-  elif n.kind notin {nkNone..nkNilLit}:
-    for i in 0.. <sonsLen(n):
-      if n.sons[i].kind == nkDotExpr:
-        result = findClosestDot(n.sons[i])
-        if result != nil: return
+  else:
+    for i in 0.. <safeLen(n):
+      result = findClosestDot(n.sons[i])
+      if result != nil: return
 
 const
   CallNodes = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit,
                nkMacroStmt}
 
 proc findClosestCall(n: PNode): PNode = 
-  if msgs.inCheckpoint(n.info) == cpExact: 
+  if n.kind in callNodes and msgs.inCheckpoint(n.info) == cpExact: 
     result = n
-  elif n.kind notin {nkNone..nkNilLit}:
-    for i in 0.. <sonsLen(n):
-      if n.sons[i].kind in callNodes:
-        result = findClosestCall(n.sons[i])
-        if result != nil: return
+  else:
+    for i in 0.. <safeLen(n):
+      result = findClosestCall(n.sons[i])
+      if result != nil: return
 
 proc findClosestSym(n: PNode): PNode = 
   if n.kind == nkSym and msgs.inCheckpoint(n.info) == cpExact: 
diff --git a/compiler/types.nim b/compiler/types.nim
index d3f2bd1b5..8bce4fc5f 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -277,7 +277,7 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
   result = analyseObjectWithTypeFieldAux(t, marker)
 
 proc isGBCRef(t: PType): bool = 
-  result = t.kind in {tyRef, tySequence, tyString}
+  result = t.kind in GcTypeKinds
 
 proc containsGarbageCollectedRef(typ: PType): bool = 
   # returns true if typ contains a reference, sequence or string (all the
diff --git a/doc/lib.txt b/doc/lib.txt
index 775223fc1..8e18ae095 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -63,6 +63,9 @@ Collections and algorithms
   Implementation of a queue. The underlying implementation uses a ``seq``.
 * `intsets <intsets.html>`_
   Efficient implementation of a set of ints as a sparse bit set.
+* `critbits <critbits.html>`_
+  This module implements a *crit bit tree* which is an efficient
+  container for a set or a mapping of strings.
 * `sequtils <sequtils.html>`_
   This module implements operations for the built-in seq type
   which were inspired by functional programming languages.
@@ -108,6 +111,9 @@ String handling
 * `matchers <matchers.html>`_
   This module contains various string matchers for email addresses, etc.
 
+* `subexes <subexes.html>`_
+  This module implements advanted string substitution operations.
+
 
 Generic Operating System Services
 ---------------------------------
diff --git a/doc/subexes.txt b/doc/subexes.txt
new file mode 100644
index 000000000..3565dbf43
--- /dev/null
+++ b/doc/subexes.txt
@@ -0,0 +1,58 @@
+================================
+Substitution Expressions (subex)
+================================
+
+A *subex* (*Substitution Expression*) represents an advanted string 
+substitution. In contrast to a `regex`:idx: which deals with string analysis, a
+*subex* deals with string synthesis.
+
+Thanks to its conditional construct ``$[0|1|2|else]`` it supports 
+`internationalization`:idx: of format string literals quite well.
+
+
+=====================   =====================================================
+Notation                meaning
+=====================   =====================================================
+``$#``                  use first or next argument
+``$name``               use named argument
+``$1``                  use first argument
+``$-1``                 use last argument
+``${1..3}``             use arguments 1 to 3
+``${..}``               use all arguments
+``${#..}``              use all remaining arguments
+``${..-2}``             use all arguments except the last argument
+``${$1}``               use argument X where ``X = parseInt(arg[1])``
+``${$1..$2}``           use arguments X to Y where ``X = parseInt(arg[1])``
+                        and ``Y = parseInt(arg[2])``
+``$','{1..3}``          use arguments 1 to 3 and join them with ','
+``$','80c'\n'{..}``     use all arguments, join them with ','. Insert '\\n'
+                        before the resulting string exceeds 80 chars.
+``$','8i'\n'{..}``      use all arguments, join them with ','. Insert '\\n'
+                        after every 8th item.
+``$' '~{1..3}``         use arguments 1 to 3 with a leading space if the
+                        concatenation of ``1..3`` is not the empty string
+``$[zero|one|def]1``    use ``X = parseInt(arg[1])`` to determine which
+                        branch to use. If ``X == 0`` the 'zero' branch is
+                        selected, if ``X == 1`` the 'one' branch is
+                        selected, etc. Otherwise the 'def' branch is 
+                        selected. ``$x`` is interpreted in branches too.
+                        If a branch needs to contain ``|``, ``]`` put
+                        them in single quotes. To produce a verbatim single
+                        quote, use ``''``.
+=====================   =====================================================
+
+Examples
+========
+
+.. code-block:: nimrod
+
+  subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
+  
+  subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
+  
+  subex"$['''|'|''''|']']#" % "0" == "'|"
+  
+  subex("type\n  TEnum = enum\n    $', '40c'\n    '{..}") % [
+    "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
+    
+
diff --git a/koch.nim b/koch.nim
index d3caa27df..bd16dedac 100755
--- a/koch.nim
+++ b/koch.nim
@@ -11,7 +11,9 @@ when defined(gcc) and defined(windows):
   {.link: "icons/koch.res".}
 
 import
-  os, strutils, parseopt
+  os, strutils, parseopt, osproc, httpclient, streams
+when defined(haveZipLib):
+  import zipfiles
 
 const
   HelpText = """
@@ -34,6 +36,7 @@ Possible Commands:
   zip                      builds the installation ZIP package
   inno [options]           builds the Inno Setup installer (for Windows)
   tests                    run the testsuite
+  update                   updates nimrod to the latest version from the repo
 Boot options:
   -d:release               produce a release version of the compiler
   -d:tinyc                 include the Tiny C backend (not supported on Windows)
@@ -42,6 +45,8 @@ Boot options:
   -d:nativeStacktrace      use native stack traces (only for Mac OS X or Linux)
 """
 
+proc boot(args: string) # Forward declaration
+
 proc exe(f: string): string = return addFileExt(f, ExeExt)
 
 proc exec(cmd: string) =
@@ -78,6 +83,68 @@ proc web(args: string) =
   exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" %
        NimrodVersion)
 
+proc update(args: string) =
+  when defined(windows):
+    echo("Windows Users: Make sure to be running this in Bash. If you aren't, press CTRL+C now.")
+
+
+  var thisDir = getAppDir()
+  var git = findExe("git")
+  echo("Checking for git repo and git executable...")
+  if existsDir(thisDir & "/.git") and git != "":
+    echo("Git repo found!")
+    # use git to download latest source
+    echo("Checking for updates...")
+    discard startCmd(git & " fetch origin master")
+    var procs = startCmd(git & " diff origin/master master")
+    var errcode = procs.waitForExit()
+    var output = readLine(procs.outputStream)
+    echo(output)
+    if errcode == 0:
+      if output == "":
+        # No changes
+        echo("No update. Exiting..")
+        return
+      else:
+        echo("Fetching updates from repo...")
+        var pullout = execCmdEx(git & " pull origin master")
+        if pullout[1] != 0:
+          echo("An error has occured.")
+          return
+        else:
+          if pullout[0] == "Already up-to-date.\r\n":
+             echo("No new changes fetched from the repo. Local branch must be ahead of it. Exiting...")
+             return
+    else:
+        echo("An error has occured.")
+        return
+    
+  else:
+    echo("No repo or executable found!")
+    when defined(haveZipLib):
+      echo("Falling back.. Downloading source code from repo...")
+      # use dom96's httpclient to download zip
+      downloadFile("https://github.com/Araq/Nimrod/zipball/master",thisDir & "/update.zip")
+    
+      try:
+        echo("Extracting source code from archive...")
+        var zip :TZipArchive
+        discard open(zip,thisDir & "/update.zip", fmRead) # will add error checking later
+        extractAll(zip, thisDir & "/")
+      except:
+        echo("Error reading archive.")
+        return
+    else:
+      echo("No failback available. Exiting...")
+      return
+  
+  echo("Starting update...")
+  boot(args)
+  echo("Update complete!")
+
+
+
+
 # -------------- boot ---------------------------------------------------------
 
 const
@@ -204,6 +271,7 @@ of cmdArgument:
   of "inno": inno(op.cmdLineRest)
   of "install": install(op.cmdLineRest)
   of "test", "tests": tests(op.cmdLineRest)
+  of "update": update(op.cmdLineRest)
   else: showHelp()
 of cmdEnd: showHelp()
 
diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim
index bdefc2c93..1ab51fdd7 100755
--- a/lib/impure/zipfiles.nim
+++ b/lib/impure/zipfiles.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2008 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -142,3 +142,26 @@ iterator walkFiles*(z: var TZipArchive): string =
   while i < num:
     yield $zip_get_name(z.w, i, 0'i32)
     inc(i)
+
+
+proc extractFile*(z: var TZipArchive, srcFile: string, dest: PStream) =
+  ## extracts a file from the zip archive `z` to the destination stream.
+  var strm = getStream(z, srcFile)
+  while true:
+    if not strm.atEnd:
+        dest.write(strm.readStr(1))
+    else: break
+  dest.flush()
+  strm.close()
+
+proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
+  ## extracts a file from the zip archive `z` to the destination filename.
+  var file = newFileStream(dest, fmReadWrite)
+  extractFile(z, srcFile, file)
+  file.close()
+
+proc extractAll*(z: var TZipArchive, dest: string) =
+  ## extracts all files from archive `z` to the destination directory.
+  for file in walkFiles(z):
+    extractFile(z, file, dest / extractFilename(file))
+
diff --git a/lib/nimbase.h b/lib/nimbase.h
index bc8c3c28c..e2afed8f9 100755
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -16,6 +16,7 @@ __GNUC__
 __DMC__
 __POCC__
 __TINYC__
+__clang__
 */
 
 
@@ -437,4 +438,11 @@ __declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) {
 #  define unlikely(x) (x)
 #endif
 
+#if defined(__GNUC__) || defined(__clang__)
+static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
+#  define GC_GUARD __attribute__ ((cleanup(GCGuard)))
+#else
+#  define GC_GUARD
+#endif
+
 #endif
diff --git a/lib/pure/actors.nim b/lib/pure/actors.nim
index c07adfd93..d51b973b7 100644
--- a/lib/pure/actors.nim
+++ b/lib/pure/actors.nim
@@ -119,15 +119,25 @@ proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
 
 proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
   ## waits for every actor of `a` to finish with its work. Currently this is
-  ## implemented as polling every `polling` ms. This will change in a later
+  ## implemented as polling every `polling` ms and has a slight chance 
+  ## of failing since we check for every actor to be in `ready` state and not
+  ## for messages still in ether. This will change in a later
   ## version, however.
+  var allReadyCount = 0
   while true:
     var wait = false
     for i in 0..high(a.actors):
       if not a.actors[i].i.ready: 
         wait = true
+        allReadyCount = 0
         break
-    if not wait: break
+    if not wait:
+      # it's possible that some actor sent a message to some other actor but
+      # both appeared to be non-working as the message takes some time to
+      # arrive. We assume that this won't take longer than `polling` and
+      # simply attempt a second time and declare victory then. ;-)
+      inc allReadyCount
+      if allReadyCount > 1: break
     sleep(polling)
 
 proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
new file mode 100644
index 000000000..a18c42e58
--- /dev/null
+++ b/lib/pure/collections/critbits.nim
@@ -0,0 +1,302 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a `crit bit tree`:idx: which is an efficient
+## container for a set or a mapping of strings. Based on the excellent paper
+## by Adam Langley.
+
+type
+  TNode[T] = object {.pure, final, acyclic.}
+    byte: int ## byte index of the difference
+    otherbits: char
+    case isLeaf: bool
+    of false: child: array[0..1, ref TNode[T]]
+    of true: 
+      key: string
+      when T isnot void:
+        val: T
+    
+  PNode[T] = ref TNode[T]
+  TCritBitTree*[T] = object {.
+      pure, final.} ## The crit bit tree can either be used
+                    ## as a mapping from strings to
+                    ## some type ``T`` or as a set of
+                    ## strings if ``T`` is void.
+    root: PNode[T]
+    count: int
+    
+proc len*[T](c: TCritBitTree[T]): int =
+  ## returns the number of elements in `c` in O(1).
+  result = c.count
+
+proc rawGet[T](c: TCritBitTree[T], key: string): PNode[T] =
+  var it = c.root
+  while it != nil:
+    if not it.isLeaf:
+      let ch = if it.byte < key.len: key[it.byte] else: '\0'
+      let dir = (1 + (ch.ord or it.otherBits.ord)) shr 8
+      it = it.child[dir]
+    else:
+      return if it.key == key: it else: nil
+
+proc contains*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
+  ## returns true iff `c` contains the given `key`.
+  result = rawGet(c, key) != nil
+
+proc hasKey*[T](c: TCritBitTree[T], key: string): bool {.inline.} =
+  ## alias for `contains`.
+  result = rawGet(c, key) != nil
+
+proc rawInsert[T](c: var TCritBitTree[T], key: string): PNode[T] =
+  if c.root == nil:
+    new c.root
+    c.root.isleaf = true
+    c.root.key = key
+    result = c.root
+  else:
+    var it = c.root
+    while not it.isLeaf:
+      let ch = if it.byte < key.len: key[it.byte] else: '\0'
+      let dir = (1 + (ch.ord or it.otherBits.ord)) shr 8
+      it = it.child[dir]
+    
+    var newOtherBits = 0
+    var newByte = 0
+    block blockX:
+      while newbyte < key.len:
+        if it.key[newbyte] != key[newbyte]:
+          newotherbits = it.key[newbyte].ord xor key[newbyte].ord
+          break blockX
+        inc newbyte
+      if it.key[newbyte] != '\0':
+        newotherbits = it.key[newbyte].ord
+      else:
+        return it
+    while (newOtherBits and (newOtherBits-1)) != 0:
+      newOtherBits = newOtherBits and (newOtherBits-1)
+    newOtherBits = newOtherBits xor 255
+    let ch = it.key[newByte]
+    let dir = (1 + (ord(ch) or newOtherBits)) shr 8
+    
+    var inner: PNode[T]
+    new inner
+    new result
+    result.isLeaf = true
+    result.key = key
+    inner.otherBits = chr(newOtherBits)
+    inner.byte = newByte
+    inner.child[1 - dir] = result
+    
+    var wherep = addr(c.root)
+    while true:
+      var p = wherep[]
+      if p.isLeaf: break
+      if p.byte > newByte: break
+      if p.byte == newByte and p.otherBits.ord > newOtherBits: break
+      let ch = if p.byte < key.len: key[p.byte] else: '\0'
+      let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
+      wherep = addr(p.child[dir])
+    inner.child[dir] = wherep[]
+    wherep[] = inner
+  inc c.count
+
+proc containsOrIncl*[T](c: var TCritBitTree[T], key: string, val: T): bool =
+  ## returns true iff `c` contains the given `key`. If the key does not exist
+  ## ``c[key] = val`` is performed.
+  let oldCount = c.count
+  var n = rawInsert(c, key)
+  result = c.count == oldCount
+  when T isnot void:
+    if not result: n.val = val
+
+proc containsOrIncl*(c: var TCritBitTree[void], key: string): bool =
+  ## returns true iff `c` contains the given `key`. If the key does not exist
+  ## it is inserted into `c`.
+  let oldCount = c.count
+  var n = rawInsert(c, key)
+  result = c.count == oldCount
+
+proc incl*(c: var TCritBitTree[void], key: string) =
+  ## includes `key` in `c`.
+  discard rawInsert(c, key)
+
+proc `[]=`*[T](c: var TCritBitTree[T], key: string, val: T) =
+  ## puts a (key, value)-pair into `t`.
+  var n = rawInsert(c, key)
+  n.val = val
+
+proc `[]`*[T](c: var TCritBitTree[T], key: string): T {.inline.} =
+  ## retrieves the value at ``c[key]``. If `key` is not in `t`,
+  ## default empty value for the type `B` is returned
+  ## and no exception is raised. One can check with ``hasKey`` whether the key
+  ## exists.
+  let n = rawGet(c, key)
+  if n != nil: result = n.val
+
+proc mget*[T](c: var TCritBitTree[T], key: string): var T {.inline.} =
+  ## retrieves the value at ``c[key]``. The value can be modified.
+  ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
+  let n = rawGet(c, key)
+  if n != nil: result = n.val
+  else: raise newException(EInvalidKey, "key not found: " & $key)
+
+proc excl*[T](c: var TCritBitTree[T], key: string) =
+  ## removes `key` (and its associated value) from the set `c`.
+  ## If the `key` does not exist, nothing happens.
+  var p = c.root
+  var wherep = addr(c.root)
+  var whereq: ptr PNode = nil
+  if p == nil: return
+  var dir = 0
+  var q: PNode
+  while not p.isLeaf:
+    whereq = wherep
+    q = p
+    let ch = if p.byte < key.len: key[p.byte] else: '\0'
+    dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
+    wherep = addr(p.child[dir])
+    p = wherep[]
+  if p.key == key:
+    # else: not in tree at all
+    if whereq == nil:
+      c.root = nil
+    else:
+      whereq[] = q.child[1 - dir]
+    dec c.count
+
+iterator leaves[T](n: PNode[T]): PNode[T] =
+  if n != nil:
+    # XXX actually we could compute the necessary stack size in advance:
+    # it's rougly log2(c.count).
+    var stack = @[n]
+    while stack.len > 0: 
+      var it = stack.pop
+      while not it.isLeaf:
+        stack.add(it.child[1])
+        it = it.child[0]
+        assert(it != nil)
+      yield it
+
+iterator keys*[T](c: TCritBitTree[T]): string =
+  ## yields all keys in lexicographical order.
+  for x in leaves(c.root): yield x.key
+
+iterator values*[T](c: TCritBitTree[T]): T =
+  ## yields all values of `c` in the lexicographical order of the
+  ## corresponding keys.
+  for x in leaves(c.root): yield x.val
+
+iterator mvalues*[T](c: var TCritBitTree[T]): var T =
+  ## yields all values of `c` in the lexicographical order of the
+  ## corresponding keys. The values can be modified.
+  for x in leaves(c.root): yield x.val
+
+iterator items*[T](c: TCritBitTree[T]): string =
+  ## yields all keys in lexicographical order.
+  for x in leaves(c.root): yield x.key
+
+iterator pairs*[T](c: TCritBitTree[T]): tuple[key: string, val: T] =
+  ## yields all (key, value)-pairs of `c`.
+  for x in leaves(c.root): yield (x.key, x.val)
+  
+iterator mpairs*[T](c: var TCritBitTree[T]): tuple[key: string, val: var T] =
+  ## yields all (key, value)-pairs of `c`. The yielded values can be modified.
+  for x in leaves(c.root): yield (x.key, x.val)
+
+proc allprefixedAux[T](c: TCritBitTree[T], key: string): PNode[T] =
+  var p = c.root
+  var top = p
+  if p != nil:
+    while not p.isLeaf:
+      var q = p
+      let ch = if p.byte < key.len: key[p.byte] else: '\0'
+      let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
+      p = p.child[dir]
+      if q.byte < key.len: top = p
+    for i in 0 .. <key.len:
+      if p.key[i] != key[i]: return
+    result = top
+
+iterator itemsWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
+  ## yields all keys starting with `prefix`.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.key
+
+iterator keysWithPrefix*[T](c: TCritBitTree[T], prefix: string): string =
+  ## yields all keys starting with `prefix`.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.key
+
+iterator valuesWithPrefix*[T](c: TCritBitTree[T], prefix: string): T =
+  ## yields all values of `c` starting with `prefix` of the
+  ## corresponding keys.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.val
+
+iterator mvaluesWithPrefix*[T](c: var TCritBitTree[T], prefix: string): var T =
+  ## yields all values of `c` starting with `prefix` of the
+  ## corresponding keys. The values can be modified.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.val
+
+iterator pairsWithPrefix*[T](c: TCritBitTree[T],
+                             prefix: string): tuple[key: string, val: T] =
+  ## yields all (key, value)-pairs of `c` starting with `prefix`.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield (x.key, x.val)
+  
+iterator mpairsWithPrefix*[T](c: var TCritBitTree[T],
+                              prefix: string): tuple[key: string, val: var T] =
+  ## yields all (key, value)-pairs of `c` starting with `prefix`.
+  ## The yielded values can be modified.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield (x.key, x.val)
+
+proc `$`*[T](c: TCritBitTree[T]): string =
+  ## turns `c` into a string representation. Example outputs:
+  ## ``{keyA: value, keyB: value}``, ``{:}``
+  ## If `T` is void the outputs look like:
+  ## ``{keyA, keyB}``, ``{}``.
+  if c.len == 0:
+    when T is void:
+      result = "{}"
+    else:
+      result = "{:}"
+  else:
+    # an educated guess is better than nothing:
+    when T is void:
+      const avgItemLen = 8
+    else:
+      const avgItemLen = 16
+    result = newStringOfCap(c.count * avgItemLen)
+    result.add("{")
+    for key, val in pairs(c):
+      if result.len > 1: result.add(", ")
+      result.add($key)
+      when T isnot void:
+        result.add(": ")
+        result.add($val)
+    result.add("}")
+
+when isMainModule:
+  var r: TCritBitTree[void]
+  r.incl "abc"
+  r.incl "xyz"
+  r.incl "def"
+  r.incl "definition"
+  r.incl "prefix"
+  doAssert r.contains"def"
+  #r.del "def"
+
+  for w in r.items:
+    echo w
+    
+  for w in r.allPrefixed("de"):
+    echo w
+
diff --git a/lib/pure/events.nim b/lib/pure/events.nim
index a3a609b20..c3c4cb0dc 100644
--- a/lib/pure/events.nim
+++ b/lib/pure/events.nim
@@ -12,25 +12,27 @@
 ## This module implements an event system that is not dependant on external
 ## graphical toolkits. It was originally called ``NimEE`` because 
 ## it was inspired by Python's PyEE module. There are two ways you can use events: one is a python-inspired way; the other is more of a C-style way.
+##
 ## .. code-block:: Nimrod
-##   var ee = initEventEmitter()
-##   var genericargs: TEventArgs
-##   proc handleevent(e: TEventArgs) =
-##       echo("Handled!")
+##    var ee = initEventEmitter()
+##    var genericargs: TEventArgs
+##    proc handleevent(e: TEventArgs) =
+##        echo("Handled!")
 ##
-##   # Python way
-##   ee.on("EventName", handleevent)
-##   ee.emit("EventName", genericargs)
+##    # Python way
+##    ee.on("EventName", handleevent)
+##    ee.emit("EventName", genericargs)
 ## 
-##   # C/Java way
-##   # Declare a type
-##   type
-##       TSomeObject = object of TObject
-##           SomeEvent: TEventHandler
-##   var myobj: TSomeObject
-##   myobj.SomeEvent = initEventHandler("SomeEvent")
-##   myobj.SomeEvent.addHandler(handleevent)
-##   ee.emit(myobj.SomeEvent, genericargs)
+##    # C/Java way
+##    # Declare a type
+##    type
+##        TSomeObject = object of TObject
+##            SomeEvent: TEventHandler
+##    var myobj: TSomeObject
+##    myobj.SomeEvent = initEventHandler("SomeEvent")
+##    myobj.SomeEvent.addHandler(handleevent)
+##    ee.emit(myobj.SomeEvent, genericargs)
+
 type
   TEventArgs* = object of TObject ## Base object for event arguments that are passed to callback functions.
   TEventHandler* = tuple[name: string, handlers: seq[proc(e:TEventArgs)]] ## An eventhandler for an event.
@@ -57,6 +59,11 @@ proc removeHandler*(handler: var TEventHandler, func: proc(e: TEventArgs)) =
       handler.handlers.del(i)
       break
     
+proc containsHandler*(handler: var TEventHandler, func: proc(e: TEventArgs)): bool =
+  ## Checks if a callback is registered to this event handler.
+  return handler.handlers.contains(func)
+
+
 proc clearHandlers*(handler: var TEventHandler) =
   ## Clears all of the callbacks from the event handler.
   setLen(handler.handlers, 0)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index f22a53dd8..f96998831 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -554,7 +554,7 @@ proc cmpPaths*(pathA, pathB: string): int {.
 proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
   ## Checks whether a given `path` is absolute.
   ##
-  ## on Windows, network paths are considered absolute too.
+  ## On Windows, network paths are considered absolute too.
   when doslike:
     var len = len(path)
     result = (len > 1 and path[0] in {'/', '\\'}) or
@@ -694,8 +694,8 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
 # iterator depends on ``environment``.
 
 var
-  envComputed: bool = false
-  environment: seq[string] = @[]
+  envComputed {.threadvar.}: bool
+  environment {.threadvar.}: seq[string]
 
 when defined(windows):
   # because we support Windows GUI applications, things get really
@@ -705,6 +705,7 @@ when defined(windows):
 
   proc getEnvVarsC() =
     if not envComputed:
+      environment = @[]
       var
         env = getEnvironmentStringsA()
         e = env
@@ -738,6 +739,7 @@ else:
   proc getEnvVarsC() =
     # retrieves the variables of char** env of C's main proc
     if not envComputed:
+      environment = @[]
       when useNSGetEnviron:
         var gEnv = NSGetEnviron()[]
       var i = 0
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index dc107b382..510dff232 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -102,7 +102,7 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
   ## returns `p`'s process ID.
   return p.id
 
-proc waitForExit*(p: PProcess): int {.rtl, extern: "nosp$1".}
+proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, extern: "nosp$1".}
   ## waits for the process to finish and returns `p`'s error code.
 
 proc peekExitCode*(p: PProcess): int
@@ -382,8 +382,9 @@ when defined(Windows) and not defined(useNimRtl):
     if running(p):
       discard TerminateProcess(p.FProcessHandle, 0)
 
-  proc waitForExit(p: PProcess): int =
-    discard WaitForSingleObject(p.FProcessHandle, Infinite)
+  proc waitForExit(p: PProcess, timeout: int = -1): int =
+    discard WaitForSingleObject(p.FProcessHandle, timeout)
+
     var res: int32
     discard GetExitCodeProcess(p.FProcessHandle, res)
     result = res
@@ -640,7 +641,7 @@ elif not defined(useNimRtl):
         if kill(-p.id, SIGKILL) != 0'i32: OSError()
     else: OSError()
 
-  proc waitForExit(p: PProcess): int =
+  proc waitForExit(p: PProcess, timeout: int = -1): int =
     #if waitPid(p.id, p.exitCode, 0) == int(p.id):
     # ``waitPid`` fails if the process is not running anymore. But then
     # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 46dcb3849..6d4544425 100755
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -140,105 +140,6 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
 

 {.pop.}

 

-proc findNormalized(x: string, inArray: openarray[string]): int =

-  var i = 0

-  while i < high(inArray):

-    if cmpIgnoreStyle(x, inArray[i]) == 0: return i

-    inc(i, 2) # incrementing by 1 would probably lead to a

-              # security hole...

-  return -1

-

-proc addf*(s: var string, formatstr: string, a: openarray[string]) {.

-  noSideEffect, rtl, extern: "nsuAddf".} =

-  ## The same as ``add(s, formatstr % a)``, but more efficient.

-  const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}

-  var i = 0

-  var num = 0

-  while i < len(formatstr):

-    if formatstr[i] == '$':

-      case formatstr[i+1] # again we use the fact that strings

-                          # are zero-terminated here

-      of '#':

-        add s, a[num]

-        inc i, 2

-        inc num

-      of '$':

-        add s, '$'

-        inc(i, 2)

-      of '1'..'9':

-        var j = 0

-        inc(i) # skip $

-        while formatstr[i] in Digits:

-          j = j * 10 + ord(formatstr[i]) - ord('0')

-          inc(i)

-        add s, a[j - 1]

-      of '{':

-        var j = i+1

-        while formatstr[j] notin {'\0', '}'}: inc(j)

-        var x = findNormalized(substr(formatstr, i+2, j-1), a)

-        if x >= 0 and x < high(a): add s, a[x+1]

-        else: raise newException(EInvalidValue, "invalid format string")

-        i = j+1

-      of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':

-        var j = i+1

-        while formatstr[j] in PatternChars: inc(j)

-        var x = findNormalized(substr(formatstr, i+1, j-1), a)

-        if x >= 0 and x < high(a): add s, a[x+1]

-        else: raise newException(EInvalidValue, "invalid format string")

-        i = j

-      else: raise newException(EInvalidValue, "invalid format string")

-    else:

-      add s, formatstr[i]

-      inc(i)

-

-proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect,

-  rtl, extern: "nsuFormatOpenArray".} =

-  ## The `substitution`:idx: operator performs string substitutions in

-  ## `formatstr` and returns a modified `formatstr`. This is often called

-  ## `string interpolation`:idx:.

-  ##

-  ## This is best explained by an example:

-  ##

-  ## .. code-block:: nimrod

-  ##   "$1 eats $2." % ["The cat", "fish"]

-  ##

-  ## Results in:

-  ##

-  ## .. code-block:: nimrod

-  ##   "The cat eats fish."

-  ##

-  ## The substitution variables (the thing after the ``$``) are enumerated

-  ## from 1 to ``a.len``.

-  ## To produce a verbatim ``$``, use ``$$``.

-  ## The notation ``$#`` can be used to refer to the next substitution variable:

-  ##

-  ## .. code-block:: nimrod

-  ##   "$# eats $#." % ["The cat", "fish"]

-  ##

-  ## Substitution variables can also be words (that is

-  ## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even

-  ## indices are keys and with odd indices are the corresponding values.

-  ## An example:

-  ##

-  ## .. code-block:: nimrod

-  ##   "$animal eats $food." % ["animal", "The cat", "food", "fish"]

-  ##

-  ## Results in:

-  ##

-  ## .. code-block:: nimrod

-  ##   "The cat eats fish."

-  ##

-  ## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is

-  ## raised if an ill-formed format string has been passed to the `%` operator.

-  result = newStringOfCap(formatstr.len + a.len shl 4)

-  addf(result, formatstr, a)

-

-proc `%` *(formatstr, a: string): string {.noSideEffect,

-  rtl, extern: "nsuFormatSingleElem".} =

-  ## This is the same as ``formatstr % [a]``.

-  result = newStringOfCap(formatstr.len + a.len)

-  addf(result, formatstr, [a])

-

 proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,

   rtl, extern: "nsuStrip".} =

   ## Strips whitespace from `s` and returns the resulting string.

@@ -467,16 +368,16 @@ proc ParseHexInt*(s: string): int {.noSideEffect, procvar,
       inc(i)

     of '\0': break

     else: raise newException(EInvalidValue, "invalid integer: " & s)

-
-proc parseBool*(s: string): bool =
-  ## Parses a value into a `bool`. If ``s`` is one of the following values:
-  ## ``y, yes, true, 1, on``, then returns `true`. If ``s`` is one of the
-  ## following values: ``n, no, false, 0, off``, then returns `false`.
-  ## If ``s`` is something else a ``EInvalidValue`` exception is raised.
-  case normalize(s)
-  of "y", "yes", "true", "1", "on": result = true
-  of "n", "no", "false", "0", "off": result = false
-  else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s)
+

+proc parseBool*(s: string): bool =

+  ## Parses a value into a `bool`. If ``s`` is one of the following values:

+  ## ``y, yes, true, 1, on``, then returns `true`. If ``s`` is one of the

+  ## following values: ``n, no, false, 0, off``, then returns `false`.

+  ## If ``s`` is something else a ``EInvalidValue`` exception is raised.

+  case normalize(s)

+  of "y", "yes", "true", "1", "on": result = true

+  of "n", "no", "false", "0", "off": result = false

+  else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s)

 

 proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,

   rtl, extern: "nsuRepeatChar".} =

@@ -709,7 +610,7 @@ proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
   rtl, extern: "nsuFindStr".} =

   ## Searches for `sub` in `s` starting at position `start`. Searching is

   ## case-sensitive. If `sub` is not in `s`, -1 is returned.

-  var a: TSkipTable

+  var a {.noinit.}: TSkipTable

   preprocessSub(sub, a)

   result = findAux(s, sub, start, a)

 

@@ -752,7 +653,7 @@ proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
 proc replace*(s, sub: string, by = ""): string {.noSideEffect,

   rtl, extern: "nsuReplaceStr".} =

   ## Replaces `sub` in `s` by the string `by`.

-  var a: TSkipTable

+  var a {.noinit.}: TSkipTable

   result = ""

   preprocessSub(sub, a)

   var i = 0

@@ -921,7 +822,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
   inc(len2)

   var half = len1 shr 1

   # initalize first row:

-  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2 * sizeof(int)))

+  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))

   var row: seq[int]

   newSeq(row, len2)

   var e = s + len2 - 1 # end marker

@@ -999,7 +900,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
   ## after the decimal point for Nimrod's ``biggestFloat`` type.

   const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e']

   var

-    frmtstr: array[0..5, char]

+    frmtstr {.noinit.}: array[0..5, char]

     buf: array[0..80, char]

   frmtstr[0] = '%'

   frmtstr[1] = '#'

@@ -1028,16 +929,150 @@ proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
   ## after the decimal point for Nimrod's ``float`` type.

   result = formatBiggestFloat(f, format, precision)

 

+proc formatSize*(bytes: biggestInt, decimalSep = '.'): string =

+  ## Rounds and formats `bytes`. Examples:

+  ##

+  ## .. code-block:: nimrod

+  ##

+  ##    formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"

+  ##    formatSize(4096) == "4KB"

+  ##

+  template frmt(a, b, c: expr): expr =

+    let bs = $b

+    insertSep($a) & decimalSep & bs.substr(0, 2) & c

+  let gigabytes = bytes shr 30

+  let megabytes = bytes shr 20

+  let kilobytes = bytes shr 10

+  if gigabytes != 0:

+    result = frmt(gigabytes, megabytes, "GB")

+  elif megabytes != 0:

+    result = frmt(megabytes, kilobytes, "MB")

+  elif kilobytes != 0:

+    result = frmt(kilobytes, bytes, "KB")

+  else:

+    result = insertSep($bytes) & "B"

+

+proc findNormalized(x: string, inArray: openarray[string]): int =

+  var i = 0

+  while i < high(inArray):

+    if cmpIgnoreStyle(x, inArray[i]) == 0: return i

+    inc(i, 2) # incrementing by 1 would probably lead to a

+              # security hole...

+  return -1

+

+proc addf*(s: var string, formatstr: string, a: openarray[string]) {.

+  noSideEffect, rtl, extern: "nsuAddf".} =

+  ## The same as ``add(s, formatstr % a)``, but more efficient.

+  const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}

+  var i = 0

+  var num = 0

+  while i < len(formatstr):

+    if formatstr[i] == '$':

+      case formatstr[i+1] # again we use the fact that strings

+                          # are zero-terminated here

+      of '#':

+        add s, a[num]

+        inc i, 2

+        inc num

+      of '$':

+        add s, '$'

+        inc(i, 2)

+      of '1'..'9', '-':

+        var j = 0

+        inc(i) # skip $

+        var negative = formatstr[i] == '-'

+        if negative: inc i

+        while formatstr[i] in Digits:

+          j = j * 10 + ord(formatstr[i]) - ord('0')

+          inc(i)

+        if not negative:

+          add s, a[j - 1]

+        else:

+          add s, a[a.len - j]

+      of '{':

+        var j = i+1

+        while formatstr[j] notin {'\0', '}'}: inc(j)

+        var x = findNormalized(substr(formatstr, i+2, j-1), a)

+        if x >= 0 and x < high(a): add s, a[x+1]

+        else: raise newException(EInvalidValue, "invalid format string")

+        i = j+1

+      of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':

+        var j = i+1

+        while formatstr[j] in PatternChars: inc(j)

+        var x = findNormalized(substr(formatstr, i+1, j-1), a)

+        if x >= 0 and x < high(a): add s, a[x+1]

+        else: raise newException(EInvalidValue, "invalid format string")

+        i = j

+      else:

+        raise newException(EInvalidValue, "invalid format string")

+    else:

+      add s, formatstr[i]

+      inc(i)

+

+proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect,

+  rtl, extern: "nsuFormatOpenArray".} =

+  ## The `substitution`:idx: operator performs string substitutions in

+  ## `formatstr` and returns a modified `formatstr`. This is often called

+  ## `string interpolation`:idx:.

+  ##

+  ## This is best explained by an example:

+  ##

+  ## .. code-block:: nimrod

+  ##   "$1 eats $2." % ["The cat", "fish"]

+  ##

+  ## Results in:

+  ##

+  ## .. code-block:: nimrod

+  ##   "The cat eats fish."

+  ##

+  ## The substitution variables (the thing after the ``$``) are enumerated

+  ## from 1 to ``a.len``.

+  ## To produce a verbatim ``$``, use ``$$``.

+  ## The notation ``$#`` can be used to refer to the next substitution

+  ## variable:

+  ##

+  ## .. code-block:: nimrod

+  ##   "$# eats $#." % ["The cat", "fish"]

+  ##

+  ## Substitution variables can also be words (that is

+  ## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even

+  ## indices are keys and with odd indices are the corresponding values.

+  ## An example:

+  ##

+  ## .. code-block:: nimrod

+  ##   "$animal eats $food." % ["animal", "The cat", "food", "fish"]

+  ##

+  ## Results in:

+  ##

+  ## .. code-block:: nimrod

+  ##   "The cat eats fish."

+  ##

+  ## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is

+  ## raised if an ill-formed format string has been passed to the `%` operator.

+  result = newStringOfCap(formatstr.len + a.len shl 4)

+  addf(result, formatstr, a)

+

+proc `%` *(formatstr, a: string): string {.noSideEffect,

+  rtl, extern: "nsuFormatSingleElem".} =

+  ## This is the same as ``formatstr % [a]``.

+  result = newStringOfCap(formatstr.len + a.len)

+  addf(result, formatstr, [a])

+

 {.pop.}

 

 when isMainModule:

-  assert align("abc", 4) == " abc"

-  assert align("a", 0) == "a"

-  assert align("1232", 6) == "  1232"

+  doAssert align("abc", 4) == " abc"

+  doAssert align("a", 0) == "a"

+  doAssert align("1232", 6) == "  1232"

   echo wordWrap(""" this is a long text --  muchlongerthan10chars and here

                    it goes""", 10, false)

-  assert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"

-  assert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11"

+  doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"

+  doAssert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11"

+

+  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"

+  echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"

+  echo formatSize(1'i64 shl 31)

 

-  assert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"

+  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==

+           "The cat eats fish."

 

diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
new file mode 100644
index 000000000..363cf6d04
--- /dev/null
+++ b/lib/pure/subexes.nim
@@ -0,0 +1,380 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Nimrod support for `substitution expressions`:idx: (`subex`:idx:).
+##
+## .. include:: ../doc/subexes.txt
+##
+
+{.push debugger:off .} # the user does not want to trace a part
+                       # of the standard library!
+
+from strutils import parseInt, cmpIgnoreStyle, Digits
+include "system/inclrtl"
+
+
+proc findNormalized(x: string, inArray: openarray[string]): int =
+  var i = 0
+  while i < high(inArray):
+    if cmpIgnoreStyle(x, inArray[i]) == 0: return i
+    inc(i, 2) # incrementing by 1 would probably lead to a
+              # security hole...
+  return -1
+
+type
+  EInvalidSubex* = object of EInvalidValue ## exception that is raised for
+                                           ## an invalid subex
+
+proc raiseInvalidFormat(msg: string) {.noinline.} =
+  raise newException(EInvalidSubex, "invalid format string: " & msg)
+
+type
+  TFormatParser = object {.pure, final.}
+    f: cstring
+    num, i, lineLen: int
+
+template call(x: stmt) =
+  p.i = i
+  x
+  i = p.i
+
+template callNoLineLenTracking(x: stmt) =
+  let oldLineLen = p.lineLen
+  p.i = i
+  x
+  i = p.i
+  p.lineLen = oldLineLen
+
+proc getFormatArg(p: var TFormatParser, a: openArray[string]): int =
+  const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
+  var i = p.i
+  var f = p.f
+  case f[i]
+  of '#':
+    result = p.num
+    inc i
+    inc p.num
+  of '1'..'9', '-':
+    var j = 0
+    var negative = f[i] == '-'
+    if negative: inc i
+    while f[i] in Digits:
+      j = j * 10 + ord(f[i]) - ord('0')
+      inc i
+    result = if not negative: j-1 else: a.len-j
+  of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
+    var name = ""
+    while f[i] in PatternChars: 
+      name.add(f[i])
+      inc(i)
+    result = findNormalized(name, a)+1
+  of '$':
+    inc(i)
+    call:
+      result = getFormatArg(p, a)
+    result = parseInt(a[result])-1
+  else:
+    raiseInvalidFormat("'#', '$', number or identifier expected")
+  if result >=% a.len: raiseInvalidFormat("index out of bounds: " & $result)
+  p.i = i
+
+proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string)
+
+proc emitChar(p: var TFormatParser, x: var string, ch: char) {.inline.} =
+  x.add(ch)
+  if ch == '\L': p.lineLen = 0
+  else: inc p.lineLen
+
+proc emitStrLinear(p: var TFormatParser, x: var string, y: string) {.inline.} =
+  for ch in items(y): emitChar(p, x, ch)
+
+proc emitStr(p: var TFormatParser, x: var string, y: string) {.inline.} =
+  x.add(y)
+  inc p.lineLen, y.len
+
+proc scanQuote(p: var TFormatParser, x: var string, toAdd: bool) =
+  var i = p.i+1
+  var f = p.f
+  while true:
+    if f[i] == '\'':
+      inc i
+      if f[i] != '\'': break
+      inc i
+      if toAdd: emitChar(p, x, '\'')
+    elif f[i] == '\0': raiseInvalidFormat("closing \"'\" expected")
+    else:
+      if toAdd: emitChar(p, x, f[i])
+      inc i
+  p.i = i
+
+proc scanBranch(p: var TFormatParser, a: openArray[string],
+                x: var string, choice: int) =
+  var i = p.i
+  var f = p.f
+  var c = 0
+  var elsePart = i
+  var toAdd = choice == 0
+  while true:
+    case f[i]
+    of ']': break
+    of '|': 
+      inc i
+      elsePart = i
+      inc c
+      if toAdd: break
+      toAdd = choice == c
+    of '\'':
+      call: scanQuote(p, x, toAdd)
+    of '\0': raiseInvalidFormat("closing ']' expected")
+    else:
+      if toAdd:
+        if f[i] == '$':
+          inc i
+          call: scanDollar(p, a, x)
+        else:
+          emitChar(p, x, f[i])
+          inc i
+      else:
+        inc i
+  if not toAdd and choice >= 0:
+    # evaluate 'else' part:
+    var last = i
+    i = elsePart
+    while true:
+      case f[i]
+      of '|', ']': break
+      of '\'':
+        call: scanQuote(p, x, true)
+      of '$':
+        inc i
+        call: scanDollar(p, a, x)
+      else:
+        emitChar(p, x, f[i])
+        inc i
+    i = last
+  p.i = i+1
+
+proc scanSlice(p: var TFormatParser, a: openarray[string]): tuple[x, y: int] =
+  var slice = false
+  var i = p.i
+  var f = p.f
+  
+  if f[i] == '{': inc i
+  else: raiseInvalidFormat("'{' expected")
+  if f[i] == '.' and f[i+1] == '.':
+    inc i, 2
+    slice = true
+  else:
+    call: result.x = getFormatArg(p, a)
+    if f[i] == '.' and f[i+1] == '.':
+      inc i, 2
+      slice = true
+  if slice:
+    if f[i] != '}':
+      call: result.y = getFormatArg(p, a)
+    else:
+      result.y = high(a)
+  else:
+    result.y = result.x
+  if f[i] != '}': raiseInvalidFormat("'}' expected")
+  inc i
+  p.i = i
+  
+proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
+  var i = p.i
+  var f = p.f
+  case f[i]
+  of '$': 
+    emitChar p, s, '$'
+    inc i
+  of '{':
+    call:
+      let (x, y) = scanSlice(p, a)
+    for j in x..y: emitStr p, s, a[j]
+  of '[':
+    inc i
+    var start = i
+    call: scanBranch(p, a, s, -1)
+    var x: int
+    if f[i] == '{':
+      inc i
+      call: x = getFormatArg(p, a)
+      if f[i] != '}': raiseInvalidFormat("'}' expected")
+      inc i
+    else:
+      call: x = getFormatArg(p, a)
+    var last = i
+    let choice = parseInt(a[x])
+    i = start
+    call: scanBranch(p, a, s, choice)
+    i = last
+  of '\'':
+    var sep = ""
+    callNoLineLenTracking: scanQuote(p, sep, true)
+    if f[i] == '~':
+      # $' '~{1..3}
+      # insert space followed by 1..3 if not empty
+      inc i
+      call: 
+        let (x, y) = scanSlice(p, a)
+      var L = 0
+      for j in x..y: inc L, a[j].len
+      if L > 0:
+        emitStrLinear p, s, sep
+        for j in x..y: emitStr p, s, a[j]
+    else:
+      block StringJoin:
+        block OptionalLineLengthSpecifier:
+          var maxLen = 0
+          case f[i]
+          of '0'..'9':
+            while f[i] in Digits:
+              maxLen = maxLen * 10 + ord(f[i]) - ord('0')
+              inc i
+          of '$':
+            # do not skip the '$' here for `getFormatArg`!
+            call:
+              maxLen = getFormatArg(p, a)
+          else: break OptionalLineLengthSpecifier
+          var indent = ""
+          case f[i]
+          of 'i':
+            inc i
+            callNoLineLenTracking: scanQuote(p, indent, true)
+            
+            call:
+              let (x, y) = scanSlice(p, a)
+            if maxLen < 1: emitStrLinear(p, s, indent)
+            var items = 1
+            emitStr p, s, a[x]
+            for j in x+1..y:
+              emitStr p, s, sep
+              if items >= maxLen: 
+                emitStrLinear p, s, indent
+                items = 0
+              emitStr p, s, a[j]
+              inc items
+          of 'c':
+            inc i
+            callNoLineLenTracking: scanQuote(p, indent, true)
+            
+            call:
+              let (x, y) = scanSlice(p, a)
+            if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent)
+            emitStr p, s, a[x]
+            for j in x+1..y:
+              emitStr p, s, sep
+              if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent)
+              emitStr p, s, a[j]
+            
+          else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected")
+          break StringJoin
+
+        call:
+          let (x, y) = scanSlice(p, a)
+        emitStr p, s, a[x]
+        for j in x+1..y:
+          emitStr p, s, sep
+          emitStr p, s, a[j]
+  else:
+    call: 
+      var x = getFormatArg(p, a)
+    emitStr p, s, a[x]
+  p.i = i
+
+
+type
+  TSubex* = distinct string ## string that contains a substitution expression
+
+proc subex*(s: string): TSubex =
+  ## constructs a *substitution expression* from `s`. Currently this performs
+  ## no syntax checking but this may change in later versions.
+  result = TSubex(s)
+
+proc addf*(s: var string, formatstr: TSubex, a: openarray[string]) {.
+           noSideEffect, rtl, extern: "nfrmtAddf".} =
+  ## The same as ``add(s, formatstr % a)``, but more efficient.
+  var p: TFormatParser
+  p.f = formatstr.string
+  var i = 0
+  while i < len(formatstr.string):
+    if p.f[i] == '$':
+      inc i
+      call: scanDollar(p, a, s)
+    else:
+      emitChar(p, s, p.f[i])
+      inc(i)
+
+proc `%` *(formatstr: TSubex, a: openarray[string]): string {.noSideEffect,
+  rtl, extern: "nfrmtFormatOpenArray".} =
+  ## The `substitution`:idx: operator performs string substitutions in
+  ## `formatstr` and returns a modified `formatstr`. This is often called
+  ## `string interpolation`:idx:.
+  ##
+  result = newStringOfCap(formatstr.string.len + a.len shl 4)
+  addf(result, formatstr, a)
+
+proc `%` *(formatstr: TSubex, a: string): string {.noSideEffect,
+  rtl, extern: "nfrmtFormatSingleElem".} =
+  ## This is the same as ``formatstr % [a]``.
+  result = newStringOfCap(formatstr.string.len + a.len)
+  addf(result, formatstr, [a])
+
+{.pop.}
+
+when isMainModule:
+
+  proc `%`(formatstr: string, a: openarray[string]): string =
+    result = newStringOfCap(formatstr.len + a.len shl 4)
+    addf(result, formatstr.TSubex, a)
+
+  proc `%`(formatstr: string, a: string): string =
+    result = newStringOfCap(formatstr.len + a.len)
+    addf(result, formatstr.TSubex, [a])
+
+
+  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
+           "The cat eats fish."
+
+
+  doAssert "$[abc|def]# $3 $# $#" % ["17", "b", "c"] == "def c b c"
+  doAssert "$[abc|def]# $3 $# $#" % ["1", "b", "c"] == "def c b c"
+  doAssert "$[abc|def]# $3 $# $#" % ["0", "b", "c"] == "abc c b c"
+  doAssert "$[abc|def|]# $3 $# $#" % ["17", "b", "c"] == " c b c"
+
+  doAssert "$[abc|def|]# $3 $# $#" % ["-9", "b", "c"] == " c b c"
+  doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)"
+
+  doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3"
+  
+  doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)"
+  doAssert "$' '~{..}" % "" == ""
+  doAssert "$' '~{..}" % "P0" == " P0"
+  doAssert "${$1}" % "1" == "1"
+  doAssert "${$$-1} $$1" % "1" == "1 $1"
+           
+  doAssert "$#($', '10c'\n    '{#..})" % ["doAssert", "longishA", "longish"] ==
+           """doAssert(
+    longishA, 
+    longish)"""
+  
+  echo "type TMyEnum* = enum\n  $', '2i'\n  '{..}" % ["fieldA", 
+    "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]
+  
+  doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
+  
+  doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
+  
+  doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
+  
+  echo subex("type\n  TEnum = enum\n    $', '40c'\n    '{..}") % [
+    "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
+  
+  
diff --git a/lib/system.nim b/lib/system.nim
index 282e03add..1161adaf6 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -309,7 +309,7 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.}
   ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
   ## efficient since no reallocation is needed.
 
-proc len*[T](x: openarray[T]): int {.magic: "LengthOpenArray", noSideEffect.}
+proc len*[T](x: openArray[T]): int {.magic: "LengthOpenArray", noSideEffect.}
 proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
 proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
 proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.}
@@ -1557,6 +1557,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   {.push stack_trace: off.}
 
   proc initGC()
+  when not defined(boehmgc):
+    proc initAllocator() {.inline.}
 
   proc initStackBottom() {.inline.} = 
     # WARNING: This is very fragile! An array size of 8 does not work on my
@@ -1795,7 +1797,9 @@ when not defined(EcmaScript) and not defined(NimrodVM):
       prev: PSafePoint # points to next safe point ON THE STACK
       status: int
       context: C_JmpBuf
-
+  
+  when defined(initAllocator):
+    initAllocator()
   when hasThreadSupport:
     include "system/syslocks"
     include "system/threads"
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 6dee145c8..e31ef47df 100755
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -20,15 +20,14 @@ when defined(posix):
     PROT_WRITE = 2             # page can be written 
     MAP_PRIVATE = 2            # Changes are private 
   
-  when defined(linux) or defined(aix):
-    const MAP_ANONYMOUS = 0x20       # don't use a file
-  elif defined(macosx) or defined(bsd):
+  when defined(macosx) or defined(bsd):
     const MAP_ANONYMOUS = 0x1000
   elif defined(solaris): 
     const MAP_ANONYMOUS = 0x100
   else:
-    {.error: "Port memory manager to your platform".}
-
+    var
+      MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+    
   proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
             off: int): pointer {.header: "<sys/mman.h>".}
 
@@ -151,15 +150,33 @@ type
     size: int                # remaining size
     acc: int                 # accumulator
     next: PLLChunk           # next low-level chunk; only needed for dealloc
+
+  PAvlNode = ptr TAvlNode
+  TAvlNode {.pure, final.} = object 
+    link: array[0..1, PAvlNode] # Left (0) and right (1) links 
+    key, upperBound: int
+    level: int
     
   TMemRegion {.final, pure.} = object
+    minLargeObj, maxLargeObj: int
+    freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
     llmem: PLLChunk
     currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
     lastSize: int # needed for the case that OS gives us pages linearly 
-    freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
     freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
     chunkStarts: TIntSet
+    root, deleted, last, freeAvlNodes: PAvlNode
   
+# shared:
+var
+  bottomData: TAvlNode
+  bottom: PAvlNode
+
+proc initAllocator() =
+  bottom = addr(bottomData)
+  bottom.link[0] = bottom
+  bottom.link[1] = bottom
+
 proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} = 
   inc(a.currMem, bytes)
 
@@ -171,7 +188,7 @@ proc getMaxMem(a: var TMemRegion): int =
   # Since we update maxPagesCount only when freeing pages, 
   # maxPagesCount may not be up to date. Thus we use the
   # maximum of these both values here:
-  return max(a.currMem, a.maxMem)
+  result = max(a.currMem, a.maxMem)
   
 proc llAlloc(a: var TMemRegion, size: int): pointer =
   # *low-level* alloc for the memory managers data structures. Deallocation
@@ -191,7 +208,25 @@ proc llAlloc(a: var TMemRegion, size: int): pointer =
   dec(a.llmem.size, size)
   inc(a.llmem.acc, size)
   zeroMem(result, size)
-  
+
+proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
+  if a.freeAvlNodes != nil:
+    result = a.freeAvlNodes
+    a.freeAvlNodes = a.freeAvlNodes.link[0]
+  else:
+    result = cast[PAvlNode](llAlloc(a, sizeof(TAvlNode)))
+  result.key = key
+  result.upperBound = upperBound
+  result.link[0] = bottom
+  result.link[1] = bottom
+  result.level = 0
+
+proc deallocAvlNode(a: var TMemRegion, n: PAvlNode) {.inline.} =
+  n.link[0] = a.freeAvlNodes
+  a.freeAvlNodes = n
+
+include "system/avltree"
+
 proc llDeallocAll(a: var TMemRegion) =
   var it = a.llmem
   while it != nil:
@@ -497,6 +532,8 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
     sysAssert c.size == size, "rawAlloc 12"
     result = addr(c.data)
     sysAssert((cast[TAddress](result) and (MemAlign-1)) == 0, "rawAlloc 13")
+    if a.root == nil: a.root = bottom
+    add(a, a.root, cast[TAddress](result), cast[TAddress](result)+%size)
   sysAssert(isAccessible(a, result), "rawAlloc 14")
 
 proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
@@ -534,7 +571,10 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
     # set to 0xff to check for usage after free bugs:
     when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
     # free big chunk
-    freeBigChunk(a, cast[PBigChunk](c))
+    var c = cast[PBigChunk](c)
+    a.deleted = bottom
+    del(a, a.root, cast[int](addr(c.data)))
+    freeBigChunk(a, c)
 
 proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = 
   if isAccessible(a, p):
@@ -550,6 +590,46 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
         var c = cast[PBigChunk](c)
         result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1
 
+proc prepareForInteriorPointerChecking(a: var TMemRegion) {.inline.} =
+  a.minLargeObj = lowGauge(a.root)
+  a.maxLargeObj = highGauge(a.root)
+
+proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
+  if isAccessible(a, p):
+    var c = pageAddr(p)
+    if not chunkUnused(c):
+      if isSmallChunk(c):
+        var c = cast[PSmallChunk](c)
+        var offset = (cast[TAddress](p) and (PageSize-1)) -% 
+                     smallChunkOverhead()
+        if c.acc >% offset:
+          sysAssert(cast[TAddress](addr(c.data)) +% offset ==
+                    cast[TAddress](p), "offset is not what you think it is")
+          var d = cast[ptr TFreeCell](cast[TAddress](addr(c.data)) +% 
+                    offset -% (offset %% c.size))
+          if d.zeroField >% 1:
+            result = d
+            sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
+      else:
+        var c = cast[PBigChunk](c)
+        var d = addr(c.data)
+        if p >= d and cast[ptr TFreeCell](d).zeroField >% 1:
+          result = d
+          sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
+  else:
+    var q = cast[int](p)
+    if q >=% a.minLargeObj and q <=% a.maxLargeObj:
+      # this check is highly effective! Test fails for 99,96% of all checks on
+      # an x86-64.
+      var avlNode = inRange(a.root, q)
+      if avlNode != nil:
+        var k = cast[pointer](avlNode.key)
+        var c = cast[PBigChunk](pageAddr(k))
+        sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
+        if cast[ptr TFreeCell](k).zeroField >% 1:
+          result = k
+          sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
+
 proc ptrSize(p: pointer): int =
   var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
   result = pageAddr(x).size - sizeof(TFreeCell)
diff --git a/lib/system/avltree.nim b/lib/system/avltree.nim
new file mode 100644
index 000000000..9bc2687f4
--- /dev/null
+++ b/lib/system/avltree.nim
@@ -0,0 +1,91 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## not really an AVL tree anymore, but still balanced ...
+
+template IsBottom(n: PAvlNode): bool = n == bottom
+
+proc lowGauge(n: PAvlNode): int =
+  var it = n
+  while not IsBottom(it):
+    result = it.key
+    it = it.link[0]
+  
+proc highGauge(n: PAvlNode): int =
+  result = -1
+  var it = n
+  while not IsBottom(it):
+    result = it.upperBound
+    it = it.link[1]
+
+proc find(root: PAvlNode, key: int): PAvlNode = 
+  var it = root
+  while not IsBottom(it):
+    if it.key == key: return it
+    it = it.link[ord(it.key <% key)]
+
+proc inRange(root: PAvlNode, key: int): PAvlNode =
+  var it = root
+  while not IsBottom(it):
+    if it.key <=% key and key <% it.upperBound: return it
+    it = it.link[ord(it.key <% key)]
+
+proc skew(t: var PAvlNode) =
+  if t.link[0].level == t.level:
+    var temp = t
+    t = t.link[0]
+    temp.link[0] = t.link[1]
+    t.link[1] = temp
+
+proc split(t: var PAvlNode) =
+  if t.link[1].link[1].level == t.level:
+    var temp = t
+    t = t.link[1]
+    temp.link[1] = t.link[0]
+    t.link[0] = temp
+    inc t.level
+
+proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) =
+  if t == bottom:
+    t = allocAvlNode(a, key, upperBound)
+  else:
+    if key <% t.key:
+      add(a, t.link[0], key, upperBound)
+    elif key >% t.key:
+      add(a, t.link[1], key, upperBound)
+    else:
+      sysAssert false, "key already exists"
+    skew(t)
+    split(t)
+
+proc del(a: var TMemRegion, t: var PAvlNode, x: int) =
+  if t == bottom: return
+  a.last = t
+  if x <% t.key:
+    del(a, t.link[0], x)
+  else:
+    a.deleted = t
+    del(a, t.link[1], x)
+  if t == a.last and a.deleted != bottom and x == a.deleted.key:
+    a.deleted.key = t.key
+    a.deleted.upperBound = t.upperBound
+    a.deleted = bottom
+    t = t.link[1]
+    deallocAvlNode(a, a.last)
+  elif t.link[0].level < t.level-1 or
+       t.link[1].level < t.level-1:
+    dec t.level
+    if t.link[1].level > t.level:
+      t.link[1].level = t.level
+    skew(t)
+    skew(t.link[1])
+    skew(t.link[1].link[1])
+    split(t)
+    split(t.link[1])
+
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index caab22e34..c477cadef 100755
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -561,24 +561,30 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
   var c = cast[TAddress](cell)
   if c >% PageSize and (c and (MemAlign-1)) == 0:
     # fast check: does it look like a cell?
-    if isAllocatedPtr(gch.region, cell): 
+    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
+    if objStart != nil:
       # mark the cell:
-      cell.refcount = cell.refcount +% rcIncrement
-      add(gch.decStack, cell)
+      objStart.refcount = objStart.refcount +% rcIncrement
+      add(gch.decStack, objStart)
     when false:
-      # Care for string->cstring and seq->openArray conversions.
-      # Now the codegen deals with it, it generated ``nimKeepAlive`` calls.
-      var b = cast[PCell](c -% sizeof(TGenericSeq))
-      if isAllocatedPtr(gch.region, b):
+      if isAllocatedPtr(gch.region, cell):
+        sysAssert false, "allocated pointer but not interior?"
         # mark the cell:
-        b.refcount = b.refcount +% rcIncrement
-        add(gch.decStack, b)
+        cell.refcount = cell.refcount +% rcIncrement
+        add(gch.decStack, cell)
 
 proc nimKeepAlive(p: PGenericSeq) {.compilerRtl, noinline.} =
   var c = usrToCell(p)
   if isAllocatedPtr(gch.region, c):
     c.refcount = c.refcount or rcMarked
 
+proc nimGCFrame(p: pointer) {.compilerRtl, noinline.} =
+  # 'cast' is correct here! no offset to add:
+  var c = cast[PCell](p)
+  var x = cast[TAddress](c)
+  if x <% PageSize and (x and (MemAlign-1)) == 0:
+    c.refcount = c.refcount or rcMarked
+
 proc markThreadStacks(gch: var TGcHeap) = 
   when hasThreadSupport and hasSharedHeap:
     {.error: "not fully implemented".}
@@ -771,6 +777,7 @@ proc collectCT(gch: var TGcHeap) =
       gch.recGcLock == 0:
     gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
     sysAssert(gch.decStack.len == 0, "collectCT")
+    prepareForInteriorPointerChecking(gch.region)
     markStackAndRegisters(gch)
     markThreadStacks(gch)
     gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index d012110f1..b0e2c567b 100755
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -112,8 +112,6 @@ proc rawFileSize(file: TFile): int =
 proc readAllFile(file: TFile, len: int): string =
   # We aquire the filesize beforehand and hope it doesn't change.
   # Speeds things up.
-  if len >= high(int):
-    raiseEIO("file too big to fit in memory")
   result = newString(int(len))
   if readBuffer(file, addr(result[0]), int(len)) != len:
     raiseEIO("error while reading from file")
diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim
index a482d93e1..40a7fd65e 100755
--- a/lib/windows/windows.nim
+++ b/lib/windows/windows.nim
@@ -361,6 +361,7 @@ type
     RASP_Amb = 0x00010000

   SECURITY_IMPERSONATION_LEVEL* = enum

 

+

     SecurityAnonymous, SecurityIdentification, SecurityImpersonation,

     SecurityDelegation

   SID_NAME_USE* = enum

@@ -2193,6 +2194,7 @@ const
   WS_SYSMENU* = 0x00080000

   WS_TABSTOP* = 0x00010000

   WS_THICKFRAME* = 0x00040000

+

   WS_TILED* = 0

   WS_TILEDWINDOW* = 0x00CF0000

   WS_VISIBLE* = 0x10000000

@@ -4061,6 +4063,7 @@ const
   SPI_GETDRAGFULLWINDOWS* = 38

   SPI_GETNONCLIENTMETRICS* = 41

   SPI_SETNONCLIENTMETRICS* = 42

+

   SPI_GETMINIMIZEDMETRICS* = 43

   SPI_SETMINIMIZEDMETRICS* = 44

   SPI_GETICONMETRICS* = 45

@@ -7683,7 +7686,7 @@ when defined(i386):
       Esp*: DWORD

       SegSs*: DWORD

 

-when defined(x86_64):

+elif defined(x86_64):

   #

   # Define 128-bit 16-byte aligned xmm register type.

   #

@@ -7780,7 +7783,7 @@ when defined(x86_64):
       LastExceptionToRip*: DWORD64

       LastExceptionFromRip*: DWORD64

 

-when defined(powerpc32):

+elif defined(powerpc32):

   # ppc

   # Floating point registers returned when CONTEXT_FLOATING_POINT is set

   # Integer registers returned when CONTEXT_INTEGER is set.

@@ -7885,6 +7888,12 @@ when defined(powerpc32):
       Dr6*: DWORD

       Dr7*: DWORD

 

+else:

+  # dummy CONTEXT so that it compiles:

+  type

+    CONTEXT* {.final, pure.} = object

+      data: array [0..255, float64]

+

 type

   LPCONTEXT* = ptr CONTEXT

   TCONTEXT* = CONTEXT

@@ -9201,6 +9210,7 @@ type
   PEMRSCALEWINDOWEXTEX* = ptr EMRSCALEVIEWPORTEXTEX

   EMRSELECTCOLORSPACE* {.final, pure.} = object

     emr*: EMR

+

     ihCS*: DWORD

 

   TEMRSELECTCOLORSPACE* = EMRSELECTCOLORSPACE

@@ -14133,6 +14143,7 @@ proc GetDiskFreeSpaceA*(lpRootPathName: LPCSTR, lpSectorsPerCluster: LPDWORD,
                         lpTotalNumberOfClusters: LPDWORD): WINBOOL{.stdcall,

     dynlib: "kernel32", importc: "GetDiskFreeSpaceA".}

 proc CreateDirectoryA*(lpPathName: LPCSTR,

+

                        lpSecurityAttributes: LPSECURITY_ATTRIBUTES): WINBOOL{.

     stdcall, dynlib: "kernel32", importc: "CreateDirectoryA".}

 proc CreateDirectoryExA*(lpTemplateDirectory: LPCSTR, lpNewDirectory: LPCSTR,

@@ -16966,6 +16977,7 @@ when defined(winUnicode):
   proc GetICMProfile*(para1: HDC, para2: DWORD, para3: LPWSTR): WINBOOL{.

       stdcall, dynlib: "gdi32", importc: "GetICMProfileW".}

   proc SetICMProfile*(para1: HDC, para2: LPWSTR): WINBOOL{.stdcall,

+

       dynlib: "gdi32", importc: "SetICMProfileW".}

   proc UpdateICMRegKey*(para1: DWORD, para2: DWORD, para3: LPWSTR, para4: UINT): WINBOOL{.

       stdcall, dynlib: "gdi32", importc: "UpdateICMRegKeyW".}

@@ -20817,6 +20829,7 @@ proc TabCtrl_SetImageList*(wnd: HWND, himl: HIMAGELIST): LRESULT
 proc TabCtrl_GetItemCount*(wnd: HWND): LRESULT

 proc TabCtrl_GetItem*(wnd: HWND, iItem: int32, item: var TC_ITEM): LRESULT

 proc TabCtrl_SetItem*(wnd: HWND, iItem: int32, item: var TC_ITEM): LRESULT

+

 proc TabCtrl_InsertItem*(wnd: HWND, iItem: int32, item: var TC_ITEM): LRESULT

 proc TabCtrl_DeleteItem*(wnd: HWND, i: int32): LRESULT

 proc TabCtrl_DeleteAllItems*(wnd: HWND): LRESULT

@@ -21954,6 +21967,7 @@ proc MakeAbsoluteSD*(pSelfRelativeSecurityDescriptor: PSecurityDescriptor,
                      pAbsoluteSecurityDescriptor: PSecurityDescriptor,

                      lpdwAbsoluteSecurityDescriptorSi: var DWORD,

                      pDacl: var TACL, lpdwDaclSize: var DWORD, pSacl: var TACL,

+

                      lpdwSaclSize: var DWORD, pOwner: PSID,

                      lpdwOwnerSize: var DWORD, pPrimaryGroup: Pointer,

                      lpdwPrimaryGroupSize: var DWORD): WINBOOL{.stdcall,

diff --git a/tests/compile/talias.nim b/tests/compile/talias.nim
new file mode 100644
index 000000000..1a93e5249
--- /dev/null
+++ b/tests/compile/talias.nim
@@ -0,0 +1,66 @@
+# Test the alias analysis
+
+type
+  TAnalysisResult* = enum
+    arNo, arMaybe, arYes
+
+proc isPartOf*[S, T](a: S, b: T): TAnalysisResult {.
+  magic: "IsPartOf", noSideEffect.}
+  ## not yet exported properly. 
+
+template compileTimeAssert(cond: expr) =
+  when not cond:
+    {.compile: "is false: " & astToStr(cond).}
+
+template `<|` (a, b: expr) =
+  compileTimeAssert isPartOf(a, b) == arYes
+
+template `!<|` (a, b: expr) =
+  compileTimeAssert isPartOf(a, b) == arNo
+
+template `?<|` (a, b: expr) =
+  compileTimeAssert isPartOf(a, b) == arMaybe
+
+type
+  TA = object
+  TC = object of TA
+    arr: array[0..3, int]
+    le, ri: ref TC
+    f: string
+    c: char
+    se: seq[TA]
+
+proc p(param1, param2: TC): TC =
+  var
+    local: TC
+    plocal: ptr TC
+    plocal2: ptr TA
+    
+  local.arr <| local
+  local.arr[0] <| local
+  local.arr[2] !<| local.arr[1]
+  
+  plocal2[] ?<| local
+
+  param1 ?<| param2
+  
+  local.arr[0] !<| param1
+  local.arr !<| param1
+  local.le[] ?<| param1
+  
+  param1 !<| local.arr[0]
+  param1 !<| local.arr
+  param1 ?<| local.le[]
+  
+  result !<| local
+  result <| result
+
+var
+  a, b: int
+  x: TC
+  
+a <| a
+a !<| b
+
+discard p(x, x)
+
diff --git a/tests/compile/tlibs.nim b/tests/compile/tlibs.nim
index 5dd70b0e8..bef16fff6 100755
--- a/tests/compile/tlibs.nim
+++ b/tests/compile/tlibs.nim
@@ -15,7 +15,7 @@ import
   lua, lualib, lauxlib, mysql, sqlite3, python, tcl,
   db_postgres, db_mysql, db_sqlite, ropes, sockets, browsers, httpserver,
   httpclient, parseutils, unidecode, xmldom, xmldomparser, xmltree, xmlparser,
-  htmlparser, re, graphics, colors, pegs
+  htmlparser, re, graphics, colors, pegs, subexes
   
 when defined(linux):
   import
diff --git a/tests/reject/trecinca.nim b/tests/reject/trecinca.nim
index 27f982e66..99750beb9 100755
--- a/tests/reject/trecinca.nim
+++ b/tests/reject/trecinca.nim
@@ -1,7 +1,7 @@
 discard """
   file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: '/home/nimrod/Nimrod/tests/reject/trecincb.nim'"
+  errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
 """
 # Test recursive includes
 
diff --git a/tests/reject/trecincb.nim b/tests/reject/trecincb.nim
index 55be2ac88..9dd7d51de 100755
--- a/tests/reject/trecincb.nim
+++ b/tests/reject/trecincb.nim
@@ -1,7 +1,7 @@
 discard """
   file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: '/home/nimrod/Nimrod/tests/reject/trecincb.nim'"
+  errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
 """
 # Test recursive includes
 
diff --git a/tests/run/tcritbits.nim b/tests/run/tcritbits.nim
new file mode 100644
index 000000000..fb447b80b
--- /dev/null
+++ b/tests/run/tcritbits.nim
@@ -0,0 +1,28 @@
+discard """
+  output: '''abc
+def
+definition
+prefix
+xyz
+def
+definition'''
+"""
+
+import critbits
+
+when isMainModule:
+  var r: TCritBitTree[void]
+  r.incl "abc"
+  r.incl "xyz"
+  r.incl "def"
+  r.incl "definition"
+  r.incl "prefix"
+  doAssert r.contains"def"
+  #r.del "def"
+
+  for w in r.items:
+    echo w
+    
+  for w in r.itemsWithPrefix("de"):
+    echo w
+
diff --git a/tests/specials.nim b/tests/specials.nim
index ea3e58fbf..f5a6545d4 100644
--- a/tests/specials.nim
+++ b/tests/specials.nim
@@ -92,7 +92,16 @@ proc runDLLTests(r: var TResults, options: string) =
   runBasicDLLTest c, r, options & " -d:release"
   runBasicDLLTest c, r, options & " --gc:boehm"
   runBasicDLLTest c, r, options & " -d:release --gc:boehm"
+
+proc compileDLLTests(r: var TResults, options: string) =
+  # dummy run result:
+  var c = initResults()
   
+  runBasicDLLTest r, c, options
+  runBasicDLLTest r, c, options & " -d:release"
+  runBasicDLLTest r, c, options & " --gc:boehm"
+  runBasicDLLTest r, c, options & " -d:release --gc:boehm"
+
 # ------------------------------ GC tests -------------------------------------
 
 proc runGcTests(r: var TResults, options: string) =
@@ -118,7 +127,8 @@ proc runThreadTests(r: var TResults, options: string) =
   
   test "tactors"
   test "threadex"
-  test "trecursive_actor"
+  # deactivated because output capturing still causes problems sometimes:
+  #test "trecursive_actor"
   #test "threadring"
   #test "tthreadanalysis"
   #test "tthreadsort"
@@ -130,14 +140,16 @@ proc rejectThreadTests(r: var TResults, options: string) =
 
 # ------------------------- IO tests -----------------------------------
 
-proc runIOTests(r: var TResults, options: string) =
+proc compileIOTests(r: var TResults, options: string) =
   compileSingleTest(r, "tests/system/helpers/readall_echo.nim", options)
+
+proc runIOTests(r: var TResults, options: string) =
   runSingleTest(r, "tests/system/io", options)
 
 # ------------------------- register special tests here -----------------------
 proc runSpecialTests(r: var TResults, options: string) =
   runRodFiles(r, options)
-  runDLLTests(r, options)
+  #runDLLTests(r, options)
   runGCTests(r, options)
   runThreadTests(r, options & " --threads:on")
   runIOTests(r, options)
@@ -151,3 +163,6 @@ proc compileSpecialTests(r: var TResults, options: string) =
   compileSingleTest(r, "compiler/c2nim/c2nim.nim", options)
   compileSingleTest(r, "compiler/pas2nim/pas2nim.nim", options)
 
+  compileIOTests(r, options)
+  compileDLLTests(r, options)
+
diff --git a/tests/system/io.nim b/tests/system/io.nim
index 4837f7093..31978feb7 100644
--- a/tests/system/io.nim
+++ b/tests/system/io.nim
@@ -1,15 +1,16 @@
 discard """output: '''[OK] stdin
 
-[OK] file'''"""
+[OK] file'''
+"""
 
 import
-  unittest, osproc, streams, os, readall_echo
+  unittest, osproc, streams, os, "helpers/readall_echo"
 const STRING_DATA = """Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."""
 const TEST_FILE = "tests/testdata/string"
 
 proc echoLoop(str: string): string =
   result = ""
-  var process = startProcess(os.addFileExt("tests/system/helpers/readall_echo", ExeExt))
+  var process = startProcess(findExe("tests/system/helpers/readall_echo"))
   var input = process.inputStream
   input.write(str)
   input.close()
diff --git a/todo.txt b/todo.txt
index 7b8aec385..714421a02 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,10 +1,8 @@
 version 0.8.14
 ==============
 
-- compiler/GC interaction need to generate code to prevent tail call
-  optimization
-
-- warning for implicit openArray -> varargs convention
+- BUG: type TX = TTable[string, int]
+- warning for implicit openArray -> varargs conversion
 - implement explicit varargs; **but** ``len(varargs)`` problem remains! 
   --> solve by implicit conversion from varargs to openarray
 - implicit invokation of `items`/`pairs` seems nice
@@ -34,7 +32,7 @@ version 0.9.0
 - optional indentation for 'case' statement; hm, keep in mind other syntax
   changes that people want; may turn out to be a bad idea
 - activate more thread tests
-
+- optimize method dispatchers
 
 Bugs
 ----
@@ -93,8 +91,8 @@ Library
 -------
 
 - wrappers for poppler; libharu
-- radix tree for strings; maybe suffix tree
-- locale support
+- suffix trees
+- locale support; i18n module
 - bignums
 
 - pdcurses bindings
diff --git a/web/news.txt b/web/news.txt
index 22e0377c5..2b7b8fca6 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -18,8 +18,8 @@ Bugfixes
 - Bugfix c2nim, c2pas: the ``--out`` option has never worked properly.
 - Bugfix: forwarding of generic procs never worked.
 - Some more bugfixes for macros and compile-time evaluation.
-- The compiler now generates code that tries to keep the C compiler from
-  optimizing stack allocated GC roots away.
+- The GC now takes into account interior pointers on the stack which may be
+  introduced by aggressive C optimizers.
 
 
 Changes affecting backwards compatibility
@@ -91,6 +91,8 @@ Compiler Additions
   and Objective C somewhat easier.
 - Added a ``--nimcache:PATH`` configuration option for control over the output
   directory for generated code.
+- The ``--genScript`` option now produces different compilation scripts 
+  which do not contain absolute paths.
 - Added ``--cincludes:dir``, ``--clibdir:lib`` configuration options for 
   modifying the C compiler's header/library search path in cross-platform way.
 - Added ``--clib:lib`` configuration option for specifying additional
@@ -112,7 +114,7 @@ Compiler Additions
 - Added ``--import:file`` and ``--include:file`` configuration options
   for specifying modules that will be automatically imported/incluced.
 - ``nimrod i`` can now optionally be given a module to execute.
-- The compiler now performs a simple aliases analysis to generate better code.
+- The compiler now performs a simple alias analysis to generate better code.
 
 
 Library Additions
@@ -138,6 +140,8 @@ Library Additions
 - Added ``irc`` module.
 - Added ``ftpclient`` module.
 - Added ``memfiles`` module.
+- Added ``subexes`` module.
+- Added ``critbits`` module.
 - Added ``osproc.startCmd``, ``osproc.execCmdEx``.
 - The ``osproc`` module now uses ``posix_spawn`` instead of ``fork`` 
   and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
diff --git a/web/nimrod.ini b/web/nimrod.ini
index 25d1e2e6a..be8bcb509 100755
--- a/web/nimrod.ini
+++ b/web/nimrod.ini
@@ -42,7 +42,7 @@ srcdoc: "impure/rdstdin;wrappers/zmq;wrappers/sphinx"
 srcdoc: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
 srcdoc: "pure/collections/intsets;pure/collections/queues;pure/encodings"
 srcdoc: "pure/events;pure/collections/sequtils;pure/irc;ecmas/dom"
-srcdoc: "pure/ftpclient;pure/memfiles"
+srcdoc: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits"
 
 webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
 webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"