summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/hashes.nim18
-rw-r--r--lib/system.nim13
-rw-r--r--tests/stdlib/thashes.nim30
-rw-r--r--tests/stdlib/tsystem.nim46
4 files changed, 93 insertions, 14 deletions
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 2dab498d7..f61fbdf2b 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -524,15 +524,21 @@ proc hash*[T: tuple | object | proc](x: T): Hash {.inline.} =
     proc hash(a: Obj2): Hash = hash((a.x))
     assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
   runnableExamples:
-    # proc and closure examples
+    # proc
     proc fn1() = discard
-    var a = 0
-    proc fn2() = a.inc
-    assert hash(fn1) != hash(fn2)
     const fn1b = fn1
     assert hash(fn1b) == hash(fn1)
-    let fn2b = fn2
-    assert hash(fn2b) == hash(fn2)
+
+    # closure
+    proc outer =
+      var a = 0
+      proc fn2() = a.inc
+      assert fn2 is "closure"
+      let fn2b = fn2
+      assert hash(fn2b) == hash(fn2)
+      assert hash(fn2) != hash(fn1)
+    outer()
+
   when T is "closure":
     result = hash((rawProc(x), rawEnv(x)))
   elif T is (proc):
diff --git a/lib/system.nim b/lib/system.nim
index e9c036b8a..aecda4a70 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2414,17 +2414,22 @@ when notJSnotNims:
 
   proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
     ## Retrieves the raw proc pointer of the closure `x`. This is
-    ## useful for interfacing closures with C.
+    ## useful for interfacing closures with C/C++, hash compuations, etc.
     when T is "closure":
+      #[
+      The conversion from function pointer to `void*` is a tricky topic, but this
+      should work at least for c++ >= c++11, e.g. for `dlsym` support.
+      refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869,
+      https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c
+      ]#
       {.emit: """
-      `result` = `x`.ClP_0;
+      `result` = (void*)`x`.ClP_0;
       """.}
     else:
       {.error: "Only closure function and iterator are allowed!".}
 
   proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
-    ## Retrieves the raw environment pointer of the closure `x`. This is
-    ## useful for interfacing closures with C.
+    ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`.
     when T is "closure":
       {.emit: """
       `result` = `x`.ClE_0;
diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim
index 66857d3ca..46576ef12 100644
--- a/tests/stdlib/thashes.nim
+++ b/tests/stdlib/thashes.nim
@@ -184,8 +184,8 @@ proc main() =
       # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417
       discard
     do:
-      assert a[0].addr.hash != a[1].addr.hash
-      assert cast[pointer](a[0].addr).hash == a[0].addr.hash
+      doAssert a[0].addr.hash != a[1].addr.hash
+      doAssert cast[pointer](a[0].addr).hash == a[0].addr.hash
 
   block: # hash(ref)
     type A = ref object
@@ -193,9 +193,31 @@ proc main() =
     let a = A(x: 3)
     disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer
       let ha = a.hash
-      assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
+      doAssert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
       a.x = 4
-      assert ha == a.hash # the hash only depends on the address
+      doAssert ha == a.hash # the hash only depends on the address
+
+  block: # hash(proc)
+    proc fn(a: int): auto = a*2
+    doAssert fn isnot "closure"
+    doAssert fn is (proc)
+    const fn2 = fn
+    let fn3 = fn
+    whenVMorJs: discard
+    do:
+      doAssert hash(fn2) == hash(fn)
+      doAssert hash(fn3) == hash(fn)
+
+  block: # hash(closure)
+    proc outer() =
+      var a = 0
+      proc inner() = a.inc
+      doAssert inner is "closure"
+      let inner2 = inner
+      whenVMorJs: discard
+      do:
+        doAssert hash(inner2) == hash(inner)
+    outer()
 
 static: main()
 main()
diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim
new file mode 100644
index 000000000..f8b172b44
--- /dev/null
+++ b/tests/stdlib/tsystem.nim
@@ -0,0 +1,46 @@
+discard """
+  targets: "c cpp js"
+"""
+
+# TODO: in future work move existing `system` tests here, where they belong
+
+import stdtest/testutils
+
+template main =
+  block: # closure
+    proc outer() =
+      var a = 0
+      proc inner1 = a.inc
+      proc inner2 = discard
+      doAssert inner1 is "closure"
+      doAssert inner2 isnot "closure"
+      doAssert inner1 is (proc)
+      doAssert inner2 is (proc)
+      let inner1b = inner1
+      doAssert inner1b is "closure"
+      doAssert inner1b == inner1
+    outer()
+
+  block: # rawProc, rawProc, bug #17911
+    proc outer() =
+      var a = 0
+      var b = 0
+      proc inner1() = a.inc
+      proc inner2() = a += 2
+      proc inner3() = b.inc
+      let inner1b = inner1
+      doAssert inner2 != inner1
+      doAssert inner3 != inner1
+      whenVMorJs: discard
+      do:
+        doAssert rawProc(inner1b) == rawProc(inner1)
+        doAssert rawProc(inner2) != rawProc(inner1)
+        doAssert rawProc(inner3) != rawProc(inner1)
+
+        doAssert rawEnv(inner1b) == rawEnv(inner1)
+        doAssert rawEnv(inner2) == rawEnv(inner1) # because both use `a`
+        # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold
+    outer()
+
+static: main()
+main()