@@ -11,16 +11,25 @@ generates a new layer.
1111struct ScopeLayer
1212 id:: LayerId
1313 mod:: Module
14+ parent_layer:: LayerId # Index of parent layer in a macro expansion. Equal to 0 for no parent
1415 is_macro_expansion:: Bool # FIXME
1516end
1617
1718struct MacroExpansionContext{GraphType} <: AbstractLoweringContext
1819 graph:: GraphType
1920 bindings:: Bindings
2021 scope_layers:: Vector{ScopeLayer}
21- current_layer :: ScopeLayer
22+ scope_layer_stack :: Vector{LayerId}
2223end
2324
25+ function MacroExpansionContext (graph:: SyntaxGraph , mod:: Module )
26+ layers = ScopeLayer[ScopeLayer (1 , mod, 0 , false )]
27+ MacroExpansionContext (graph, Bindings (), layers, LayerId[length (layers)])
28+ end
29+
30+ current_layer (ctx:: MacroExpansionContext ) = ctx. scope_layers[last (ctx. scope_layer_stack)]
31+ current_layer_id (ctx:: MacroExpansionContext ) = last (ctx. scope_layer_stack)
32+
2433# --------------------------------------------------
2534# Expansion of quoted expressions
2635function collect_unquoted! (ctx, unquoted, ex, depth)
@@ -130,7 +139,7 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
130139 ctx3, ex3 = resolve_scopes (ctx2, ex2)
131140 ctx4, ex4 = convert_closures (ctx3, ex3)
132141 ctx5, ex5 = linearize_ir (ctx4, ex4)
133- mod = ctx . current_layer. mod
142+ mod = current_layer (ctx) . mod
134143 expr_form = to_lowered_expr (mod, ex5)
135144 try
136145 eval (mod, expr_form)
@@ -139,54 +148,124 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
139148 end
140149end
141150
142- function expand_macro (ctx:: MacroExpansionContext , ex:: SyntaxTree )
151+ # Record scope layer information for symbols passed to a macro by setting
152+ # scope_layer for each expression and also processing any K"escape" arising
153+ # from previous expansion of old-style macros.
154+ #
155+ # See also set_scope_layer()
156+ function set_macro_arg_hygiene (ctx, ex, layer_ids, layer_idx)
157+ k = kind (ex)
158+ scope_layer = get (ex, :scope_layer , layer_ids[layer_idx])
159+ if k == K " module" || k == K " toplevel" || k == K " inert"
160+ makenode (ctx, ex, ex, children (ex);
161+ scope_layer= scope_layer)
162+ elseif k == K " ."
163+ makenode (ctx, ex, ex, set_macro_arg_hygiene (ctx, ex[1 ], layer_ids, layer_idx), ex[2 ],
164+ scope_layer= scope_layer)
165+ elseif ! is_leaf (ex)
166+ inner_layer_idx = layer_idx
167+ if k == K " escape"
168+ inner_layer_idx = layer_idx - 1
169+ if inner_layer_idx < 1
170+ # If we encounter too many escape nodes, there's probably been
171+ # an error in the previous macro expansion.
172+ # todo: The error here isn't precise about that - maybe we
173+ # should record that macro call expression with the scope layer
174+ # if we want to report the error against the macro call?
175+ throw (MacroExpansionError (ex, " `escape` node in outer context" ))
176+ end
177+ end
178+ mapchildren (e-> set_macro_arg_hygiene (ctx, e, layer_ids, inner_layer_idx),
179+ ctx, ex; scope_layer= scope_layer)
180+ else
181+ makeleaf (ctx, ex, ex; scope_layer= scope_layer)
182+ end
183+ end
184+
185+ function expand_macro (ctx, ex)
143186 @assert kind (ex) == K " macrocall"
144187
145188 macname = ex[1 ]
146- mctx = MacroContext (ctx. graph, ex, ctx . current_layer)
189+ mctx = MacroContext (ctx. graph, ex, current_layer (ctx) )
147190 macfunc = eval_macro_name (ctx, mctx, macname)
148- # Macro call arguments may be either
149- # * Unprocessed by the macro expansion pass
150- # * Previously processed, but spliced into a further macro call emitted by
151- # a macro expansion.
152- # In either case, we need to set any unset scope layers before passing the
153- # arguments to the macro call.
154- macro_args = Any[mctx]
155- for i in 2 : numchildren (ex)
156- push! (macro_args, set_scope_layer (ctx, ex[i], ctx. current_layer. id, false ))
157- end
158- macro_invocation_world = Base. get_world_counter ()
159- expanded = try
160- # TODO : Allow invoking old-style macros for compat
161- invokelatest (macfunc, macro_args... )
162- catch exc
163- if exc isa MacroExpansionError
164- # Add context to the error.
165- newexc = MacroExpansionError (mctx, exc. ex, exc. msg, exc. position, exc. err)
191+ raw_args = ex[2 : end ]
192+ # We use a specific well defined world age for the next checks and macro
193+ # expansion invocations. This avoids inconsistencies if the latest world
194+ # age changes concurrently.
195+ #
196+ # TODO : Allow this to be passed in
197+ macro_world = Base. get_world_counter ()
198+ if hasmethod (macfunc, Tuple{typeof (mctx), typeof .(raw_args)... }; world= macro_world)
199+ macro_args = Any[mctx]
200+ for arg in raw_args
201+ # Add hygiene information to be carried along with macro arguments.
202+ #
203+ # Macro call arguments may be either
204+ # * Unprocessed by the macro expansion pass
205+ # * Previously processed, but spliced into a further macro call emitted by
206+ # a macro expansion.
207+ # In either case, we need to set scope layers before passing the
208+ # arguments to the macro call.
209+ push! (macro_args, set_macro_arg_hygiene (ctx, arg, ctx. scope_layer_stack,
210+ length (ctx. scope_layer_stack)))
211+ end
212+ expanded = try
213+ Base. invoke_in_world (macro_world, macfunc, macro_args... )
214+ catch exc
215+ newexc = exc isa MacroExpansionError ?
216+ MacroExpansionError (mctx, exc. ex, exc. msg, exc. position, exc. err) :
217+ MacroExpansionError (mctx, ex, " Error expanding macro" , :all , exc)
218+ # TODO : We can delete this rethrow when we move to AST-based error propagation.
219+ rethrow (newexc)
220+ end
221+ if expanded isa SyntaxTree
222+ if ! is_compatible_graph (ctx, expanded)
223+ # If the macro has produced syntax outside the macro context,
224+ # copy it over. TODO : Do we expect this always to happen? What
225+ # is the API for access to the macro expansion context?
226+ expanded = copy_ast (ctx, expanded)
227+ end
166228 else
167- newexc = MacroExpansionError (mctx, ex, " Error expanding macro" , :all , exc)
229+ expanded = @ast ctx ex expanded:: K"Value"
230+ end
231+ else
232+ # Compat: attempt to invoke an old-style macro if there's no applicable
233+ # method for new-style macro arguments.
234+ macro_loc = source_location (LineNumberNode, ex)
235+ macro_args = Any[macro_loc, current_layer (ctx). mod]
236+ for arg in raw_args
237+ # For hygiene in old-style macros, we omit any additional scope
238+ # layer information from macro arguments. Old-style macros will
239+ # handle that using manual escaping in the macro itself.
240+ #
241+ # Note that there's one slight incompatibility here for identifiers
242+ # interpolated into the `raw_args` from outer macro expansions of
243+ # new-style macros which call old-style macros. Instead of seeing
244+ # `Expr(:escape)` in such situations, old-style macros will now see
245+ # `Expr(:scope_layer)` inside `macro_args`.
246+ push! (macro_args, Expr (arg))
168247 end
169- # TODO : We can delete this rethrow when we move to AST-based error propagation.
170- rethrow (newexc)
248+ # TODO : Error handling
249+ expanded = try
250+ Base. invoke_in_world (macro_world, macfunc, macro_args... )
251+ catch exc
252+ rethrow (MacroExpansionError (mctx, ex, " Error expanding macro" , :all , exc))
253+ end
254+ expanded = expr_to_syntaxtree (ctx, expanded, macro_loc)
171255 end
172256
173- if expanded isa SyntaxTree
174- if ! is_compatible_graph (ctx, expanded)
175- # If the macro has produced syntax outside the macro context, copy it over.
176- # TODO : Do we expect this always to happen? What is the API for access
177- # to the macro expansion context?
178- expanded = copy_ast (ctx, expanded)
179- end
257+ if kind (expanded) != K " Value"
180258 expanded = append_sourceref (ctx, expanded, ex)
181259 # Module scope for the returned AST is the module where this particular
182260 # method was defined (may be different from `parentmodule(macfunc)`)
183- mod_for_ast = lookup_method_instance (macfunc, macro_args, macro_invocation_world). def. module
184- new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast, true )
261+ mod_for_ast = lookup_method_instance (macfunc, macro_args,
262+ macro_world). def. module
263+ new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast,
264+ current_layer_id (ctx), true )
185265 push! (ctx. scope_layers, new_layer)
186- inner_ctx = MacroExpansionContext (ctx. graph, ctx. bindings, ctx. scope_layers, new_layer)
187- expanded = expand_forms_1 (inner_ctx, expanded)
188- else
189- expanded = @ast ctx ex expanded:: K"Value"
266+ push! (ctx. scope_layer_stack, new_layer. id)
267+ expanded = expand_forms_1 (ctx, expanded)
268+ pop! (ctx. scope_layer_stack)
190269 end
191270 return expanded
192271end
@@ -231,18 +310,37 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
231310 # turned into normal bindings (eg, assigned to)
232311 @ast ctx ex name_str:: K"core"
233312 else
234- layerid = get (ex, :scope_layer , ctx. current_layer . id )
313+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
235314 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
236315 end
237316 elseif k == K " Identifier" || k == K " MacroName" || k == K " StringMacroName"
238- layerid = get (ex, :scope_layer , ctx. current_layer . id )
317+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
239318 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
240319 elseif k == K " var" || k == K " char" || k == K " parens"
241320 # Strip "container" nodes
242321 @chk numchildren (ex) == 1
243322 expand_forms_1 (ctx, ex[1 ])
323+ elseif k == K " escape"
324+ # For processing of old-style macros
325+ @chk numchildren (ex) >= 1 " `escape` requires an argument"
326+ if length (ctx. scope_layer_stack) === 1
327+ throw (MacroExpansionError (ex, " `escape` node in outer context" ))
328+ end
329+ top_layer = pop! (ctx. scope_layer_stack)
330+ escaped_ex = expand_forms_1 (ctx, ex[1 ])
331+ push! (ctx. scope_layer_stack, top_layer)
332+ escaped_ex
333+ elseif k == K " hygienic_scope"
334+ @chk numchildren (ex) >= 2 && ex[2 ]. value isa Module (ex," `hygienic_scope` requires an AST and a module" )
335+ new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , ex[2 ]. value,
336+ current_layer_id (ctx), true )
337+ push! (ctx. scope_layers, new_layer)
338+ push! (ctx. scope_layer_stack, new_layer. id)
339+ hyg_ex = expand_forms_1 (ctx, ex[1 ])
340+ pop! (ctx. scope_layer_stack)
341+ hyg_ex
244342 elseif k == K " juxtapose"
245- layerid = get (ex, :scope_layer , ctx. current_layer . id )
343+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
246344 @chk numchildren (ex) == 2
247345 @ast ctx ex [K " call"
248346 " *" :: K"Identifier" (scope_layer= layerid)
@@ -330,7 +428,7 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
330428 elseif k == K " <:" || k == K " >:" || k == K " -->"
331429 # TODO : Should every form get layerid systematically? Or only the ones
332430 # which expand_forms_2 needs?
333- layerid = get (ex, :scope_layer , ctx. current_layer . id )
431+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
334432 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex; scope_layer= layerid)
335433 else
336434 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex)
@@ -343,13 +441,12 @@ function expand_forms_1(mod::Module, ex::SyntaxTree)
343441 scope_layer= LayerId,
344442 __macro_ctx__= Nothing,
345443 meta= CompileHints)
346- layers = ScopeLayer[ScopeLayer (1 , mod, false )]
347- ctx = MacroExpansionContext (graph, Bindings (), layers, layers[1 ])
444+ ctx = MacroExpansionContext (graph, mod)
348445 ex2 = expand_forms_1 (ctx, reparent (ctx, ex))
349446 graph2 = delete_attributes (graph, :__macro_ctx__ )
350447 # TODO : Returning the context with pass-specific mutable data is a bad way
351- # to carry state into the next pass.
352- ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx . scope_layers,
353- ctx. current_layer )
448+ # to carry state into the next pass. We might fix this by attaching such
449+ # data to the graph itself as global attributes?
450+ ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx. scope_layers, LayerId[] )
354451 return ctx2, reparent (ctx2, ex2)
355452end
0 commit comments