summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim24
-rw-r--r--compiler/astalgo.nim4
-rw-r--r--compiler/ccgexprs.nim3
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/condsyms.nim3
-rw-r--r--compiler/lambdalifting.nim31
-rw-r--r--compiler/lexer.nim4
-rw-r--r--compiler/lowerings.nim178
-rw-r--r--compiler/msgs.nim13
-rw-r--r--compiler/options.nim29
-rw-r--r--compiler/parser.nim3
-rw-r--r--compiler/passes.nim5
-rw-r--r--compiler/pragmas.nim40
-rw-r--r--compiler/renderer.nim6
-rw-r--r--compiler/sem.nim10
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/semfold.nim2
-rw-r--r--compiler/seminst.nim3
-rw-r--r--compiler/sempass2.nim118
-rw-r--r--compiler/semthreads.nim390
-rw-r--r--compiler/semtypes.nim9
-rw-r--r--compiler/semtypinst.nim16
-rw-r--r--compiler/sigmatch.nim10
-rw-r--r--compiler/types.nim9
-rw-r--r--compiler/vm.nim7
-rw-r--r--compiler/vmgen.nim20
-rw-r--r--compiler/wordrecg.nim14
28 files changed, 443 insertions, 514 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 0652bd3e7..97f48b253 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -13,7 +13,7 @@ import
   msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, 
   intsets, idgen
 
-type 
+type
   TCallingConvention* = enum 
     ccDefault,                # proc has no explicit calling convention
     ccStdCall,                # procedure is stdcall
@@ -299,7 +299,7 @@ const
   nkEffectList* = nkArgList 
   # hacks ahead: an nkEffectList is a node with 4 children:
   exceptionEffects* = 0 # exceptions at position 0
-  readEffects* = 1      # read effects at position 1
+  usesEffects* = 1      # read effects at position 1
   writeEffects* = 2     # write effects at position 2
   tagEffects* = 3       # user defined tag ('gc', 'time' etc.)
   effectListLen* = 4    # list of effects list
@@ -432,7 +432,7 @@ type
     tfAcyclic,        # type is acyclic (for GC optimization)
     tfEnumHasHoles,   # enum cannot be mapped into a range
     tfShallow,        # type can be shallow copied on assignment
-    tfThread,         # proc type is marked as ``thread``
+    tfThread,         # proc type is marked as ``thread``; alias for ``gcsafe``
     tfFromGeneric,    # type is an instantiation of a generic; this is needed
                       # because for instantiations of objects, structural
                       # type equality has to be used
@@ -509,6 +509,7 @@ const
   tfIncompleteStruct* = tfVarargs
   tfUncheckedArray* = tfVarargs
   tfUnion* = tfNoSideEffect
+  tfGcSafe* = tfThread
   skError* = skUnknown
   
   # type flags that are essential for type equality:
@@ -559,7 +560,7 @@ type
     mFloat, mFloat32, mFloat64, mFloat128,
     mBool, mChar, mString, mCstring,
     mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
-    mVoidType, mPNimrodNode,
+    mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn,
     mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
     mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType,
     mNaN, mInf, mNegInf,
@@ -978,6 +979,8 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt =
 var emptyNode* = newNode(nkEmpty)
 # There is a single empty node that is shared! Do not overwrite it!
 
+var anyGlobal* = newSym(skVar, getIdent("*"), nil, unknownLineInfo())
+
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil) or
@@ -1308,10 +1311,13 @@ 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,
-                                             tfHasGCedMem})
+  owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta})
   if tfNotNil in elem.flags:
     if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
       owner.flags.incl tfNotNil
@@ -1328,9 +1334,9 @@ proc propagateToOwner*(owner, elem: PType) =
   if elem.isMetaType:
     owner.flags.incl tfHasMeta
 
-  if elem.kind in {tyString, tyRef, tySequence} or
-      elem.kind == tyProc and elem.callConv == ccClosure:
-    owner.flags.incl tfHasGCedMem
+  if owner.kind != tyProc:
+    if elem.isGCedMem or tfHasGCedMem in elem.flags:
+      owner.flags.incl tfHasGCedMem
 
 proc rawAddSon*(father, son: PType) =
   if isNil(father.sons): father.sons = @[]
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 36dd7f562..dbf13f764 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -448,9 +448,9 @@ proc debug(n: PSym) =
     writeln(stdout, "skUnknown")
   else:
     #writeln(stdout, ropeToStr(symToYaml(n, 0, 1)))
-    writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [
+    writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4, $5", [
       toRope(n.name.s), toRope(n.id), flagsToStr(n.flags), 
-      flagsToStr(n.loc.flags)])))
+      flagsToStr(n.loc.flags), lineInfoToStr(n.info)])))
 
 proc debug(n: PType) = 
   writeln(stdout, ropeToStr(debugType(n)))
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 7c4cc2b80..49350fa9c 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1635,6 +1635,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     localError(e.info, errCannotGenerateCodeForX, e.sons[0].sym.name.s)
   of mSlurp..mQuoteAst:
     localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
+  of mSpawn:
+    let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1])
+    expr(p, n, d)
   else: internalError(e.info, "genMagicExpr: " & $op)
 
 proc genConstExpr(p: BProc, n: PNode): PRope
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 683aed069..f64ebacfb 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -14,7 +14,7 @@ import
   options, intsets,
   nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
   times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
-  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases
+  rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings
 
 when options.hasTinyCBackend:
   import tccgen
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index ddf2075b4..4117fc461 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2014 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -48,6 +48,7 @@ proc initDefines*() =
   defineSymbol("nimbabel")
   defineSymbol("nimcomputedgoto")
   defineSymbol("nimunion")
+  defineSymbol("nimnewshared")
   
   # add platform specific symbols:
   case targetCPU
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 440468ac4..c5b9d0f00 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -141,12 +141,6 @@ type
     closureParam, state, resultSym: PSym # only if isIter
     obj: PType # only if isIter
 
-proc createObj(owner: PSym, info: TLineInfo): PType =
-  result = newType(tyObject, owner)
-  rawAddSon(result, nil)
-  incl result.flags, tfFinal
-  result.n = newNodeI(nkRecList, info)
-
 proc getStateType(iter: PSym): PType =
   var n = newNodeI(nkRange, iter.info)
   addSon(n, newIntNode(nkIntLit, -1))
@@ -189,15 +183,6 @@ proc getEnvParam(routine: PSym): PSym =
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.name.s == paramName:
     result = hidden.sym
-    
-proc addField(obj: PType; s: PSym) =
-  # because of 'gensym' support, we have to mangle the name with its ID.
-  # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
-  let t = skipIntLit(s.typ)
-  field.typ = t
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
 
 proc initIterContext(c: POuterContext, iter: PSym) =
   c.fn = iter
@@ -273,22 +258,6 @@ proc addDep(e, d: PEnv, owner: PSym): PSym =
   rawAddSon(result.typ, d.obj)
   addField(e.obj, result)
   e.deps.add((d, result))
-  
-proc indirectAccess(a: PNode, b: PSym, info: TLineInfo): PNode = 
-  # returns a[].b as a node
-  var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.sons[0]
-  assert deref.typ.kind == tyObject
-  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
-  assert field != nil, b.name.s
-  addSon(deref, a)
-  result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
-  result.typ = field.typ
-
-proc indirectAccess(a, b: PSym, info: TLineInfo): PNode =
-  result = indirectAccess(newSymNode(a), b, info)
 
 proc newCall(a, b: PSym): PNode =
   result = newNodeI(nkCall, a.info)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 217e33675..2bfd8d1eb 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -43,7 +43,7 @@ type
     tkLambda, tkLet,
     tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin, 
     tkObject, tkOf, tkOr, tkOut, 
-    tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShared, tkShl, tkShr, tkStatic,
+    tkProc, tkPtr, tkRaise, tkRef, tkReturn, tkShl, tkShr, tkStatic,
     tkTemplate, 
     tkTry, tkTuple, tkType, tkUsing, 
     tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor,
@@ -79,7 +79,7 @@ const
     "macro", "method", "mixin", "mod", 
     "nil", "not", "notin", "object", "of", "or", 
     "out", "proc", "ptr", "raise", "ref", "return", 
-    "shared", "shl", "shr", "static",
+    "shl", "shr", "static",
     "template", 
     "try", "tuple", "type", "using",
     "var", "when", "while", "with", "without", "xor",
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 2cf641d93..1b9e5fe0f 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,7 +12,7 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, types, idents, magicsys
+import ast, astalgo, types, idents, magicsys, msgs, options
 
 proc newTupleAccess*(tup: PNode, i: int): PNode =
   result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
@@ -34,6 +34,11 @@ proc newAsgnStmt(le, ri: PNode): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
+proc newFastAsgnStmt(le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result.sons[0] = le
+  result.sons[1] = ri
+
 proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
@@ -50,3 +55,174 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
   result.add newAsgnStmt(newSymNode(temp), value)
   for i in 0 .. n.len-3:
     result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i))
+
+proc createObj*(owner: PSym, info: TLineInfo): PType =
+  result = newType(tyObject, owner)
+  rawAddSon(result, nil)
+  incl result.flags, tfFinal
+  result.n = newNodeI(nkRecList, info)
+
+proc addField*(obj: PType; s: PSym) =
+  # because of 'gensym' support, we have to mangle the name with its ID.
+  # This is hacky but the clean solution is much more complex than it looks.
+  var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
+  let t = skipIntLit(s.typ)
+  field.typ = t
+  field.position = sonsLen(obj.n)
+  addSon(obj.n, newSymNode(field))
+
+proc newDotExpr(obj, b: PSym): PNode =
+  result = newNodeI(nkDotExpr, obj.info)
+  let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id))
+  assert field != nil, b.name.s
+  addSon(result, newSymNode(obj))
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = 
+  # returns a[].b as a node
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.sons[0]
+  assert deref.typ.kind == tyObject
+  let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
+  assert field != nil, b.name.s
+  addSon(deref, a)
+  result = newNodeI(nkDotExpr, info)
+  addSon(result, deref)
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
+  result = indirectAccess(newSymNode(a), b, info)
+
+proc genAddrOf*(n: PNode): PNode =
+  result = newNodeI(nkAddr, n.info, 1)
+  result.sons[0] = n
+  result.typ = newType(tyPtr, n.typ.owner)
+  result.typ.rawAddSon(n.typ)
+
+proc callCodegenProc*(name: string, arg1: PNode; 
+                      arg2, arg3: PNode = nil): PNode =
+  result = newNodeI(nkCall, arg1.info)
+  let sym = magicsys.getCompilerProc(name)
+  if sym == nil:
+    localError(arg1.info, errSystemNeeds, name)
+  else:
+    result.add newSymNode(sym)
+    result.add arg1
+    if arg2 != nil: result.add arg2
+    if arg3 != nil: result.add arg3
+
+proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
+                       varSection, call: PNode): PSym =
+  var body = newNodeI(nkStmtList, f.info)
+  body.add varSection
+  body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam))
+  body.add call
+
+  var params = newNodeI(nkFormalParams, f.info)
+  params.add emptyNode
+  params.add threadParam.newSymNode
+  params.add argsParam.newSymNode
+
+  var t = newType(tyProc, threadParam.owner)
+  t.rawAddSon nil
+  t.rawAddSon threadParam.typ
+  t.rawAddSon argsParam.typ
+  t.n = newNodeI(nkFormalParams, f.info)
+  t.n.add newNodeI(nkEffectList, f.info)
+  t.n.add threadParam.newSymNode
+  t.n.add argsParam.newSymNode
+
+  let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
+  result = newSym(skProc, getIdent(name), argsParam.owner, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+  result.typ = t
+
+proc createCastExpr(argsParam: PSym; objType: PType): PNode =
+  result = newNodeI(nkCast, argsParam.info)
+  result.add emptyNode
+  result.add newSymNode(argsParam)
+  result.typ = newType(tyPtr, objType.owner)
+  result.typ.rawAddSon(objType)
+
+proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  if n.kind notin nkCallKinds or not n.typ.isEmptyType:
+    localError(n.info, "'spawn' takes a call expression of type void")
+    return
+  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)
+  block:
+    let ptrType = getSysType(tyPointer)
+    threadParam.typ = ptrType
+    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)
+  block:
+    scratchObj.typ = objType
+    incl(scratchObj.flags, sfFromGeneric)
+    var varSectionB = newNodeI(nkVarSection, n.info)
+    varSectionB.addVar(scratchObj.newSymNode)
+    result.add varSectionB
+
+  var call = newNodeI(nkCall, n.info)
+  var fn = n.sons[0]
+  # templates and macros are in fact valid here due to the nature of
+  # the transformation:
+  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
+                                               skMethod, skConverter}):
+    # for indirect calls we pass the function pointer in the scratchObj
+    var argType = n[0].typ.skipTypes(abstractInst)
+    var field = newSym(skField, getIdent"fn", owner, n.info)
+    field.typ = argType
+    objType.addField(field)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
+    fn = indirectAccess(castExpr, field, n.info)
+  elif fn.kind == nkSym and fn.sym.kind in {skClosureIterator, skIterator}:
+    localError(n.info, "iterator in spawn environment is not allowed")
+  elif fn.typ.callConv == ccClosure:
+    localError(n.info, "closure in spawn environment is not allowed")
+
+  call.add(fn)
+  var varSection = newNodeI(nkVarSection, n.info)
+  let formals = n[0].typ.n
+  let tmpName = getIdent(genPrefix)
+  for i in 1 .. <n.len:
+    # we pick n's type here, which hopefully is 'tyArray' and not
+    # 'tyOpenArray':
+    var argType = n[i].typ.skipTypes(abstractInst)
+    if i < formals.len and formals[i].typ.kind == tyVar:
+      localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
+    elif containsTyRef(argType):
+      localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, owner, n.info)
+    field.typ = argType
+    objType.addField(field)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
+
+    var temp = newSym(skTemp, tmpName, owner, n.info)
+    temp.typ = argType
+    incl(temp.flags, sfFromGeneric)
+
+    var vpart = newNodeI(nkIdentDefs, n.info, 3)
+    vpart.sons[0] = newSymNode(temp)
+    vpart.sons[1] = ast.emptyNode
+    vpart.sons[2] = indirectAccess(castExpr, field, n.info)
+    varSection.add vpart
+
+    call.add(newSymNode(temp))
+
+  let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call)
+  result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
+                             genAddrOf(scratchObj.newSymNode))
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a63fbca7f..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,
+    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnUser,
     hintSuccess, hintSuccessX,
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
@@ -170,7 +170,7 @@ const
     errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", 
     errCannotReturnExpr: "current routine cannot return an expression", 
     errAttemptToRedefine: "redefinition of \'$1\'", 
-    errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\' or \'raise\'", 
+    errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'", 
     errStmtExpected: "statement expected", 
     errInvalidLabel: "\'$1\' is no label", 
     errInvalidCmdLineOption: "invalid command line option: \'$1\'", 
@@ -386,6 +386,8 @@ const
     warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]",
     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]", 
@@ -407,7 +409,7 @@ const
     hintUser: "$1 [User]"]
 
 const
-  WarningsToStr*: array[0..24, string] = ["CannotOpenFile", "OctalEscape", 
+  WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", 
     "XIsNeverRead", "XmightNotBeenInit",
     "Deprecated", "ConfigDeprecated",
     "SmallLshouldNotBeUsed", "UnknownMagic", 
@@ -415,7 +417,8 @@ const
     "CommentXIgnored", "NilStmt",
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
     "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", 
-    "ProveInit", "ProveField", "ProveIndex", "Uninit", "GcMem", "User"]
+    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
+    "GcMem", "User"]
 
   HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", 
     "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", 
@@ -557,7 +560,7 @@ proc sourceLine*(i: TLineInfo): PRope
 var
   gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - 
                         {warnShadowIdent, warnUninit,
-                         warnProveField, warnProveIndex}
+                         warnProveField, warnProveIndex, warnGcUnsafe}
   gErrorCounter*: int = 0     # counts the number of errors
   gHintCounter*: int = 0
   gWarnCounter*: int = 0
diff --git a/compiler/options.nim b/compiler/options.nim
index fa8b77ead..36d343d1b 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  os, lists, strutils, strtabs
+  os, lists, strutils, strtabs, osproc, sets
   
 const
   hasTinyCBackend* = defined(tinyc)
@@ -16,6 +16,7 @@ const
   hasFFI* = defined(useFFI)
   newScopeForIf* = true
   useCaas* = not defined(noCaas)
+  noTimeMachine = defined(avoidTimeMachine) and defined(macosx)
 
 type                          # please make sure we have under 32 options
                               # (improves code efficiency a lot!)
@@ -94,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
@@ -263,6 +264,28 @@ proc toGeneratedFile*(path, ext: string): string =
   result = joinPath([getGeneratedPath(), changeFileExt(tail, ext)])
   #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
 
+when noTimeMachine:
+  var alreadyExcludedDirs = initSet[string]()
+  proc excludeDirFromTimeMachine(dir: string) {.raises: [].} =
+    ## Calls a macosx command on the directory to exclude it from backups.
+    ##
+    ## The macosx tmutil command is invoked to mark the specified path as an
+    ## item to be excluded from time machine backups. If a path already exists
+    ## with files before excluding it, newer files won't be added to the
+    ## directory, but previous files won't be removed from the backup until the
+    ## user deletes that directory.
+    ##
+    ## The whole proc is optional and will ignore all kinds of errors. The only
+    ## way to be sure that it works is to call ``tmutil isexcluded path``.
+    if alreadyExcludedDirs.contains(dir): return
+    alreadyExcludedDirs.incl(dir)
+    try:
+      var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
+      discard p.waitForExit
+      p.close
+    except E_Base, EOS:
+      discard
+
 proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = 
   var (head, tail) = splitPath(f)
   #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
@@ -270,6 +293,8 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
   if createSubDir:
     try: 
       createDir(subdir)
+      when noTimeMachine:
+       excludeDirFromTimeMachine(subdir)
     except EOS: 
       writeln(stdout, "cannot create directory: " & subdir)
       quit(1)
diff --git a/compiler/parser.nim b/compiler/parser.nim
index c68c80b46..5c7b86240 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -967,7 +967,7 @@ proc isExprStart(p: TParser): bool =
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, 
      tkProc, tkIterator, tkBind, tkAddr,
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, 
-     tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared:
+     tkTuple, tkObject, tkType, tkWhen, tkCase:
     result = true
   else: result = false
 
@@ -1040,7 +1040,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
   of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
   of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
   of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
-  of tkShared: result = parseTypeDescKAux(p, nkSharedTy, mode)
   of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
   of tkType: result = parseTypeDescKAux(p, nkTypeOfExpr, mode)
   of tkTuple: result = parseTuple(p, mode == pmTypeDef)
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/pragmas.nim b/compiler/pragmas.nim
index 14d155539..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.
@@ -24,7 +24,7 @@ const
     wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, 
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
     wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
-    wGensym, wInject, wRaises, wTags, wOperator, wDelegator}
+    wGensym, wInject, wRaises, wTags, wUses, wOperator, wDelegator, wGcSafe}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas
   templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@@ -35,7 +35,7 @@ const
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, 
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
     wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
-    wTags, wOperator}
+    wTags, wUses, wOperator, wGcSafe}
   exprPragmas* = {wLine}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
     wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
@@ -48,12 +48,12 @@ const
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, 
     wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
-    wRaises, wTags}
+    wRaises, wUses, wTags, wGcSafe}
   typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, 
     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, 
@@ -64,7 +64,7 @@ const
     wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
-                      wThread, wRaises, wTags}
+                      wThread, wRaises, wUses, wTags, wGcSafe}
   allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
 
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
@@ -513,6 +513,27 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
   else:
     invalidPragma(n)
 
+proc pragmaUses(c: PContext, n: PNode) =
+  proc processExc(c: PContext, x: PNode): PNode =
+    if x.kind in {nkAccQuoted, nkIdent, nkSym,
+                  nkOpenSymChoice, nkClosedSymChoice}:
+      if considerAcc(x).s == "*":
+        return newSymNode(ast.anyGlobal)
+    result = c.semExpr(c, x)
+    if result.kind != nkSym or sfGlobal notin result.sym.flags:
+      localError(x.info, "'$1' is not a global variable" % result.renderTree)
+      result = newSymNode(ast.anyGlobal)
+    
+  if n.kind == nkExprColonExpr:
+    let it = n.sons[1]
+    if it.kind notin {nkCurly, nkBracket}:
+      n.sons[1] = processExc(c, it)
+    else:
+      for i in 0 .. <it.len:
+        it.sons[i] = processExc(c, it.sons[i])
+  else:
+    invalidPragma(n)
+
 proc typeBorrow(sym: PSym, n: PNode) =
   if n.kind == nkExprColonExpr:
     let it = n.sons[1]
@@ -667,6 +688,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           incl(sym.flags, sfThread)
           incl(sym.flags, sfProcvar)
           if sym.typ != nil: incl(sym.typ.flags, tfThread)
+        of wGcSafe:
+          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)
@@ -759,6 +786,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           if sym == nil: invalidPragma(it)
         of wLine: pragmaLine(c, it)
         of wRaises, wTags: pragmaRaisesOrTags(c, it)
+        of wUses: pragmaUses(c, it)
         of wOperator:
           if sym == nil: invalidPragma(it)
           else: sym.position = expectIntLit(c, it)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index fa119eba9..6b62c48c5 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1082,12 +1082,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       gsub(g, n.sons[1])
     else:
       put(g, tkIterator, "iterator")
-  of nkSharedTy:
-    if sonsLen(n) > 0:
-      putWithSpace(g, tkShared, "shared")
-      gsub(g, n.sons[0])
-    else:
-      put(g, tkShared, "shared")
   of nkStaticTy:
     put(g, tkStatic, "static")
     put(g, tkBracketLe, "[")
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/semexprs.nim b/compiler/semexprs.nim
index 6c1721bdd..652e1dd4b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   if result.typ != nil:
     # XXX tyGenericInst here?
     if result.typ.kind == tyVar: result = newDeref(result)
+  elif efWantStmt in flags:
+    result.typ = newTypeS(tyEmpty, c)
   else:
     localError(n.info, errExprXHasNoType, 
                renderTree(result, {renderNoComments}))
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 a00325277..6235fb76a 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -59,15 +59,19 @@ discard """
  --> we need a stack of scopes for this analysis
 """
 
+const trackGlobals = false ## we don't need it for now
+
 type
   TEffects = object
     exc: PNode  # stack of exceptions
     tags: PNode # list of tags
+    uses: PNode # list of used global variables
     bottom: int
     owner: PSym
     init: seq[int] # list of initialized variables
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
+    gcUnsafe, isRecursive: bool
   PEffects = var TEffects
 
 proc isLocalVar(a: PEffects, s: PSym): bool =
@@ -89,20 +93,30 @@ proc initVarViaNew(a: PEffects, n: PNode) =
     # are initialized:
     initVar(a, n)
 
+when trackGlobals: 
+  proc addUse(a: PEffects, e: PNode) =
+    var aa = a.uses
+    for i in 0 .. <aa.len:
+      if aa[i].sym.id == e.sym.id: return
+    a.uses.add(e)
+
 proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
     if s.id notin a.init:
       if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
-        when true:
-          message(n.info, warnProveInit, s.name.s)
-        else:
-          Message(n.info, errGenerated,
-            "'$1' might not have been initialized" % s.name.s)
+        message(n.info, warnProveInit, s.name.s)
       else:
         message(n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
+  if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar:
+    when trackGlobals:
+      a.addUse(copyNode(n))
+    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
 
 type
   TIntersection = seq[tuple[id, count: int]] # a simple count table
@@ -132,6 +146,10 @@ proc createTag(n: PNode): PNode =
   result.typ = sysTypeFromName"TEffect"
   if not n.isNil: result.info = n.info
 
+proc createAnyGlobal(n: PNode): PNode =
+  result = newSymNode(anyGlobal)
+  result.info = n.info
+
 proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
   assert e.kind != nkRaiseStmt
   var aa = a.exc
@@ -161,9 +179,17 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
   else:
     for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
 
+when trackGlobals:
+  proc mergeUses(a: PEffects, b, comesFrom: PNode) =
+    if b.isNil:
+      addUse(a, createAnyGlobal(comesFrom))
+    else:
+      for effect in items(b): addUse(a, effect)
+
 proc listEffects(a: PEffects) =
   for e in items(a.exc):  message(e.info, hintUser, typeToString(e.typ))
   for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
+  for e in items(a.uses): message(e.info, hintUser, e.sym.name.s)
 
 proc catches(tracked: PEffects, e: PType) =
   let e = skipTypes(e, skipPtrs)
@@ -289,6 +315,13 @@ proc documentRaises*(n: PNode) =
   if n.sons[namePos].kind != nkSym: return
   documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
   documentEffect(n, n.sons[pragmasPos], wTags, tagEffects)
+  documentEffect(n, n.sons[pragmasPos], wUses, usesEffects)
+
+template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
+
+proc importedFromC(n: PNode): bool =
+  # when imported from C, we assume GC-safety.
+  result = n.kind == nkSym and sfImportc in n.sym.flags
 
 proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let pragma = s.ast.sons[pragmasPos]
@@ -298,6 +331,14 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let tagSpec = effectSpec(pragma, wTags)
   mergeTags(tracked, tagSpec, n)
 
+  if notGcSafe(s.typ) and sfImportc notin s.flags:
+    message(n.info, warnGcUnsafe, renderTree(n))
+    tracked.gcUnsafe = true
+
+  when trackGlobals:
+    let usesSpec = effectSpec(pragma, wUses)
+    mergeUses(tracked, usesSpec, n)
+
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   let n = n.skipConv
   if paramType != nil and tfNotNil in paramType.flags and 
@@ -330,9 +371,18 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
       else:
         addEffect(tracked, createRaise(n))
         addTag(tracked, createTag(n))
+        when trackGlobals: addUse(tracked, createAnyGlobal(n))
+      # assume GcUnsafe unless in its type:
+      if notGcSafe(op): 
+        message(n.info, warnGcUnsafe, renderTree(n))
+        tracked.gcUnsafe = true
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
+      when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
+      if notGcSafe(op):
+        message(n.info, warnGcUnsafe, renderTree(n))
+        tracked.gcUnsafe = true
   notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
@@ -451,8 +501,13 @@ proc track(tracked: PEffects, n: PNode) =
     # XXX: in rare situations, templates and macros will reach here after
     # calling getAst(templateOrMacro()). Currently, templates and macros
     # are indistinguishable from normal procs (both have tyProc type) and
-    # we can detect them only by cheking for attached nkEffectList.
+    # 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 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]
       if a.kind == nkSym and a.sym.kind == skMethod:
         propagateEffects(tracked, n, a.sym)
@@ -462,9 +517,12 @@ proc track(tracked: PEffects, n: PNode) =
         elif isIndirectCall(a, tracked.owner):
           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)
+        when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
     for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
       # may not look like an assignment, but it is:
@@ -531,14 +589,21 @@ proc track(tracked: PEffects, n: PNode) =
   else:
     for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
 
-proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) =
+proc subtypeRelation(spec, real: PNode): bool =
+  result = safeInheritanceDiff(real.excType, spec.typ) <= 0
+
+proc symbolPredicate(spec, real: PNode): bool =
+  result = real.sym.id == spec.sym.id
+
+proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
+                     effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
   # check that any real exception is listed in 'spec'; mark those as used;
   # report any unused exception
   var used = initIntSet()
   for r in items(real):
     block search:
       for s in 0 .. <spec.len:
-        if safeInheritanceDiff(r.excType, spec[s].typ) <= 0:
+        if effectPredicate(spec[s], r):
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
@@ -560,11 +625,18 @@ proc checkMethodEffects*(disp, branch: PSym) =
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
     checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects],
-      "can raise an unlisted exception: ", hints=off)
+      "can raise an unlisted exception: ", hints=off, subtypeRelation)
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
-      "can have an unlisted effect: ", hints=off)
+      "can have an unlisted effect: ", hints=off, subtypeRelation)
+  let usesSpec = effectSpec(p, wUses)
+  if not isNil(usesSpec):
+    checkRaisesSpec(usesSpec, actual.sons[usesEffects],
+      "may use an unlisted global variable: ", hints=off, symbolPredicate)
+  if sfThread in disp.flags and notGcSafe(branch.typ):
+    localError(branch.info, "base method is GC-safe, but '$1' is not" % 
+                                branch.name.s)
 
 proc setEffectsForProcType*(t: PType, n: PNode) =
   var effects = t.n.sons[0]
@@ -573,21 +645,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
   let
     raisesSpec = effectSpec(n, wRaises)
     tagsSpec = effectSpec(n, wTags)
-  if not isNil(raisesSpec) or not isNil(tagsSpec):
+    usesSpec = effectSpec(n, wUses)
+  if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec):
     internalAssert effects.len == 0
     newSeq(effects.sons, effectListLen)
     if not isNil(raisesSpec):
       effects.sons[exceptionEffects] = raisesSpec
     if not isNil(tagsSpec):
       effects.sons[tagEffects] = tagsSpec
+    if not isNil(usesSpec):
+      effects.sons[usesEffects] = usesSpec
 
 proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
+  effects.sons[usesEffects] = newNodeI(nkArgList, s.info)
   
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
+  t.uses = effects.sons[usesEffects]
   t.owner = s
   t.init = @[]
   t.guards = @[]
@@ -602,7 +679,6 @@ proc trackProc*(s: PSym, body: PNode) =
   var t: TEffects
   initEffects(effects, s, t)
   track(t, body)
-  
   if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
       s.kind in {skProc, skConverter, skMethod}:
     var res = s.ast.sons[resultPos].sym # get result symbol
@@ -612,17 +688,29 @@ proc trackProc*(s: PSym, body: PNode) =
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
     checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ",
-                    hints=on)
+                    hints=on, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[exceptionEffects] = raisesSpec
 
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
-                    hints=off)
+                    hints=off, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[tagEffects] = tagsSpec
-    
+
+  when trackGlobals:
+    let usesSpec = effectSpec(p, wUses)
+    if not isNil(usesSpec):
+      checkRaisesSpec(usesSpec, t.uses,
+        "uses an unlisted global variable: ", hints=on, symbolPredicate)
+      effects.sons[usesEffects] = usesSpec
+  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 5e391cc93..384bdc8a3 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1277,6 +1277,15 @@ proc processMagicType(c: PContext, m: PSym) =
     setMagicType(m, tyOrdinal, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mPNimrodNode: discard
+  of mShared:
+    setMagicType(m, tyObject, 0)
+    m.typ.n = newNodeI(nkRecList, m.info)
+    incl m.typ.flags, tfShared
+  of mGuarded:
+    setMagicType(m, tyObject, 0)
+    m.typ.n = newNodeI(nkRecList, m.info)
+    incl m.typ.flags, tfShared
+    rawAddSon(m.typ, sysTypeFromName"shared")
   else: localError(m.info, errTypeExpected)
   
 proc semGenericConstraints(c: PContext, x: PType): PType =
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 4a8a463f5..271a01266 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.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.
@@ -14,11 +14,22 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer
 const
   tfInstClearedFlags = {tfHasMeta}
 
+proc sharedPtrCheck(info: TLineInfo, t: PType) =
+  if t.kind == tyPtr and t.len > 1:
+    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 or t.isGCedMem:
+        localError(info, errGenerated,
+                   "shared memory may not refer to GC'ed thread local memory")
+
 proc checkPartialConstructedType(info: TLineInfo, t: PType) =
   if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
     localError(info, errInvalidPragmaX, "acyclic")
   elif t.kind == tyVar and t.sons[0].kind == tyVar:
     localError(info, errVarVarTypeNotAllowed)
+  else:
+    sharedPtrCheck(info, t)
 
 proc checkConstructedType*(info: TLineInfo, typ: PType) =
   var t = typ.skipTypes({tyDistinct})
@@ -29,7 +40,8 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
     localError(info, errVarVarTypeNotAllowed)
   elif computeSize(t) == szIllegalRecursion:
     localError(info, errIllegalRecursionInTypeX, typeToString(t))
-  
+  else:
+    sharedPtrCheck(info, t)
   when false:
     if t.kind == tyObject and t.sons[0] != nil:
       if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 9d1585c56..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
 
@@ -402,7 +405,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
       return isNone
     elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
-      # noSideEffect implies ``tfThread``! XXX really?
+      # noSideEffect implies ``tfThread``!
       return isNone
     elif f.flags * {tfIterator} != a.flags * {tfIterator}:
       return isNone
@@ -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
@@ -1284,6 +1287,7 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
     result = a
   elif a.typ.isNil:
     let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
+                elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
                 else: {efDetermineType}
     result = c.semOperand(c, a, flags)
   else:
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/vm.nim b/compiler/vm.nim
index fb8749250..69a410f18 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)
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
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 9fdb3bac6..fbe005031 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.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.
@@ -30,7 +30,7 @@ type
     wInclude, wInterface, wIs, wIsnot, wIterator, wLambda, wLet,
     wMacro, wMethod, wMixin, wMod, wNil, 
     wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, 
-    wShared, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, 
+    wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, 
     wWhen, wWhile, wWith, wWithout, wXor, wYield,
     
     wColon, wColonColon, wEquals, wDot, wDotDot,
@@ -44,7 +44,8 @@ type
     wImportCompilerProc,
     wImportc, wExportc, wIncompleteStruct, wRequiresInit,
     wAlign, wNodecl, wPure, wSideeffect, wHeader,
-    wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, 
+    wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, 
+    wCompilerproc, wProcVar, 
     wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, 
     wLinedir, wStacktrace, wLinetrace, wLink, wCompile, 
     wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, 
@@ -63,7 +64,7 @@ type
     wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
     wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, 
     wAsmNoStackFrame,
-    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
+    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wUses,
 
     wAuto, wBool, wCatch, wChar, wClass,
     wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -110,7 +111,7 @@ const
     "macro", "method", "mixin", "mod", "nil", "not", "notin",
     "object", "of", "or", 
     "out", "proc", "ptr", "raise", "ref", "return",
-    "shared", "shl", "shr", "static",
+    "shl", "shr", "static",
     "template", "try", "tuple", "type", "using", "var", 
     "when", "while", "with", "without", "xor",
     "yield",
@@ -125,7 +126,7 @@ const
     "importcpp", "importobjc",
     "importcompilerproc", "importc", "exportc", "incompletestruct",
     "requiresinit", "align", "nodecl", "pure", "sideeffect",
-    "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", 
+    "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", 
     "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", 
     "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", 
     "link", "compile", "linksys", "deprecated", "varargs", 
@@ -146,6 +147,7 @@ const
     "computedgoto", "injectstmt",
     "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
     "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
+    "guard", "uses",
     
     "auto", "bool", "catch", "char", "class",
     "const_cast", "default", "delete", "double",