@@ -212,13 +212,44 @@ function prepare_macro_args(ctx, mctx, raw_args)
212212 return macro_args
213213end
214214
215+ # TODO : Do we need to handle :scope_layer or multiple escapes here?
216+ # See https://github.com/c42f/JuliaLowering.jl/issues/39
217+ """
218+ Insert a hygienic-scope around each arg of K"toplevel" returned from a macro.
219+
220+ It isn't correct for macro expansion to recurse into a K"toplevel" expression
221+ since one child may define a macro and the next may use it. However, not
222+ recursing now means we lose some important context: the module of the macro we
223+ just expanded, which is necessary for resolving the identifiers in the
224+ K"toplevel" AST. The solution implemented in JuliaLang/julia#53515 was to save
225+ our place and expand later using `Expr(:hygienic-scope toplevel_child mod)`.
226+
227+ Of course, these hygienic-scopes are also necessary because existing user code
228+ contains the corresponding escaping, which would otherwise cause errors. We
229+ already consumed the hygienic-scope that comes with every expansion, but won't
230+ be looking for escapes under :toplevel, so push hygienic-scope under toplevel
231+ """
232+ function fix_toplevel_expansion (ctx, ex:: SyntaxTree , mod:: Module , lnn:: LineNumberNode )
233+ if kind (ex) === K " toplevel"
234+ mapchildren (ctx, ex) do e
235+ @ast ctx ex [K " hygienic_scope" e mod:: K"Value" lnn:: K"Value" ]
236+ end
237+ else
238+ mapchildren (e-> fix_toplevel_expansion (ctx, e, mod, lnn), ctx, ex)
239+ end
240+ end
241+
215242function expand_macro (ctx, ex)
216243 @assert kind (ex) == K " macrocall"
217244
218245 macname = ex[1 ]
219246 mctx = MacroContext (ctx. graph, ex, current_layer (ctx))
220247 macfunc = eval_macro_name (ctx, mctx, macname)
221248 raw_args = ex[2 : end ]
249+ macro_loc = let loc = source_location (LineNumberNode, ex)
250+ # Some macros, e.g. @cmd, don't play nicely with file == nothing
251+ isnothing (loc. file) ? LineNumberNode (loc. line, :none ) : loc
252+ end
222253 # We use a specific well defined world age for the next checks and macro
223254 # expansion invocations. This avoids inconsistencies if the latest world
224255 # age changes concurrently.
@@ -248,10 +279,6 @@ function expand_macro(ctx, ex)
248279 else
249280 # Compat: attempt to invoke an old-style macro if there's no applicable
250281 # method for new-style macro arguments.
251- macro_loc = let loc = source_location (LineNumberNode, ex)
252- # Some macros, e.g. @cmd, don't play nicely with file == nothing
253- isnothing (loc. file) ? LineNumberNode (loc. line, :none ) : loc
254- end
255282 macro_args = Any[macro_loc, current_layer (ctx). mod]
256283 for arg in raw_args
257284 # For hygiene in old-style macros, we omit any additional scope
@@ -287,6 +314,7 @@ function expand_macro(ctx, ex)
287314 # method was defined (may be different from `parentmodule(macfunc)`)
288315 mod_for_ast = lookup_method_instance (macfunc, macro_args,
289316 ctx. macro_world). def. module
317+ expanded = fix_toplevel_expansion (ctx, expanded, mod_for_ast, macro_loc)
290318 new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast,
291319 current_layer_id (ctx), true )
292320 push! (ctx. scope_layers, new_layer)
0 commit comments