@@ -32,8 +32,13 @@ then calling the resulting function. The differences are:
32
32
* The result is not a named generic function, and doesn't participate in
33
33
generic function dispatch; it's more like a callable method.
34
34
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
+
35
38
# Examples
36
39
```
40
+ RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
41
+
37
42
function foo()
38
43
expression = :((x,y)->x+y+1) # May be generated dynamically
39
44
f = @RuntimeGeneratedFunction(expression)
42
47
```
43
48
"""
44
49
macro RuntimeGeneratedFunction (ex)
45
- _ensure_cache_exists! (__module__)
46
50
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
47
55
RuntimeGeneratedFunction (
48
56
$ (esc (_tagname)),
49
57
$ (esc (ex))
59
67
60
68
(f:: RuntimeGeneratedFunction )(args:: Vararg{Any,N} ) where N = generated_callfunc (f, args... )
61
69
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)
63
75
setup = (:($ (argnames[i]) = @inbounds __args[$ i]) for i in 1 : length (argnames))
64
76
body = _lookup_body (moduletag, id)
65
77
@assert body != = nothing
@@ -122,13 +134,34 @@ function _lookup_body(moduletag, id)
122
134
end
123
135
end
124
136
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)
126
144
lock (_cache_lock) do
127
145
if ! isdefined (mod, _cachename)
128
146
mod. eval (quote
129
147
const $ _cachename = Dict ()
130
148
struct $ _tagname
131
149
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
132
165
end )
133
166
end
134
167
end
0 commit comments