Revise.jl
Revise.jl copied to clipboard
World age error when revising in Plots
With Plots
checked out for development:
julia> using Revise
julia> using Plots
[ Info: Precompiling Plots [91a5bcdd-55d7-5caf-9e0b-520d859cae80]
julia> # edit Plots/src/utils.jl
julia> plot(rand(4))
┌ Warning: likely failure to return to toplevel, try `ExprSplitter`
└ @ JuliaInterpreter C:\Users\sternlab\.julia\packages\JuliaInterpreter\QbBZX\src\interpret.jl:622
┌ Error: Failed to revise C:\Users\sternlab\.julia\dev\Plots\src\utils.jl
│ exception =
│ MethodError: no method matching (::Plots.var"#486#487")(::Int64)
│ The applicable method may be too new: running in world age 27894, while current world is 27901.
│ Closest candidates are:
│ #486(::Any) at none:0 (method too new to be called from this world context.)
│ top-level scope at C:\Users\sternlab\.julia\dev\Plots\src\utils.jl:154
│ Revise evaluation error at C:\Users\sternlab\.julia\dev\Plots\src\utils.jl:154
│
└ @ Revise C:\Users\sternlab\.julia\packages\Revise\mvD4N\src\packagedef.jl:707
┌ Warning: The running code does not match the saved version for the following files:
│
│ C:\Users\sternlab\.julia\dev\Plots\src\utils.jl
│
│ If the error was due to evaluation order, it can sometimes be resolved by calling `Revise.retry()`.
│ Use Revise.errors() to report errors again. Only the first error in each file is shown.
│ Your prompt color may be yellow until the errors are resolved.
└ @ Revise C:\Users\sternlab\.julia\packages\Revise\mvD4N\src\packagedef.jl:805
Oof, this is a really tough one. It comes from here:
for i in 2:4
@eval begin
RecipesPipeline.unzip(
v::Union{AVec{<:Tuple{Vararg{T,$i} where T}}, AVec{<:GeometryBasics.Point{$i}}},
) = $(Expr(:tuple, (:([t[$j] for t in v]) for j=1:i)...))
end
end
The problem is that the method body it built via comprehension, which defines an anonymous function, and this is where the world age issue comes from. The ExprSplitter
framework of JuliaInterpreter is designed to allow everything to happen in successive eval
s and thus avoid world age problems, but this is so deeply tangled inside its own @eval
that it defeats all the logic we've created to handle this.
I'm not immediately sure how to fix this. Julia itself can play nice tricks on the C side with the world age, but none of those capabilities are available to packages.
An easy way to circumvent this problem (make it rarer) is to split this definition out into a separate file. As long as you never change that file, then it won't rear its head. Meanwhile I'll think about how to handle this.
For my own future debugging reference, here's the lowered framecode (the t
/f
shows which statements Revise will evaluate in its quest to spot method signatures...which there are, so it's not wrong to be trying to do this):
1 t 1 ─ $(Expr(:thunk, CodeInfo(
@ none within `top-level scope'
1 ─ global var"#474#475"
│ const var"#474#475"
│ %3 = Core._structtype(Plots, Symbol("#474#475"), Core.svec(), Core.svec(), false, 0)
│ var"#474#475" = %3
│ Core._setsuper!(var"#474#475", Core.Function)
│ Core._typebody!(var"#474#475", Core.svec())
└── return nothing
)))
2 t │ %2 = Core.svec(var"#474#475", $(QuoteNode(Any)))
3 t │ %3 = Core.svec()
4 t │ %4 = Core.svec(%2, %3, $(QuoteNode(:(#= none:0 =#))))
5 t │ $(Expr(:method, false, :(%4), CodeInfo(
@ none within `none'
1 ─ %1 = Core._expr(:ref, :t, j)
│ %2 = $(Expr(:copyast, :($(QuoteNode(:(t = v))))))
│ %3 = Core._expr(:generator, %1, %2)
│ %4 = Core._expr(:comprehension, %3)
└── return %4
)))
6 t │ %6 = 2:4
7 t │ _1 = Base.iterate(%6)
8 t │ %8 = _1 === nothing
9 t │ %9 = ($(QuoteNode(Core.Intrinsics.not_int)))(%8)
10 t └── goto #4 if not %9
11 t 2 ┄ %11 = _1
12 t │ _3 = Core.getfield(%11, 1)
13 t │ %13 = Core.getfield(%11, 2)
14 t │ %14 = $(Expr(:copyast, :($(QuoteNode(:(RecipesPipeline.unzip))))))
15 t │ %15 = Core._expr(:curly, :Vararg, :T, _3)
16 t │ %16 = Core._expr(:where, %15, :T)
17 t │ %17 = Core._expr(:curly, :Tuple, %16)
18 t │ %18 = Core._expr(:<:, %17)
19 t │ %19 = Core._expr(:curly, :AVec, %18)
20 t │ %20 = $(Expr(:copyast, :($(QuoteNode(:(GeometryBasics.Point))))))
21 t │ %21 = Core._expr(:curly, %20, _3)
22 t │ %22 = Core._expr(:<:, %21)
23 t │ %23 = Core._expr(:curly, :AVec, %22)
24 t │ %24 = Core._expr(:curly, :Union, %19, %23)
25 t │ %25 = Core._expr(:(::), :v, %24)
26 t │ %26 = Core._expr(:call, %14, %25)
27 t │ %27 = Core.tuple(:tuple)
28 t │ _2 = %new(var"#474#475")
29 t │ %29 = _2
30 t │ %30 = 1:_3
31 t │ %31 = ($(QuoteNode(Base.Generator)))(%29, %30)
32 t │ %32 = Core._apply_iterate($(QuoteNode(iterate)), Expr, %27, %31)
33 t │ %33 = Core._expr(:block, $(QuoteNode(:(#= /home/tim/.julia/dev/Plots/src/utils_eval.jl:5 =#))), %32)
34 t │ %34 = Core._expr(:(=), %26, %33)
35 t │ %35 = Core._expr(:block, $(QuoteNode(:(#= /home/tim/.julia/dev/Plots/src/utils_eval.jl:3 =#))), %34)
36 t │ Core.eval(Plots, %35)
37 t │ _1 = Base.iterate(%6, %13)
38 t │ %38 = _1 === nothing
39 t │ %39 = ($(QuoteNode(Core.Intrinsics.not_int)))(%38)
40 t └── goto #4 if not %39
41 t 3 ─ goto #2
42 f 4 ┄ return nothing
It fails at %32, because this line tries to apply the anonymous function (it never even makes it to the Core.eval
). If we could interpret statements 1-5 in one world age, and the rest in another, we'd be fine, but how to do that without absolutely littering JuliaInterpreter with invokelatest
s (likely hurting performance drastically, as we'd have to abandon our local method tables) is not obvious.
Any ideas @KristofferC, @aviatesk?