@@ -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)
@@ -130,7 +133,7 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
130133 ctx3, ex3 = resolve_scopes (ctx2, ex2)
131134 ctx4, ex4 = convert_closures (ctx3, ex3)
132135 ctx5, ex5 = linearize_ir (ctx4, ex4)
133- mod = ctx . current_layer. mod
136+ mod = current_layer (ctx) . mod
134137 expr_form = to_lowered_expr (mod, ex5)
135138 try
136139 eval (mod, expr_form)
@@ -139,54 +142,119 @@ function eval_macro_name(ctx::MacroExpansionContext, mctx::MacroContext, ex::Syn
139142 end
140143end
141144
142- function expand_macro (ctx:: MacroExpansionContext , ex:: SyntaxTree )
145+ # Record scope layer information for symbols passed to a macro by setting
146+ # scope_layer for each expression and also processing any K"escape" arising
147+ # from previous expansion of old-style macros.
148+ #
149+ # See also set_scope_layer()
150+ function set_macro_arg_hygiene (ctx, ex, layer_ids, layer_idx)
151+ k = kind (ex)
152+ scope_layer = get (ex, :scope_layer , layer_ids[layer_idx])
153+ if k == K " module" || k == K " toplevel" || k == K " inert"
154+ makenode (ctx, ex, ex, children (ex);
155+ scope_layer= scope_layer)
156+ elseif k == K " ."
157+ makenode (ctx, ex, ex, set_macro_arg_hygiene (ctx, ex[1 ], layer_ids, layer_idx), ex[2 ],
158+ scope_layer= scope_layer)
159+ elseif ! is_leaf (ex)
160+ inner_layer_idx = layer_idx
161+ if k == K " escape"
162+ inner_layer_idx = layer_idx - 1
163+ if inner_layer_idx < 1
164+ # If we encounter too many escape nodes, there's probably been
165+ # an error in the previous macro expansion.
166+ # todo: The error here isn't precise about that - maybe we
167+ # should record that macro call expression with the scope layer
168+ # if we want to report the error against the macro call?
169+ throw (MacroExpansionError (ex, " `escape` node in outer context" ))
170+ end
171+ end
172+ mapchildren (e-> set_macro_arg_hygiene (ctx, e, layer_ids, inner_layer_idx),
173+ ctx, ex; scope_layer= scope_layer)
174+ else
175+ makeleaf (ctx, ex, ex; scope_layer= scope_layer)
176+ end
177+ end
178+
179+ function expand_macro (ctx, ex)
143180 @assert kind (ex) == K " macrocall"
144181
145182 macname = ex[1 ]
146- mctx = MacroContext (ctx. graph, ex, ctx . current_layer)
183+ mctx = MacroContext (ctx. graph, ex, current_layer (ctx) )
147184 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)
185+ raw_args = ex[2 : end ]
186+ # We use a specific well defined world age for the next checks and macro
187+ # expansion invocations. This avoids inconsistencies if the latest world
188+ # age changes concurrently.
189+ #
190+ # TODO : Allow this to be passed in
191+ macro_world = Base. get_world_counter ()
192+ if hasmethod (macfunc, Tuple{typeof (mctx), typeof .(raw_args)... }; world= macro_world)
193+ macro_args = Any[mctx]
194+ for arg in raw_args
195+ # Add hygiene information to be carried along with macro arguments.
196+ #
197+ # Macro call arguments may be either
198+ # * Unprocessed by the macro expansion pass
199+ # * Previously processed, but spliced into a further macro call emitted by
200+ # a macro expansion.
201+ # In either case, we need to set scope layers before passing the
202+ # arguments to the macro call.
203+ push! (macro_args, set_macro_arg_hygiene (ctx, arg, ctx. scope_layer_stack,
204+ length (ctx. scope_layer_stack)))
205+ end
206+ expanded = try
207+ Base. invoke_in_world (macro_world, macfunc, macro_args... )
208+ catch exc
209+ newexc = exc isa MacroExpansionError ?
210+ MacroExpansionError (mctx, exc. ex, exc. msg, exc. position, exc. err) :
211+ MacroExpansionError (mctx, ex, " Error expanding macro" , :all , exc)
212+ # TODO : We can delete this rethrow when we move to AST-based error propagation.
213+ rethrow (newexc)
214+ end
215+ if expanded isa SyntaxTree
216+ if ! is_compatible_graph (ctx, expanded)
217+ # If the macro has produced syntax outside the macro context,
218+ # copy it over. TODO : Do we expect this always to happen? What
219+ # is the API for access to the macro expansion context?
220+ expanded = copy_ast (ctx, expanded)
221+ end
166222 else
167- newexc = MacroExpansionError (mctx, ex, " Error expanding macro " , :all , exc)
223+ expanded = @ast ctx ex expanded :: K"Value"
168224 end
169- # TODO : We can delete this rethrow when we move to AST-based error propagation.
170- rethrow (newexc)
225+ else
226+ # Compat: attempt to invoke an old-style macro if there's no applicable
227+ # method for new-style macro arguments.
228+ macro_loc = source_location (LineNumberNode, ex)
229+ macro_args = Any[macro_loc, current_layer (ctx). mod]
230+ for arg in raw_args
231+ # For hygiene in old-style macros, we omit any additional scope
232+ # layer information from macro arguments. Old-style macros will
233+ # handle that using manual escaping in the macro itself.
234+ #
235+ # Note that there's one somewhat-incompatibility here for
236+ # identifiers interpolated into the `raw_args` from outer macro
237+ # expansions of new-style macros which call old-style macros.
238+ # Instead of seeing `Expr(:escape)` in such situations, old-style
239+ # macros will now see `Expr(:scope_layer)` inside `macro_args`.
240+ push! (macro_args, Expr (arg))
241+ end
242+ # TODO : Error handling
243+ expanded = Base. invoke_in_world (macro_world, macfunc, macro_args... )
244+ expanded = expr_to_syntaxtree (expanded, macro_loc, syntax_graph (ctx))
171245 end
172246
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
247+ if kind (expanded) != K " Value"
180248 expanded = append_sourceref (ctx, expanded, ex)
181249 # Module scope for the returned AST is the module where this particular
182250 # method was defined (may be different from `parentmodule(macfunc)`)
183- mod_for_ast = lookup_method_instance (macfunc, macro_args, macro_invocation_world). def. module
251+ mod_for_ast = lookup_method_instance (macfunc, macro_args,
252+ macro_world). def. module
184253 new_layer = ScopeLayer (length (ctx. scope_layers)+ 1 , mod_for_ast, true )
185254 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"
255+ push! (ctx. scope_layer_stack, new_layer. id)
256+ expanded = expand_forms_1 (ctx, expanded)
257+ pop! (ctx. scope_layer_stack)
190258 end
191259 return expanded
192260end
@@ -231,18 +299,24 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
231299 # turned into normal bindings (eg, assigned to)
232300 @ast ctx ex name_str:: K"core"
233301 else
234- layerid = get (ex, :scope_layer , ctx. current_layer . id )
302+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
235303 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
236304 end
237305 elseif k == K " Identifier" || k == K " MacroName" || k == K " StringMacroName"
238- layerid = get (ex, :scope_layer , ctx. current_layer . id )
306+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
239307 makeleaf (ctx, ex, ex, kind= K " Identifier" , scope_layer= layerid)
240308 elseif k == K " var" || k == K " char" || k == K " parens"
241309 # Strip "container" nodes
242310 @chk numchildren (ex) == 1
243311 expand_forms_1 (ctx, ex[1 ])
312+ elseif k == K " escape"
313+ # For processing of old-style macros
314+ top_layer = pop! (ctx. scope_layer_stack)
315+ escaped_ex = expand_forms_1 (ctx, ex[1 ])
316+ push! (ctx. scope_layer_stack, top_layer)
317+ escaped_ex
244318 elseif k == K " juxtapose"
245- layerid = get (ex, :scope_layer , ctx. current_layer . id )
319+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
246320 @chk numchildren (ex) == 2
247321 @ast ctx ex [K " call"
248322 " *" :: K"Identifier" (scope_layer= layerid)
@@ -330,7 +404,7 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
330404 elseif k == K " <:" || k == K " >:" || k == K " -->"
331405 # TODO : Should every form get layerid systematically? Or only the ones
332406 # which expand_forms_2 needs?
333- layerid = get (ex, :scope_layer , ctx. current_layer . id )
407+ layerid = get (ex, :scope_layer , current_layer_id ( ctx) )
334408 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex; scope_layer= layerid)
335409 else
336410 mapchildren (e-> expand_forms_1 (ctx,e), ctx, ex)
@@ -344,12 +418,12 @@ function expand_forms_1(mod::Module, ex::SyntaxTree)
344418 __macro_ctx__= Nothing,
345419 meta= CompileHints)
346420 layers = ScopeLayer[ScopeLayer (1 , mod, false )]
347- ctx = MacroExpansionContext (graph, Bindings (), layers, layers [1 ])
421+ ctx = MacroExpansionContext (graph, Bindings (), layers, LayerId [1 ])
348422 ex2 = expand_forms_1 (ctx, reparent (ctx, ex))
349423 graph2 = delete_attributes (graph, :__macro_ctx__ )
350424 # 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 )
425+ # to carry state into the next pass. We might fix this by attaching such
426+ # data to the graph itself as global attributes?
427+ ctx2 = MacroExpansionContext (graph2, ctx . bindings, ctx. scope_layers, LayerId[] )
354428 return ctx2, reparent (ctx2, ex2)
355429end
0 commit comments