@@ -18,9 +18,12 @@ struct MacroExpansionContext{GraphType} <: AbstractLoweringContext
1818 graph:: GraphType
1919 bindings:: Bindings
2020 scope_layers:: Vector{ScopeLayer}
21- current_layer :: ScopeLayer
21+ scope_layer_stack :: Vector{LayerId}
2222end
2323
24+ current_layer (ctx:: MacroExpansionContext ) = ctx. scope_layers[last (ctx. scope_layer_stack)]
25+ current_layer_id (ctx:: MacroExpansionContext ) = last (ctx. scope_layer_stack)
26+
2427# --------------------------------------------------
2528# Expansion of quoted expressions
2629function collect_unquoted! (ctx, unquoted, ex, depth)
@@ -122,58 +125,119 @@ function eval_macro_name(ctx, ex)
122125 ctx3, ex3 = resolve_scopes (ctx2, ex2)
123126 ctx4, ex4 = convert_closures (ctx3, ex3)
124127 ctx5, ex5 = linearize_ir (ctx4, ex4)
125- mod = ctx . current_layer. mod
128+ mod = current_layer (ctx) . mod
126129 expr_form = to_lowered_expr (mod, ex5)
127130 eval (mod, expr_form)
128131end
129132
133+ # Record scope layer information for symbols passed to a macro by setting
134+ # scope_layer for each expression and also processing any K"escape" arising
135+ # from previous expansion of old-style macros.
136+ #
137+ # See also set_scope_layer()
138+ function set_macro_arg_hygiene (ctx, ex, layer_ids, layer_idx)
139+ k = kind (ex)
140+ scope_layer = get (ex, :scope_layer , layer_ids[layer_idx])
141+ if k == K " module" || k == K " toplevel" || k == K " inert"
142+ makenode (ctx, ex, ex, children (ex);
143+ scope_layer= scope_layer)
144+ elseif k == K " ."
145+ makenode (ctx, ex, ex, set_macro_arg_hygiene (ctx, ex[1 ], layer_ids, layer_idx), ex[2 ],
146+ scope_layer= scope_layer)
147+ elseif ! is_leaf (ex)
148+ inner_layer_idx = layer_idx
149+ if k == K " escape"
150+ inner_layer_idx = layer_idx - 1
151+ if inner_layer_idx < 1
152+ # If we encounter too many escape nodes, there's probably been
153+ # an error in the previous macro expansion.
154+ # todo: The error here isn't precise about that - maybe we
155+ # should record that macro call expression with the scope layer
156+ # if we want to report the error against the macro call?
157+ throw (MacroExpansionError (ex, " `escape` node in outer context" ))
158+ end
159+ end
160+ mapchildren (e-> set_macro_arg_hygiene (ctx, e, layer_ids, inner_layer_idx),
161+ ctx, ex; scope_layer= scope_layer)
162+ else
163+ makeleaf (ctx, ex, ex; scope_layer= scope_layer)
164+ end
165+ end
166+
130167function expand_macro (ctx, ex)
131168 @assert kind (ex) == K " macrocall"
132169
133170 macname = ex[1 ]
134171 macfunc = eval_macro_name (ctx, macname)
135- # Macro call arguments may be either
136- # * Unprocessed by the macro expansion pass
137- # * Previously processed, but spliced into a further macro call emitted by
138- # a macro expansion.
139- # In either case, we need to set any unset scope layers before passing the
140- # arguments to the macro call.
141- mctx = MacroContext (ctx. graph, ex, ctx. current_layer)
142- macro_args = Any[mctx]
143- for i in 2 : numchildren (ex)
144- push! (macro_args, set_scope_layer (ctx, ex[i], ctx. current_layer. id, false ))
145- end
172+ mctx = MacroContext (ctx. graph, ex, current_layer (ctx))
173+ raw_args = ex[2 : end ]
146174 macro_invocation_world = Base. get_world_counter ()
147- expanded = try
148- # TODO : Allow invoking old-style macros for compat
149- invokelatest (macfunc, macro_args... )
150- catch exc
151- if exc isa MacroExpansionError
152- # Add context to the error.
153- # TODO : Using rethrow() is kinda ugh. Is there a way to avoid it?
154- rethrow (MacroExpansionError (mctx, exc. ex, exc. msg, exc. position))
175+ if hasmethod (macfunc, Tuple{typeof (mctx), typeof .(raw_args)... }; world= Base. get_world_counter ())
176+ macro_args = Any[mctx]
177+ for arg in raw_args
178+ # Add hygiene information to be carried along with macro arguments.
179+ #
180+ # Macro call arguments may be either
181+ # * Unprocessed by the macro expansion pass
182+ # * Previously processed, but spliced into a further macro call emitted by
183+ # a macro expansion.
184+ # In either case, we need to set scope layers before passing the
185+ # arguments to the macro call.
186+ push! (macro_args, set_macro_arg_hygiene (ctx, arg, ctx. scope_layer_stack,
187+ length (ctx. scope_layer_stack)))
188+ end
189+ expanded = try
190+ invokelatest (macfunc, macro_args... )
191+ catch exc
192+ if exc isa MacroExpansionError
193+ # Add context to the error.
194+ # TODO : Using rethrow() is kinda ugh. Is there a way to avoid it?
195+ rethrow (MacroExpansionError (mctx, exc. ex, exc. msg, exc. position))
196+ else
197+ throw (MacroExpansionError (mctx, ex, " Error expanding macro" , :all ))
198+ end
199+ end
200+ if expanded isa SyntaxTree
201+ if ! is_compatible_graph (ctx, expanded)
202+ # If the macro has produced syntax outside the macro context,
203+ # copy it over. TODO : Do we expect this always to happen? What
204+ # is the API for access to the macro expansion context?
205+ expanded = copy_ast (ctx, expanded)
206+ end
155207 else
156- throw ( MacroExpansionError (mctx, ex, " Error expanding macro " , :all ))
208+ expanded = @ast ctx ex expanded :: K"Value"
157209 end
210+ else
211+ # Compat: attempt to invoke an old-style macro if there's no applicable
212+ # method for new-style macro arguments.
213+ macro_args = Any[source_location (LineNumberNode, ex), current_layer (ctx). mod]
214+ for arg in raw_args
215+ # For hygiene in old-style macros, we omit any additional scope
216+ # layer information from macro arguments. Old-style macros will
217+ # handle that using manual escaping in the macro itself.
218+ #
219+ # Note that there's one somewhat-incompatibility here for
220+ # identifiers interpolated into the `raw_args` from outer macro
221+ # expansions of new-style macros which call old-style macros.
222+ # Instead of seeing `Expr(:escape)` in such situations, old-style
223+ # macros will now see `Expr(:scope_layer)` inside `macro_args`.
224+ push! (macro_args, Expr (arg))
225+ end
226+ expanded = invokelatest (macfunc, macro_args... )
227+ expanded = expr_to_SyntaxTree (syntax_graph (ctx), expanded)
158228 end
159229
160- if expanded isa SyntaxTree
161- if ! is_compatible_graph (ctx, expanded)
162- # If the macro has produced syntax outside the macro context, copy it over.
163- # TODO : Do we expect this always to happen? What is the API for access
164- # to the macro expansion context?
165- expanded = copy_ast (ctx, expanded)
166- end
230+ if kind (expanded) != K " Value"
167231 expanded = append_sourceref (ctx, expanded, ex)
168232 # Module scope for the returned AST is the module where this particular
169233 # method was defined (may be different from `parentmodule(macfunc)`)
170- mod_for_ast = lookup_method_instance (macfunc, macro_args, macro_invocation_world). def. module
234+ mod_for_ast = lookup_method_instance (macfunc, macro_args,
235+ macro_invocation_world). def. module
171236 new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast, true )
172237 push! (ctx. scope_layers, new_layer)
173- inner_ctx = MacroExpansionContext (ctx. graph, ctx. bindings, ctx. scope_layers, new_layer)
174- expanded = expand_forms_1 (inner_ctx, expanded)
175- else
176- expanded = @ast ctx ex expanded:: K"Value"
238+ push! (ctx. scope_layer_stack, new_layer. id)
239+ expanded = expand_forms_1 (ctx, expanded)
240+ pop! (ctx. scope_layer_stack)
177241 end
178242 return expanded
179243end
@@ -215,18 +279,24 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
215279 elseif is_ccall_or_cglobal (name_str)
216280 @ast ctx ex name_str:: K"core"
217281 else
218- layerid = get (ex, :scope_layer , ctx. current_layer . id )
282+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
219283 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
220284 end
221285 elseif k == K " Identifier" || k == K " MacroName" || k == K " StringMacroName"
222- layerid = get (ex, :scope_layer , ctx. current_layer . id )
286+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
223287 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
224288 elseif k == K " var" || k == K " char" || k == K " parens"
225289 # Strip "container" nodes
226290 @chk numchildren (ex) == 1
227291 expand_forms_1 (ctx, ex[1 ])
292+ elseif k == K " escape"
293+ # For processing of old-style macros
294+ top_layer = pop! (ctx. scope_layer_stack)
295+ escaped_ex = expand_forms_1 (ctx, ex[1 ])
296+ push! (ctx. scope_layer_stack, top_layer)
297+ escaped_ex
228298 elseif k == K " juxtapose"
229- layerid = get (ex, :scope_layer , ctx. current_layer . id )
299+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
230300 @chk numchildren (ex) == 2
231301 @ast ctx ex [K " call"
232302 " *" :: K"Identifier" (scope_layer= layerid)
@@ -311,7 +381,7 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
311381 elseif k == K " <:" || k == K " >:" || k == K " -->"
312382 # TODO : Should every form get layerid systematically? Or only the ones
313383 # which expand_forms_2 needs?
314- layerid = get (ex, :scope_layer , ctx. current_layer . id )
384+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
315385 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex; scope_layer= layerid)
316386 else
317387 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex)
@@ -325,13 +395,13 @@ function expand_forms_1(mod::Module, ex::SyntaxTree)
325395 __macro_ctx__= Nothing,
326396 meta= CompileHints)
327397 layers = ScopeLayer[ScopeLayer (1 , mod, false )]
328- ctx = MacroExpansionContext (graph, Bindings (), layers, layers [1 ])
398+ ctx = MacroExpansionContext (graph, Bindings (), layers, LayerId [1 ])
329399 ex2 = expand_forms_1 (ctx, reparent (ctx, ex))
330400 graph2 = delete_attributes (graph, :__macro_ctx__ )
331401 # TODO : Returning the context with pass-specific mutable data is a bad way
332- # to carry state into the next pass.
333- ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx . scope_layers,
334- ctx. current_layer )
402+ # to carry state into the next pass. We might fix this by attaching such
403+ # data to the graph itself as global attributes?
404+ ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx. scope_layers, LayerId[] )
335405 return ctx2, reparent (ctx2, ex2)
336406end
337407
0 commit comments