summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2020-01-09 23:51:37 -0800
committerAndreas Rumpf <rumpf_a@web.de>2020-01-10 08:51:37 +0100
commit4cd86c08427205d2e26510a07a3c6980c14e1608 (patch)
treeefab63d6385cfe3f9368f390ececb06793eb30b9
parentfcd2f305ad5ad2af37284caf7b33907afb8ad834 (diff)
downloadNim-4cd86c08427205d2e26510a07a3c6980c14e1608.tar.gz
typetraits: fixes #6454; genericParams; added lenTuple; added tuple type get (#13064)
-rw-r--r--changelog.md4
-rw-r--r--lib/pure/typetraits.nim40
-rw-r--r--lib/system/inclrtl.nim2
-rw-r--r--tests/metatype/ttypetraits.nim18
4 files changed, 60 insertions, 4 deletions
diff --git a/changelog.md b/changelog.md
index aeb6987c4..17eedb3a1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -47,6 +47,10 @@
 - Added `sugar.capture` for capturing some local loop variables when creating a closure.
   This is an enhanced version of `closureScope`.
 
+- Added `typetraits.lenTuple` to get number of elements of a tuple/type tuple,
+  and `typetraits.get` to get the ith element of a type tuple.
+- Added `typetraits.genericParams` to return a tuple of generic params from a generic instantiation
+
 ## Library changes
 
 - `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 7a493cb6c..26cbce4d1 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -14,6 +14,7 @@
 
 export system.`$` # for backward compatibility
 
+include "system/inclrtl"
 
 proc name*(t: typedesc): string {.magic: "TypeTrait".}
   ## Returns the name of the given type.
@@ -70,6 +71,45 @@ proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".}
   ## Returns base type for distinct types, works only for distinct types.
   ## compile time error otherwise
 
+import std/macros
+
+macro lenTuple*(t: tuple): int {.since: (1, 1).} =
+  ## Return number of elements of `t`
+  newLit t.len
+
+macro lenTuple*(t: typedesc[tuple]): int {.since: (1, 1).} =
+  ## Return number of elements of `T`
+  newLit t.len
+
+when (NimMajor, NimMinor) >= (1, 1):
+  template get*(T: typedesc[tuple], i: static int): untyped =
+    ## Return `i`th element of `T`
+    # Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
+    type(default(T)[i])
+
+macro genericParams*(T: typedesc): untyped {.since: (1, 1).} =
+  ## return tuple of generic params for generic `T`
+  runnableExamples:
+    type Foo[T1, T2]=object
+    doAssert genericParams(Foo[float, string]) is (float, string)
+  result = newNimNode(nnkTupleConstr)
+  var impl = getTypeImpl(T)
+  expectKind(impl, nnkBracketExpr)
+  impl = impl[1]
+  while true:
+    case impl.kind
+      of nnkSym:
+        impl = impl.getImpl
+        continue
+      of nnkTypeDef:
+        impl = impl[2]
+        continue
+      of nnkBracketExpr:
+        for i in 1..<impl.len:
+          result.add impl[i]
+        break
+      else:
+        error "wrong kind: " & $impl.kind
 
 when isMainModule:
   static:
diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim
index 6bff819e6..33b8545c4 100644
--- a/lib/system/inclrtl.nim
+++ b/lib/system/inclrtl.nim
@@ -50,5 +50,7 @@ else:
   {.pragma: benign, gcsafe.}
 
 template since(version, body: untyped) {.dirty.} =
+  ## limitation: can't be used to annotate a template (eg typetraits.get), would
+  ## error: cannot attach a custom pragma.
   when version <= (NimMajor, NimMinor):
     body
diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim
index 3215a21b0..8ae41f1e6 100644
--- a/tests/metatype/ttypetraits.nim
+++ b/tests/metatype/ttypetraits.nim
@@ -44,9 +44,6 @@ block: # typeToString
   doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 ==
     "tuple[a: C2b{C}[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]"
 
-
-#----------------------------------------------------
-
 block distinctBase:
   block:
     type
@@ -90,4 +87,17 @@ block distinctBase:
         doAssert($distinctBase(typeof(b2)) == "string")
         doAssert($distinctBase(typeof(c2)) == "int")
 
-
+block genericParams:
+  type Foo[T1, T2]=object
+  doAssert genericParams(Foo[float, string]) is (float, string)
+  type Foo1 = Foo[float, int]
+  doAssert genericParams(Foo1) is (float, int)
+  type Foo2 = Foo[float, Foo1]
+  doAssert genericParams(Foo2) is (float, Foo[float, int])
+  doAssert genericParams(Foo2) is (float, Foo1)
+  doAssert genericParams(Foo2).get(1) is Foo1
+  doAssert (int,).get(0) is int
+  doAssert (int, float).get(1) is float
+  static: doAssert (int, float).lenTuple == 2
+  static: doAssert (1, ).lenTuple == 1
+  static: doAssert ().lenTuple == 0