@@ -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)
@@ -126,7 +129,7 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
126129 ctx3, ex3 = resolve_scopes (ctx2, ex2)
127130 ctx4, ex4 = convert_closures (ctx3, ex3)
128131 ctx5, ex5 = linearize_ir (ctx4, ex4)
129- mod = ctx . current_layer. mod
132+ mod = current_layer (ctx) . mod
130133 expr_form = to_lowered_expr (mod, ex5)
131134 try
132135 eval (mod, expr_form)
@@ -135,54 +138,119 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
135138 end
136139end
137140
138- function expand_macro (ctx:: MacroExpansionContext , ex:: SyntaxTree )
141+ # Record scope layer information for symbols passed to a macro by setting
142+ # scope_layer for each expression and also processing any K"escape" arising
143+ # from previous expansion of old-style macros.
144+ #
145+ # See also set_scope_layer()
146+ function set_macro_arg_hygiene (ctx, ex, layer_ids, layer_idx)
147+ k = kind (ex)
148+ scope_layer = get (ex, :scope_layer , layer_ids[layer_idx])
149+ if k == K " module" || k == K " toplevel" || k == K " inert"
150+ makenode (ctx, ex, ex, children (ex);
151+ scope_layer= scope_layer)
152+ elseif k == K " ."
153+ makenode (ctx, ex, ex, set_macro_arg_hygiene (ctx, ex[1 ], layer_ids, layer_idx), ex[2 ],
154+ scope_layer= scope_layer)
155+ elseif ! is_leaf (ex)
156+ inner_layer_idx = layer_idx
157+ if k == K " escape"
158+ inner_layer_idx = layer_idx - 1
159+ if inner_layer_idx < 1
160+ # If we encounter too many escape nodes, there's probably been
161+ # an error in the previous macro expansion.
162+ # todo: The error here isn't precise about that - maybe we
163+ # should record that macro call expression with the scope layer
164+ # if we want to report the error against the macro call?
165+ throw (MacroExpansionError (ex, " `escape` node in outer context" ))
166+ end
167+ end
168+ mapchildren (e-> set_macro_arg_hygiene (ctx, e, layer_ids, inner_layer_idx),
169+ ctx, ex; scope_layer= scope_layer)
170+ else
171+ makeleaf (ctx, ex, ex; scope_layer= scope_layer)
172+ end
173+ end
174+
175+ function expand_macro (ctx, ex)
139176 @assert kind (ex) == K " macrocall"
140177
141178 macname = ex[1 ]
142- mctx = MacroContext (ctx. graph, ex, ctx . current_layer)
179+ mctx = MacroContext (ctx. graph, ex, current_layer (ctx) )
143180 macfunc = eval_macro_name (ctx, mctx, macname)
144- # Macro call arguments may be either
145- # * Unprocessed by the macro expansion pass
146- # * Previously processed, but spliced into a further macro call emitted by
147- # a macro expansion.
148- # In either case, we need to set any unset scope layers before passing the
149- # arguments to the macro call.
150- macro_args = Any[mctx]
151- for i in 2 : numchildren (ex)
152- push! (macro_args, set_scope_layer (ctx, ex[i], ctx. current_layer. id, false ))
153- end
154- macro_invocation_world = Base. get_world_counter ()
155- expanded = try
156- # TODO : Allow invoking old-style macros for compat
157- invokelatest (macfunc, macro_args... )
158- catch exc
159- if exc isa MacroExpansionError
160- # Add context to the error.
161- newexc = MacroExpansionError (mctx, exc. ex, exc. msg, exc. position, exc. err)
181+ raw_args = ex[2 : end ]
182+ # We use a specific well defined world age for the next checks and macro
183+ # expansion invocations. This avoids inconsistencies if the latest world
184+ # age changes concurrently.
185+ #
186+ # TODO : Allow this to be passed in
187+ macro_world = Base. get_world_counter ()
188+ if hasmethod (macfunc, Tuple{typeof (mctx), typeof .(raw_args)... }; world= macro_world)
189+ macro_args = Any[mctx]
190+ for arg in raw_args
191+ # Add hygiene information to be carried along with macro arguments.
192+ #
193+ # Macro call arguments may be either
194+ # * Unprocessed by the macro expansion pass
195+ # * Previously processed, but spliced into a further macro call emitted by
196+ # a macro expansion.
197+ # In either case, we need to set scope layers before passing the
198+ # arguments to the macro call.
199+ push! (macro_args, set_macro_arg_hygiene (ctx, arg, ctx. scope_layer_stack,
200+ length (ctx. scope_layer_stack)))
201+ end
202+ expanded = try
203+ Base. invoke_in_world (macro_world, macfunc, macro_args... )
204+ catch exc
205+ newexc = exc isa MacroExpansionError ?
206+ MacroExpansionError (mctx, exc. ex, exc. msg, exc. position, exc. err) :
207+ MacroExpansionError (mctx, ex, " Error expanding macro" , :all , exc)
208+ # TODO : We can delete this rethrow when we move to AST-based error propagation.
209+ rethrow (newexc)
210+ end
211+ if expanded isa SyntaxTree
212+ if ! is_compatible_graph (ctx, expanded)
213+ # If the macro has produced syntax outside the macro context,
214+ # copy it over. TODO : Do we expect this always to happen? What
215+ # is the API for access to the macro expansion context?
216+ expanded = copy_ast (ctx, expanded)
217+ end
162218 else
163- newexc = MacroExpansionError (mctx, ex, " Error expanding macro " , :all , exc)
219+ expanded = @ast ctx ex expanded :: K"Value"
164220 end
165- # TODO : We can delete this rethrow when we move to AST-based error propagation.
166- rethrow (newexc)
221+ else
222+ # Compat: attempt to invoke an old-style macro if there's no applicable
223+ # method for new-style macro arguments.
224+ macro_loc = source_location (LineNumberNode, ex)
225+ macro_args = Any[macro_loc, current_layer (ctx). mod]
226+ for arg in raw_args
227+ # For hygiene in old-style macros, we omit any additional scope
228+ # layer information from macro arguments. Old-style macros will
229+ # handle that using manual escaping in the macro itself.
230+ #
231+ # Note that there's one somewhat-incompatibility here for
232+ # identifiers interpolated into the `raw_args` from outer macro
233+ # expansions of new-style macros which call old-style macros.
234+ # Instead of seeing `Expr(:escape)` in such situations, old-style
235+ # macros will now see `Expr(:scope_layer)` inside `macro_args`.
236+ push! (macro_args, Expr (arg))
237+ end
238+ # TODO : Error handling
239+ expanded = Base. invoke_in_world (macro_world, macfunc, macro_args... )
240+ expanded = expr_to_syntaxtree (expanded, macro_loc, syntax_graph (ctx))
167241 end
168242
169- if expanded isa SyntaxTree
170- if ! is_compatible_graph (ctx, expanded)
171- # If the macro has produced syntax outside the macro context, copy it over.
172- # TODO : Do we expect this always to happen? What is the API for access
173- # to the macro expansion context?
174- expanded = copy_ast (ctx, expanded)
175- end
243+ if kind (expanded) != K " Value"
176244 expanded = append_sourceref (ctx, expanded, ex)
177245 # Module scope for the returned AST is the module where this particular
178246 # method was defined (may be different from `parentmodule(macfunc)`)
179- mod_for_ast = lookup_method_instance (macfunc, macro_args, macro_invocation_world). def. module
247+ mod_for_ast = lookup_method_instance (macfunc, macro_args,
248+ macro_world). def. module
180249 new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast, true )
181250 push! (ctx. scope_layers, new_layer)
182- inner_ctx = MacroExpansionContext (ctx. graph, ctx. bindings, ctx. scope_layers, new_layer)
183- expanded = expand_forms_1 (inner_ctx, expanded)
184- else
185- expanded = @ast ctx ex expanded:: K"Value"
251+ push! (ctx. scope_layer_stack, new_layer. id)
252+ expanded = expand_forms_1 (ctx, expanded)
253+ pop! (ctx. scope_layer_stack)
186254 end
187255 return expanded
188256end
@@ -224,18 +292,24 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
224292 elseif is_ccall_or_cglobal (name_str)
225293 @ast ctx ex name_str:: K"core"
226294 else
227- layerid = get (ex, :scope_layer , ctx. current_layer . id )
295+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
228296 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
229297 end
230298 elseif k == K " Identifier" || k == K " MacroName" || k == K " StringMacroName"
231- layerid = get (ex, :scope_layer , ctx. current_layer . id )
299+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
232300 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
233301 elseif k == K " var" || k == K " char" || k == K " parens"
234302 # Strip "container" nodes
235303 @chk numchildren (ex) == 1
236304 expand_forms_1 (ctx, ex[1 ])
305+ elseif k == K " escape"
306+ # For processing of old-style macros
307+ top_layer = pop! (ctx. scope_layer_stack)
308+ escaped_ex = expand_forms_1 (ctx, ex[1 ])
309+ push! (ctx. scope_layer_stack, top_layer)
310+ escaped_ex
237311 elseif k == K " juxtapose"
238- layerid = get (ex, :scope_layer , ctx. current_layer . id )
312+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
239313 @chk numchildren (ex) == 2
240314 @ast ctx ex [K " call"
241315 " *" :: K"Identifier" (scope_layer= layerid)
@@ -323,7 +397,7 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
323397 elseif k == K " <:" || k == K " >:" || k == K " -->"
324398 # TODO : Should every form get layerid systematically? Or only the ones
325399 # which expand_forms_2 needs?
326- layerid = get (ex, :scope_layer , ctx. current_layer . id )
400+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
327401 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex; scope_layer= layerid)
328402 else
329403 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex)
@@ -337,12 +411,12 @@ function expand_forms_1(mod::Module, ex::SyntaxTree)
337411 __macro_ctx__= Nothing,
338412 meta= CompileHints)
339413 layers = ScopeLayer[ScopeLayer (1 , mod, false )]
340- ctx = MacroExpansionContext (graph, Bindings (), layers, layers [1 ])
414+ ctx = MacroExpansionContext (graph, Bindings (), layers, LayerId [1 ])
341415 ex2 = expand_forms_1 (ctx, reparent (ctx, ex))
342416 graph2 = delete_attributes (graph, :__macro_ctx__ )
343417 # TODO : Returning the context with pass-specific mutable data is a bad way
344- # to carry state into the next pass.
345- ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx . scope_layers,
346- ctx. current_layer )
418+ # to carry state into the next pass. We might fix this by attaching such
419+ # data to the graph itself as global attributes?
420+ ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx. scope_layers, LayerId[] )
347421 return ctx2, reparent (ctx2, ex2)
348422end
0 commit comments