summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim8
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/charsets.nim49
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/lowerings.nim8
-rw-r--r--compiler/msgs.nim7
-rw-r--r--compiler/nversion.nim6
-rw-r--r--compiler/options.nim2
-rw-r--r--compiler/parsecfg.nim346
-rw-r--r--compiler/passes.nim5
-rw-r--r--compiler/pendx.nim18
-rw-r--r--compiler/pragmas.nim13
-rw-r--r--compiler/sem.nim10
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semfold.nim2
-rw-r--r--compiler/seminst.nim3
-rw-r--r--compiler/sempass2.nim20
-rw-r--r--compiler/semthreads.nim390
-rw-r--r--compiler/semtypes.nim13
-rw-r--r--compiler/semtypinst.nim7
-rw-r--r--compiler/sigmatch.nim7
-rw-r--r--compiler/suggest.nim1
-rw-r--r--compiler/syntaxes.nim2
-rw-r--r--compiler/types.nim9
-rw-r--r--compiler/typesrenderer.nim8
-rw-r--r--compiler/vm.nim10
-rw-r--r--compiler/vmgen.nim20
27 files changed, 92 insertions, 877 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index e575f317d..97f48b253 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1311,6 +1311,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
   result = t
   while result.kind in kinds: result = lastSon(result)
 
+proc isGCedMem*(t: PType): bool {.inline.} =
+  result = t.kind in {tyString, tyRef, tySequence} or
+           t.kind == tyProc and t.callConv == ccClosure
+
 proc propagateToOwner*(owner, elem: PType) =
   const HaveTheirOwnEmpty = {tySequence, tySet}
   owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta})
@@ -1331,9 +1335,7 @@ proc propagateToOwner*(owner, elem: PType) =
     owner.flags.incl tfHasMeta
 
   if owner.kind != tyProc:
-    if elem.kind in {tyString, tyRef, tySequence} or
-        elem.kind == tyProc and elem.callConv == ccClosure or
-        tfHasGCedMem in elem.flags:
+    if elem.isGCedMem or tfHasGCedMem in elem.flags:
       owner.flags.incl tfHasGCedMem
 
 proc rawAddSon*(father, son: PType) =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index f64ebacfb..8d66d7a3b 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -730,7 +730,7 @@ proc retIsNotVoid(s: PSym): bool =
   result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0])
 
 proc initFrame(p: BProc, procname, filename: PRope): PRope =
-  discard cgsym(p.module, "pushFrame")
+  discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
     discard cgsym(p.module, "TVarSlot")
     result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N",
diff --git a/compiler/charsets.nim b/compiler/charsets.nim
deleted file mode 100644
index d3d00b687..000000000
--- a/compiler/charsets.nim
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-#
-#           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-const 
-  CharSize* = SizeOf(Char)
-  Lrz* = ' '
-  Apo* = '\''
-  Tabulator* = '\x09'
-  ESC* = '\x1B'
-  CR* = '\x0D'
-  FF* = '\x0C'
-  LF* = '\x0A'
-  BEL* = '\x07'
-  BACKSPACE* = '\x08'
-  VT* = '\x0B'
-
-when defined(macos): 
-  DirSep == ':'
-  "\n" == CR & ""
-  FirstNLchar == CR
-  PathSep == ';'              # XXX: is this correct?
-else: 
-  when defined(unix): 
-    DirSep == '/'
-    "\n" == LF & ""
-    FirstNLchar == LF
-    PathSep == ':'
-  else: 
-    # windows, dos
-    DirSep == '\\'
-    "\n" == CR + LF
-    FirstNLchar == CR
-    DriveSeparator == ':'
-    PathSep == ';'
-UpLetters == {'A'..'Z', '\xC0'..'\xDE'}
-DownLetters == {'a'..'z', '\xDF'..'\xFF'}
-Numbers == {'0'..'9'}
-Letters == UpLetters + DownLetters
-type 
-  TCharSet* = set[Char]
-  PCharSet* = ref TCharSet
-
-# implementation
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 4117fc461..17bb5db55 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -49,6 +49,7 @@ proc initDefines*() =
   defineSymbol("nimcomputedgoto")
   defineSymbol("nimunion")
   defineSymbol("nimnewshared")
+  defineSymbol("nimrequiresnimframe")
   
   # add platform specific symbols:
   case targetCPU
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 0ca07e828..1b9e5fe0f 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,7 +12,7 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, astalgo, types, idents, magicsys, msgs
+import ast, astalgo, types, idents, magicsys, msgs, options
 
 proc newTupleAccess*(tup: PNode, i: int): PNode =
   result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
@@ -151,8 +151,9 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
   if n.kind notin nkCallKinds or not n.typ.isEmptyType:
     localError(n.info, "'spawn' takes a call expression of type void")
     return
-  if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
-    localError(n.info, "'spawn' takes a GC safe call expression")
+  if optThreadAnalysis in gGlobalOptions:
+    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
+      localError(n.info, "'spawn' takes a GC safe call expression")
   var
     threadParam = newSym(skParam, getIdent"thread", owner, n.info)
     argsParam = newSym(skParam, getIdent"args", owner, n.info)
@@ -162,6 +163,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
     argsParam.typ = ptrType
     argsParam.position = 1
   var objType = createObj(owner, n.info)
+  incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType)
 
   var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 4d471bdac..8374c92a7 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -118,7 +118,7 @@ type
     warnNilStatement, warnAnalysisLoophole,
     warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
     warnEachIdentIsTuple, warnShadowIdent, 
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe,
+    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnUser,
     hintSuccess, hintSuccessX,
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
@@ -387,6 +387,7 @@ const
     warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
     warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
     warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
+    warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.",
     warnUninit: "'$1' might not have been initialized [Uninit]",
     warnGcMem: "'$1' uses GC'ed memory [GcMem]",
     warnUser: "$1 [User]", 
@@ -408,7 +409,7 @@ const
     hintUser: "$1 [User]"]
 
 const
-  WarningsToStr*: array[0..25, string] = ["CannotOpenFile", "OctalEscape", 
+  WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", 
     "XIsNeverRead", "XmightNotBeenInit",
     "Deprecated", "ConfigDeprecated",
     "SmallLshouldNotBeUsed", "UnknownMagic", 
@@ -416,7 +417,7 @@ const
     "CommentXIgnored", "NilStmt",
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
     "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", 
-    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "Uninit",
+    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
     "GcMem", "User"]
 
   HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", 
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index db38354ce..b996d0b9b 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -15,8 +15,8 @@ const
   defaultAsmMarkerSymbol* = '!'
   VersionMajor* = 0
   VersionMinor* = 9
-  VersionPatch* = 3
+  VersionPatch* = 4
   VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch
 
-  RodFileVersion* = "1214"       # modify this if the rod-format changes!
+  RodFileVersion* = "1215"       # modify this if the rod-format changes!
 
diff --git a/compiler/options.nim b/compiler/options.nim
index 02719cacc..58a340d21 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -95,7 +95,7 @@ var
                          optBoundsCheck, optOverflowCheck, optAssert, optWarns, 
                          optHints, optStackTrace, optLineTrace,
                          optPatterns, optNilCheck}
-  gGlobalOptions*: TGlobalOptions = {optThreadAnalysis}
+  gGlobalOptions*: TGlobalOptions = {}
   gExitcode*: int8
   gCmd*: TCommands = cmdNone  # the command
   gSelectedGC* = gcRefc       # the selected GC
diff --git a/compiler/parsecfg.nim b/compiler/parsecfg.nim
deleted file mode 100644
index e0d1afff1..000000000
--- a/compiler/parsecfg.nim
+++ /dev/null
@@ -1,346 +0,0 @@
-#
-#
-#            Nimrod's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# A HIGH-PERFORMANCE configuration file parser;
-# the Nimrod version of this file is part of the
-# standard library.
-
-import 
-  llstream, nhashes, strutils, nimlexbase
-
-type 
-  TCfgEventKind* = enum 
-    cfgEof,                   # end of file reached
-    cfgSectionStart,          # a ``[section]`` has been parsed
-    cfgKeyValuePair,          # a ``key=value`` pair has been detected
-    cfgOption,                # a ``--key=value`` command line option
-    cfgError # an error ocurred during parsing; msg contains the
-             # error message
-  TCfgEvent* = object of TObject
-    case kind*: TCfgEventKind
-    of cfgEof: 
-        nil
-
-    of cfgSectionStart: 
-        section*: string
-
-    of cfgKeyValuePair, cfgOption: 
-        key*, value*: string
-
-    of cfgError: 
-        msg*: string
-
-  
-  TTokKind* = enum 
-    tkInvalid, tkEof,         # order is important here!
-    tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
-  TToken*{.final.} = object   # a token
-    kind*: TTokKind           # the type of the token
-    literal*: string          # the parsed (string) literal
-  
-  TParserState* = enum 
-    startState, commaState
-  TCfgParser* = object of TBaseLexer
-    tok*: TToken
-    state*: TParserState
-    filename*: string
-
-
-proc Open*(c: var TCfgParser, filename: string, inputStream: PLLStream)
-proc Close*(c: var TCfgParser)
-proc next*(c: var TCfgParser): TCfgEvent
-proc getColumn*(c: TCfgParser): int
-proc getLine*(c: TCfgParser): int
-proc getFilename*(c: TCfgParser): string
-proc errorStr*(c: TCfgParser, msg: string): string
-# implementation
-
-const 
-  SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} # 
-                                                                           # ----------------------------------------------------------------------------
-
-proc rawGetTok(c: var TCfgParser, tok: var TToken)
-proc open(c: var TCfgParser, filename: string, inputStream: PLLStream) = 
-  openBaseLexer(c, inputStream)
-  c.filename = filename
-  c.state = startState
-  c.tok.kind = tkInvalid
-  c.tok.literal = ""
-  rawGetTok(c, c.tok)
-
-proc close(c: var TCfgParser) = 
-  closeBaseLexer(c)
-
-proc getColumn(c: TCfgParser): int = 
-  result = getColNumber(c, c.bufPos)
-
-proc getLine(c: TCfgParser): int = 
-  result = c.linenumber
-
-proc getFilename(c: TCfgParser): string = 
-  result = c.filename
-
-proc handleHexChar(c: var TCfgParser, xi: var int) = 
-  case c.buf[c.bufpos]
-  of '0'..'9': 
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-  of 'a'..'f': 
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
-    inc(c.bufpos)
-  of 'A'..'F': 
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
-    inc(c.bufpos)
-  else: 
-    nil
-
-proc handleDecChars(c: var TCfgParser, xi: var int) = 
-  while c.buf[c.bufpos] in {'0'..'9'}: 
-    xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-
-proc getEscapedChar(c: var TCfgParser, tok: var TToken) = 
-  var xi: int
-  inc(c.bufpos)               # skip '\'
-  case c.buf[c.bufpos]
-  of 'n', 'N': 
-    tok.literal = tok.literal & "\n"
-    Inc(c.bufpos)
-  of 'r', 'R', 'c', 'C': 
-    add(tok.literal, CR)
-    Inc(c.bufpos)
-  of 'l', 'L': 
-    add(tok.literal, LF)
-    Inc(c.bufpos)
-  of 'f', 'F': 
-    add(tok.literal, FF)
-    inc(c.bufpos)
-  of 'e', 'E': 
-    add(tok.literal, ESC)
-    Inc(c.bufpos)
-  of 'a', 'A': 
-    add(tok.literal, BEL)
-    Inc(c.bufpos)
-  of 'b', 'B': 
-    add(tok.literal, BACKSPACE)
-    Inc(c.bufpos)
-  of 'v', 'V': 
-    add(tok.literal, VT)
-    Inc(c.bufpos)
-  of 't', 'T': 
-    add(tok.literal, Tabulator)
-    Inc(c.bufpos)
-  of '\'', '\"': 
-    add(tok.literal, c.buf[c.bufpos])
-    Inc(c.bufpos)
-  of '\\': 
-    add(tok.literal, '\\')
-    Inc(c.bufpos)
-  of 'x', 'X': 
-    inc(c.bufpos)
-    xi = 0
-    handleHexChar(c, xi)
-    handleHexChar(c, xi)
-    add(tok.literal, Chr(xi))
-  of '0'..'9': 
-    xi = 0
-    handleDecChars(c, xi)
-    if (xi <= 255): add(tok.literal, Chr(xi))
-    else: tok.kind = tkInvalid
-  else: tok.kind = tkInvalid
-  
-proc HandleCRLF(c: var TCfgParser, pos: int): int = 
-  case c.buf[pos]
-  of CR: result = lexbase.HandleCR(c, pos)
-  of LF: result = lexbase.HandleLF(c, pos)
-  else: result = pos
-  
-proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = 
-  var 
-    pos: int
-    ch: Char
-    buf: cstring
-  pos = c.bufPos + 1          # skip "
-  buf = c.buf                 # put `buf` in a register
-  tok.kind = tkSymbol
-  if (buf[pos] == '\"') and (buf[pos + 1] == '\"'): 
-    # long string literal:
-    inc(pos, 2)               # skip ""
-                              # skip leading newline:
-    pos = HandleCRLF(c, pos)
-    buf = c.buf
-    while true: 
-      case buf[pos]
-      of '\"': 
-        if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break 
-        add(tok.literal, '\"')
-        Inc(pos)
-      of CR, LF: 
-        pos = HandleCRLF(c, pos)
-        buf = c.buf
-        tok.literal = tok.literal & "\n"
-      of lexbase.EndOfFile: 
-        tok.kind = tkInvalid
-        break 
-      else: 
-        add(tok.literal, buf[pos])
-        Inc(pos)
-    c.bufpos = pos +
-        3                     # skip the three """
-  else: 
-    # ordinary string literal
-    while true: 
-      ch = buf[pos]
-      if ch == '\"': 
-        inc(pos)              # skip '"'
-        break 
-      if ch in {CR, LF, lexbase.EndOfFile}: 
-        tok.kind = tkInvalid
-        break 
-      if (ch == '\\') and not rawMode: 
-        c.bufPos = pos
-        getEscapedChar(c, tok)
-        pos = c.bufPos
-      else: 
-        add(tok.literal, ch)
-        Inc(pos)
-    c.bufpos = pos
-
-proc getSymbol(c: var TCfgParser, tok: var TToken) = 
-  var 
-    pos: int
-    buf: cstring
-  pos = c.bufpos
-  buf = c.buf
-  while true: 
-    add(tok.literal, buf[pos])
-    Inc(pos)
-    if not (buf[pos] in SymChars): break 
-  c.bufpos = pos
-  tok.kind = tkSymbol
-
-proc skip(c: var TCfgParser) = 
-  var 
-    buf: cstring
-    pos: int
-  pos = c.bufpos
-  buf = c.buf
-  while true: 
-    case buf[pos]
-    of ' ': 
-      Inc(pos)
-    of Tabulator: 
-      inc(pos)
-    of '#', ';': 
-      while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): inc(pos)
-    of CR, LF: 
-      pos = HandleCRLF(c, pos)
-      buf = c.buf
-    else: 
-      break                   # EndOfFile also leaves the loop
-  c.bufpos = pos
-
-proc rawGetTok(c: var TCfgParser, tok: var TToken) = 
-  tok.kind = tkInvalid
-  setlen(tok.literal, 0)
-  skip(c)
-  case c.buf[c.bufpos]
-  of '=': 
-    tok.kind = tkEquals
-    inc(c.bufpos)
-    tok.literal = "="
-  of '-': 
-    inc(c.bufPos)
-    if c.buf[c.bufPos] == '-': inc(c.bufPos)
-    tok.kind = tkDashDash
-    tok.literal = "--"
-  of ':': 
-    tok.kind = tkColon
-    inc(c.bufpos)
-    tok.literal = ":"
-  of 'r', 'R': 
-    if c.buf[c.bufPos + 1] == '\"': 
-      Inc(c.bufPos)
-      getString(c, tok, true)
-    else: 
-      getSymbol(c, tok)
-  of '[': 
-    tok.kind = tkBracketLe
-    inc(c.bufpos)
-    tok.literal = "["
-  of ']': 
-    tok.kind = tkBracketRi
-    Inc(c.bufpos)
-    tok.literal = "]"
-  of '\"': 
-    getString(c, tok, false)
-  of lexbase.EndOfFile: 
-    tok.kind = tkEof
-  else: getSymbol(c, tok)
-  
-proc errorStr(c: TCfgParser, msg: string): string = 
-  result = `%`("$1($2, $3) Error: $4", 
-               [c.filename, $(getLine(c)), $(getColumn(c)), msg])
-
-proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = 
-  if c.tok.kind == tkSymbol: 
-    result.kind = kind
-    result.key = c.tok.literal
-    result.value = ""
-    rawGetTok(c, c.tok)
-    while c.tok.literal == ".": 
-      add(result.key, '.')
-      rawGetTok(c, c.tok)
-      if c.tok.kind == tkSymbol: 
-        add(result.key, c.tok.literal)
-        rawGetTok(c, c.tok)
-      else: 
-        result.kind = cfgError
-        result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
-        break 
-    if c.tok.kind in {tkEquals, tkColon}: 
-      rawGetTok(c, c.tok)
-      if c.tok.kind == tkSymbol: 
-        result.value = c.tok.literal
-      else: 
-        result.kind = cfgError
-        result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
-      rawGetTok(c, c.tok)
-  else: 
-    result.kind = cfgError
-    result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
-    rawGetTok(c, c.tok)
-
-proc next(c: var TCfgParser): TCfgEvent = 
-  case c.tok.kind
-  of tkEof: 
-    result.kind = cfgEof
-  of tkDashDash: 
-    rawGetTok(c, c.tok)
-    result = getKeyValPair(c, cfgOption)
-  of tkSymbol: 
-    result = getKeyValPair(c, cfgKeyValuePair)
-  of tkBracketLe: 
-    rawGetTok(c, c.tok)
-    if c.tok.kind == tkSymbol: 
-      result.kind = cfgSectionStart
-      result.section = c.tok.literal
-    else: 
-      result.kind = cfgError
-      result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
-    rawGetTok(c, c.tok)
-    if c.tok.kind == tkBracketRi: 
-      rawGetTok(c, c.tok)
-    else: 
-      result.kind = cfgError
-      result.msg = errorStr(c, "\']\' expected, but found: " & c.tok.literal)
-  of tkInvalid, tkBracketRi, tkEquals, tkColon: 
-    result.kind = cfgError
-    result.msg = errorStr(c, "invalid token: " & c.tok.literal)
-    rawGetTok(c, c.tok)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 3dc31e7ac..66a1a4954 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,7 +13,7 @@
 import 
   strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, 
   condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, 
-  nimsets, syntaxes, times, rodread, semthreads, idgen
+  nimsets, syntaxes, times, rodread, idgen
 
 type  
   TPassContext* = object of TObject # the pass's context
@@ -74,7 +74,8 @@ proc astNeeded*(s: PSym): bool =
       ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
       (s.typ.callConv != ccInline) and 
       (s.ast.sons[genericParamsPos].kind == nkEmpty): 
-    result = semthreads.needsGlobalAnalysis()
+    result = false
+    # XXX this doesn't really make sense with excessive CTFE
   else:
     result = true
   
diff --git a/compiler/pendx.nim b/compiler/pendx.nim
deleted file mode 100644
index 2942968a0..000000000
--- a/compiler/pendx.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-#
-#           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import 
-  llstream, lexer, parser, idents, strutils, ast, msgs
-
-proc ParseAll*(p: var TParser): PNode = 
-  result = nil
-
-proc parseTopLevelStmt*(p: var TParser): PNode = 
-  result = nil
-  
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 88fa516bf..db9fe7cbe 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -53,7 +53,7 @@ const
     wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
     wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
     wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
-    wBorrow}
+    wBorrow, wGcSafe}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, 
     wImportCpp, wImportObjC, wError}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, 
@@ -689,10 +689,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           incl(sym.flags, sfProcvar)
           if sym.typ != nil: incl(sym.typ.flags, tfThread)
         of wGcSafe:
-          noVal(it)
-          incl(sym.flags, sfThread)
-          if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
-          else: invalidPragma(it)
+          if optThreadAnalysis in gGlobalOptions:
+            noVal(it)
+            if sym.kind != skType: incl(sym.flags, sfThread)
+            if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
+            else: invalidPragma(it)
         of wPacked:
           noVal(it)
           if sym.typ == nil: invalidPragma(it)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index e7bff0665..7d129caf4 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -14,7 +14,7 @@ import
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
-  semthreads, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
+  intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity
 
 # implementation
@@ -415,12 +415,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
       if getCurrentException() of ESuggestDone: result = nil
       else: result = ast.emptyNode
       #if gCmd == cmdIdeTools: findSuggest(c, n)
-  
-proc checkThreads(c: PContext) =
-  if not needsGlobalAnalysis(): return
-  for i in 0 .. c.threadEntries.len-1:
-    semthreads.analyseThreadProc(c.threadEntries[i])
-  
+    
 proc myClose(context: PPassContext, n: PNode): PNode = 
   var c = PContext(context)
   closeScope(c)         # close module's scope
@@ -431,7 +426,6 @@ proc myClose(context: PPassContext, n: PNode): PNode =
   addCodeForGenerics(c, result)
   if c.module.ast != nil:
     result.add(c.module.ast)
-  checkThreads(c)
   popOwner()
   popProcCon(c)
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 4cb5ad38c..987a70a41 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -57,7 +57,6 @@ type
                                # can access private object fields
     instCounter*: int          # to prevent endless instantiations
    
-    threadEntries*: TSymSeq    # list of thread entries to check
     ambiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
                                # store this info in the syms themselves!)
     inTypeClass*: int          # > 0 if we are in a user-defined type class
@@ -170,7 +169,6 @@ proc newContext(module: PSym): PContext =
   append(result.optionStack, newOptionEntry())
   result.module = module
   result.friendModule = module
-  result.threadEntries = @[]
   result.converters = @[]
   result.patterns = @[]
   result.includedFiles = initIntSet()
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index caaab2291..79abfaf4d 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -404,7 +404,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
      mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, 
      mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, 
      mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait,
-     mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym: 
+     mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn:
     discard
   of mRand:
     result = newIntNodeT(math.random(a.getInt.int), n)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index a5149a842..f7d5fa6f8 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -127,9 +127,6 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
   if {sfNoSideEffect, sfSideEffect} * s.flags ==
       {sfNoSideEffect, sfSideEffect}: 
     localError(s.info, errXhasSideEffects, s.name.s)
-  elif sfThread in s.flags and semthreads.needsGlobalAnalysis() and 
-      s.ast.sons[genericParamsPos].kind == nkEmpty:
-    c.threadEntries.add(s)
 
 proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index b2b91490c..6235fb76a 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -71,7 +71,7 @@ type
     init: seq[int] # list of initialized variables
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
-    gcUnsafe: bool
+    gcUnsafe, isRecursive: bool
   PEffects = var TEffects
 
 proc isLocalVar(a: PEffects, s: PSym): bool =
@@ -113,7 +113,8 @@ proc useVar(a: PEffects, n: PNode) =
   if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar:
     when trackGlobals:
       a.addUse(copyNode(n))
-    if tfHasGCedMem in s.typ.flags: 
+    if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and 
+        tfGcSafe notin s.typ.flags:
       message(n.info, warnGcUnsafe, renderTree(n))
       a.gcUnsafe = true
 
@@ -502,7 +503,9 @@ proc track(tracked: PEffects, n: PNode) =
     # are indistinguishable from normal procs (both have tyProc type) and
     # we can detect them only by checking for attached nkEffectList.
     if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList:
-      if notGcSafe(op) and not importedFromC(a):
+      if a.kind == nkSym and a.sym == tracked.owner:
+        tracked.isRecursive = true
+      elif notGcSafe(op) and not importedFromC(a):
         message(n.info, warnGcUnsafe, renderTree(n))
         tracked.gcUnsafe = true
       var effectList = op.n.sons[0]
@@ -515,6 +518,7 @@ proc track(tracked: PEffects, n: PNode) =
           addEffect(tracked, createRaise(n))
           addTag(tracked, createTag(n))
           when trackGlobals: addUse(tracked, createAnyGlobal(n))
+          # XXX handle 'gcsafe' properly for callbacks!
       else:
         mergeEffects(tracked, effectList.sons[exceptionEffects], n)
         mergeTags(tracked, effectList.sons[tagEffects], n)
@@ -701,10 +705,12 @@ proc trackProc*(s: PSym, body: PNode) =
       checkRaisesSpec(usesSpec, t.uses,
         "uses an unlisted global variable: ", hints=on, symbolPredicate)
       effects.sons[usesEffects] = usesSpec
-  if sfThread in s.flags and t.gcUnsafe:
-    localError(s.info, "'$1' is not GC-safe" % s.name.s)
-  if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
-  
+  if optThreadAnalysis in gGlobalOptions:
+    if sfThread in s.flags and t.gcUnsafe:
+      localError(s.info, warnGcUnsafe2, s.name.s)
+      #localError(s.info, "'$1' is not GC-safe" % s.name.s)
+    if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
+
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
                 nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim
deleted file mode 100644
index d3426ca3e..000000000
--- a/compiler/semthreads.nim
+++ /dev/null
@@ -1,390 +0,0 @@
-#
-#
-#           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Semantic analysis that deals with threads: Possible race conditions should
-## be reported some day.
-##
-## 
-## ========================
-## No heap sharing analysis
-## ========================
-##
-## The only crucial operation that can violate the heap invariants is the
-## write access. The analysis needs to distinguish between 'unknown', 'mine',
-## and 'theirs' memory and pointers. Assignments 'whatever <- unknown' are 
-## invalid, and so are 'theirs <- whatever' but not 'mine <- theirs'. Since
-## strings and sequences are heap allocated they are affected too:
-##
-## .. code-block:: nimrod
-##   proc p() = 
-##     global = "alloc this string" # ugh!
-##
-## Thus the analysis is concerned with any type that contains a GC'ed
-## reference...
-## If the type system would distinguish between 'ref' and '!ref' and threads
-## could not have '!ref' as input parameters the analysis could simply need to
-## reject any write access to a global variable which contains GC'ed data.
-## Thanks to the write barrier of the GC, this is exactly what needs to be
-## done! Every write access to a global that contains GC'ed data needs to
-## be prevented! Unfortunately '!ref' is not implemented yet...
-##
-## The assignment target is essential for the algorithm: only 
-## write access to heap locations and global variables are critical and need
-## to be checked. Access via 'var' parameters is no problem to analyse since
-## we need the arguments' locations in the analysis.
-##
-## However, this is tricky: 
-##  
-##  var x = globalVar     # 'x' points to 'theirs'
-##  while true:
-##    globalVar = x       # NOT OK: 'theirs <- theirs' invalid due to
-##                        # write barrier!
-##    x = "new string"    # ugh: 'x is toUnknown'!
-##
-##  --> Solution: toUnknown is never allowed anywhere!
-##
-##
-## Beware that the same proc might need to be
-## analysed multiple times! Oh and watch out for recursion! Recursion is handled
-## by a stack of symbols that we are processing, if we come back to the same
-## symbol, we have to skip this check (assume no error in the recursive case).
-## However this is wrong. We need to check for the particular combination
-## of (procsym, threadOwner(arg1), threadOwner(arg2), ...)!
-
-import
-  ast, astalgo, strutils, hashes, options, msgs, idents, types, os,
-  renderer, tables, rodread
-
-type
-  TThreadOwner = enum
-    toUndefined, # not computed yet 
-    toVoid,      # no return type
-    toNil,       # cycle in computation or nil: can be overwritten
-    toTheirs,    # some other heap
-    toMine       # mine heap
-
-  TCall = object {.pure.}
-    callee: PSym              # what if callee is an indirect call?
-    args: seq[TThreadOwner]
-
-  PProcCtx = ref TProcCtx
-  TProcCtx = object {.pure.}
-    nxt: PProcCtx             # can be stacked
-    mapping: tables.TTable[int, TThreadOwner] # int = symbol ID
-    owner: PSym               # current owner
-
-var
-  computed = tables.initTable[TCall, TThreadOwner]()
-
-proc hash(c: TCall): THash =
-  result = hash(c.callee.id)
-  for a in items(c.args): result = result !& hash(ord(a))
-  result = !$result
-
-proc `==`(a, b: TCall): bool =
-  if a.callee != b.callee: return
-  if a.args.len != b.args.len: return
-  for i in 0..a.args.len-1:
-    if a.args[i] != b.args[i]: return
-  result = true
-
-proc newProcCtx(owner: PSym): PProcCtx =
-  assert owner != nil
-  new(result)
-  result.mapping = tables.initTable[int, TThreadOwner]()
-  result.owner = owner
-
-proc analyse(c: PProcCtx, n: PNode): TThreadOwner
-
-proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner =
-  var v = n.sym
-  result = c.mapping[v.id]
-  if result != toUndefined: return
-  case v.kind
-  of skVar, skForVar, skLet, skResult:
-    result = toNil
-    if sfGlobal in v.flags:
-      if sfThread in v.flags: 
-        result = toMine 
-      elif containsGarbageCollectedRef(v.typ):
-        result = toTheirs
-  of skTemp: result = toNil
-  of skConst: result = toMine
-  of skParam: 
-    result = c.mapping[v.id]
-    if result == toUndefined:
-      internalError(n.info, "param not set: " & v.name.s)
-  else:
-    result = toNil
-  c.mapping[v.id] = result
-
-proc lvalueSym(n: PNode): PNode =
-  result = n
-  while result.kind in {nkDotExpr, nkCheckedFieldExpr,
-                        nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
-    result = result.sons[0]
-
-proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) =
-  if owner notin {toNil, toMine, toTheirs}:
-    internalError(n.info, "writeAccess: " & $owner)
-  var a = lvalueSym(n)
-  if a.kind == nkSym: 
-    var v = a.sym
-    var lastOwner = analyseSym(c, a)
-    case lastOwner
-    of toNil:
-      # fine, toNil can be overwritten
-      var newOwner: TThreadOwner
-      if sfGlobal in v.flags:
-        newOwner = owner
-      elif containsTyRef(v.typ):
-        # ``var local = gNode`` --> ok, but ``local`` is theirs! 
-        newOwner = owner
-      else:
-        # ``var local = gString`` --> string copy: ``local`` is mine! 
-        newOwner = toMine
-        # XXX BUG what if the tuple contains both ``tyRef`` and ``tyString``?
-      c.mapping[v.id] = newOwner
-    of toVoid, toUndefined: internalError(n.info, "writeAccess")
-    of toTheirs: message(n.info, warnWriteToForeignHeap)
-    of toMine:
-      if lastOwner != owner and owner != toNil:
-        message(n.info, warnDifferentHeaps)
-  else:
-    # we could not backtrack to a concrete symbol, but that's fine:
-    var lastOwner = analyse(c, n)
-    case lastOwner
-    of toNil: discard # fine, toNil can be overwritten
-    of toVoid, toUndefined: internalError(n.info, "writeAccess")
-    of toTheirs: message(n.info, warnWriteToForeignHeap)
-    of toMine:
-      if lastOwner != owner and owner != toNil:
-        message(n.info, warnDifferentHeaps)
-
-proc analyseAssign(c: PProcCtx, le, ri: PNode) =
-  var y = analyse(c, ri) # read access; ok
-  writeAccess(c, le, y)
-
-proc analyseAssign(c: PProcCtx, n: PNode) =
-  analyseAssign(c, n.sons[0], n.sons[1])
-
-proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner =
-  var prc = n[0].sym
-  var newCtx = newProcCtx(prc)
-  var call: TCall
-  call.callee = prc
-  newSeq(call.args, n.len-1)
-  for i in 1..n.len-1:
-    call.args[i-1] = analyse(c, n[i])
-  if not computed.hasKey(call):
-    computed[call] = toUndefined # we are computing it
-    let prctyp = skipTypes(prc.typ, abstractInst).n
-    for i in 1.. prctyp.len-1: 
-      var formal = prctyp.sons[i].sym 
-      newCtx.mapping[formal.id] = call.args[i-1]
-    pushInfoContext(n.info)
-    result = analyse(newCtx, prc.getBody)
-    if prc.ast.sons[bodyPos].kind == nkEmpty and 
-       {sfNoSideEffect, sfThread, sfImportc} * prc.flags == {}:
-      message(n.info, warnAnalysisLoophole, renderTree(n))
-      if result == toUndefined: result = toNil
-    if prc.typ.sons[0] != nil:
-      if prc.ast.len > resultPos:
-        result = newCtx.mapping[prc.ast.sons[resultPos].sym.id]
-        # if the proc body does not set 'result', nor 'return's something
-        # explicitely, it returns a binary zero, so 'toNil' is correct:
-        if result == toUndefined: result = toNil
-      else:
-        result = toNil
-    else:
-      result = toVoid
-    computed[call] = result
-    popInfoContext()
-  else:
-    result = computed[call]
-    if result == toUndefined:
-      # ugh, cycle! We are already computing it but don't know the
-      # outcome yet...
-      if prc.typ.sons[0] == nil: result = toVoid
-      else: result = toNil
-
-proc analyseVarTuple(c: PProcCtx, n: PNode) =
-  if n.kind != nkVarTuple: internalError(n.info, "analyseVarTuple")
-  var L = n.len
-  for i in countup(0, L-3): analyseAssign(c, n.sons[i], n.sons[L-1])
-
-proc analyseSingleVar(c: PProcCtx, a: PNode) =
-  if a.sons[2].kind != nkEmpty: analyseAssign(c, a.sons[0], a.sons[2])
-
-proc analyseVarSection(c: PProcCtx, n: PNode): TThreadOwner = 
-  for i in countup(0, sonsLen(n) - 1): 
-    var a = n.sons[i]
-    if a.kind == nkCommentStmt: continue 
-    if a.kind == nkIdentDefs: 
-      #assert(a.sons[0].kind == nkSym); also valid for after
-      # closure transformation:
-      analyseSingleVar(c, a)
-    else:
-      analyseVarTuple(c, a)
-  result = toVoid
-
-proc analyseConstSection(c: PProcCtx, t: PNode): TThreadOwner =
-  for i in countup(0, sonsLen(t) - 1): 
-    var it = t.sons[i]
-    if it.kind == nkCommentStmt: continue 
-    if it.kind != nkConstDef: internalError(t.info, "analyseConstSection")
-    if sfFakeConst in it.sons[0].sym.flags: analyseSingleVar(c, it)
-  result = toVoid
-
-template aggregateOwner(result, ana: expr) =
-  var a = ana # eval once
-  if result != a:
-    if result == toNil: result = a
-    elif a != toNil: message(n.info, warnDifferentHeaps)
-
-proc analyseArgs(c: PProcCtx, n: PNode, start = 1) =
-  for i in start..n.len-1: discard analyse(c, n[i])
-
-proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner =
-  if n[0].kind != nkSym or n[0].sym.kind != skProc:
-    if {tfNoSideEffect, tfThread} * n[0].typ.flags == {}:
-      message(n.info, warnAnalysisLoophole, renderTree(n))
-    result = toNil
-  else:
-    var prc = n[0].sym
-    case prc.magic
-    of mNone: 
-      if sfSystemModule in prc.owner.flags:
-        # System module proc does no harm :-)
-        analyseArgs(c, n)
-        if prc.typ.sons[0] == nil: result = toVoid
-        else: result = toNil
-      else:
-        result = analyseCall(c, n)
-    of mNew, mNewFinalize, mNewSeq, mSetLengthStr, mSetLengthSeq,
-        mAppendSeqElem, mReset, mAppendStrCh, mAppendStrStr:
-      writeAccess(c, n[1], toMine)
-      result = toVoid
-    of mSwap:
-      var a = analyse(c, n[2])
-      writeAccess(c, n[1], a)
-      writeAccess(c, n[2], a)
-      result = toVoid
-    of mIntToStr, mInt64ToStr, mFloatToStr, mBoolToStr, mCharToStr, 
-        mCStrToStr, mStrToStr, mEnumToStr,
-        mConStrStr, mConArrArr, mConArrT, 
-        mConTArr, mConTT, mSlice, 
-        mRepr, mArrToSeq, mCopyStr, mCopyStrLast, 
-        mNewString, mNewStringOfCap:
-      analyseArgs(c, n)
-      result = toMine
-    else:
-      # don't recurse, but check args:
-      analyseArgs(c, n)
-      if prc.typ.sons[0] == nil: result = toVoid
-      else: result = toNil
-
-proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
-  case n.kind
-  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
-     nkCallStrLit, nkHiddenCallConv:
-    result = analyseOp(c, n)
-  of nkAsgn, nkFastAsgn:
-    analyseAssign(c, n)
-    result = toVoid
-  of nkSym: result = analyseSym(c, n)
-  of nkEmpty, nkNone: result = toVoid
-  of nkNilLit, nkCharLit..nkFloat64Lit: result = toNil
-  of nkStrLit..nkTripleStrLit: result = toMine
-  of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
-    # field access:
-    # pointer deref or array access:
-    result = analyse(c, n.sons[0])
-  of nkBind: result = analyse(c, n.sons[0])
-  of nkPar, nkCurly, nkBracket, nkRange:
-    # container construction:
-    result = toNil # nothing until later
-    for i in 0..n.len-1: aggregateOwner(result, analyse(c, n[i]))
-  of nkObjConstr:
-    if n.typ != nil and containsGarbageCollectedRef(n.typ):
-      result = toMine
-    else:
-      result = toNil # nothing until later
-    for i in 1..n.len-1: aggregateOwner(result, analyse(c, n[i]))
-  of nkAddr, nkHiddenAddr:
-    var a = lvalueSym(n)
-    if a.kind == nkSym:
-      result = analyseSym(c, a)
-      assert result in {toNil, toMine, toTheirs}
-      if result == toNil:
-        # assume toMine here for consistency:
-        c.mapping[a.sym.id] = toMine
-        result = toMine
-    else:
-      # should never really happen:
-      result = analyse(c, n.sons[0])
-  of nkIfExpr: 
-    result = toNil
-    for i in countup(0, sonsLen(n) - 1):
-      var it = n.sons[i]
-      if it.len == 2:
-        discard analyse(c, it.sons[0])
-        aggregateOwner(result, analyse(c, it.sons[1]))
-      else:
-        aggregateOwner(result, analyse(c, it.sons[0]))
-  of nkStmtListExpr, nkBlockExpr:
-    var n = if n.kind == nkBlockExpr: n.sons[1] else: n
-    var L = sonsLen(n)
-    for i in countup(0, L-2): discard analyse(c, n.sons[i])
-    if L > 0: result = analyse(c, n.sons[L-1])
-    else: result = toVoid
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: 
-    result = analyse(c, n.sons[1])
-  of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64,
-     nkChckRange, nkCheckedFieldExpr, nkObjDownConv, 
-     nkObjUpConv:
-    result = analyse(c, n.sons[0])
-  of nkRaiseStmt:
-    var a = analyse(c, n.sons[0])
-    if a != toMine: message(n.info, warnDifferentHeaps)
-    result = toVoid
-  of nkVarSection, nkLetSection: result = analyseVarSection(c, n)
-  of nkConstSection: result = analyseConstSection(c, n)
-  of nkTypeSection, nkCommentStmt: result = toVoid
-  of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, 
-     nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally:
-    for i in 0 .. <n.len: discard analyse(c, n[i])
-    result = toVoid
-  of nkBreakStmt, nkContinueStmt: result = toVoid
-  of nkReturnStmt, nkDiscardStmt: 
-    if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0])
-    else: result = toVoid
-  of nkLambdaKinds, nkClosure:
-    result = toMine
-  of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
-     nkConverterDef, nkMacroDef, nkTemplateDef,
-     nkGotoState, nkState, nkBreakState, nkType, nkIdent:
-      result = toVoid
-  of nkExprColonExpr:
-    result = analyse(c, n.sons[1])
-  else: internalError(n.info, "analysis not implemented for: " & $n.kind)
-
-proc analyseThreadProc*(prc: PSym) =
-  var c = newProcCtx(prc)
-  var formals = skipTypes(prc.typ, abstractInst).n
-  for i in 1 .. formals.len-1:
-    var formal = formals.sons[i].sym 
-    # the input is copied and belongs to the thread:
-    c.mapping[formal.id] = toMine
-  discard analyse(c, prc.getBody)
-
-proc needsGlobalAnalysis*: bool =
-  result = gGlobalOptions * {optThreads, optThreadAnalysis} == 
-                            {optThreads, optThreadAnalysis}
-
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 384bdc8a3..6289ecc85 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -194,10 +194,15 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
     if isRange(n[1]):
       result = semRangeAux(c, n[1], prev)
       let n = result.n
-      if n.sons[0].kind in {nkCharLit..nkUInt64Lit}:
-        if n.sons[0].intVal > 0 or n.sons[1].intVal < 0:
-          incl(result.flags, tfNeedsInit)
-      elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0:
+      if n.sons[0].kind in {nkCharLit..nkUInt64Lit} and n.sons[0].intVal > 0:
+        incl(result.flags, tfNeedsInit)
+      elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0:
+        incl(result.flags, tfNeedsInit)
+      elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and 
+          n.sons[0].floatVal > 0.0:
+        incl(result.flags, tfNeedsInit)
+      elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and 
+          n.sons[1].floatVal < 0.0:
         incl(result.flags, tfNeedsInit)
     else:
       localError(n.sons[0].info, errRangeExpected)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 8b9b7b13b..33de40f34 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -19,7 +19,7 @@ proc sharedPtrCheck(info: TLineInfo, t: PType) =
     if t.sons[0].sym.magic in {mShared, mGuarded}:
       incl(t.flags, tfShared)
       if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded)
-      if tfHasGCedMem in t.flags:
+      if tfHasGCedMem in t.flags or t.isGCedMem:
         localError(info, errGenerated,
                    "shared memory may not refer to GC'ed thread local memory")
 
@@ -352,8 +352,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     result = handleGenericInvokation(cl, t)
 
   of tyGenericBody:
-    internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" )
-    result = replaceTypeVarsT(cl, lastSon(t))
+    localError(cl.info, errCannotInstantiateX, typeToString(t))
+    result = t
+    #result = replaceTypeVarsT(cl, lastSon(t))
 
   of tyFromExpr:
     if cl.allowMetaTypes: return
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 5d3ed05f7..4b91a067e 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -148,6 +148,9 @@ proc sumGeneric(t: PType): int =
       result = ord(t.kind == tyGenericInvokation)
       for i in 0 .. <t.len: result += t.sons[i].sumGeneric
       break
+    of tyProc:
+      # proc matche proc better than 'stmt' to disambiguate 'spawn'
+      return 1
     of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
     else: return 0
 
@@ -851,7 +854,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyAnd:
     considerPreviousT:
       for branch in f.sons:
-        if typeRel(c, branch, aOrig) == isNone:
+        if typeRel(c, branch, aOrig) < isSubtype:
           return isNone
 
       bindingRet isGeneric
@@ -859,7 +862,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyOr:
     considerPreviousT:
       for branch in f.sons:
-        if typeRel(c, branch, aOrig) != isNone:
+        if typeRel(c, branch, aOrig) >= isSubtype:
           bindingRet isGeneric
        
       return isNone
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 49611f649..fc6ba2f77 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -253,6 +253,7 @@ proc findUsages(node: PNode, s: PSym) =
     lastLineInfo = node.info
 
 proc findDefinition(node: PNode, s: PSym) =
+  if node.isNil or s.isNil: return
   if isTracked(node.info, s.name.s.len):
     suggestWriteln(symToStr(s, isLocal=false, sectionDef))
     suggestQuit()
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 478c2a837..d5062544f 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -62,7 +62,6 @@ proc parseAll(p: var TParsers): PNode =
   of skinEndX: 
     internalError("parser to implement") 
     result = ast.emptyNode
-    # skinEndX: result := pendx.parseAll(p.parser);
   
 proc parseTopLevelStmt(p: var TParsers): PNode = 
   case p.skin
@@ -73,7 +72,6 @@ proc parseTopLevelStmt(p: var TParsers): PNode =
   of skinEndX: 
     internalError("parser to implement") 
     result = ast.emptyNode
-    #skinEndX: result := pendx.parseTopLevelStmt(p.parser);
   
 proc utf8Bom(s: string): int = 
   if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): 
diff --git a/compiler/types.nim b/compiler/types.nim
index 1de0fc103..1f266d64f 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -342,9 +342,10 @@ proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool =
       result = t.id == startId
     # Inheritance can introduce cyclic types, however this is not relevant
     # as the type that is passed to 'new' is statically known!
-    #if t.kind == tyObject and tfFinal notin t.flags:
-    #  # damn inheritance may introduce cycles:
-    #  result = true
+    # er but we use it also for the write barrier ...
+    if t.kind == tyObject and tfFinal notin t.flags:
+      # damn inheritance may introduce cycles:
+      result = true
   of tyProc: result = typ.callConv == ccClosure
   else: discard
 
@@ -537,7 +538,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       add(prag, "noSideEffect")
     if tfThread in t.flags:
       addSep(prag)
-      add(prag, "thread")
+      add(prag, "gcsafe")
     if len(prag) != 0: add(result, "{." & prag & ".}")
   of tyVarargs, tyIter:
     result = typeToStr[t.kind] % typeToString(t.sons[0])
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index ebb9b7e15..790bd1047 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -69,7 +69,7 @@ proc renderType(n: PNode): string =
     for i in 1 .. <len(n): result.add(renderType(n[i]) & ',')
     result[<len(result)] = ']'
   else: result = ""
-  assert (not result.isNil)
+  assert(not result.isNil)
 
 
 proc renderParamTypes(found: var seq[string], n: PNode) =
@@ -86,13 +86,11 @@ proc renderParamTypes(found: var seq[string], n: PNode) =
     let typePos = len(n) - 2
     assert typePos > 0
     var typeStr = renderType(n[typePos])
-    if typeStr.len < 1:
+    if typeStr.len < 1 and n[typePos+1].kind != nkEmpty:
       # Try with the last node, maybe its a default value.
-      assert n[typePos+1].kind != nkEmpty
       let typ = n[typePos+1].typ
       if not typ.isNil: typeStr = typeToString(typ, preferExported)
-      if typeStr.len < 1:
-        return
+      if typeStr.len < 1: return
     for i in 0 .. <typePos:
       assert n[i].kind == nkIdent
       found.add(typeStr)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index fb8749250..836f90967 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -354,6 +354,11 @@ template handleJmpBack() {.dirty.} =
       globalError(c.debug[pc], errTooManyIterations)
   dec(c.loopIterations)
 
+proc skipColon(n: PNode): PNode =
+  result = n
+  if n.kind == nkExprColonExpr:
+    result = n.sons[1]
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
   var pc = start
   var tos = tos
@@ -454,7 +459,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeBC(rkNode)
       let src = regs[rb].node
       if src.kind notin {nkEmpty..nkNilLit}:
-        let n = src.sons[rc]
+        let n = src.sons[rc].skipColon
         regs[ra].node = n
       else:
         stackTrace(c, tos, pc, errNilAccess)
@@ -969,7 +974,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcRepr:
       decodeB(rkNode)
       createStr regs[ra]
-      regs[ra].node.strVal = renderTree(regs[rb].node, {renderNoComments})
+      regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments})
     of opcQuit:
       if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
         message(c.debug[pc], hintQuitCalled)
@@ -1099,6 +1104,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                                      c.module)
     of opcGorge:
       decodeBC(rkNode)
+      createStr regs[ra]
       regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
                                      regs[rc].node.strVal)
     of opcNError:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 7c0c3d4f5..84577bb22 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -70,6 +70,9 @@ proc echoCode*(c: PCtx, start=0) {.deprecated.} =
   echo buf
 
 proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
+  ## Takes the registers `b` and `c`, applies the operation `opc` to them, and
+  ## stores the result into register `a`
+  ## The node is needed for debug information
   assert opc.ord < 255
   let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
                            (b.uint32 shl 16'u32) or
@@ -78,6 +81,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
   ctx.debug.add(n.info)
 
 proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
+  # Takes the `b` register and the immediate `imm`, appies the operation `opc`,
+  # and stores the output value into `a`.
+  # `imm` is signed and must be within [-127, 128]
+  assert(imm >= -127 and imm <= 128)
   let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
                            (b.uint32 shl 16'u32) or
                            (imm+byteExcess).uint32 shl 24'u32).TInstr
@@ -85,6 +92,9 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
   c.debug.add(n.info)
 
 proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
+  # Applies `opc` to `bx` and stores it into register `a`
+  # `bx` must be signed and in the range [-32767, 32768]
+  assert(bx >= -32767 and bx <= 32768)
   let ins = (opc.uint32 or a.uint32 shl 8'u32 or 
             (bx+wordExcess).uint32 shl 16'u32).TInstr
   c.code.add(ins)
@@ -316,15 +326,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
   c.patch(L1)
 
 proc canonValue*(n: PNode): PNode =
-  if n.kind == nkExprColonExpr:
-    result = n.sons[1]
-  elif n.hasSubnodeWith(nkExprColonExpr):
-    result = n.copyNode
-    newSeq(result.sons, n.len)
-    for i in 0.. <n.len:
-      result.sons[i] = canonValue(n.sons[i])
-  else:
-    result = n
+  result = n
 
 proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len