diff --git a/src/signatures.jl b/src/signatures.jl index 7cda176..2448267 100644 --- a/src/signatures.jl +++ b/src/signatures.jl @@ -488,12 +488,22 @@ end Advance the program counter without executing the corresponding line. If `frame` is finished, `nextpc` will be `nothing`. + +`next_or_nothing!` still advances the world of `frame` for a `:latestworld` statement, so +that the statements that follow see the definitions made before it. """ next_or_nothing(frame::Frame, pc::Int) = next_or_nothing(RecursiveInterpreter(), frame, pc) next_or_nothing(::Interpreter, frame::Frame, pc::Int) = pc < nstatements(frame.framecode) ? pc+1 : nothing next_or_nothing!(frame::Frame) = next_or_nothing!(RecursiveInterpreter(), frame) function next_or_nothing!(::Interpreter, frame::Frame) pc = frame.pc + # Since Julia 1.12, top-level code advances its world only at `:latestworld`, which + # JuliaInterpreter follows, so skipping one would leave later statements reading + # bindings in a world prior to their definition. This is done here rather than by + # selecting `:latestworld` in `lines_required!`, since a selected statement pulls in its + # control flow and type definition, which would grow the slice by branch conditions, + # loops, and types it does not need. + isexpr(pc_expr(frame, pc), :latestworld) && (frame.world = Base.get_world_counter()) if pc < nstatements(frame.framecode) return frame.pc = pc + 1 end diff --git a/test/codeedges.jl b/test/codeedges.jl index 80f2177..c66c7e6 100644 --- a/test/codeedges.jl +++ b/test/codeedges.jl @@ -725,4 +725,23 @@ end end end +@testset "skipped :latestworld still advances the world" begin + # Selective evaluation skips the `:latestworld` statements it does not select, e.g. + # the one after the anonymous function of `max_values` in the "CodeEdges" tests above, + # which then fail on JuliaInterpreter versions that advance the world of top-level code + # only at `:latestworld`. + m = Module(:SkippedLatestworld) + world = Base.get_world_counter() + Core.eval(m, :(defined_after = 1)) + lwsrc = (Meta.lower(m, :(global gl = 1; gl))::Expr).args[1]::Core.CodeInfo + lwidx = findfirst(stmt -> Meta.isexpr(stmt, :latestworld), lwsrc.code) + if lwidx !== nothing + frame = Frame(m, lwsrc; world) + frame.pc = lwidx + LoweredCodeUtils.next_or_nothing!(frame) + @test frame.pc == lwidx + 1 + @test frame.world > world + end +end + end # module codeedges