typetraits.nim 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Nim Contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module defines compile-time reflection procs for
  10. ## working with types.
  11. ##
  12. ## Unstable API.
  13. import std/private/since
  14. export system.`$` # for backward compatibility
  15. proc name*(t: typedesc): string {.magic: "TypeTrait".}
  16. ## Returns the name of the given type.
  17. ##
  18. ## Alias for system.`$`(t) since Nim v0.20.
  19. proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
  20. ## Returns the arity of the given type. This is the number of "type"
  21. ## components or the number of generic parameters a given type ``t`` has.
  22. runnableExamples:
  23. assert arity(seq[string]) == 1
  24. assert arity(array[3, int]) == 2
  25. assert arity((int, int, float, string)) == 4
  26. proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".}
  27. ## Accepts an instantiated generic type and returns its
  28. ## uninstantiated form.
  29. ##
  30. ## For example:
  31. ## * `seq[int].genericHead` will be just `seq`
  32. ## * `seq[int].genericHead[float]` will be `seq[float]`
  33. ##
  34. ## A compile-time error will be produced if the supplied type
  35. ## is not generic.
  36. ##
  37. ## See also:
  38. ## * `stripGenericParams <#stripGenericParams,typedesc>`_
  39. ##
  40. ## Example:
  41. ##
  42. ## .. code-block:: nim
  43. ## type
  44. ## Functor[A] = concept f
  45. ## type MatchedGenericType = genericHead(typeof(f))
  46. ## # `f` will be a value of a type such as `Option[T]`
  47. ## # `MatchedGenericType` will become the `Option` type
  48. proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
  49. ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but
  50. ## instead of producing error for non-generic types, it will just return
  51. ## them unmodified.
  52. proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
  53. ## This trait returns true if the type ``t`` is safe to use for
  54. ## `copyMem`:idx:.
  55. ##
  56. ## Other languages name a type like these `blob`:idx:.
  57. proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".}
  58. ## Return true for named tuples, false for any other type.
  59. proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".}
  60. ## Returns base type for distinct types, works only for distinct types.
  61. ## compile time error otherwise
  62. since (1, 1):
  63. template distinctBase*[T](a: T): untyped =
  64. ## overload for values
  65. runnableExamples:
  66. type MyInt = distinct int
  67. doAssert 12.MyInt.distinctBase == 12
  68. distinctBase(type(a))(a)
  69. proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".}
  70. ## Return number of elements of `T`
  71. template tupleLen*(t: tuple): int =
  72. ## Return number of elements of `t`
  73. tupleLen(type(t))
  74. template get*(T: typedesc[tuple], i: static int): untyped =
  75. ## Return `i`\th element of `T`
  76. # Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
  77. type(default(T)[i])
  78. type StaticParam*[value: static type] = object
  79. ## used to wrap a static value in `genericParams`
  80. since (1, 3, 5):
  81. template elementType*(a: untyped): typedesc =
  82. ## return element type of `a`, which can be any iterable (over which you
  83. ## can iterate)
  84. runnableExamples:
  85. iterator myiter(n: int): auto =
  86. for i in 0..<n: yield i
  87. doAssert elementType(@[1,2]) is int
  88. doAssert elementType("asdf") is char
  89. doAssert elementType(myiter(3)) is int
  90. typeof(block: (for ai in a: ai))
  91. import std/macros
  92. macro enumLen*(T: typedesc[enum]): int =
  93. ## Returns the number of items in the enum `T`.
  94. runnableExamples:
  95. type Foo = enum fooItem1 fooItem2
  96. doAssert Foo.enumLen == 2
  97. let bracketExpr = getType(T)
  98. expectKind(bracketExpr, nnkBracketExpr)
  99. let enumTy = bracketExpr[1]
  100. expectKind(enumTy, nnkEnumTy)
  101. result = newLit(enumTy.len - 1)
  102. macro genericParamsImpl(T: typedesc): untyped =
  103. # auxiliary macro needed, can't do it directly in `genericParams`
  104. result = newNimNode(nnkTupleConstr)
  105. var impl = getTypeImpl(T)
  106. expectKind(impl, nnkBracketExpr)
  107. impl = impl[1]
  108. while true:
  109. case impl.kind
  110. of nnkSym:
  111. impl = impl.getImpl
  112. of nnkTypeDef:
  113. impl = impl[2]
  114. of nnkTypeOfExpr:
  115. impl = getTypeInst(impl[0])
  116. of nnkBracketExpr:
  117. for i in 1..<impl.len:
  118. let ai = impl[i]
  119. var ret: NimNode = nil
  120. case ai.typeKind
  121. of ntyTypeDesc:
  122. ret = ai
  123. of ntyStatic: doAssert false
  124. else:
  125. # getType from a resolved symbol might return a typedesc symbol.
  126. # If so, use it directly instead of wrapping it in StaticParam.
  127. if (ai.kind == nnkSym and ai.symKind == nskType) or
  128. (ai.kind == nnkBracketExpr and ai[0].kind == nnkSym and
  129. ai[0].symKind == nskType) or ai.kind in {nnkRefTy, nnkVarTy, nnkPtrTy, nnkProcTy}:
  130. ret = ai
  131. elif ai.kind == nnkInfix and ai[0].kind == nnkIdent and
  132. ai[0].strVal == "..":
  133. # For built-in array types, the "2" is translated to "0..1" then
  134. # automagically translated to "range[0..1]". However this is not
  135. # reflected in the AST, thus requiring manual transformation here.
  136. #
  137. # We will also be losing some context here:
  138. # var a: array[10, int]
  139. # will be translated to:
  140. # var a: array[0..9, int]
  141. # after typecheck. This means that we can't get the exact
  142. # definition as typed by the user, which will cause confusion for
  143. # users expecting:
  144. # genericParams(typeof(a)) is (StaticParam(10), int)
  145. # to be true while in fact the result will be:
  146. # genericParams(typeof(a)) is (range[0..9], int)
  147. ret = newTree(nnkBracketExpr, @[bindSym"range", ai])
  148. else:
  149. since (1, 1):
  150. echo ai.typeKind
  151. ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
  152. result.add ret
  153. break
  154. else:
  155. error "wrong kind: " & $impl.kind, impl
  156. since (1, 1):
  157. template genericParams*(T: typedesc): untyped =
  158. ## return tuple of generic params for generic `T`
  159. runnableExamples:
  160. type Foo[T1, T2] = object
  161. doAssert genericParams(Foo[float, string]) is (float, string)
  162. type Bar[N: static float, T] = object
  163. doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
  164. doAssert genericParams(Bar[1.0, string]).get(0).value == 1.0
  165. doAssert genericParams(seq[Bar[2.0, string]]).get(0) is Bar[2.0, string]
  166. var s: seq[Bar[3.0, string]]
  167. doAssert genericParams(typeof(s)) is (Bar[3.0, string],)
  168. # NOTE: For the builtin array type, the index generic param will
  169. # **always** become a range type after it's bound to a variable.
  170. doAssert genericParams(array[10, int]) is (StaticParam[10], int)
  171. var a: array[10, int]
  172. doAssert genericParams(typeof(a)) is (range[0..9], int)
  173. type T2 = T
  174. genericParamsImpl(T2)