summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2014-04-20 14:00:04 +0200
committerAraq <rumpf_a@web.de>2014-04-20 14:00:04 +0200
commitbe6474af638b72aabeb70cfc5f477cc5fb7af0ce (patch)
treef25002ea96fcfbd69997e22208c55eb3930eeaf0
parent39e4e3f20570e804e3059574eafe8f78b2d8a9df (diff)
downloadNim-be6474af638b72aabeb70cfc5f477cc5fb7af0ce.tar.gz
removed flawed thread analysis pass
-rw-r--r--compiler/ast.nim8
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/passes.nim5
-rw-r--r--compiler/sem.nim10
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/seminst.nim3
-rw-r--r--compiler/sempass2.nim2
-rw-r--r--compiler/semthreads.nim390
-rw-r--r--compiler/semtypinst.nim2
-rw-r--r--compiler/types.nim2
-rw-r--r--doc/advopt.txt1
-rw-r--r--tests/actiontable/tactiontable2.nim2
-rw-r--r--tests/bind/tnicerrorforsymchoice.nim8
-rw-r--r--tests/closure/tinvalidclosure.nim2
-rw-r--r--tests/threads/tthreadanalysis2.nim6
-rw-r--r--tests/threads/tthreadanalysis3.nim51
17 files changed, 23 insertions, 476 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/commands.nim b/compiler/commands.nim
index 8339219ed..53e05aea1 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -168,7 +168,6 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake)
   of "warnings", "w": result = contains(gOptions, optWarns)
   of "hints": result = contains(gOptions, optHints)
-  of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis)
   of "stacktrace": result = contains(gOptions, optStackTrace)
   of "linetrace": result = contains(gOptions, optLineTrace)
   of "debugger": result = contains(gOptions, optEndb)
@@ -330,7 +329,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   of "warning": processSpecificNote(arg, wWarning, pass, info)
   of "hint": processSpecificNote(arg, wHint, pass, info)
   of "hints": processOnOffSwitch({optHints}, arg, pass, info)
-  of "threadanalysis": processOnOffSwitchG({optThreadAnalysis}, arg, pass, info)
   of "stacktrace": processOnOffSwitch({optStackTrace}, arg, pass, info)
   of "linetrace": processOnOffSwitch({optLineTrace}, arg, pass, info)
   of "debugger": 
diff --git a/compiler/options.nim b/compiler/options.nim
index f05354666..211eee360 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -60,7 +60,6 @@ type                          # please make sure we have under 32 options
     optContext,               # ideTools: 'context'
     optDef,                   # ideTools: 'def'
     optUsages,                # ideTools: 'usages'
-    optThreadAnalysis,        # thread analysis pass
     optTaintMode,             # taint mode turned on
     optTlsEmulation,          # thread var emulation turned on
     optGenIndex               # generate index file for documentation;
@@ -95,7 +94,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/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/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/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 72666e47d..295321f4b 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -113,7 +113,7 @@ 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:
       message(n.info, warnGcUnsafe, renderTree(n))
       a.gcUnsafe = true
 
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/semtypinst.nim b/compiler/semtypinst.nim
index 8b9b7b13b..271a01266 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")
 
diff --git a/compiler/types.nim b/compiler/types.nim
index e0e0162ec..1f266d64f 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -538,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/doc/advopt.txt b/doc/advopt.txt
index f5ff90791..143c465fa 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -63,7 +63,6 @@ Advanced options:
   --lineDir:on|off          generation of #line directive on|off
   --embedsrc                embeds the original source code as comments
                             in the generated output
-  --threadanalysis:on|off   turn thread analysis on|off
   --tlsEmulation:on|off     turn thread local storage emulation on|off
   --taintMode:on|off        turn taint mode on|off
   --symbolFiles:on|off      turn symbol files on|off (experimental)
diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim
index 878356321..bfeb1c169 100644
--- a/tests/actiontable/tactiontable2.nim
+++ b/tests/actiontable/tactiontable2.nim
@@ -1,6 +1,6 @@
 discard """
   line: 21
-  errormsg: "invalid type: 'TTable[string, proc (string)]'"
+  errormsg: "invalid type: 'TTable[string, proc (string){.gcsafe.}]'"
 """
 
 import tables
diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim
index bc271dcab..4e7a271b3 100644
--- a/tests/bind/tnicerrorforsymchoice.nim
+++ b/tests/bind/tnicerrorforsymchoice.nim
@@ -1,18 +1,18 @@
 discard """
   line: 18
-  errormsg: "type mismatch: got (proc (TScgi) | proc (PAsyncSocket, PStringTable, string))"
+  errormsg: "type mismatch: got (proc (TScgi) | proc (PAsyncSocket, PStringTable, string){.gcsafe.})"
 """
 
 #bug #442
 import scgi, sockets, asyncio, strtabs
 proc handleSCGIRequest[TScgi: TScgiState | PAsyncScgiState](s: TScgi) =
-  nil
+  discard
 proc handleSCGIRequest(client: PAsyncSocket, headers: PStringTable, 
                        input: string) =
-  nil
+  discard
 
 proc test(handle: proc (client: PAsyncSocket, headers: PStringTable, 
                         input: string), b: int) =
-  nil
+  discard
 
 test(handleSCGIRequest)
diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim
index c06270bfa..06e19df3c 100644
--- a/tests/closure/tinvalidclosure.nim
+++ b/tests/closure/tinvalidclosure.nim
@@ -1,6 +1,6 @@
 discard """
   line: 12
-  errormsg: "type mismatch: got (proc (int){.closure.})"
+  errormsg: "type mismatch: got (proc (int){.closure, gcsafe.})"
 """
 
 proc ugh[T](x: T) {.closure.} =
diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim
index 697e2cb22..bcc09db98 100644
--- a/tests/threads/tthreadanalysis2.nim
+++ b/tests/threads/tthreadanalysis2.nim
@@ -1,7 +1,7 @@
 discard """
   file: "tthreadanalysis2.nim"
-  line: 42
-  errormsg: "write to foreign heap"
+  line: 37
+  errormsg: "'threadFunc' is not GC-safe"
   cmd: "nimrod $target --hints:on --threads:on $options $file"
 """
 
@@ -10,7 +10,7 @@ import os
 var
   thr: array [0..5, TThread[tuple[a, b: int]]]
 
-proc doNothing() = nil
+proc doNothing() = discard
 
 type
   PNode = ref TNode
diff --git a/tests/threads/tthreadanalysis3.nim b/tests/threads/tthreadanalysis3.nim
deleted file mode 100644
index 3c17fe7e2..000000000
--- a/tests/threads/tthreadanalysis3.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-discard """
-  file: "tthreadanalysis3.nim"
-  line: 35
-  errormsg: "write to foreign heap"
-  cmd: "nimrod $target --hints:on --threads:on $options $file"
-"""
-
-import os
-
-var
-  thr: array [0..5, TThread[tuple[a, b: int]]]
-
-proc doNothing() = nil
-
-type
-  PNode = ref TNode
-  TNode = object {.pure.}
-    le, ri: PNode
-    data: string
-
-var
-  root: PNode
-
-proc buildTree(depth: int): PNode =
-  if depth == 3: return nil
-  new(result)
-  result.le = buildTree(depth-1)
-  result.ri = buildTree(depth-1)
-  result.data = $depth
-
-proc echoLeTree(n: PNode) =
-  var it = n
-  while it != nil:
-    echo it.data
-    it = it.le
-
-proc threadFunc(interval: tuple[a, b: int]) {.thread.} = 
-  doNothing()
-  for i in interval.a..interval.b: 
-    var r = buildTree(i)
-    echoLeTree(r) # for local data
-  echoLeTree(root) # and the same for foreign data :-)
-
-proc main =
-  root = buildTree(5)
-  for i in 0..high(thr):
-    createThread(thr[i], threadFunc, (i*100, i*100+50))
-  joinThreads(thr)
-
-main()
-