@@ -7,9 +7,9 @@ export RuntimeGeneratedFunction, @RuntimeGeneratedFunction
77
88"""
99 @RuntimeGeneratedFunction(function_expression)
10- @RuntimeGeneratedFunction(context_module, function_expression)
10+ @RuntimeGeneratedFunction(context_module, function_expression, opaque_closures=true )
1111
12- RuntimeGeneratedFunction(cache_module, context_module, function_expression)
12+ RuntimeGeneratedFunction(cache_module, context_module, function_expression; opaque_closures=true )
1313
1414Construct a function from `function_expression` which can be called immediately
1515without world age problems. Somewhat like using `eval(function_expression)` and
@@ -31,6 +31,13 @@ If provided, `context_module` is module in which symbols within
3131which is currently being precompiled. Normally this would be set to
3232`@__MODULE__` using one of the macro constructors.
3333
34+ If `opaque_closures` is `true`, all closures in `function_expression` are
35+ converted to
36+ [opaque closures](https://github.com/JuliaLang/julia/pull/37849#issue-496641229).
37+ This allows for the use of closures and generators inside the generated function,
38+ but may not work in all cases due to slightly different semantics. This feature
39+ requires Julia 1.7.
40+
3441# Examples
3542```
3643RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
4451"""
4552struct RuntimeGeneratedFunction{argnames, cache_tag, context_tag, id} <: Function
4653 body:: Expr
47- function RuntimeGeneratedFunction (cache_tag, context_tag, ex)
54+ function RuntimeGeneratedFunction (cache_tag, context_tag, ex; opaque_closures = true )
4855 def = splitdef (ex)
4956 args, body = normalize_args (def[:args ]), def[:body ]
57+ if opaque_closures && isdefined (Base, :Experimental ) &&
58+ isdefined (Base. Experimental, Symbol (" @opaque" ))
59+ body = closures_to_opaque (body)
60+ end
5061 id = expr_to_id (body)
5162 cached_body = _cache_body (cache_tag, id, body)
5263 new {Tuple(args), cache_tag, context_tag, id} (cached_body)
@@ -73,9 +84,12 @@ macro RuntimeGeneratedFunction(code)
7384 RuntimeGeneratedFunction (@__MODULE__ , @__MODULE__ , $ (esc (code)))
7485 end
7586end
76- macro RuntimeGeneratedFunction (context_module, code)
87+ macro RuntimeGeneratedFunction (context_module, code, opaque_closures = true )
7788 quote
78- RuntimeGeneratedFunction (@__MODULE__ , $ (esc (context_module)), $ (esc (code)))
89+ RuntimeGeneratedFunction (
90+ @__MODULE__ , $ (esc (context_module)), $ (esc (code));
91+ opaque_closures = $ (esc (opaque_closures)),
92+ )
7993 end
8094end
8195
@@ -208,4 +222,51 @@ function expr_to_id(ex)
208222 return Tuple (reinterpret (UInt32, sha1 (take! (io))))
209223end
210224
225+ @nospecialize
226+
227+ closures_to_opaque (x, _= nothing ) = x
228+ _tconvert (T, x) = Expr (:(:: ), Expr (:call , GlobalRef (Base, :convert ), T, x), T)
229+ function closures_to_opaque (ex:: Expr , return_type= nothing )
230+ head, args = ex. head, ex. args
231+ fdef = splitdef (ex; throw= false )
232+ if fdef != = nothing
233+ body = get (fdef, :body , nothing )
234+ if haskey (fdef, :rtype )
235+ body = _tconvert (fdef[:rtype ], closures_to_opaque (body, fdef[:rtype ]))
236+ delete! (fdef, :rtype )
237+ else
238+ body = closures_to_opaque (body)
239+ end
240+ fdef[:head ] = :(-> )
241+ fdef[:body ] = body
242+ name = get (fdef, :name , nothing )
243+ name != = nothing && delete! (fdef, :name )
244+ _ex = Expr (:opaque_closure , combinedef (fdef))
245+ # TODO : emit named opaque closure for better stacktraces
246+ # (ref https://github.com/JuliaLang/julia/pull/40242)
247+ if name != = nothing
248+ name isa Symbol ||
249+ error (" Unsupported function definition `$ex ` in RuntimeGeneratedFunction." )
250+ _ex = Expr (:(= ), name, _ex)
251+ end
252+ return _ex
253+ elseif head === :generator
254+ f_args = Expr (:tuple , Any[x. args[1 ] for x in args[2 : end ]]. .. )
255+ iters = Any[x. args[2 ] for x in args[2 : end ]]
256+ return Expr (
257+ :call ,
258+ GlobalRef (Base, :Generator ),
259+ closures_to_opaque (Expr (:(-> ), f_args, args[1 ])),
260+ iters... ,
261+ )
262+ elseif head === :opaque_closure
263+ return closures_to_opaque (args[1 ])
264+ elseif head === :return && return_type != = nothing
265+ return Expr (:return , _tconvert (return_type, closures_to_opaque (args[1 ], return_type)))
266+ end
267+ return Expr (head, Any[closures_to_opaque (x, return_type) for x in args]. .. )
268+ end
269+
270+ @specialize
271+
211272end
0 commit comments