@@ -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)
@@ -62,20 +73,29 @@ function _check_rgf_initialized(mods...)
6273 end
6374end
6475
65- function RuntimeGeneratedFunction (cache_module:: Module , context_module:: Module , code)
76+ function RuntimeGeneratedFunction (
77+ cache_module:: Module , context_module:: Module , code; opaque_closures= true ,
78+ )
6679 _check_rgf_initialized (cache_module, context_module)
67- RuntimeGeneratedFunction (getfield (cache_module, _tagname),
68- getfield (context_module, _tagname), code)
80+ RuntimeGeneratedFunction (
81+ getfield (cache_module, _tagname),
82+ getfield (context_module, _tagname),
83+ code;
84+ opaque_closures = opaque_closures
85+ )
6986end
7087
7188macro RuntimeGeneratedFunction (code)
7289 quote
7390 RuntimeGeneratedFunction (@__MODULE__ , @__MODULE__ , $ (esc (code)))
7491 end
7592end
76- macro RuntimeGeneratedFunction (context_module, code)
93+ macro RuntimeGeneratedFunction (context_module, code, opaque_closures = true )
7794 quote
78- RuntimeGeneratedFunction (@__MODULE__ , $ (esc (context_module)), $ (esc (code)))
95+ RuntimeGeneratedFunction (
96+ @__MODULE__ , $ (esc (context_module)), $ (esc (code));
97+ opaque_closures = $ (esc (opaque_closures)),
98+ )
7999 end
80100end
81101
@@ -208,4 +228,51 @@ function expr_to_id(ex)
208228 return Tuple (reinterpret (UInt32, sha1 (take! (io))))
209229end
210230
231+ @nospecialize
232+
233+ closures_to_opaque (x, _= nothing ) = x
234+ _tconvert (T, x) = Expr (:(:: ), Expr (:call , GlobalRef (Base, :convert ), T, x), T)
235+ function closures_to_opaque (ex:: Expr , return_type= nothing )
236+ head, args = ex. head, ex. args
237+ fdef = splitdef (ex; throw= false )
238+ if fdef != = nothing
239+ body = get (fdef, :body , nothing )
240+ if haskey (fdef, :rtype )
241+ body = _tconvert (fdef[:rtype ], closures_to_opaque (body, fdef[:rtype ]))
242+ delete! (fdef, :rtype )
243+ else
244+ body = closures_to_opaque (body)
245+ end
246+ fdef[:head ] = :(-> )
247+ fdef[:body ] = body
248+ name = get (fdef, :name , nothing )
249+ name != = nothing && delete! (fdef, :name )
250+ _ex = Expr (:opaque_closure , combinedef (fdef))
251+ # TODO : emit named opaque closure for better stacktraces
252+ # (ref https://github.com/JuliaLang/julia/pull/40242)
253+ if name != = nothing
254+ name isa Symbol ||
255+ error (" Unsupported function definition `$ex ` in RuntimeGeneratedFunction." )
256+ _ex = Expr (:(= ), name, _ex)
257+ end
258+ return _ex
259+ elseif head === :generator
260+ f_args = Expr (:tuple , Any[x. args[1 ] for x in args[2 : end ]]. .. )
261+ iters = Any[x. args[2 ] for x in args[2 : end ]]
262+ return Expr (
263+ :call ,
264+ GlobalRef (Base, :Generator ),
265+ closures_to_opaque (Expr (:(-> ), f_args, args[1 ])),
266+ iters... ,
267+ )
268+ elseif head === :opaque_closure
269+ return closures_to_opaque (args[1 ])
270+ elseif head === :return && return_type != = nothing
271+ return Expr (:return , _tconvert (return_type, closures_to_opaque (args[1 ], return_type)))
272+ end
273+ return Expr (head, Any[closures_to_opaque (x, return_type) for x in args]. .. )
274+ end
275+
276+ @specialize
277+
211278end
0 commit comments