summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/pragmas.nim8
-rw-r--r--doc/manual/locking.txt19
-rw-r--r--tests/pragmas/tlocks.nim13
3 files changed, 39 insertions, 1 deletions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 1fe1c81d2..bcb0461f2 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -582,7 +582,13 @@ proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
   if it.kind != nkExprColonExpr:
     invalidPragma(it)
   else:
-    if it[1].kind != nkNilLit:
+    case it[1].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      if it[1].strVal == "unknown":
+        result = UnknownLockLevel
+      else:
+        localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
+    else:
       let x = expectIntLit(c, it)
       if x < 0 or x > MaxLockLevel:
         localError(it[1].info, "integer must be within 0.." & $MaxLockLevel)
diff --git a/doc/manual/locking.txt b/doc/manual/locking.txt
index c00efdd91..c1bd5ca46 100644
--- a/doc/manual/locking.txt
+++ b/doc/manual/locking.txt
@@ -198,3 +198,22 @@ This is essential so that procs can be called within a ``locks`` section:
 As usual ``locks`` is an inferred effect and there is a subtype
 relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
 iff (M <= N).
+
+The ``locks`` pragma can also take the special value ``"unknown"``. This
+is useful in the context of dynamic method dispatching. In the following
+example, the compiler can infer a lock level of 0 for the ``base`` case.
+However, one of the overloaded methods calls a procvar which is
+potentially locking. Thus, the lock level of calling ``g.testMethod``
+cannot be inferred statically, leading to compiler warnings. By using
+``{.locks: "unknown".}``, the base method can be marked explicitly as
+having unknown lock level as well:
+
+.. code-block:: nim
+  type SomeBase* = ref object of RootObj
+  type SomeDerived* = ref object of SomeBase
+    memberProc*: proc ()
+
+  method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard
+  method testMethod(g: SomeDerived) =
+    if g.memberProc != nil:
+      g.memberProc()
diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim
new file mode 100644
index 000000000..ba66a2dca
--- /dev/null
+++ b/tests/pragmas/tlocks.nim
@@ -0,0 +1,13 @@
+
+type SomeBase* = ref object of RootObj
+type SomeDerived* = ref object of SomeBase
+  memberProc*: proc ()
+
+method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard
+method testMethod(g: SomeDerived) =
+  if g.memberProc != nil:
+    g.memberProc()
+
+# ensure int literals still work
+proc plain*() {.locks: 0.} =
+  discard