typetraits.nim 13 KB

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