summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-06-12 12:35:12 -0700
committerGitHub <noreply@github.com>2021-06-12 21:35:12 +0200
commit897e50d5fe274850d4772612a90b325e33cca8a2 (patch)
tree7ee31687296ef774ec4810169f506454777fe8d2
parent2ca6169ba10dca704712b59339a24208e30e79ac (diff)
downloadNim-897e50d5fe274850d4772612a90b325e33cca8a2.tar.gz
getType now works with tyInferred (arising from concepts); refs #18220 (#18241)
* getType now works with tyInferred (concepts); refs #18220

* avoid cast

* add more docs
-rw-r--r--compiler/vmdeps.nim2
-rw-r--r--tests/macros/tgettype.nim100
2 files changed, 79 insertions, 23 deletions
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index b67e2e48a..1afda14b0 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -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
diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim
index bd70a1c30..d91efe1fe 100644
--- a/tests/macros/tgettype.nim
+++ b/tests/macros/tgettype.nim
@@ -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")"""