summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/ccgtypes.nim2
-rw-r--r--compiler/pragmas.nim12
-rw-r--r--compiler/sempass2.nim49
-rw-r--r--compiler/semtypinst.nim7
-rw-r--r--lib/nimbase.h2
-rw-r--r--todo.txt2
7 files changed, 58 insertions, 19 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index e3e8fd49f..919a1a3fd 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1165,6 +1165,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
   result.sons = @[name, pattern, genericParams, params,
                   pragmas, exceptions, body]
 
+const UnspecifiedLockLevel* = -1
 
 proc newType(kind: TTypeKind, owner: PSym): PType = 
   new(result)
@@ -1173,6 +1174,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
   result.size = - 1
   result.align = 2            # default alignment
   result.id = getID()
+  result.lockLevel = UnspecifiedLockLevel
   when debugIds:
     registerId(result)
   #if result.id < 2000:
@@ -1195,6 +1197,7 @@ proc assignType(dest, src: PType) =
   dest.align = src.align
   dest.destructor = src.destructor
   dest.deepCopy = src.deepCopy
+  dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
     if dest.sym != nil:
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 1b0b9e103..4b52d3956 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -898,7 +898,7 @@ include ccgtrav
 
 proc genDeepCopyProc(m: BModule; s: PSym; result: PRope) =
   genProc(m, s)
-  appf(m.s[cfsTypeInit3], "$1.deepcopy =(N_NIMCALL_PTR(void*,)(void*))$2;$n",
+  appf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
      [result, s.loc.r])
 
 proc genTypeInfo(m: BModule, t: PType): PRope =
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 815369066..5f9ee218f 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -529,12 +529,12 @@ proc pragmaLocks(c: PContext, it: PNode): int16 =
   if it.kind != nkExprColonExpr:
     invalidPragma(it)
   else:
-    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)
+    if it[1].kind != nkNilLit:
+      let x = expectIntLit(c, it)
+      if x < 0 or x > high(int16):
+        localError(it[1].info, "integer must be within 0..high(int16)")
+      else:
+        result = int16(x)
 
 proc typeBorrow(sym: PSym, n: PNode) =
   if n.kind == nkExprColonExpr:
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index a330e0ecd..981e69900 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -69,24 +69,44 @@ 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, isToplevel: bool
+    maxLockLevel, currLockLevel: int16
   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 getLockLevel(t: PType): int16 =
+  var t = t
+  # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject):
+  if t.kind == tyGenericInst and t.len == 3: t = t.sons[1]
+  if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}:
+    result = t.n.intVal.int16
+
 proc lockLocations(a: PEffects; pragma: PNode) =
   if pragma.kind != nkExprColonExpr:
     internalError(pragma.info, "no colon")
     return
+  var firstLL = -1'i16
   for x in pragma[1]:
+    let thisLL = getLockLevel(x.typ)
+    if thisLL != 0:
+      if firstLL < 0: firstLL = thisLL
+      elif firstLL != thisLL:
+        localError(x.info, errGenerated,
+          "multi-lock requires the same static lock level for every operand")
+      a.maxLockLevel = max(a.maxLockLevel, firstLL)
     a.locked.add x
+  if firstLL >= 0 and firstLL != a.currLockLevel:
+    if a.currLockLevel > 0 and a.currLockLevel < firstLL:
+      localError(pragma.info, errGenerated,
+        "invalid nested locking")
+    a.currLockLevel = firstLL
 
 proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
   # check whether the corresponding lock is held:
@@ -242,7 +262,8 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
 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)
+  #if a.maxLockLevel != 0:
+  #  message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
 
 proc catches(tracked: PEffects, e: PType) =
   let e = skipTypes(e, skipPtrs)
@@ -368,7 +389,6 @@ 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 == {}
 
@@ -376,6 +396,15 @@ proc importedFromC(n: PNode): bool =
   # when imported from C, we assume GC-safety.
   result = n.kind == nkSym and sfImportc in n.sym.flags
 
+proc mergeLockLevels(tracked: PEffects, n: PNode, t: PType) =
+  if t.lockLevel > tracked.currLockLevel:
+    # if in lock section:
+    if tracked.currLockLevel > 0:
+      localError n.info, errGenerated,
+        "expected lock level <= " & $tracked.currLockLevel &
+        " but got lock level " & $t.lockLevel
+    tracked.maxLockLevel = max(tracked.maxLockLevel, t.lockLevel)
+
 proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let pragma = s.ast.sons[pragmasPos]
   let spec = effectSpec(pragma, wRaises)
@@ -387,6 +416,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   if notGcSafe(s.typ) and sfImportc notin s.flags:
     if warnGcUnsafe in gNotes: message(n.info, warnGcUnsafe, renderTree(n))
     tracked.gcUnsafe = true
+  mergeLockLevels(tracked, n, s.typ)
 
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   let n = n.skipConv
@@ -577,6 +607,7 @@ proc track(tracked: PEffects, n: PNode) =
           if not (a.kind == nkSym and a.sym == tracked.owner):
             message(n.info, warnGcUnsafe, renderTree(n))
             tracked.gcUnsafe = true
+        mergeLockLevels(tracked, n, op)
     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:
@@ -644,11 +675,13 @@ proc track(tracked: PEffects, n: PNode) =
   of nkPragmaBlock:
     let pragmaList = n.sons[0]
     let oldLocked = tracked.locked.len
+    let oldLockLevel = tracked.currLockLevel
     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)
+    tracked.currLockLevel = oldLockLevel
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
       nkMacroDef, nkTemplateDef:
     discard
@@ -699,6 +732,10 @@ proc checkMethodEffects*(disp, branch: PSym) =
   if sfThread in disp.flags and notGcSafe(branch.typ):
     localError(branch.info, "base method is GC-safe, but '$1' is not" % 
                                 branch.name.s)
+  if branch.typ.lockLevel > disp.typ.lockLevel:
+    localError(branch.info,
+      "base method has lock level $1, but dispatcher has $2" %
+        [$branch.typ.lockLevel, $disp.typ.lockLevel])
 
 proc setEffectsForProcType*(t: PType, n: PNode) =
   var effects = t.n.sons[0]
@@ -764,6 +801,12 @@ proc trackProc*(s: PSym, body: PNode) =
       else:
         localError(s.info, warnGcUnsafe2, s.name.s)
     if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
+    if s.typ.lockLevel == UnspecifiedLockLevel:
+      s.typ.lockLevel = t.maxLockLevel
+    elif t.maxLockLevel > s.typ.lockLevel:
+      localError(s.info,
+        "declared lock level is $1, but real lock level is $2" %
+          [$s.typ.lockLevel, $t.maxLockLevel])
 
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index fa4d2cc0b..9c15be635 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -304,13 +304,6 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
     # 'deepCopy' needs to be instantiated for
     # generics *when the type is constructed*:
     newbody.deepCopy = cl.c.instDeepCopy(cl.c, dc, result, cl.info)
-    when false:
-      var bindings: TIdTable
-      initIdTable(bindings)
-      debug newbody
-      bindings.idTablePut(dc.ast[genericParamsPos].sons[0].typ, newbody)
-      newbody.deepCopy = cl.c.semGenerateInstance(cl.c, dc, bindings, cl.info)
-      assert sfFromGeneric in newbody.deepCopy.flags
 
 proc eraseVoidParams*(t: PType) =
   if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
diff --git a/lib/nimbase.h b/lib/nimbase.h
index f49855076..2ca3bd886 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -141,9 +141,11 @@ __clang__
 /* these compilers have a fastcall so use it: */
 #  define N_NIMCALL(rettype, name) rettype __fastcall name
 #  define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name)
+#  define N_RAW_NIMCALL __fastcall
 #else
 #  define N_NIMCALL(rettype, name) rettype name /* no modifier */
 #  define N_NIMCALL_PTR(rettype, name) rettype (*name)
+#  define N_RAW_NIMCALL
 #endif
 
 #define N_CLOSURE(rettype, name) N_NIMCALL(rettype, name)
diff --git a/todo.txt b/todo.txt
index 7f89678e2..c43132cd5 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,7 +1,6 @@
 version 0.10
 ============
 
-- use the effect system for static deadlock prevention
 - Test nimfix on various babel packages
 - deprecate recursive tuples; tuple needs laxer type checking
 - string case should require an 'else'
@@ -30,7 +29,6 @@ Low priority:
 - support for exception propagation? (hard to implement)
 - the copying of the 'ref Promise' into the thead local storage only
   happens to work due to the write barrier's implementation
-- implement lock levels
 
 
 Misc