Skip to content

Commit

Permalink
getType now works with tyInferred (arising from concepts); refs nim-l…
Browse files Browse the repository at this point in the history
…ang#18220 (nim-lang#18241)

* getType now works with tyInferred (concepts); refs nim-lang#18220

* avoid cast

* add more docs
  • Loading branch information
timotheecour authored Jun 12, 2021
1 parent 2ca6169 commit 897e50d
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 23 deletions.
2 changes: 1 addition & 1 deletion compiler/vmdeps.nim
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
of tyNot: result = mapTypeToBracket("not", mNot, t, info)
of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info)
of tyAnything: result = atomicType("anything", mNone)
of tyInferred: assert false
of tyInferred: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
of tyStatic, tyFromExpr:
if inst:
if t.n != nil: result = t.n.copyTree
Expand Down
100 changes: 78 additions & 22 deletions tests/macros/tgettype.nim
Original file line number Diff line number Diff line change
@@ -1,29 +1,85 @@
discard """
output: '''
(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))
(BracketExpr (Sym "typeDesc") (Sym "User"))
'''
"""
import macros
import std/macros
import stdtest/testutils

type
Model = object of RootObj
User = object of Model
name : string
password : string
# getType

macro testUser: string =
result = newLit(User.getType.lispRepr)
block:
type
Model = object of RootObj
User = object of Model
name : string
password : string

macro testGeneric(T: typedesc[Model]): string=
result = newLit(T.getType.lispRepr)
macro testUser: string =
result = newLit(User.getType.lispRepr)

echo testUser
echo User.testGeneric
macro testGeneric(T: typedesc[Model]): string=
result = newLit(T.getType.lispRepr)

macro assertVoid(e: typed): untyped =
assert(getTypeInst(e).typeKind == ntyVoid)
doAssert testUser == """(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))"""
doAssert User.testGeneric == """(BracketExpr (Sym "typeDesc") (Sym "User"))"""

proc voidProc() = discard
macro assertVoid(e: typed): untyped =
assert(getTypeInst(e).typeKind == ntyVoid)

assertVoid voidProc()
proc voidProc() = discard

assertVoid voidProc()

block:
# refs #18220; not an actual solution (yet) but at least shows what's currently
# possible

type Callable1[R, T, U] = concept fn
fn(default(T)) is R
fn is U

# note that typetraits.arity doesn't work
macro arity(a: typed): int =
# number of params
# this is not production code!
let a2 = a.getType[1] # this used to crash nim, with: `vmdeps.nim(292, 25) `false``
newLit a2.len - 1

type Callable2[R, T, U] = concept fn
fn(default(T)) is R
fn is U
arity(U) == 2

proc map1[T, R, U](a: T, fn: Callable1[R, T, U]): R =
let fn = U(fn)
# `cast[U](fn)` would also work;
# this is currently needed otherwise, sigmatch errors with:
# Error: attempting to call routine: 'fn'
# found 'fn' [param declared in tgettype.nim(53, 28)]
# this can be fixed in future work
fn(a)

proc map2[T, R, U](a: T, fn: Callable2[R, T, U]): R =
let fn = U(fn)
fn(a)

proc fn1(a: int, a2 = 'x'): string = $(a, a2, "fn1")
proc fn2(a: int, a2 = "zoo"): string = $(a, a2, "fn2")
proc fn3(a: int, a2 = "zoo2"): string = $(a, a2, "fn3")
proc fn4(a: int): string {.inline.} = $(a, "fn4")
proc fn5(a: int): string = $(a, "fn5")

assertAll:
# Callable1
1.map1(fn1) == """(1, 'x', "fn1")"""
1.map1(fn2) == """(1, "zoo", "fn2")"""
1.map1(fn3) == """(1, "zoo", "fn3")"""
# fn3's optional param is not honored, because fn3 and fn2 yield same
# generic instantiation; this is a caveat with this approach
# There are several possible ways to improve things in future work.
1.map1(fn4) == """(1, "fn4")"""
1.map1(fn5) == """(1, "fn5")"""

# Callable2; prevents passing procs with optional params to avoid above
# mentioned caveat, but more restrictive
not compiles(1.map2(fn1))
not compiles(1.map2(fn2))
not compiles(1.map2(fn3))
1.map2(fn4) == """(1, "fn4")"""
1.map2(fn5) == """(1, "fn5")"""

0 comments on commit 897e50d

Please sign in to comment.