-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Macro hygiene is hard to use correctly in nested macro expansion #37691
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
Comments
Thanks for opening an issue about this. This is one of the reasons I try to avoid writing nontrivial macros in Julia. I think that sticking to a Common Lisp-style "we do our own gensyms, thank you" approach would solve this; but I am not sure how that would compose with the existing hygiene mechanism (even if the user could disable the hygiene transformation for particular macros). |
I believe we need practical examples showing how this works. I have asked a related question on the discourse: |
#6910 is a relevant PR from David Moon. He mentions in a comment
|
i also tried try non recursive macroexpand to get intermediary macrocalls but it fails because of JuliaLang/julia#37691 try ```julia @enum a b c ``` which will call `Base.@__doc__($(Expr(:escape, :sym))))` breaking
With nested macro expansion, the inner macro sees an expression generated by the outer macro which may include
Expr(:escape)
. However, macro writers generally test macros only with a single level of expansion, not includingExpr(:escape)
.This means that macros which pattern match their input are incorrect by default when used in a nested expansion.
As a simple example of how pervasive this problem is, consider that
Base.@view
cannot generally be used within the AST generated by another macro:The problem here is that
@view
gets provided withesc(:(A[1:2]))
as an argument, which is not anExpr(:ref)
as naturally expected by the authors of@view
This problem occurs whenever macros try to pattern match their input rather than simply substituting it into a larger expression. The pattern matching must be aware that Expr(:escape) could occur anywhere. Anybody writing macros directly against the Expr API (by using the head field, etc) is going to handle this incorrectly.
This usability issue has also been discussed at length in #23221. However that issue doesn't describe the problem very clearly as a problem of usability, so I thought I'd restate it here.
Here's another interesting case:
What to do?
A possible way forward is to treat this as an
Expr
API problem: if pattern matching within macros is incorrect by default, maybe we need better ways to pattern match expressions — for example as inMacroTools
orMLStyle
— ensuring that any appearance ofExpr(:escape)
doesn't break the matching process, and returning matched pieces with a correctly nested level of escape.A larger overhaul of the macro system as in #6910 has also been mentioned in relation to this. In that PR,
quote
ed code created within macros is transformed during lowering, such that every quoted symbol made by the macro is unescaped withExpr(:hygenic, sym)
. I'm not sure whether it solves the problem completely or simply shifts it around to create new and exciting footguns for macro writers.The text was updated successfully, but these errors were encountered: