typetraits.nim 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. type HoleyEnum* = (not Ordinal) and enum ## Enum with holes.
  16. type OrdinalEnum* = Ordinal and enum ## Enum without holes.
  17. runnableExamples:
  18. type A = enum a0 = 2, a1 = 4, a2
  19. type B = enum b0 = 2, b1, b2
  20. assert A is enum
  21. assert A is HoleyEnum
  22. assert A isnot OrdinalEnum
  23. assert B isnot HoleyEnum
  24. assert B is OrdinalEnum
  25. assert int isnot HoleyEnum
  26. type C[T] = enum h0 = 2, h1 = 4
  27. assert C[float] is HoleyEnum
  28. proc name*(t: typedesc): string {.magic: "TypeTrait".} =
  29. ## Returns the name of the given type.
  30. ##
  31. ## Alias for `system.\`$\`(t) <dollars.html#$,typedesc>`_ since Nim v0.20.
  32. runnableExamples:
  33. doAssert name(int) == "int"
  34. doAssert name(seq[string]) == "seq[string]"
  35. proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
  36. ## Returns the arity of the given type. This is the number of "type"
  37. ## components or the number of generic parameters a given type `t` has.
  38. runnableExamples:
  39. doAssert arity(int) == 0
  40. doAssert arity(seq[string]) == 1
  41. doAssert arity(array[3, int]) == 2
  42. doAssert arity((int, int, float, string)) == 4
  43. proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} =
  44. ## Accepts an instantiated generic type and returns its
  45. ## uninstantiated form.
  46. ## A compile-time error will be produced if the supplied type
  47. ## is not generic.
  48. ##
  49. ## **See also:**
  50. ## * `stripGenericParams proc <#stripGenericParams,typedesc>`_
  51. runnableExamples:
  52. type
  53. Foo[T] = object
  54. FooInst = Foo[int]
  55. Foo2 = genericHead(FooInst)
  56. doAssert Foo2 is Foo and Foo is Foo2
  57. doAssert genericHead(Foo[seq[string]]) is Foo
  58. doAssert not compiles(genericHead(int))
  59. type Generic = concept f
  60. type _ = genericHead(typeof(f))
  61. proc bar(a: Generic): typeof(a) = a
  62. doAssert bar(Foo[string].default) == Foo[string]()
  63. doAssert not compiles bar(string.default)
  64. when false: # these don't work yet
  65. doAssert genericHead(Foo[int])[float] is Foo[float]
  66. doAssert seq[int].genericHead is seq
  67. proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} =
  68. ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but
  69. ## instead of producing an error for non-generic types, it will just return
  70. ## them unmodified.
  71. runnableExamples:
  72. type Foo[T] = object
  73. doAssert stripGenericParams(Foo[string]) is Foo
  74. doAssert stripGenericParams(int) is int
  75. proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
  76. ## This trait returns true if the type `t` is safe to use for
  77. ## `copyMem`:idx:.
  78. ##
  79. ## Other languages name a type like these `blob`:idx:.
  80. proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} =
  81. ## Returns true for named tuples, false for any other type.
  82. runnableExamples:
  83. doAssert not isNamedTuple(int)
  84. doAssert not isNamedTuple((string, int))
  85. doAssert isNamedTuple(tuple[name: string, age: int])
  86. template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc =
  87. ## Returns `T` for `ref T | ptr T`.
  88. runnableExamples:
  89. assert (ref int).pointerBase is int
  90. type A = ptr seq[float]
  91. assert A.pointerBase is seq[float]
  92. assert (ref A).pointerBase is A # not seq[float]
  93. assert (var s = "abc"; s[0].addr).typeof.pointerBase is char
  94. T
  95. proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} =
  96. ## Returns the base type for distinct types, or the type itself otherwise.
  97. ## If `recursive` is false, only the immediate distinct base will be returned.
  98. ##
  99. ## **See also:**
  100. ## * `distinctBase template <#distinctBase.t,T,static[bool]>`_
  101. runnableExamples:
  102. type MyInt = distinct int
  103. type MyOtherInt = distinct MyInt
  104. doAssert distinctBase(MyInt) is int
  105. doAssert distinctBase(MyOtherInt) is int
  106. doAssert distinctBase(MyOtherInt, false) is MyInt
  107. doAssert distinctBase(int) is int
  108. since (1, 1):
  109. template distinctBase*[T](a: T, recursive: static bool = true): untyped =
  110. ## Overload of `distinctBase <#distinctBase,typedesc,static[bool]>`_ for values.
  111. runnableExamples:
  112. type MyInt = distinct int
  113. type MyOtherInt = distinct MyInt
  114. doAssert 12.MyInt.distinctBase == 12
  115. doAssert 12.MyOtherInt.distinctBase == 12
  116. doAssert 12.MyOtherInt.distinctBase(false) is MyInt
  117. doAssert 12.distinctBase == 12
  118. when T is distinct:
  119. distinctBase(typeof(a), recursive)(a)
  120. else: # avoids hint ConvFromXtoItselfNotNeeded
  121. a
  122. proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".} =
  123. ## Returns the number of elements of the tuple type `T`.
  124. ##
  125. ## **See also:**
  126. ## * `tupleLen template <#tupleLen.t>`_
  127. runnableExamples:
  128. doAssert tupleLen((int, int, float, string)) == 4
  129. doAssert tupleLen(tuple[name: string, age: int]) == 2
  130. template tupleLen*(t: tuple): int =
  131. ## Returns the number of elements of the tuple `t`.
  132. ##
  133. ## **See also:**
  134. ## * `tupleLen proc <#tupleLen,typedesc>`_
  135. runnableExamples:
  136. doAssert tupleLen((1, 2)) == 2
  137. tupleLen(typeof(t))
  138. template get*(T: typedesc[tuple], i: static int): untyped =
  139. ## Returns the `i`-th element of `T`.
  140. # Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
  141. runnableExamples:
  142. doAssert get((int, int, float, string), 2) is float
  143. typeof(default(T)[i])
  144. type StaticParam*[value: static type] = object
  145. ## Used to wrap a static value in `genericParams <#genericParams.t,typedesc>`_.
  146. since (1, 3, 5):
  147. template elementType*(a: untyped): typedesc =
  148. ## Returns the element type of `a`, which can be any iterable (over which you
  149. ## can iterate).
  150. runnableExamples:
  151. iterator myiter(n: int): auto =
  152. for i in 0 ..< n:
  153. yield i
  154. doAssert elementType(@[1,2]) is int
  155. doAssert elementType("asdf") is char
  156. doAssert elementType(myiter(3)) is int
  157. typeof(block: (for ai in a: ai))
  158. import macros
  159. macro enumLen*(T: typedesc[enum]): int =
  160. ## Returns the number of items in the enum `T`.
  161. runnableExamples:
  162. type Foo = enum
  163. fooItem1
  164. fooItem2
  165. doAssert Foo.enumLen == 2
  166. let bracketExpr = getType(T)
  167. expectKind(bracketExpr, nnkBracketExpr)
  168. let enumTy = bracketExpr[1]
  169. expectKind(enumTy, nnkEnumTy)
  170. result = newLit(enumTy.len - 1)
  171. macro genericParamsImpl(T: typedesc): untyped =
  172. # auxiliary macro needed, can't do it directly in `genericParams`
  173. result = newNimNode(nnkTupleConstr)
  174. var impl = getTypeImpl(T)
  175. expectKind(impl, nnkBracketExpr)
  176. impl = impl[1]
  177. while true:
  178. case impl.kind
  179. of nnkSym:
  180. impl = impl.getImpl
  181. of nnkTypeDef:
  182. impl = impl[2]
  183. of nnkTypeOfExpr:
  184. impl = getTypeInst(impl[0])
  185. of nnkBracketExpr:
  186. for i in 1..<impl.len:
  187. let ai = impl[i]
  188. var ret: NimNode = nil
  189. case ai.typeKind
  190. of ntyTypeDesc:
  191. ret = ai
  192. of ntyStatic: doAssert false
  193. else:
  194. # getType from a resolved symbol might return a typedesc symbol.
  195. # If so, use it directly instead of wrapping it in StaticParam.
  196. if (ai.kind == nnkSym and ai.symKind == nskType) or
  197. (ai.kind == nnkBracketExpr and ai[0].kind == nnkSym and
  198. ai[0].symKind == nskType) or ai.kind in {nnkRefTy, nnkVarTy, nnkPtrTy, nnkProcTy}:
  199. ret = ai
  200. elif ai.kind == nnkInfix and ai[0].kind == nnkIdent and
  201. ai[0].strVal == "..":
  202. # For built-in array types, the "2" is translated to "0..1" then
  203. # automagically translated to "range[0..1]". However this is not
  204. # reflected in the AST, thus requiring manual transformation here.
  205. #
  206. # We will also be losing some context here:
  207. # var a: array[10, int]
  208. # will be translated to:
  209. # var a: array[0..9, int]
  210. # after typecheck. This means that we can't get the exact
  211. # definition as typed by the user, which will cause confusion for
  212. # users expecting:
  213. # genericParams(typeof(a)) is (StaticParam(10), int)
  214. # to be true while in fact the result will be:
  215. # genericParams(typeof(a)) is (range[0..9], int)
  216. ret = newTree(nnkBracketExpr, @[bindSym"range", ai])
  217. else:
  218. since (1, 1):
  219. ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
  220. result.add ret
  221. break
  222. else:
  223. error "wrong kind: " & $impl.kind, impl
  224. since (1, 1):
  225. template genericParams*(T: typedesc): untyped =
  226. ## Returns the tuple of generic parameters for the generic type `T`.
  227. ##
  228. ## **Note:** For the builtin array type, the index generic parameter will
  229. ## **always** become a range type after it's bound to a variable.
  230. runnableExamples:
  231. type Foo[T1, T2] = object
  232. doAssert genericParams(Foo[float, string]) is (float, string)
  233. type Bar[N: static float, T] = object
  234. doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
  235. doAssert genericParams(Bar[1.0, string]).get(0).value == 1.0
  236. doAssert genericParams(seq[Bar[2.0, string]]).get(0) is Bar[2.0, string]
  237. var s: seq[Bar[3.0, string]]
  238. doAssert genericParams(typeof(s)) is (Bar[3.0, string],)
  239. doAssert genericParams(array[10, int]) is (StaticParam[10], int)
  240. var a: array[10, int]
  241. doAssert genericParams(typeof(a)) is (range[0..9], int)
  242. type T2 = T
  243. genericParamsImpl(T2)
  244. proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
  245. proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} =
  246. ## Return true if the func/proc/etc `fn` has `closure`.
  247. ## `fn` has to be a resolved symbol of kind `nnkSym`. This
  248. ## implies that the macro that calls this proc should accept `typed`
  249. ## arguments and not `untyped` arguments.
  250. expectKind fn, nnkSym
  251. result = hasClosureImpl(fn)