123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2012 Nim Contributors
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module defines compile-time reflection procs for
- ## working with types.
- ##
- ## Unstable API.
- import std/private/since
- export system.`$` # for backward compatibility
- when defined(nimPreviewSlimSystem):
- import std/assertions
- type HoleyEnum* = (not Ordinal) and enum ## Enum with holes.
- type OrdinalEnum* = Ordinal and enum ## Enum without holes.
- runnableExamples:
- type A = enum a0 = 2, a1 = 4, a2
- type B = enum b0 = 2, b1, b2
- assert A is enum
- assert A is HoleyEnum
- assert A isnot OrdinalEnum
- assert B isnot HoleyEnum
- assert B is OrdinalEnum
- assert int isnot HoleyEnum
- type C[T] = enum h0 = 2, h1 = 4
- assert C[float] is HoleyEnum
- proc name*(t: typedesc): string {.magic: "TypeTrait".} =
- ## Returns the name of `t`.
- ##
- ## Alias for `system.\`$\`(t) <dollars.html#$,typedesc>`_ since Nim v0.20.
- runnableExamples:
- doAssert name(int) == "int"
- doAssert name(seq[string]) == "seq[string]"
- proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
- ## Returns the arity of `t`. This is the number of "type"
- ## components or the number of generic parameters a given type `t` has.
- runnableExamples:
- doAssert arity(int) == 0
- doAssert arity(seq[string]) == 1
- doAssert arity(array[3, int]) == 2
- doAssert arity((int, int, float, string)) == 4
- proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} =
- ## Accepts an instantiated generic type and returns its
- ## uninstantiated form.
- ## A compile-time error will be produced if the supplied type
- ## is not generic.
- ##
- ## **See also:**
- ## * `stripGenericParams proc <#stripGenericParams,typedesc>`_
- runnableExamples:
- type
- Foo[T] = object
- FooInst = Foo[int]
- Foo2 = genericHead(FooInst)
- doAssert Foo2 is Foo and Foo is Foo2
- doAssert genericHead(Foo[seq[string]]) is Foo
- doAssert not compiles(genericHead(int))
- type Generic = concept f
- type _ = genericHead(typeof(f))
- proc bar(a: Generic): typeof(a) = a
- doAssert bar(Foo[string].default) == Foo[string]()
- doAssert not compiles bar(string.default)
- when false: # these don't work yet
- doAssert genericHead(Foo[int])[float] is Foo[float]
- doAssert seq[int].genericHead is seq
- proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} =
- ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but
- ## instead of producing an error for non-generic types, it will just return
- ## them unmodified.
- runnableExamples:
- type Foo[T] = object
- doAssert stripGenericParams(Foo[string]) is Foo
- doAssert stripGenericParams(int) is int
- proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
- ## Returns true if `t` is safe to use for `copyMem`:idx:.
- ##
- ## Other languages name a type like these `blob`:idx:.
- proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} =
- ## Returns true for named tuples, false for any other type.
- runnableExamples:
- doAssert not isNamedTuple(int)
- doAssert not isNamedTuple((string, int))
- doAssert isNamedTuple(tuple[name: string, age: int])
- template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc =
- ## Returns `T` for `ref T | ptr T`.
- runnableExamples:
- assert (ref int).pointerBase is int
- type A = ptr seq[float]
- assert A.pointerBase is seq[float]
- assert (ref A).pointerBase is A # not seq[float]
- assert (var s = "abc"; s[0].addr).typeof.pointerBase is char
- T
- proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} =
- ## Returns the base type for distinct types, or the type itself otherwise.
- ## If `recursive` is false, only the immediate distinct base will be returned.
- ##
- ## **See also:**
- ## * `distinctBase template <#distinctBase.t,T,static[bool]>`_
- runnableExamples:
- type MyInt = distinct int
- type MyOtherInt = distinct MyInt
- doAssert distinctBase(MyInt) is int
- doAssert distinctBase(MyOtherInt) is int
- doAssert distinctBase(MyOtherInt, false) is MyInt
- doAssert distinctBase(int) is int
- since (1, 1):
- template distinctBase*[T](a: T, recursive: static bool = true): untyped =
- ## Overload of `distinctBase <#distinctBase,typedesc,static[bool]>`_ for values.
- runnableExamples:
- type MyInt = distinct int
- type MyOtherInt = distinct MyInt
- doAssert 12.MyInt.distinctBase == 12
- doAssert 12.MyOtherInt.distinctBase == 12
- doAssert 12.MyOtherInt.distinctBase(false) is MyInt
- doAssert 12.distinctBase == 12
- when T is distinct:
- distinctBase(typeof(a), recursive)(a)
- else: # avoids hint ConvFromXtoItselfNotNeeded
- a
- proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".} =
- ## Returns the number of elements of the tuple type `T`.
- ##
- ## **See also:**
- ## * `tupleLen template <#tupleLen.t>`_
- runnableExamples:
- doAssert tupleLen((int, int, float, string)) == 4
- doAssert tupleLen(tuple[name: string, age: int]) == 2
- template tupleLen*(t: tuple): int =
- ## Returns the number of elements of the tuple `t`.
- ##
- ## **See also:**
- ## * `tupleLen proc <#tupleLen,typedesc>`_
- runnableExamples:
- doAssert tupleLen((1, 2)) == 2
- tupleLen(typeof(t))
- template get*(T: typedesc[tuple], i: static int): untyped =
- ## Returns the `i`-th element of `T`.
- # Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
- runnableExamples:
- doAssert get((int, int, float, string), 2) is float
- typeof(default(T)[i])
- type StaticParam*[value: static type] = object
- ## Used to wrap a static value in `genericParams <#genericParams.t,typedesc>`_.
- since (1, 3, 5):
- template elementType*(a: untyped): typedesc =
- ## Returns the element type of `a`, which can be any iterable (over which you
- ## can iterate).
- runnableExamples:
- iterator myiter(n: int): auto =
- for i in 0 ..< n:
- yield i
- doAssert elementType(@[1,2]) is int
- doAssert elementType("asdf") is char
- doAssert elementType(myiter(3)) is int
- typeof(block: (for ai in a: ai))
- import macros
- macro enumLen*(T: typedesc[enum]): int =
- ## Returns the number of items in the enum `T`.
- runnableExamples:
- type Foo = enum
- fooItem1
- fooItem2
- doAssert Foo.enumLen == 2
- let bracketExpr = getType(T)
- expectKind(bracketExpr, nnkBracketExpr)
- let enumTy = bracketExpr[1]
- expectKind(enumTy, nnkEnumTy)
- result = newLit(enumTy.len - 1)
- macro genericParamsImpl(T: typedesc): untyped =
- # auxiliary macro needed, can't do it directly in `genericParams`
- result = newNimNode(nnkTupleConstr)
- var impl = getTypeImpl(T)
- expectKind(impl, nnkBracketExpr)
- impl = impl[1]
- while true:
- case impl.kind
- of nnkSym:
- impl = impl.getImpl
- of nnkTypeDef:
- impl = impl[2]
- of nnkTypeOfExpr:
- impl = getTypeInst(impl[0])
- of nnkBracketExpr:
- for i in 1..<impl.len:
- let ai = impl[i]
- var ret: NimNode = nil
- case ai.typeKind
- of ntyTypeDesc:
- ret = ai
- of ntyStatic: doAssert false
- else:
- # getType from a resolved symbol might return a typedesc symbol.
- # If so, use it directly instead of wrapping it in StaticParam.
- if (ai.kind == nnkSym and ai.symKind == nskType) or
- (ai.kind == nnkBracketExpr and ai[0].kind == nnkSym and
- ai[0].symKind == nskType) or ai.kind in {nnkRefTy, nnkVarTy, nnkPtrTy, nnkProcTy}:
- ret = ai
- elif ai.kind == nnkInfix and ai[0].kind == nnkIdent and
- ai[0].strVal == "..":
- # For built-in array types, the "2" is translated to "0..1" then
- # automagically translated to "range[0..1]". However this is not
- # reflected in the AST, thus requiring manual transformation here.
- #
- # We will also be losing some context here:
- # var a: array[10, int]
- # will be translated to:
- # var a: array[0..9, int]
- # after typecheck. This means that we can't get the exact
- # definition as typed by the user, which will cause confusion for
- # users expecting:
- # genericParams(typeof(a)) is (StaticParam(10), int)
- # to be true while in fact the result will be:
- # genericParams(typeof(a)) is (range[0..9], int)
- ret = newTree(nnkBracketExpr, @[bindSym"range", ai])
- else:
- since (1, 1):
- ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
- result.add ret
- break
- else:
- error "wrong kind: " & $impl.kind, impl
- since (1, 1):
- template genericParams*(T: typedesc): untyped =
- ## Returns the tuple of generic parameters for the generic type `T`.
- ##
- ## **Note:** For the builtin array type, the index generic parameter will
- ## **always** become a range type after it's bound to a variable.
- runnableExamples:
- type Foo[T1, T2] = object
- doAssert genericParams(Foo[float, string]) is (float, string)
- type Bar[N: static float, T] = object
- doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
- doAssert genericParams(Bar[1.0, string]).get(0).value == 1.0
- doAssert genericParams(seq[Bar[2.0, string]]).get(0) is Bar[2.0, string]
- var s: seq[Bar[3.0, string]]
- doAssert genericParams(typeof(s)) is (Bar[3.0, string],)
- doAssert genericParams(array[10, int]) is (StaticParam[10], int)
- var a: array[10, int]
- doAssert genericParams(typeof(a)) is (range[0..9], int)
- type T2 = T
- genericParamsImpl(T2)
- proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
- proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} =
- ## Returns true if the func/proc/etc `fn` has `closure`.
- ## `fn` has to be a resolved symbol of kind `nnkSym`. This
- ## implies that the macro that calls this proc should accept `typed`
- ## arguments and not `untyped` arguments.
- expectKind fn, nnkSym
- result = hasClosureImpl(fn)
- template toUnsigned*(T: typedesc[SomeInteger and not range]): untyped =
- ## Returns an unsigned type with same bit size as `T`.
- runnableExamples:
- assert int8.toUnsigned is uint8
- assert uint.toUnsigned is uint
- assert int.toUnsigned is uint
- # range types are currently unsupported:
- assert not compiles(toUnsigned(range[0..7]))
- when T is int8: uint8
- elif T is int16: uint16
- elif T is int32: uint32
- elif T is int64: uint64
- elif T is int: uint
- else: T
- template toSigned*(T: typedesc[SomeInteger and not range]): untyped =
- ## Returns a signed type with same bit size as `T`.
- runnableExamples:
- assert int8.toSigned is int8
- assert uint16.toSigned is int16
- # range types are currently unsupported:
- assert not compiles(toSigned(range[0..7]))
- when T is uint8: int8
- elif T is uint16: int16
- elif T is uint32: int32
- elif T is uint64: int64
- elif T is uint: int
- else: T
|