Skip to content

Commit

Permalink
Use normal RuntimeGeneratedFunction constructor rather than macro
Browse files Browse the repository at this point in the history
Now that users need to manually call init(), this macro really doesn't
do much and obscures the scope which the function will pull global
symbols from.
  • Loading branch information
c42f committed Dec 4, 2020
1 parent b1f9daa commit ff1fd50
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 42 deletions.
58 changes: 27 additions & 31 deletions src/RuntimeGeneratedFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,16 @@ module RuntimeGeneratedFunctions

using ExprTools, Serialization, SHA

export @RuntimeGeneratedFunction
export RuntimeGeneratedFunction, @RuntimeGeneratedFunction


"""
RuntimeGeneratedFunction
RuntimeGeneratedFunction(module, function_expression)
This type should be constructed via the macro @RuntimeGeneratedFunction.
"""
struct RuntimeGeneratedFunction{argnames,moduletag,id} <: Function
body::Expr
function RuntimeGeneratedFunction(moduletag, ex)
def = splitdef(ex)
args, body = normalize_args(def[:args]), def[:body]
id = expr_to_id(body)
cached_body = _cache_body(moduletag, id, body)
new{Tuple(args),moduletag,id}(cached_body)
end
end

"""
@RuntimeGeneratedFunction(function_expression)
Construct a function from `function_expression` which can be called immediately
without world age problems. Somewhat like using `eval(function_expression)` and
then calling the resulting function. The differences are:
Construct a function from `function_expression` in the scope of `module` which
can be called immediately without world age problems. Somewhat like using
`eval(function_expression)` and then calling the resulting function. The
differences are:
* The result can be called immediately (immune to world age errors)
* The result is not a named generic function, and doesn't participate in
Expand All @@ -41,21 +26,32 @@ RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
function foo()
expression = :((x,y)->x+y+1) # May be generated dynamically
f = @RuntimeGeneratedFunction(expression)
f = RuntimeGeneratedFunction(@__MODULE__, expression)
f(1,2) # May be called immediately
end
```
"""
macro RuntimeGeneratedFunction(ex)
quote
if !($(esc(:(@isdefined($_tagname)))))
struct RuntimeGeneratedFunction{argnames,moduletag,id} <: Function
body::Expr
function RuntimeGeneratedFunction(mod::Module, ex)
if !isdefined(mod, _tagname)
error("""You must use `RuntimeGeneratedFunctions.init(@__MODULE__)` at module
top level before using runtime generated functions""")
end
RuntimeGeneratedFunction(
$(esc(_tagname)),
$(esc(ex))
)
moduletag = getfield(mod, _tagname)
def = splitdef(ex)
args, body = normalize_args(def[:args]), def[:body]
id = expr_to_id(body)
cached_body = _cache_body(moduletag, id, body)
new{Tuple(args),moduletag,id}(cached_body)
end
end


macro RuntimeGeneratedFunction(ex)
Base.depwarn("`@RuntimeGeneratedFunction(ex)` is deprecated, use `RuntimeGeneratedFunction(@__MODULE__, ex)` instead.", :RuntimeGeneratedFunction)
quote
RuntimeGeneratedFunction(@__MODULE__, $(esc(ex)))
end
end

Expand All @@ -68,7 +64,7 @@ end
(f::RuntimeGeneratedFunction)(args::Vararg{Any,N}) where N = generated_callfunc(f, args...)

# We'll generate a method of this function in every module which wants to use
# @RuntimeGeneratedFunction
# RuntimeGeneratedFunction
function generated_callfunc end

function generated_callfunc_body(argnames, moduletag, id, __args)
Expand Down Expand Up @@ -138,7 +134,7 @@ end
RuntimeGeneratedFunctions.init(mod)
Use this at top level to set up your module `mod` before using
`@RuntimeGeneratedFunction`.
`RuntimeGeneratedFunction(mod, ...)`.
"""
function init(mod)
lock(_cache_lock) do
Expand Down
2 changes: 1 addition & 1 deletion test/precomp/RGFPrecompTest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ module RGFPrecompTest
using RuntimeGeneratedFunctions
RuntimeGeneratedFunctions.init(@__MODULE__)

f = @RuntimeGeneratedFunction(:((x,y)->x+y))
f = RuntimeGeneratedFunction(@__MODULE__, :((x,y)->x+y))
end
20 changes: 10 additions & 10 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ ex3 = :(function (_du::T,_u::Vector{E},_p::P,_t::Any) where {T<:Vector,E,P}
nothing
end)

f1 = @RuntimeGeneratedFunction(ex1)
f2 = @RuntimeGeneratedFunction(ex2)
f3 = @RuntimeGeneratedFunction(ex3)
f1 = RuntimeGeneratedFunction(@__MODULE__, ex1)
f2 = RuntimeGeneratedFunction(@__MODULE__, ex2)
f3 = RuntimeGeneratedFunction(@__MODULE__, ex3)

@test f1 isa Function

Expand Down Expand Up @@ -62,7 +62,7 @@ function no_worldage()
@inbounds _du[2] = _u[2]
nothing
end)
f1 = @RuntimeGeneratedFunction(ex)
f1 = RuntimeGeneratedFunction(@__MODULE__, ex)
du = rand(2)
u = rand(2)
p = nothing
Expand All @@ -72,7 +72,7 @@ end
@test no_worldage() === nothing

# Test show()
@test sprint(show, @RuntimeGeneratedFunction(Base.remove_linenums!(:((x,y)->x+y+1)))) ==
@test sprint(show, RuntimeGeneratedFunction(@__MODULE__, Base.remove_linenums!(:((x,y)->x+y+1)))) ==
"""
RuntimeGeneratedFunction(#=in $(@__MODULE__)=#, :((x, y)->begin
x + y + 1
Expand All @@ -86,9 +86,9 @@ using RGFPrecompTest

# Test that RuntimeGeneratedFunction with identical body expressions (but
# allocated separately) don't clobber each other when one is GC'd.
f_gc = @RuntimeGeneratedFunction(Base.remove_linenums!(:((x,y)->x+y+100001)))
f_gc = RuntimeGeneratedFunction(@__MODULE__, Base.remove_linenums!(:((x,y)->x+y+100001)))
let
@RuntimeGeneratedFunction(Base.remove_linenums!(:((x,y)->x+y+100001)))
RuntimeGeneratedFunction(@__MODULE__, Base.remove_linenums!(:((x,y)->x+y+100001)))
end
GC.gc()
@test f_gc(1,-1) == 100001
Expand All @@ -100,7 +100,7 @@ for k=1:4
t = Threads.@spawn begin
r = Bool[]
for i=1:100
f = @RuntimeGeneratedFunction(Base.remove_linenums!(:((x,y)->x+y+$i*$k)))
f = RuntimeGeneratedFunction(@__MODULE__, Base.remove_linenums!(:((x,y)->x+y+$i*$k)))
x = 1; y = 2;
push!(r, f(x,y) == x + y + i*k)
end
Expand All @@ -119,13 +119,13 @@ module GlobalsTest
RuntimeGeneratedFunctions.init(@__MODULE__)

y = 40
f = @RuntimeGeneratedFunction(:(x->x+y))
f = RuntimeGeneratedFunction(@__MODULE__, :(x->x+y))
end

@test GlobalsTest.f(2) == 42

@test_throws ErrorException @eval(module NotInitTest
using RuntimeGeneratedFunctions
# RuntimeGeneratedFunctions.init(@__MODULE__) # <-- missing
f = @RuntimeGeneratedFunction(:(x->x+y))
f = RuntimeGeneratedFunction(@__MODULE__, :(x->x+y))
end)

0 comments on commit ff1fd50

Please sign in to comment.