Skip to content

Commit

Permalink
fix nim-lang#17911 rawProc for cpp (nim-lang#17912)
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour authored May 1, 2021
1 parent fb86271 commit ee6d561
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 14 deletions.
18 changes: 12 additions & 6 deletions lib/pure/hashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
13 changes: 9 additions & 4 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 26 additions & 4 deletions tests/stdlib/thashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,40 @@ 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
x: int
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()
46 changes: 46 additions & 0 deletions tests/stdlib/tsystem.nim
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit ee6d561

Please sign in to comment.