diff --git a/NEWS.md b/NEWS.md index 1e73f8b53a05a2..34f807cc559daa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -132,6 +132,10 @@ Library improvements implemented; the semantics are as if the `Nullable` were a container with zero or one elements ([#16961]). + * 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]). + Compiler/Runtime improvements ----------------------------- diff --git a/base/essentials.jl b/base/essentials.jl index 8bd8d17ad6aaa2..40237820036998 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -239,3 +239,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) diff --git a/base/exports.jl b/base/exports.jl index 7109b9d136b228..ce341904e76d55 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1066,6 +1066,7 @@ export atreplinit, clipboard, exit, + invokelatest, ntuple, quit, diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 071b45b7cf03a3..d5c9caac62a447 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -121,6 +121,7 @@ Base.instances Base.method_exists Core.applicable Core.invoke +Base.invokelatest Base.:(|>) ``` diff --git a/src/gf.c b/src/gf.c index 60d98366878f68..b9df541937ba55 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2197,6 +2197,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; iworld_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; diff --git a/test/misc.jl b/test/misc.jl index 7598c6d3f3eea2..052dc54562c5dc 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -482,3 +482,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