Skip to content

Commit

Permalink
add invokelatest function to work around world-age problems (JuliaLan…
Browse files Browse the repository at this point in the history
…g#19774) without using eval
  • Loading branch information
stevengj committed Jan 7, 2017
1 parent 28a11ff commit 4475ee8
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 0 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ Library improvements
* New `@test_warn` and `@test_nowarn` macros in the `Base.Test` module to
test for the presence or absence of warning messages ([#19903]).

* New function `invokelatest(f, args...)` to call the latest version
of a function in circumstances where an older version may be called
instead (in a `cfunction` or a function calling `eval`) ([#19784]).

* `logging` can be used to redirect `info`, `warn`, and `error` messages
either universally or on a per-module/function basis ([#16213]).

Expand Down
13 changes: 13 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,16 @@ function vector_any(xs::ANY...)
end

isempty(itr) = done(itr, start(itr))

"""
invokelatest(f, args...)
Calls `f(args...)`, but guarantees that the most recent method of `f`
will be executed. This is useful in specialized circumstances,
especially `cfunction` callbacks that may extract a Julia `Function`
from a pointer, or Julia functions that call `eval` or similar,
in which case obsolete versions of `f` may otherwise be called.
(The drawback is that `invokelatest` is somewhat slower than calling
`f` directly, and the type of the result cannot be inferred by the compiler.)
"""
invokelatest(f, args...) = ccall(:jl_invoke_latest, Any, (Any,Any), f, args)
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,7 @@ export
atreplinit,
clipboard,
exit,
invokelatest,
ntuple,
quit,

Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Base.instances
Base.method_exists
Core.applicable
Core.invoke
Base.invokelatest
Base.:(|>)
Base.:(∘)
```
Expand Down
18 changes: 18 additions & 0 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2199,6 +2199,24 @@ JL_DLLEXPORT jl_value_t *jl_apply_generic(jl_value_t **args, uint32_t nargs)
return verify_type(res);
}

JL_DLLEXPORT jl_value_t *jl_invoke_latest(jl_value_t *f, jl_value_t *argtuple)
{
assert(jl_is_tuple(argtuple));
size_t nargs = jl_nfields(argtuple);
jl_value_t **argv;
JL_GC_PUSHARGS(argv, nargs+1);
argv[0] = f;
for(int i=1; i<nargs+1; i++)
argv[i] = jl_fieldref(argtuple, i-1);
jl_ptls_t ptls = jl_get_ptls_states();
size_t last_age = ptls->world_age;
ptls->world_age = jl_get_world_counter();
jl_value_t *v = jl_apply(argv, nargs+1);
ptls->world_age = last_age;
JL_GC_POP();
return v;
}

JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world)
{
jl_methtable_t *mt = ((jl_datatype_t*)jl_tparam0(types))->name->mt;
Expand Down
9 changes: 9 additions & 0 deletions test/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -589,3 +589,12 @@ let
eval(Base, :(have_color = $(old_have_color)))
end
end

# invokelatest function for issue #19774
issue19774(x) = 1
let foo() = begin
eval(:(issue19774(x::Int) = 2))
return invokelatest(issue19774, 0)
end
@test foo() == 2
end

0 comments on commit 4475ee8

Please sign in to comment.