Skip to content

Commit 671c6a1

Browse files
authored
cleanup old builtins (#57532)
The `_apply_pure` function only has one user, and that user is unsound anyways and should not be used, so replace that with equivalent `_call_in_world_total` call and remove unnecessary definitions. The awkward distinction between `invokelatest` and `_call_latest` has not been relevant (and indeed causes performance issues) since kwfunc was introduced in #47157.
1 parent 1e03ed6 commit 671c6a1

File tree

15 files changed

+91
-133
lines changed

15 files changed

+91
-133
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState,
778778
# If the method defines a recursion relation, give it a chance
779779
# to tell us that this recursion is actually ok.
780780
if isdefined(method, :recursion_relation)
781-
if Core._apply_pure(method.recursion_relation, Any[method, callee_method2, sig, frame_instance(frame).specTypes])
781+
if Core._call_in_world_total(get_world_counter(), method.recursion_relation, method, callee_method2, sig, frame_instance(frame).specTypes)
782782
return false
783783
end
784784
end

Compiler/src/ssair/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function builtin_call_has_dispatch(
6767
return true
6868
end
6969
end
70-
elseif (f === Core._apply_pure || f === Core._call_in_world || f === Core._call_in_world_total || f === Core._call_latest)
70+
elseif (f === Core.invoke_in_world || f === Core._call_in_world_total || f === Core.invokelatest)
7171
# These apply-like builtins are effectively dynamic calls
7272
return true
7373
end

Compiler/src/verifytrim.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,10 @@ function may_dispatch(@nospecialize ftyp)
153153
# other builtins (including the IntrinsicFunctions) are good
154154
return Core._apply isa ftyp ||
155155
Core._apply_iterate isa ftyp ||
156-
Core._apply_pure isa ftyp ||
157-
Core._call_in_world isa ftyp ||
158156
Core._call_in_world_total isa ftyp ||
159-
Core._call_latest isa ftyp ||
160157
Core.invoke isa ftyp ||
158+
Core.invoke_in_world isa ftyp ||
159+
Core.invokelatest isa ftyp ||
161160
Core.finalizer isa ftyp ||
162161
Core.modifyfield! isa ftyp ||
163162
Core.modifyglobal! isa ftyp ||

base/Base_compiler.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,63 @@ function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has
202202
return Core._hasmethod(tt)
203203
end
204204

205+
"""
206+
invokelatest(f, args...; kwargs...)
207+
208+
Calls `f(args...; kwargs...)`, but guarantees that the most recent method of `f`
209+
will be executed. This is useful in specialized circumstances,
210+
e.g. long-running event loops or callback functions that may
211+
call obsolete versions of a function `f`.
212+
(The drawback is that `invokelatest` is somewhat slower than calling
213+
`f` directly, and the type of the result cannot be inferred by the compiler.)
214+
215+
!!! compat "Julia 1.9"
216+
Prior to Julia 1.9, this function was not exported, and was called as `Base.invokelatest`.
217+
"""
218+
const invokelatest = Core.invokelatest
219+
220+
# define invokelatest(f, args...; kwargs...), without kwargs wrapping
221+
# to forward to invokelatest
222+
function Core.kwcall(kwargs::NamedTuple, ::typeof(invokelatest), f, args...)
223+
@inline
224+
return Core.invokelatest(Core.kwcall, kwargs, f, args...)
225+
end
226+
setfield!(typeof(invokelatest).name.mt, :max_args, 2, :monotonic) # invokelatest, f, args...
227+
228+
"""
229+
invoke_in_world(world, f, args...; kwargs...)
230+
231+
Call `f(args...; kwargs...)` in a fixed world age, `world`.
232+
233+
This is useful for infrastructure running in the user's Julia session which is
234+
not part of the user's program. For example, things related to the REPL, editor
235+
support libraries, etc. In these cases it can be useful to prevent unwanted
236+
method invalidation and recompilation latency, and to prevent the user from
237+
breaking supporting infrastructure by mistake.
238+
239+
The current world age can be queried using [`Base.get_world_counter()`](@ref)
240+
and stored for later use within the lifetime of the current Julia session, or
241+
when serializing and reloading the system image.
242+
243+
Technically, `invoke_in_world` will prevent any function called by `f` from
244+
being extended by the user during their Julia session. That is, generic
245+
function method tables seen by `f` (and any functions it calls) will be frozen
246+
as they existed at the given `world` age. In a sense, this is like the opposite
247+
of [`invokelatest`](@ref).
248+
249+
!!! note
250+
It is not valid to store world ages obtained in precompilation for later use.
251+
This is because precompilation generates a "parallel universe" where the
252+
world age refers to system state unrelated to the main Julia session.
253+
"""
254+
const invoke_in_world = Core.invoke_in_world
255+
256+
function Core.kwcall(kwargs::NamedTuple, ::typeof(invoke_in_world), world::UInt, f, args...)
257+
@inline
258+
return Core.invoke_in_world(world, Core.kwcall, kwargs, f, args...)
259+
end
260+
setfield!(typeof(invoke_in_world).name.mt, :max_args, 3, :monotonic) # invoke_in_world, world, f, args...
261+
205262
# core operations & types
206263
include("promotion.jl")
207264
include("tuple.jl")

base/boot.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,8 +1014,11 @@ _parse = nothing
10141014

10151015
_setparser!(parser) = setglobal!(Core, :_parse, parser)
10161016

1017-
# support for deprecated uses of internal _apply function
1018-
_apply(x...) = Core._apply_iterate(Main.Base.iterate, x...)
1017+
# support for deprecated uses of builtin functions
1018+
_apply(x...) = _apply_iterate(Main.Base.iterate, x...)
1019+
_apply_pure(x...) = invoke_in_world_total(typemax_UInt, x...)
1020+
const _call_latest = invokelatest
1021+
const _call_in_world = invoke_in_world
10191022

10201023
struct Pair{A, B}
10211024
first::A

base/essentials.jl

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,63 +1070,6 @@ end
10701070

10711071
Val(x) = Val{x}()
10721072

1073-
"""
1074-
invokelatest(f, args...; kwargs...)
1075-
1076-
Calls `f(args...; kwargs...)`, but guarantees that the most recent method of `f`
1077-
will be executed. This is useful in specialized circumstances,
1078-
e.g. long-running event loops or callback functions that may
1079-
call obsolete versions of a function `f`.
1080-
(The drawback is that `invokelatest` is somewhat slower than calling
1081-
`f` directly, and the type of the result cannot be inferred by the compiler.)
1082-
1083-
!!! compat "Julia 1.9"
1084-
Prior to Julia 1.9, this function was not exported, and was called as `Base.invokelatest`.
1085-
"""
1086-
function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...)
1087-
@inline
1088-
kwargs = merge(NamedTuple(), kwargs)
1089-
if isempty(kwargs)
1090-
return Core._call_latest(f, args...)
1091-
end
1092-
return Core._call_latest(Core.kwcall, kwargs, f, args...)
1093-
end
1094-
1095-
"""
1096-
invoke_in_world(world, f, args...; kwargs...)
1097-
1098-
Call `f(args...; kwargs...)` in a fixed world age, `world`.
1099-
1100-
This is useful for infrastructure running in the user's Julia session which is
1101-
not part of the user's program. For example, things related to the REPL, editor
1102-
support libraries, etc. In these cases it can be useful to prevent unwanted
1103-
method invalidation and recompilation latency, and to prevent the user from
1104-
breaking supporting infrastructure by mistake.
1105-
1106-
The current world age can be queried using [`Base.get_world_counter()`](@ref)
1107-
and stored for later use within the lifetime of the current Julia session, or
1108-
when serializing and reloading the system image.
1109-
1110-
Technically, `invoke_in_world` will prevent any function called by `f` from
1111-
being extended by the user during their Julia session. That is, generic
1112-
function method tables seen by `f` (and any functions it calls) will be frozen
1113-
as they existed at the given `world` age. In a sense, this is like the opposite
1114-
of [`invokelatest`](@ref).
1115-
1116-
!!! note
1117-
It is not valid to store world ages obtained in precompilation for later use.
1118-
This is because precompilation generates a "parallel universe" where the
1119-
world age refers to system state unrelated to the main Julia session.
1120-
"""
1121-
function invoke_in_world(world::UInt, @nospecialize(f), @nospecialize args...; kwargs...)
1122-
@inline
1123-
kwargs = Base.merge(NamedTuple(), kwargs)
1124-
if isempty(kwargs)
1125-
return Core._call_in_world(world, f, args...)
1126-
end
1127-
return Core._call_in_world(world, Core.kwcall, kwargs, f, args...)
1128-
end
1129-
11301073
"""
11311074
inferencebarrier(x)
11321075

base/reflection.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,9 +1283,9 @@ function invokelatest_gr(gr::GlobalRef, @nospecialize args...; kwargs...)
12831283
@inline
12841284
kwargs = merge(NamedTuple(), kwargs)
12851285
if isempty(kwargs)
1286-
return Core._call_latest(apply_gr, gr, args...)
1286+
return invokelatest(apply_gr, gr, args...)
12871287
end
1288-
return Core._call_latest(apply_gr_kw, kwargs, gr, args...)
1288+
return invokelatest(apply_gr_kw, kwargs, gr, args...)
12891289
end
12901290

12911291
"""

contrib/generate_precompile.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ precompile(Tuple{typeof(Base.Threads.atomic_sub!), Base.Threads.Atomic{Int}, Int
4646
precompile(Tuple{Type{Base.Val{x} where x}, Module})
4747
precompile(Tuple{Type{NamedTuple{(:honor_overrides,), T} where T<:Tuple}, Tuple{Bool}})
4848
precompile(Tuple{typeof(Base.unique!), Array{String, 1}})
49-
precompile(Tuple{typeof(Base.invokelatest), Any})
5049
precompile(Tuple{typeof(Base.vcat), Array{String, 1}, Array{String, 1}})
5150
5251
# Pkg loading

contrib/juliac-buildscript.jl

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,8 @@ end
3838
init_active_project() = nothing
3939
disable_library_threading() = nothing
4040
start_profile_listener() = nothing
41-
@inline function invokelatest(f::F, args...; kwargs...) where F
42-
return f(args...; kwargs...)
43-
end
44-
@inline function invokelatest_gr(gr::GlobalRef, @nospecialize args...; kwargs...)
45-
@inline
46-
kwargs = merge(NamedTuple(), kwargs)
47-
if isempty(kwargs)
48-
return apply_gr(gr, args...)
49-
end
50-
return apply_gr_kw(kwargs, gr, args...)
51-
end
41+
invokelatest_trimmed(f, args...; kwargs...) = f(args...; kwargs...)
42+
const invokelatest = invokelatest_trimmed
5243
function sprint(f::F, args::Vararg{Any,N}; context=nothing, sizehint::Integer=0) where {F<:Function,N}
5344
s = IOBuffer(sizehint=sizehint)
5445
if context isa Tuple

src/builtin_proto.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ extern "C" {
2222
#endif
2323

2424
DECLARE_BUILTIN(_apply_iterate);
25-
DECLARE_BUILTIN(_apply_pure);
26-
DECLARE_BUILTIN(_call_in_world);
25+
DECLARE_BUILTIN(invoke_in_world);
2726
DECLARE_BUILTIN(_call_in_world_total);
28-
DECLARE_BUILTIN(_call_latest);
27+
DECLARE_BUILTIN(invokelatest);
2928
DECLARE_BUILTIN(_compute_sparams);
3029
DECLARE_BUILTIN(_expr);
3130
DECLARE_BUILTIN(_svec_ref);

0 commit comments

Comments
 (0)