@@ -32,8 +32,13 @@ then calling the resulting function. The differences are:
3232* The result is not a named generic function, and doesn't participate in
3333 generic function dispatch; it's more like a callable method.
3434
35+ You need to use `RuntimeGeneratedFunctions.init(your_module)` a single time at
36+ the top level of `your_module` before any other uses of the macro.
37+
3538# Examples
3639```
40+ RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
41+
3742function foo()
3843 expression = :((x,y)->x+y+1) # May be generated dynamically
3944 f = @RuntimeGeneratedFunction(expression)
4247```
4348"""
4449macro RuntimeGeneratedFunction (ex)
45- _ensure_cache_exists! (__module__)
4650 quote
51+ if ! ($ (esc (:(@isdefined ($ _tagname)))))
52+ error (""" You must use `RuntimeGeneratedFunctions.init(@__MODULE__)` at module
53+ top level before using runtime generated functions""" )
54+ end
4755 RuntimeGeneratedFunction (
4856 $ (esc (_tagname)),
4957 $ (esc (ex))
5967
6068(f:: RuntimeGeneratedFunction )(args:: Vararg{Any,N} ) where N = generated_callfunc (f, args... )
6169
62- @inline @generated function generated_callfunc (f:: RuntimeGeneratedFunction{moduletag, id, argnames} , __args... ) where {moduletag,id,argnames}
70+ # We'll generate a method of this function in every module which wants to use
71+ # @RuntimeGeneratedFunction
72+ function generated_callfunc end
73+
74+ function generated_callfunc_body (moduletag, id, argnames, __args)
6375 setup = (:($ (argnames[i]) = @inbounds __args[$ i]) for i in 1 : length (argnames))
6476 body = _lookup_body (moduletag, id)
6577 @assert body != = nothing
@@ -122,13 +134,34 @@ function _lookup_body(moduletag, id)
122134 end
123135end
124136
125- function _ensure_cache_exists! (mod)
137+ """
138+ RuntimeGeneratedFunctions.init(mod)
139+
140+ Use this at top level to set up your module `mod` before using
141+ `@RuntimeGeneratedFunction`.
142+ """
143+ function init (mod)
126144 lock (_cache_lock) do
127145 if ! isdefined (mod, _cachename)
128146 mod. eval (quote
129147 const $ _cachename = Dict ()
130148 struct $ _tagname
131149 end
150+
151+ # We create method of `generated_callfunc` in the user's module
152+ # so that any global symbols within the body will be looked up
153+ # in the user's module scope.
154+ #
155+ # This is straightforward but clunky. A neater solution should
156+ # be to explicitly expand in the user's module and return a
157+ # CodeInfo from `generated_callfunc`, but it seems we'd need
158+ # `jl_expand_and_resolve` which doesn't exist until Julia 1.3
159+ # or so. See:
160+ # https://github.com/JuliaLang/julia/pull/32902
161+ # https://github.com/NHDaly/StagedFunctions.jl/blob/master/src/StagedFunctions.jl#L30
162+ @inline @generated function $RuntimeGeneratedFunctions. generated_callfunc (f:: $RuntimeGeneratedFunctions.RuntimeGeneratedFunction{$_tagname, id, argnames} , __args... ) where {id,argnames}
163+ $ RuntimeGeneratedFunctions. generated_callfunc_body ($ _tagname, id, argnames, __args)
164+ end
132165 end )
133166 end
134167 end
0 commit comments