Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typetraits: fix #6454; genericParams; tuple len; tuple type get #13064

Merged
merged 3 commits into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
40 changes: 40 additions & 0 deletions lib/pure/typetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions lib/system/inclrtl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 14 additions & 4 deletions tests/metatype/ttypetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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