summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-05-31 01:51:20 -0700
committerGitHub <noreply@github.com>2021-05-31 10:51:20 +0200
commit18b477431138d944b90e7a6e6e0412496634b518 (patch)
tree23c74b9eb166c38d2fce05fb33321cf6135d4689
parent98ea61f09b4bfdc13c911b1ff3fb404dfc0d1338 (diff)
downloadNim-18b477431138d944b90e7a6e6e0412496634b518.tar.gz
document macros.unpackVarargs (#18106)
* deprecate macros.unpackVarargs

* un-deprecate unpackVarargs and add docs+runnableExamples

* update examples + tests with varargs[typed]
-rw-r--r--lib/core/macros.nim15
-rw-r--r--tests/destructor/tatomicptrs.nim1
-rw-r--r--tests/stdlib/tmacros.nim50
3 files changed, 66 insertions, 0 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 79c3ba28b..89f90a23b 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1682,6 +1682,21 @@ macro getCustomPragmaVal*(n: typed, cp: typed): untyped =
     error(n.repr & " doesn't have a pragma named " & cp.repr, n)
 
 macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
+  ## Calls `callee` with `args` unpacked as individual arguments.
+  ## This is useful in 2 cases:
+  ## * when forwarding `varargs[T]` for some typed `T`
+  ## * when forwarding `varargs[untyped]` when `args` can potentially be empty,
+  ##   due to a compiler limitation
+  runnableExamples:
+    template call1(fun: typed; args: varargs[untyped]): untyped =
+      unpackVarargs(fun, args)
+      # when varargsLen(args) > 0: fun(args) else: fun() # this would also work
+    template call2(fun: typed; args: varargs[typed]): untyped =
+      unpackVarargs(fun, args)
+    proc fn1(a = 0, b = 1) = discard (a, b)
+    call1(fn1, 10, 11)
+    call1(fn1) # `args` is empty in this case
+    if false: call2(echo, 10, 11) # would print 1011
   result = newCall(callee)
   for i in 0 ..< args.len:
     result.add args[i]
diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim
index 7313afbf5..36f0cab8a 100644
--- a/tests/destructor/tatomicptrs.nim
+++ b/tests/destructor/tatomicptrs.nim
@@ -72,6 +72,7 @@ template `.=`*[T](s: SharedPtr[T]; field, value: untyped) =
 from macros import unpackVarargs
 
 template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped =
+  # xxx this isn't used, the test should be improved
   unpackVarargs(s.x.field, args)
 
 
diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim
index effe1032f..52ea1e898 100644
--- a/tests/stdlib/tmacros.nim
+++ b/tests/stdlib/tmacros.nim
@@ -10,3 +10,53 @@ block: # hasArgOfName
 
 block: # bug #17454
   proc f(v: NimNode): string {.raises: [].} = $v
+
+block: # unpackVarargs
+  block:
+    proc bar1(a: varargs[int]): string =
+      for ai in a: result.add " " & $ai
+    proc bar2(a: varargs[int]) =
+      let s1 = bar1(a)
+      let s2 = unpackVarargs(bar1, a) # `unpackVarargs` makes no difference here
+      doAssert s1 == s2
+    bar2(1, 2, 3)
+    bar2(1)
+    bar2()
+
+  block:
+    template call1(fun: typed; args: varargs[untyped]): untyped =
+      unpackVarargs(fun, args)
+    template call2(fun: typed; args: varargs[untyped]): untyped =
+      # fun(args) # works except for last case with empty `args`, pending bug #9996
+      when varargsLen(args) > 0: fun(args)
+      else: fun()
+
+    proc fn1(a = 0, b = 1) = discard (a, b)
+
+    call1(fn1)
+    call1(fn1, 10)
+    call1(fn1, 10, 11)
+
+    call2(fn1)
+    call2(fn1, 10)
+    call2(fn1, 10, 11)
+
+  block:
+    template call1(fun: typed; args: varargs[typed]): untyped =
+      unpackVarargs(fun, args)
+    template call2(fun: typed; args: varargs[typed]): untyped =
+      # xxx this would give a confusing error message:
+      # required type for a: varargs[typed] [varargs] but expression '[10]' is of type: varargs[typed] [varargs]
+      when varargsLen(args) > 0: fun(args)
+      else: fun()
+    macro toString(a: varargs[typed, `$`]): string =
+      var msg = genSym(nskVar, "msg")
+      result = newStmtList()
+      result.add quote do:
+        var `msg` = ""
+      for ai in a:
+        result.add quote do: `msg`.add $`ai`
+      result.add quote do: `msg`
+    doAssert call1(toString) == ""
+    doAssert call1(toString, 10) == "10"
+    doAssert call1(toString, 10, 11) == "1011"