For example, the following code doesn't compile:
template t2() =
template t1() = res.add "hello"
var res = ""
t1()
echo res
proc p1() =
t2()
when isMainModule:
p1()
The compiler out:
:\Tool\Nim\tmp\a59.nim(9, 6) template/generic instantiation of `t2` from here
D:\Tool\Nim\tmp\a59.nim(5, 6) template/generic instantiation of `t1` from here
D:\Tool\Nim\tmp\a59.nim(2, 20) Error: undeclared identifier: 'res'
One way fix this is use var res {.inject.} = "".
It is very annoying, when we chang the template t2 to proc t2, the code is fine, because the t2 will be expanded in proc t2 context. When an template call another template, we have to pass all variable as parameter to caller template.
When the reference variable is parameter, pragma {.dirty.} cannot helpful.
template t1() {.dirty.} =
echo i
template t2(i: int) {.dirty.} =
t1()
proc p1() =
t2(x)
when isMainModule:
p1()
Compile output:
D:\Tool\Nim\tmp\a59.nim(8, 6) template/generic instantiation of `t2` from here
D:\Tool\Nim\tmp\a59.nim(2, 9) Error: undeclared identifier: 'i'
Yes, it is a "known" difficulty that nesting templates won't always work, because the compiler might substitute one template before another. I generally rely on macro on those cases.
But the fact that the following example don't work worries me more :
import strformat
import macros
macro t1(): untyped =
return quote do:
var i = 10
echo fmt"i is {i}" # undeclared identifier: 'i'
proc p1() =
t1()
when isMainModule:
p1()
.inject still works.
import strformat
import macros
macro t1(): untyped =
return quote do:
var i {.inject.} = 10 # add {.inject.} here
echo fmt"i is {i}" # now works
proc p1() =
t1()
when isMainModule:
p1()
before the macro substitution.
I think it happens after the macro substitution, in which i has been gensym'd. This later caused i to be unaccessible with its original name (when fmt resolves).
This macro/template thing seems to be some kind of FIFO. And the secret is to use the symbol directly.
import strformat
import macros
macro t1(): untyped =
return quote do:
var i = 10
echo "i is " & $i
proc p1() =
t1()
when isMainModule:
p1()
You need to give it the symbol (by passing it as an argument).
template t2() =
template t1(r: untyped) =
r.add("hello")
var res = ""
t1(res)
echo res
proc p1() =
t2()
when isMainModule:
p1()