-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Nested dirty template in generic proc #23889
Comments
!nim c template inner(i: static int) {.dirty.} =
let thing = 1
template outer() =
proc p[T](x: T) =
inner(5)
echo thing
outer()
p(0) |
🐧 Linux bisect by @Graveflo (contributor)devel 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) stable 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 2.0.4 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 2.0.0 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 1.6.20 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 1.4.8 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 1.2.18 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) 1.0.10 👎 FAILOutput
IRCompiled filesize0 (0 bytes)
Stats
ASTnnkStmtList.newTree(
nnkTemplateDef.newTree(
newIdentNode("inner"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("i"),
nnkCommand.newTree(
newIdentNode("static"),
newIdentNode("int")
),
newEmptyNode()
)
),
nnkPragma.newTree(
newIdentNode("dirty")
),
newEmptyNode(),
nnkStmtList.newTree(
nnkLetSection.newTree(
nnkIdentDefs.newTree(
newIdentNode("thing"),
newEmptyNode(),
newLit(1)
)
)
)
),
nnkTemplateDef.newTree(
newIdentNode("outer"),
newEmptyNode(),
newEmptyNode(),
nnkFormalParams.newTree(
newEmptyNode()
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkProcDef.newTree(
newIdentNode("p"),
newEmptyNode(),
nnkGenericParams.newTree(
nnkIdentDefs.newTree(
newIdentNode("T"),
newEmptyNode(),
newEmptyNode()
)
),
nnkFormalParams.newTree(
newEmptyNode(),
nnkIdentDefs.newTree(
newIdentNode("x"),
newIdentNode("T"),
newEmptyNode()
)
),
newEmptyNode(),
newEmptyNode(),
nnkStmtList.newTree(
nnkCall.newTree(
newIdentNode("inner"),
newLit(5)
),
nnkCommand.newTree(
newIdentNode("echo"),
newIdentNode("thing")
)
)
)
)
),
nnkCall.newTree(
newIdentNode("outer")
),
nnkCall.newTree(
newIdentNode("p"),
newLit(0)
)
) Stats
🤖 Bug found in |
yea has nothing to do with statics actually |
The failure probably happens because typed templates are deferred for too long (or maybe it would be unsafe to expand), so it's not expanded by the time the generic body is checked. Works with untyped. |
Take a look around this location Line 284 in c1f91c2
And potentially here, maybe Line 640 in c1f91c2
|
Thanks for looking into this. As I said here #23890 (comment) I'm not too sure if early expansion is going to to be the right call, but it's certainly something to try. Intuitively it makes sense, but I wonder why this expansion was conditional in the first place. |
This is an issue with generics more than templates and there are other issues that cover the exact same problem which I will edit this comment to link later. (Edit: #21376, #21249, #22084, #15693) This is a simpler example: template inner(i: int) {.dirty.} = # or just a normal template with {.inject.}
let thing = 1
proc p[T](x: T) =
inner(5)
echo thing
p(0) The undeclared identifier error isn't actually fatal but as discovered in #23890 not erroring early can lead to some quirky behavior. The existing attitude to this issue has been "will be fixed with proper generic expression typechecking" in the comments of the other issues but maybe in the meantime we can find ways to signal to the compiler that there shouldn't be an error, these might prove useful even when we get generic typechecking. Or they might not be useful at all. Some ways I can think of right off the bat:
proc p[T](x: T) =
inner(5)
{.inject: thing.} # generic proc is told that a symbol `thing` has been injected
echo thing
template inner(i: int) {.injects: [thing].} =
let thing {.inject.} = 1
proc p[T](x: T) =
inner(5) # compiler knows `thing` was injected
echo thing |
Thanks for explaining that. I've been messing with simpler examples too. I don't like this duct tape syntax because it'll just force a refactor later, or worse. It could end up being something that sticks around longer then it needs to. If there is a problem why not just go for the throat and look into this "proper generic expression type checking" problem? If the solution to that problem is anything like what we currently have, there is still a problem with when to do the template expansion. edit: posted a bad example. I'll try and come up with a better one |
ah nvm maybe it is just the type checking. I'm assuming the operands to the template and macro are the problem according to SirOlaf's |
I edited my comment to link the other issues but it seems like you found them already, yes this only triggers when the template has a typed argument or is overloaded, fully untyped single templates get evaluated early at a syntax level in generic procs.
It's mentioned here: nim-lang/RFCs#168, Araq can probably explain it better. It's also in the roadmap under Upcoming Versions -> Language. I would encourage asking Araq about it, it's really his plan and he might have ideas about how to speed it up. It needs (for new functionality) a stronger type system, especially more fleshed out concepts, hence the RFC it was brought up in. The PRs that focus on |
FWIW I had it working in a prototype and it didn't need |
Description
This might be mentioned elsewhere in another form, but this is a self contained example
this does not work:
but this does:
Nim Version
Nim Compiler Version 2.1.9 [Linux: amd64]
Compiled at 2024-07-24
Copyright (c) 2006-2024 by Andreas Rumpf
git hash: c1f91c2
active boot switches: -d:releas
Current Output
Expected Output
Possible Solution
No response
Additional Information
No response
The text was updated successfully, but these errors were encountered: