summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2014-09-21 18:39:00 +0200
committerAraq <rumpf_a@web.de>2014-09-21 18:39:00 +0200
commit7916b1f9aa93fca368afda8b18396230ac7f338c (patch)
tree04072aeee4ae211b91c088a7267455b82daf9787
parent4800acf6ab92799316b270c991b6e8c01454608f (diff)
downloadNim-7916b1f9aa93fca368afda8b18396230ac7f338c.tar.gz
implemented 'guard' annotation
-rw-r--r--compiler/ast.nim16
-rw-r--r--compiler/ccgexprs.nim4
-rw-r--r--compiler/ccgstmts.nim4
-rw-r--r--compiler/cgen.nim8
-rw-r--r--compiler/guards.nim16
-rw-r--r--compiler/jsgen.nim5
-rw-r--r--compiler/magicsys.nim2
-rw-r--r--compiler/msgs.nim9
-rw-r--r--compiler/pragmas.nim86
-rw-r--r--compiler/rodread.nim7
-rw-r--r--compiler/rodwrite.nim3
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/sempass2.nim134
-rw-r--r--compiler/semstmts.nim9
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/types.nim2
-rw-r--r--compiler/vmgen.nim2
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--tests/parallel/tguard1.nim37
-rw-r--r--tests/parallel/tguard2.nim27
-rw-r--r--todo.txt7
21 files changed, 266 insertions, 122 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 2ba9cdb2e..e3e8fd49f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -679,7 +679,7 @@ type
     heapRoot*: PRope          # keeps track of the enclosing heap object that
                               # owns this location (required by GC algorithms
                               # employing heap snapshots or sliding views)
-    a*: int                   # location's "address", i.e. slot for temporaries
+    #a*: int                   # location's "address", i.e. slot for temporaries
 
   # ---------------- end of backend information ------------------------------
 
@@ -732,8 +732,9 @@ type
       # check for the owner when touching 'usedGenerics'.
       usedGenerics*: seq[PInstantiation]
       tab*: TStrTable         # interface table for modules
+    of skLet, skVar, skField:
+      guard*: PSym
     else: nil
-
     magic*: TMagic
     typ*: PType
     name*: PIdent
@@ -795,7 +796,8 @@ type
     deepCopy*: PSym           # overriden 'deepCopy' operation
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
-    align*: int               # the type's alignment requirements
+    align*: int16             # the type's alignment requirements
+    lockLevel*: int16         # lock level as required for deadlock checking
     loc*: TLoc
 
   TPair*{.final.} = object 
@@ -1173,8 +1175,8 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
   result.id = getID()
   when debugIds:
     registerId(result)
-  #if result.id < 2000 then
-  #  MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
+  #if result.id < 2000:
+  #  messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
   
 proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.k == low(a.k): a.k = b.k
@@ -1182,7 +1184,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
   a.flags = a.flags + b.flags
   if a.t == nil: a.t = b.t
   if a.r == nil: a.r = b.r
-  if a.a == 0: a.a = b.a
+  #if a.a == 0: a.a = b.a
   
 proc assignType(dest, src: PType) = 
   dest.kind = src.kind
@@ -1230,6 +1232,8 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
   result.position = s.position
   result.loc = s.loc
   result.annex = s.annex      # BUGFIX
+  if result.kind in {skVar, skLet, skField}:
+    result.guard = s.guard
 
 proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
   result = newSym(s.kind, newIdent, s.owner, info)
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 8914ad0de..ca34cfa3a 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -409,7 +409,6 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
     d.k = locData
     d.t = getUniqueType(t)
     d.r = r
-    d.a = -1
 
 proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
   var a: TLoc
@@ -425,7 +424,6 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
     d.k = locExpr
     d.t = getUniqueType(t)
     d.r = r
-    d.a = -1
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a, b: TLoc
@@ -1508,7 +1506,6 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
       getTypeDesc(p.module, srct), getTypeDesc(p.module, destt), lbl)
     tmp.k = locExpr
-    tmp.a = -1
     tmp.t = srct
     tmp.s = OnStack
     tmp.flags = {}
@@ -2043,6 +2040,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
      nkFromStmt, nkTemplateDef, nkMacroDef: 
     discard
   of nkPragma: genPragma(p, n)
+  of nkPragmaBlock: expr(p, n.lastSon, d)
   of nkProcDef, nkMethodDef, nkConverterDef: 
     if (n.sons[genericParamsPos].kind == nkEmpty):
       var prc = n.sons[namePos].sym
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index efa8b0adc..e30b2c561 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -443,7 +443,7 @@ proc genBlock(p: BProc, t: PNode, d: var TLoc) =
       assert(t.sons[0].kind == nkSym)
       var sym = t.sons[0].sym
       sym.loc.k = locOther
-      sym.loc.a = p.breakIdx
+      sym.position = p.breakIdx+1
     expr(p, t.sons[1], d)
     endBlock(p)
 
@@ -482,7 +482,7 @@ proc genBreakStmt(p: BProc, t: PNode) =
     assert(t.sons[0].kind == nkSym)
     var sym = t.sons[0].sym
     assert(sym.loc.k == locOther)
-    idx = sym.loc.a
+    idx = sym.position-1
   else:
     # an unnamed 'break' can only break a loop after 'transf' pass:
     while idx >= 0 and not p.blocks[idx].isLoop: dec idx
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 490aeec4b..743e877a1 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -55,7 +55,7 @@ proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
   result.s = s
   result.t = getUniqueType(typ)
   result.r = nil
-  result.a = - 1
+  #result.a = - 1
   result.flags = {}
 
 proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) = 
@@ -63,7 +63,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
   if a.k == locNone: 
     a.k = k
     a.t = getUniqueType(typ)
-    a.a = - 1
+    #a.a = - 1
     a.s = s
     if a.r == nil: a.r = r
   
@@ -407,7 +407,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
     result.r = con("LOC", toRope(p.labels))
     linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
   result.k = locTemp
-  result.a = - 1
+  #result.a = - 1
   result.t = getUniqueType(t)
   result.s = OnStack
   result.flags = {}
@@ -425,7 +425,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
         [getTypeDesc(p.module, toKeepAlive.t), fid])
     inc(p.gcFrameId)
     result.k = locTemp
-    result.a = -1
+    #result.a = -1
     result.t = toKeepAlive.t
     result.s = OnStack
     result.flags = {}
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 05128f409..cd0aaf296 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -838,6 +838,22 @@ proc addAsgnFact*(m: var TModel, key, value: PNode) =
   fact.sons[2] = value
   m.add fact
 
+proc sameSubexprs*(m: TModel; a, b: PNode): bool =
+  # This should be used to check whether two *path expressions* refer to the
+  # same memory location according to 'm'. This is tricky:
+  # lock a[i].guard:
+  #   ...
+  #   access a[i].guarded
+  #
+  # Here a[i] is the same as a[i] iff 'i' and 'a' are not changed via '...'.
+  # However, nil checking requires exactly the same mechanism! But for now
+  # we simply use sameTree and live with the unsoundness of the analysis.
+  var check = newNodeI(nkCall, a.info, 3)
+  check.sons[0] = newSymNode(opEq)
+  check.sons[1] = a
+  check.sons[2] = b
+  result = m.doesImply(check) == impYes
+
 proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
   let branch = n.sons[i]
   if branch.kind == nkOfBranch:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 652f439a9..4772aecb5 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -724,7 +724,7 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
     if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock")
     var sym = n.sons[0].sym
     sym.loc.k = locOther
-    sym.loc.a = idx
+    sym.position = idx+1
   setLen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
   let labl = p.unique
@@ -741,7 +741,7 @@ proc genBreakStmt(p: PProc, n: PNode) =
     assert(n.sons[0].kind == nkSym)
     let sym = n.sons[0].sym
     assert(sym.loc.k == locOther)
-    idx = sym.loc.a
+    idx = sym.position-1
   else:
     # an unnamed 'break' can only break a loop after 'transf' pass:
     idx = len(p.blocks) - 1
@@ -1654,6 +1654,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.res = nil
   of nkGotoState, nkState:
     internalError(n.info, "first class iterators not implemented")
+  of nkPragmaBlock: gen(p, n.lastSon, r)
   else: internalError(n.info, "gen: unknown node type: " & $n.kind)
   
 var globals: PGlobals
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index a794253df..6d4c65268 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -35,7 +35,7 @@ proc registerSysType(t: PType) =
 proc newSysType(kind: TTypeKind, size: int): PType = 
   result = newType(kind, systemModule)
   result.size = size
-  result.align = size
+  result.align = size.int16
 
 proc getSysSym(name: string): PSym = 
   result = strTableGet(systemModule.tab, getIdent(name))
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index da2fe122d..8368fe5fd 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -119,7 +119,7 @@ type
     warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
     warnEachIdentIsTuple, warnShadowIdent, 
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnUser,
+    warnUninit, warnGcMem, warnUnguardedAccess, warnUser,
     hintSuccess, hintSuccessX,
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@@ -387,9 +387,10 @@ const
     warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
     warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
     warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
-    warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.",
+    warnGcUnsafe2: "cannot prove '$1' is GC-safe. Does not compile with --threads:on.",
     warnUninit: "'$1' might not have been initialized [Uninit]",
     warnGcMem: "'$1' uses GC'ed memory [GcMem]",
+    warnUnguardedAccess: "'$1' is accessed without its guard [UnguardedAccess]",
     warnUser: "$1 [User]", 
     hintSuccess: "operation successful [Success]", 
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", 
@@ -410,7 +411,7 @@ const
     hintUser: "$1 [User]"]
 
 const
-  WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", 
+  WarningsToStr*: array[0..27, string] = ["CannotOpenFile", "OctalEscape", 
     "XIsNeverRead", "XmightNotBeenInit",
     "Deprecated", "ConfigDeprecated",
     "SmallLshouldNotBeUsed", "UnknownMagic", 
@@ -419,7 +420,7 @@ const
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
     "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", 
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
-    "GcMem", "User"]
+    "GcMem", "UnguardedAccess", "User"]
 
   HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong", 
     "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 24b2f6750..815369066 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -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, wUses, wOperator, wDelegator, wGcSafe,
+    wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
     wOverride}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas
@@ -36,8 +36,8 @@ const
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, 
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
     wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
-    wTags, wUses, wOperator, wGcSafe}
-  exprPragmas* = {wLine}
+    wTags, wLocks, wGcSafe}
+  exprPragmas* = {wLine, wLocks}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
     wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
     wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@@ -49,23 +49,23 @@ const
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, 
     wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
-    wRaises, wUses, wTags, wGcSafe}
+    wRaises, wLocks, 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, wGcSafe}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, 
-    wImportCpp, wImportObjC, wError}
+    wImportCpp, wImportObjC, wError, wGuard}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, 
     wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
     wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
-    wGensym, wInject, wCodegenDecl}
+    wGensym, wInject, wCodegenDecl, wGuard}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
     wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
-                      wThread, wRaises, wUses, wTags, wGcSafe}
+                      wThread, wRaises, wLocks, wTags, wGcSafe}
   allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
 
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
@@ -514,26 +514,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 considerQuotedIdent(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)
+proc pragmaLockStmt(c: PContext; it: PNode) =
+  if it.kind != nkExprColonExpr:
+    invalidPragma(it)
+  else:
+    let n = it[1]
+    if n.kind != nkBracket:
+      localError(n.info, errGenerated, "locks pragma takes a list of expressions")
     else:
-      for i in 0 .. <it.len:
-        it.sons[i] = processExc(c, it.sons[i])
+      for i in 0 .. <n.len:
+        n.sons[i] = c.semExpr(c, n.sons[i])
+
+proc pragmaLocks(c: PContext, it: PNode): int16 =
+  if it.kind != nkExprColonExpr:
+    invalidPragma(it)
   else:
-    invalidPragma(n)
+    let n = it[1]
+    let x = expectIntLit(c, n)
+    if x < low(int16) or x > high(int16):
+      localError(n.info, "integer must be in the valid range of int16")
+    else:
+      result = int16(x)
 
 proc typeBorrow(sym: PSym, n: PNode) =
   if n.kind == nkExprColonExpr:
@@ -563,11 +564,25 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
     else:
       localError(n.info, "key:value pair expected")
 
+proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
+  if it.kind != nkExprColonExpr: 
+    invalidPragma(it); return
+  let n = it[1]
+  if n.kind == nkSym:
+    result = n.sym
+  elif kind == skField:
+    # we return a dummy symbol; later passes over the type will repair it. Note
+    # that generic instantiation needs to know about this too. But we're lazy
+    # and perform the lookup on demand instead.
+    result = newSym(skUnknown, considerQuotedIdent(n), nil, n.info)
+  else:
+    result = qualifiedLookUp(c, n)
+
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
                   validPragmas: TSpecialWords): bool =
   var it = n.sons[i]
   var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
-  if key.kind == nkIdent: 
+  if key.kind == nkIdent:
     var userPragma = strTableGet(c.userPragmas, key.ident)
     if userPragma != nil: 
       inc c.instCounter
@@ -599,11 +614,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         of wAlign:
           if sym.typ == nil: invalidPragma(it)
           var align = expectIntLit(c, it)
-          if not isPowerOfTwo(align) and align != 0: 
+          if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
             localError(it.info, errPowerOfTwoExpected)
           else: 
-            sym.typ.align = align              
-        of wSize: 
+            sym.typ.align = align.int16
+        of wSize:
           if sym.typ == nil: invalidPragma(it)
           var size = expectIntLit(c, it)
           if not isPowerOfTwo(size) or size <= 0 or size > 8: 
@@ -806,10 +821,15 @@ 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)
+        of wLocks:
+          if sym == nil: pragmaLockStmt(c, it)
+          elif sym.typ == nil: invalidPragma(it)
+          else: sym.typ.lockLevel = pragmaLocks(c, it)
+        of wGuard:
+          if sym == nil or sym.kind notin {skVar, skLet, skField}:
+            invalidPragma(it)
+          else:
+            sym.guard = pragmaGuard(c, it, sym.kind)
         of wInjectStmt:
           if it.kind != nkExprColonExpr:
             localError(it.info, errExprExpected)
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 3cc37fb98..3b3538e5d 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -280,11 +280,6 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
       loc.r = toRope(decodeStr(r.s, r.pos))
     else: 
       loc.r = nil
-    if r.s[r.pos] == '?': 
-      inc(r.pos)
-      loc.a = decodeVInt(r.s, r.pos)
-    else: 
-      loc.a = 0
     if r.s[r.pos] == '>': inc(r.pos)
     else: internalError(info, "decodeLoc " & r.s[r.pos])
   
@@ -326,7 +321,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
     result.size = - 1
   if r.s[r.pos] == '=': 
     inc(r.pos)
-    result.align = decodeVInt(r.s, r.pos)
+    result.align = decodeVInt(r.s, r.pos).int16
   else: 
     result.align = 2
   decodeLoc(r, result.loc, info)
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index 7eacb0883..9fed7ac52 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -187,9 +187,6 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
   if loc.r != nil: 
     add(result, '!')
     encodeStr(ropeToStr(loc.r), result)
-  if loc.a != 0: 
-    add(result, '?')
-    encodeVInt(loc.a, result)
   if oldLen + 1 == result.len:
     # no data was necessary, so remove the '<' again:
     setLen(result, oldLen)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 11215ae68..64914cb25 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1626,6 +1626,10 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
   initIdTable(bindings)
   bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
   result = c.semGenerateInstance(c, sym, bindings, info)
+  # since it's an instantiation, we unmark it as a compilerproc. Otherwise
+  # codegen would fail:
+  result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC}
+  result.loc.r = nil
 
 proc setMs(n: PNode, s: PSym): PNode = 
   result = n
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index fee70af15..794b7ec80 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -57,9 +57,13 @@ discard """
     c()
  
  --> we need a stack of scopes for this analysis
-"""
 
-const trackGlobals = false ## we don't need it for now
+  # XXX enhance the algorithm to care about 'dirty' expressions:
+  lock a[i].L:
+    inc i # mark 'i' dirty
+    lock a[j].L:
+      access a[i], a[j]  # --> reject a[i]
+"""
 
 type
   TEffects = object
@@ -71,12 +75,69 @@ type
     init: seq[int] # list of initialized variables
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
-    gcUnsafe, isRecursive: bool
+    gcUnsafe, isRecursive, isToplevel: bool
   PEffects = var TEffects
 
 proc isLocalVar(a: PEffects, s: PSym): bool =
   s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner
 
+proc lockLocations(a: PEffects; pragma: PNode) =
+  if pragma.kind != nkExprColonExpr:
+    internalError(pragma.info, "no colon")
+    return
+  for x in pragma[1]:
+    a.locked.add x
+
+proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
+  # check whether the corresponding lock is held:
+  for L in a.locked:
+    if L.kind == nkSym and L.sym == guard: return
+  # we allow accesses nevertheless in top level statements for
+  # easier initialization:
+  #if a.isTopLevel:
+  #  message(n.info, warnUnguardedAccess, renderTree(n))
+  #else:
+  if not a.isTopLevel:
+    localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
+
+# 'guard*' are checks which are concerned with 'guard' annotations
+# (var x{.guard: y.}: int)
+proc guardDotAccess(a: PEffects; n: PNode) =
+  let ri = n.sons[1]
+  internalAssert ri.kind == nkSym and ri.sym.kind == skField
+  var g = ri.sym.guard
+  if g.isNil or a.isTopLevel: return
+  # fixup guard:
+  if g.kind == skUnknown:
+    var field: PSym = nil
+    var ty = n.sons[0].typ.skipTypes(abstractPtrs)
+    if ty.kind == tyTuple:
+      field = lookupInRecord(ty.n, g.name)
+    else:
+      while ty != nil and ty.kind == tyObject:
+        field = lookupInRecord(ty.n, g.name)
+        if field != nil: break
+        ty = ty.sons[0]
+        if ty == nil: break
+        ty = ty.skipTypes(abstractPtrs)
+    if field == nil:
+      localError(n.info, errGenerated, "invalid guard field: " & g.name.s)
+      return
+    g = field
+    #ri.sym.guard = field
+    # XXX unfortunately this is not correct for generic instantiations!
+  if g.kind == skField:
+    let dot = newNodeI(nkDotExpr, n.info, 2)
+    dot.sons[0] = n.sons[0]
+    dot.sons[1] = newSymNode(g)
+    dot.typ = g.typ
+    for L in a.locked:
+      #if a.guards.sameSubexprs(dot, L): return
+      if guards.sameTree(dot, L): return
+    localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
+  else:
+    guardGlobal(a, n, g)
+
 proc initVar(a: PEffects, n: PNode) =
   if n.kind != nkSym: return
   let s = n.sym
@@ -93,13 +154,6 @@ 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):
@@ -110,9 +164,8 @@ proc useVar(a: PEffects, n: PNode) =
         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 {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind in {skVar, skLet}:
+    if s.guard != nil: guardGlobal(a, n, s.guard)
     if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and 
         tfGcSafe notin s.typ.flags:
       if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@@ -186,13 +239,6 @@ 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))
@@ -322,7 +368,7 @@ 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)
+  #documentEffect(n, n.sons[pragmasPos], wUses, usesEffects)
 
 template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
@@ -342,10 +388,6 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
     if warnGcUnsafe in gNotes: 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 
@@ -381,7 +423,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
         # we have no explicit effects so assume the worst:
         addEffect(tracked, createRaise(n))
         addTag(tracked, createTag(n))
-        when trackGlobals: addUse(tracked, createAnyGlobal(n))
       # assume GcUnsafe unless in its type; 'forward' does not matter:
       if notGcSafe(op):
         if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
@@ -389,7 +430,6 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
-      when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
       if notGcSafe(op):
         if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
         tracked.gcUnsafe = true
@@ -529,12 +569,9 @@ 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)
         if notGcSafe(op) and not importedFromC(a):
           # and it's not a recursive call:
           if not (a.kind == nkSym and a.sym == tracked.owner):
@@ -546,6 +583,9 @@ proc track(tracked: PEffects, n: PNode) =
       initVarViaNew(tracked, n.sons[1])
     for i in 0 .. <safeLen(n):
       track(tracked, n.sons[i])
+  of nkDotExpr:
+    guardDotAccess(tracked, n)
+    for i in 0 .. <len(n): track(tracked, n.sons[i])
   of nkCheckedFieldExpr:
     track(tracked, n.sons[0])
     if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
@@ -601,6 +641,14 @@ proc track(tracked: PEffects, n: PNode) =
       if sfDiscriminant in x.sons[0].sym.flags:
         addDiscriminantFact(tracked.guards, x)
     setLen(tracked.guards, oldFacts)
+  of nkPragmaBlock:
+    let pragmaList = n.sons[0]
+    let oldLocked = tracked.locked.len
+    for i in 0 .. <pragmaList.len:
+      if whichPragma(pragmaList.sons[i]) == wLocks:
+        lockLocations(tracked, pragmaList.sons[i])
+    track(tracked, n.lastSon)
+    setLen(tracked.locked, oldLocked)
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
       nkMacroDef, nkTemplateDef:
     discard
@@ -648,10 +696,6 @@ proc checkMethodEffects*(disp, branch: PSym) =
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
       "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)
@@ -663,29 +707,25 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
   let
     raisesSpec = effectSpec(n, wRaises)
     tagsSpec = effectSpec(n, wTags)
-    usesSpec = effectSpec(n, wUses)
-  if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec):
+  if not isNil(raisesSpec) or not isNil(tagsSpec):
     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 = @[]
+  t.locked = @[]
   
 proc trackProc*(s: PSym, body: PNode) =
   var effects = s.typ.n.sons[0]
@@ -717,16 +757,12 @@ proc trackProc*(s: PSym, body: PNode) =
     # 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 optThreads in gGlobalOptions:
+        localError(s.info, "'$1' is not GC-safe" % s.name.s)
+      else:
+        localError(s.info, warnGcUnsafe2, s.name.s)
     if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
 
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
@@ -736,5 +772,5 @@ proc trackTopLevelStmt*(module: PSym; n: PNode) =
   var effects = newNode(nkEffectList, n.info)
   var t: TEffects
   initEffects(effects, module, t)
-
+  t.isToplevel = true
   track(t, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 5cb2eabf8..bd9ca6a0e 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1300,9 +1300,14 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
   let pragmaList = n.sons[0]
   pragma(c, nil, pragmaList, exprPragmas)
   result = semExpr(c, n.sons[1])
+  n.sons[1] = result
   for i in 0 .. <pragmaList.len:
-    if whichPragma(pragmaList.sons[i]) == wLine:
-      setLine(result, pragmaList.sons[i].info)
+    case whichPragma(pragmaList.sons[i])
+    of wLine: setLine(result, pragmaList.sons[i].info)
+    of wLocks: 
+      result = n
+      result.typ = n.sons[1].typ
+    else: discard
 
 proc semStaticStmt(c: PContext, n: PNode): PNode =
   let a = semStmt(c, n.sons[0])
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 559bf2e06..765ab5be8 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1244,7 +1244,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   
 proc setMagicType(m: PSym, kind: TTypeKind, size: int) = 
   m.typ.kind = kind
-  m.typ.align = size
+  m.typ.align = size.int16
   m.typ.size = size
   
 proc processMagicType(c: PContext, m: PSym) = 
diff --git a/compiler/types.nim b/compiler/types.nim
index 7a9b6bb85..27282e684 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1271,7 +1271,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     #internalError("computeSizeAux()")
     result = szUnknownSize
   typ.size = result
-  typ.align = int(a)
+  typ.align = int16(a)
 
 proc computeSize(typ: PType): BiggestInt = 
   var a: BiggestInt = 1
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index e7f7ad742..fac3cf1bf 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1595,6 +1595,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     let L = n.len-1
     for i in 0 .. <L: gen(c, n.sons[i])
     gen(c, n.sons[L], dest, flags)
+  of nkPragmaBlock:
+    gen(c, n.lastSon, dest, flags)
   of nkDiscardStmt:
     unused(n, dest)
     gen(c, n.sons[0])
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index e3b4b3062..068f59c71 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -64,7 +64,7 @@ type
     wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
     wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, 
     wAsmNoStackFrame,
-    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wUses,
+    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
 
     wAuto, wBool, wCatch, wChar, wClass,
     wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -147,7 +147,7 @@ const
     "computedgoto", "injectstmt",
     "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
     "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
-    "guard", "uses",
+    "guard", "locks",
     
     "auto", "bool", "catch", "char", "class",
     "const_cast", "default", "delete", "double",
diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim
new file mode 100644
index 000000000..d96e17589
--- /dev/null
+++ b/tests/parallel/tguard1.nim
@@ -0,0 +1,37 @@
+
+when false:
+  template lock(a, b: ptr TLock; body: stmt) =
+    if cast[ByteAddress](a) < cast[ByteAddress](b):
+      pthread_mutex_lock(a)
+      pthread_mutex_lock(b)
+    else:
+      pthread_mutex_lock(b)
+      pthread_mutex_lock(a)
+    {.locks: [a, b].}:
+      try:
+        body
+      finally:
+        pthread_mutex_unlock(a)
+        pthread_mutex_unlock(b)
+
+type
+  ProtectedCounter[T] = object
+    i {.guard: L.}: T
+    L: int
+
+var
+  c: ProtectedCounter[int]
+
+c.i = 89
+
+template atomicRead(L, x): expr =
+  {.locks: [L].}:
+    x
+
+proc main =
+  {.locks: [c.L].}:
+    inc c.i
+    discard
+  echo(atomicRead(c.L, c.i))
+
+main()
diff --git a/tests/parallel/tguard2.nim b/tests/parallel/tguard2.nim
new file mode 100644
index 000000000..b69ea3371
--- /dev/null
+++ b/tests/parallel/tguard2.nim
@@ -0,0 +1,27 @@
+discard """
+  errormsg: "unguarded access: c.i"
+  line: 25
+"""
+
+type
+  ProtectedCounter[T] = object
+    i {.guard: L.}: T
+    L: int
+
+var
+  c: ProtectedCounter[int]
+
+c.i = 89
+
+template atomicRead(L, x): expr =
+  {.locks: [L].}:
+    x
+
+proc main =
+  {.locks: [c.L].}:
+    inc c.i
+    discard
+  echo(atomicRead(c.L, c.i))
+  echo c.i
+
+main()
diff --git a/todo.txt b/todo.txt
index 66f47a1d8..3aa96294c 100644
--- a/todo.txt
+++ b/todo.txt
@@ -3,7 +3,7 @@ version 0.10
 
 - Test nimfix on various babel packages
 - Pegs do not work at compile-time
-
+- # echo type.int
 
 version 0.9.6
 =============
@@ -15,7 +15,6 @@ version 0.9.6
 - split docgen into separate tool
 - .benign pragma
 - scopes are still broken for generic instantiation!
-- implicit deref for parameter matching
 
 Concurrency
 -----------
@@ -23,7 +22,6 @@ Concurrency
 - 'deepCopy' needs to be instantiated for
   generics *when the type is constructed*
 - test 'deepCopy'
-- overloading of '='; general lift mechanism
 
 - the disjoint checker needs to deal with 'a = spawn f(); g = spawn f()'
 - implement 'foo[1..4] = spawn(f[4..7])'
@@ -63,6 +61,9 @@ Bugs
 version 0.9.x
 =============
 
+
+- implicit deref for parameter matching
+- overloading of '='; general lift mechanism
 - pragmas need 'bindSym' support
 - pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work
   reliably with user-defined pragmas