diff --git a/test/snoop_inference.jl b/test/snoop_inference.jl index 356534f8..5a78f290 100644 --- a/test/snoop_inference.jl +++ b/test/snoop_inference.jl @@ -1,6 +1,7 @@ using SnoopCompile using SnoopCompile.SnoopCompileCore using SnoopCompile.FlameGraphs +using AbstractTrees using Test using InteractiveUtils using Random @@ -776,7 +777,7 @@ include("testmodules/SnoopBench.jl") rm(td, recursive=true, force=true) SnoopCompile.write(td, prs; ioreport=io, header=false) str = String(take!(io)) # just to clear it in case we use it again - @test !occursin("ccall(:jl_generating_output", read(file_base, String)) + @test !isfile(file_base) || !occursin("ccall(:jl_generating_output", read(file_base, String)) rm(td, recursive=true, force=true) # issue #197 @@ -836,10 +837,11 @@ end # pgdsgui(axs[2], rit; bystr="Inclusive", consts=true, interactive=false) end -@testset "Stale" begin +@testset "Stale and precompile_blockers" begin cproj = Base.active_project() cd(joinpath("testmodules", "Stale")) do Pkg.activate(pwd()) + Pkg.resolve() Pkg.precompile() end invalidations = @snoop_invalidations begin @@ -851,6 +853,13 @@ end stalenames = [mi.def.name for mi in smis] @test :build_stale ∈ stalenames @test :use_stale ∈ stalenames + + # Loading StaleC should "heal" StaleA.use_stale(::Vector{Any}) + mius = only(methodinstances(StaleA.use_stale)) + cius = mius.cache + @test cius.max_world == typemax(UInt) + requires_compensation = cius.invoke == C_NULL && cius.specptr == C_NULL && cius.inferred === nothing # see below + trees = invalidation_trees(invalidations) tree = length(trees) == 1 ? only(trees) : trees[findfirst(tree -> !isempty(tree.backedges), trees)] @test tree.method == which(StaleA.stale, (String,)) # defined in StaleC @@ -865,8 +874,19 @@ end @test convert(Core.MethodInstance, root.children[1].children[1]).def == m2 end tinf = @snoop_inference begin - StaleB.useA() - StaleC.call_buildstale("hi") + StaleB.useA() # this should require recompilation + StaleC.call_buildstale("hi") # this should still be valid (healed during loading of StaleC) + end + if requires_compensation + @assert Base.VERSION < v"1.12" + @warn "Compensating for a Julia bug, xref https://github.com/timholy/SnoopCompile.jl/pull/413#issuecomment-2743794491" + badidx = Int[] + for (idx, child) in pairs(tinf.children) + if MethodInstance(child) == mius + push!(badidx, idx) + end + end + deleteat!(tinf.children, badidx) end @test isempty(SnoopCompile.StaleTree(first(smis).def, :noreason).backedges) # constructor test healed = true diff --git a/test/testmodules/Stale/StaleA/src/StaleA.jl b/test/testmodules/Stale/StaleA/src/StaleA.jl index d5361357..b80da0c9 100644 --- a/test/testmodules/Stale/StaleA/src/StaleA.jl +++ b/test/testmodules/Stale/StaleA/src/StaleA.jl @@ -6,7 +6,7 @@ stale(x::Int) = length(digits(x)) not_stale(x::String) = first(x) use_stale(c) = stale(c[1]) + not_stale("hello") -build_stale(x) = use_stale(Any[x]) +build_stale(x) = use_stale(Any[x]) # deliberately defeat inference so `use_stale` is vulnerable to invalidation # force precompilation build_stale(37) diff --git a/test/testmodules/Stale/StaleC/src/StaleC.jl b/test/testmodules/Stale/StaleC/src/StaleC.jl index d4f8e48f..ec42ca23 100644 --- a/test/testmodules/Stale/StaleC/src/StaleC.jl +++ b/test/testmodules/Stale/StaleC/src/StaleC.jl @@ -2,9 +2,9 @@ module StaleC using StaleA -StaleA.stale(x::String) = length(x) -call_buildstale(x) = StaleA.build_stale(x) +StaleA.stale(x::String) = length(x) # piracy is great (but not required) for triggering invalidations +call_buildstale(x) = StaleA.build_stale(x) # linking build_stale to a method in this module ensures caching -call_buildstale("hey") +call_buildstale("hey") # forces re/precompilation of `StaleA.use_stale(::Vector{Any})` end # module