-
-
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
Identical AST succeeds, but fails when produced by a macro, with "cannot use symbol of kind 'proc' as a 'field'" #11182
Comments
It looks like the In my opinion that is also the source of the problem, If you just want to make it work: You know the trick with converting all the symbols in an AST back to idents? (Here is a snippet, quite a few of my recently opened tickets were related to using symbols instead of idents). Alternatively you can use |
Oh, I didn't know the argument to
I thought about that, but it seemed like too much of a confusing hack to find every nnkSym and run gensym to make an appropriate identifier for it, the way that the compiler should already be doing automatically.
I... never considered that. That's actually not entirely a bad idea. Only trouble is you can't have templates with a variable sized list of arguments, that I'm aware of, so a template like template make_object(name: untyped, fields: varargs[untyped]) =
type name = object
#{(fields)=>+=>(field => >>>*** field: int ***<<<)+magic pixies} would have to be a macro, if it wanted to do anything with the syntax of each field, other than just plunk down the fields in between |
That's true, in case it helps to solve your problem this code does exactly that. |
when you add another overload of import macros
macro examine(x: untyped): untyped =
debugEcho(x.treeRepr)
return x
type Kind = enum
Thingy = 23
examine:
type
thingy = object
case kind: Kind
of Thingy:
value: int
proc kind(arg: string): void = discard # <--- *** this is new ***
macro fail(): untyped =
result = quote:
type
thingy = object
case kind: Kind
of Thingy:
value: int
debugEcho(result.treeRepr)
const broken = true
when broken:
fail() output
two alternatives
|
Now that explains some issues I had where I had to build the AST from scratch because quote would fail with a cryptic error. |
@mratsim can you link or mention the issues you had? I just talked this morning with Araq about how strange symbols in templates get resolved, and then I saw this issue, which is cause by exactly the problem I mentioned. |
Unfortunately I didn't raise issues at the time because it was part of a huge macro base and I interrupted myself a lot to raise various bugs at the time so I wanted to get things done. Looking back into the history of my project, I had this "Make import with no use compile. Stuck at undeclared identifier contiguousIndex_" where I changed a getAST call to result quote do to apparently solve a bug where a proc/macro is not used even if imported. The next commit is mratsim/laser@e6db3be which makes the code compile with a long comment: let index = newIdentNode("contiguousIndex_")
# [...] skipped
let # quote do, doesn't interpolate properly to static
# We get "undeclared identifier contiguousIndex_" otherwise
use_simd_node = newLit use_simd
nowait_node = newLit nowait
|
this isn't related to quote do reduced with 0 imports: when true:
proc fn1()=discard
proc fn2()=discard
proc fn2(a: int)=discard
proc fn3()=discard
template baz =
var fn1: int # works
# mixin fn3 # workaround: would make it "work" but with a wrong AST still
type A = object
fn2: int
fn3: int # Error: cannot use symbol of kind 'proc' as a 'field'
baz() proposal fixfields should be gensym'd (as do var's) detailswhen true:
import macros
proc fn1()=discard
proc fn2()=discard
proc fn2(a: int)=discard
proc fn3()=discard
macro fail(): untyped =
result = quote:
var fn1: int # works
# mixin fn3 # workaround: would make it "work"
type A = object
fn2: int
fn3: int # Error: cannot use symbol of kind 'proc' as a 'field'
debugEcho(result.treeRepr)
debugEcho(result.repr)
fail() Error: cannot use symbol of kind 'proc' as a 'field'
|
Read basically none of the text above, but from what I understand the issue described here is fixed by #22106, the original example now gives:
I'm guessing this is fine, changing |
Original test cases in this issue are too convoluted for what was such a simple problem. Just closing. |
The macro produces an identical AST, except with "Sym" instead of "Ident" some places, for hygiene, but compilation fails with a mysterious error. I'm not even using proc nodes!
Example
Current Output
Expected Output
Compilation succeeds.
Possible Solution
Any
Sym
node in a macro is resolved to anIdent
(aka afield
) node by the hygiene system, so the symbolThingy
will turn intoThingy1234567
for the actual code, thus preventing macros from polluting the global namespace. This is done in compiler/sem.nim in newSymG wheren.sym
takes theIdent
out of a not yet set-in-stoneSym
, but aTSymKind
is also passed to that function, causing the above error if the underlyingn.sym
kind is different. That makes sense, because if a symbol resolves to something that is not an Ident, then using it in the AST as an Ident would produce invalid code.The problem is when a symbol just happens to be the same name as a procedure from any imported module. In the above example, using
kind
as theIdent
naming typethingy
is perfectly valid code, but when done inside a macro,kind
is supposed to be replaced withkind1234etc
during the conversion fromSym
toIdent
. That should happen, but what happens instead is theSym
resolution process examines all imported procedures first, finding a procedure namedkind
(wildcard imported from the macros package). Sokind
has the symbol type ofskProc
now. Even thoughkind
would make a perfectly good field, andkind1234etc
would make an even more distinguised field, the code errors out thinking that we stuffed an entire procedure into a symbol that was only supposed to have an ident.This can be avoided by using your psychic powers and genius level code analysis abilities to instantly calculate all imported symbols, so that when you write code, you never choose a symbol for your identifier that has the same name as an imported procedure, thus:
Or another way of thinking about it is when writing symbols in macros, always tack on some large random number to the end, and you probably won't have symbols that are the same name as any procedure you imported. You can also do this:
Provided your psychic abilities are at least powerful enough to divine which module your colliding symbol was imported from, because Nim won't give you any help with that.
Another solution is to do this:
But then you have to slog through the errors when importing quote should also pull in newIdentNode, since quote uses newIdentNode, but instead it errors out until you manually add it in. But after you've added all the super secret surprise imports, then it's somewhat possible for someone analyzing your code to figure out what it does, without doing an exhaustive search through all the symbols of every import, as is required with the
import X
syntax.Some actual solutions that aren't hacks would be:
from X import
syntax actually useful and reducing name collisions.So... that's my suggestion. Not sure exactly how to do it though.
Additional Information
The text was updated successfully, but these errors were encountered: