Expect "Hello" but nothing output ()
import macros
macro check(t: typedesc) =
echo "hello"
newLit(true)
type
Checkable = concept type t
check(t) == true
assert int is Checkable
Playground link https://play.nim-lang.org/#ix=3T9D
concept uses a form of compiles to work, meaning no code is actually executed in the verbatim statements. You can do something like this to get around it:
proc check(t: typedesc): bool =
t is int
type Int = concept type t
when not check(t): {.error.}
echo int is Int
echo float is Int
Wow, thank you!
I thought that only the generated nodes of the macro will be executed at runtime, why does echo need to be placed in static: here?
I am trying to figure out why the latest 3 assertions can't get pass
import macros
type
Root = object
Child = object
base: Root
Friend = object
base: Root
macro hasBase[T: object; U: object](t1: typedesc[T]; t2: typedesc[U]): bool =
let falseLit = newLit(false)
var t = t1
while true:
if t == t2: return newLit(true)
let impl = t.getImpl
let objty = impl[2]
if objty[2].kind != nnkRecList:
return falseLit
let field = objty[2][0]
if field[1].kind != nnkSym or field[1].symKind != nskType:
return falseLit
if field[1].getImpl[2].kind != nnkObjectTy:
return falseLit
t = field[1]
falseLit
assert Root.hasBase(Root)
assert Child.hasBase(Root)
assert Friend.hasBase(Root)
assert not (Friend.hasBase(Child))
type
RootTy = concept type t
t.hasBase(Root)
assert Root is RootTy
assert Child is RootTy
assert Friend is RootTy
I also try this template version, and it works as expect, but it slow down the compiler dramatically
type
Root = object
Child = object
base: Root
Friend = object
base: Root
template base(_: typedesc[Root]): typedesc = void
template base(_: typedesc[Child]): typedesc = Root
template base(_: typedesc[Friend]): typedesc = Root
assert Root.base is void
assert Child.base is Root
assert Friend.base is Root
template hasBase[T](t1: typedesc[T]; t2: typedesc[T]): bool = true
template hasBase(t1: typedesc[void]; t2: typedesc): bool = false
template hasBase(t1: typedesc; t2: typedesc[void]): bool = false
template hasBase(t1, t2: typedesc): bool = t1.base.hasBase(t2)
assert Root.hasBase(Root)
assert Child.hasBase(Root)
assert Friend.hasBase(Root)
assert not (Friend.hasBase(Child))
type
RootTy = concept type t
t.hasBase(Root)
assert Root is RootTy
assert Child is RootTy
assert Friend is RootTy
assert int isnot RootTy
import macros
macro isBaseType(t: typedesc): bool =
let td = getTypeInst(t)
assert td.typeKind == ntyTypeDesc
var x = getTypeImpl(td[1])
if x.typeKind == ntyRef:
x = getTypeImpl(x[0])
if x.typeKind == ntyObject:
result = newLit(x.getTypeImpl()[1].kind == nnkEmpty)
else:
echo x.typeKind
result = newLit(false)
type
Base = ref object
Obj = ref object of RootObj
echo isBaseType(Obj)
echo isBaseType(Base)
Use this instead.
Thanks for the example. But since I'm trying to wrap "class"es from C, I can't use ref and nim's own inheritance to provide type information to nim's type system.
Also thanks The flexibility provided by nim's design allows for more solutions to problems.
OK, I found a way to print debugging message at concept triggered macro evaluation time. Use the PR enable FFI at CT done by timotheecour.
First, install libffi. Second, rebuild nim compiler
$ nim c -d:release -d:nimHasLibFFI compiler/nim.nim
In your macro, replace echo with function imported from C to print messages, for example
stderr.write(node.treeRepr & "\n")
Now compile your code with --experimental:compiletimeFFI flag
$ Nim/compiler/nim --experimental:compiletimeFFI macro_concept_lib.nim