diff --git a/changelog.md b/changelog.md index 78817cf85f..5c5e7ac8bc 100644 --- a/changelog.md +++ b/changelog.md @@ -244,6 +244,8 @@ - Added dollar `$` and `len` for `jsre.RegExp`. +- Added `hasClosure` to `std/effecttraits`. + ## Language changes diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 5748b41b33..ce449f6658 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -316,3 +316,7 @@ proc registerAdditionalOps*(c: PCtx) = let fn = getNode(a, 0) setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or (fn.kind == nkSym and fn.sym.kind == skFunc)) + + registerCallback c, "stdlib.effecttraits.hasClosureImpl", proc (a: VmArgs) = + let fn = getNode(a, 0) + setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure)) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index 358280db0e..04a24fae8f 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -14,12 +14,14 @@ ## One can test for the existance of this standard module ## via `defined(nimHasEffectTraitsModule)`. -import macros +import std/macros +import std/private/since proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" proc isGcSafeImpl(n: NimNode): bool = discard "see compiler/vmops.nim" proc hasNoSideEffectsImpl(n: NimNode): bool = discard "see compiler/vmops.nim" +proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim" proc getRaisesList*(fn: NimNode): NimNode = ## Extracts the `.raises` list of the func/proc/etc `fn`. @@ -52,3 +54,11 @@ proc hasNoSideEffects*(fn: NimNode): bool = ## arguments and not `untyped` arguments. expectKind fn, nnkSym result = hasNoSideEffectsImpl(fn) + +proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} = + ## Return true if the func/proc/etc `fn` has `closure`. + ## `fn` has to be a resolved symbol of kind `nnkSym`. This + ## implies that the macro that calls this proc should accept `typed` + ## arguments and not `untyped` arguments. + expectKind fn, nnkSym + result = hasClosureImpl(fn) diff --git a/tests/stdlib/teffecttraits.nim b/tests/stdlib/teffecttraits.nim new file mode 100644 index 0000000000..4aae5ca83e --- /dev/null +++ b/tests/stdlib/teffecttraits.nim @@ -0,0 +1,92 @@ +discard """ + targets: "c cpp js" +""" + +import std/effecttraits + + + +macro testClosure(fn: typed, flag: static bool) = + if flag: + doAssert hasClosure(fn) + else: + doAssert not hasClosure(fn) + +block: + proc h1() = + echo 1 + + testClosure(h1, false) + + proc h2() {.nimcall.} = + echo 2 + + testClosure(h2, false) + + +block: + proc fn(): auto = + proc hello() {.nimcall.} = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + proc hello() = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + var x = 0 + proc hello() = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + + let name2 = name + testClosure(name2, true) + +block: + iterator hello(): int = + yield 1 + + testClosure(hello, false) + +when not defined(js): + block: + iterator hello(): int {.closure.}= + yield 1 + + testClosure(hello, true)