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

getType now works with tyInferred (arising from concepts); refs #18220 #18241

Merged
merged 3 commits into from
Jun 12, 2021
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
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")"""