First of all, thank you for the awesome tooling around this. I appreciate there isn’t a straightforward answer to each case.

I’ve re-read and re-watched a few times:

- the blog post on invalidations and the post
- SnoopCompile.jl docs on invalidations
- analyzing and fixing invalidations
- and the discussion on invalidations in GMT

It all makes sense when explained, but I’m struggling to convert my understanding into actions on real-life examples (eg, when to `invokelatest`

, add concrete type annotations to caller body, open an issue with an upstream pkg or even with Julia Base)

Is there a resource that I missed / that I should look into?

**Could you perhaps comment on what you would do in the following cases?**

I’ve been looking at precompiling some Genie/Stipple-based apps and noticed a few cases where I didn’t know what the right next step would be.

Using the standard snippet (awesome idea to have this copy&paste-able snippet!):

```
using SnoopCompileCore
invalidations = @snoopr begin
using GenieFramework
end
using SnoopCompile
length(uinvalidated(invalidations))
trees = invalidation_trees(invalidations)
methinvs = trees[end];
root = methinvs.backedges[end]
ascend(root)
```

**1) Example with unlock(io)**

On the last record (most invalidations), I get

```
methinvs = trees[end]
inserting unlock(io::GarishPrint.GarishIO) @ GarishPrint ~/.julia/packages/GarishPrint/214Ip/src/io.jl:153 invalidated:
backedges: 1: superseding unlock(::IO) @ Base io.jl:27 with MethodInstance for unlock(::IO) (385 children)
6 mt_cache
```

I hit the ascend and jump to the last concrete call

```
julia> ascend(root)
Choose a call for analysis (q to quit):
unlock(::IO)
print(::IO, ::String, ::String)
println(::IO, ::String)
> println(::String)
deep_clean!(::Pkg.Resolve.Graph)
#simplify_graph!#117(::Bool, ::typeof(Pkg.Resolve.simplify_graph!), ::Pkg.Resolve.Graph, ::Set{Int64})
simplify_graph!(::Pkg.Resolve.Graph, ::Set{Int64})
simplify_graph!(::Pkg.Resolve.Graph)
trigger_failure!(::Pkg.Resolve.Graph, ::Vector{Int64}, ::Tuple{Int64, Int64})
v _resolve(::Pkg.Resolve.Graph, ::Nothing, ::Nothing)
# that shows
println(xs...) @ Base ~/.julia/juliaup/julia-1.9.0-rc3+0.aarch64.apple.darwin14/share/julia/base/coreio.jl:4
4 println(xs::Tuple{String}...)::Core.Const(nothing) = println(stdout::Any, xs::Tuple{String}...)
```

It tells me that the inference fails because Any comes from printing into `stdout`

.

That’s confusing because:

- it’s extremely common, so I would expect problems everywhere
- I thought
`stdout`

is a concrete type (Base.TTY) - the inference correctly identifies that all the returns are nothing

Perhaps I shouldn’t be looking at the backedges, so I looked at the code that inserted the new method, but that’s defined on a concrete type.

It doesn’t feel like “stomping out the Any type” applies here.

**2) Example with peek(io)**

After the failure with the last “tree”, I move to the one before last

```
julia> ascend(trees[end-1].backedges[end])
Choose a call for analysis (q to quit):
peek(::IO)
read(::IO, ::Type{Char})
iterate(::Base.ReadEachIterator{Char}, ::Nothing)
iterate(::Base.ReadEachIterator{Char})
> #readuntil#433(::Bool, ::typeof(readuntil), ::IO, ::Char)
readuntil(::IO, ::Char)
#readuntil#411(::Base.Pairs, ::typeof(readuntil), ::Base.AbstractPipe, ::Char)
readuntil(::Base.AbstractPipe, ::Char)
#readuntil#411(::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(re
v readuntil(::Base.Process, ::Char)
```

So I descend into the last line with Base.Process, as it’s the last concrete call and I ultimately end up with:

```
readuntil((pipe_reader(io::Base.AbstractPipe)::Any::IO::Type{IO})::IO, arg::Char; kw::Base.Pairs{Symbol,…
```

I’m bit lost because I have no idea how to concretize what will come out of that pipe.

That’s another failure on my part.

**3) Example with convert(T,…)**

So I clearly have no idea when it comes to IO, so I figured to jump to `convert`

function next, because I have noticed that it features in every single attempt I’ve made at analyzing invalidations (across many different packages).

So I jump into:

```
inserting convert(::Type{T}, x::EzXML.ReaderType) where T<:Integer @ EzXML ~/.julia/packages/EzXML/ZNwhK/src/streamreader.jl:54 invalidated:
backedges: 1: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int64}, ::Integer) (22 children)
2: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (28 children)
3: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (1 children)
4: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (7 children)
5: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int8}, ::Integer) (40 children)
6: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (10 children)
7: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int64}, ::Integer) (68 children)
```

And descript scrolling to the bottom of the list, I haven’t found any line where the “reds” (abstract types) start:

```
julia> ascend(trees[end-4].backedges[end])
Choose a call for analysis (q to quit):
^ to_index(::Integer)
to_index(::Vector{Int64}, ::Integer)
_to_indices1(::Vector{Int64}, ::Tuple{}, ::Integer)
to_indices(::Vector{Int64}, ::Tuple{}, ::Tuple{Integer})
to_indices(::Vector{Int64}, ::Tuple{Integer})
setindex!(::Vector{Int64}, ::Int64, ::Union{Integer, CartesianIndex})
sortperm_int_range(::Vector{T} where T<:Integer, ::Any, ::Any)
> #_sortperm#33(::Base.Sort.MissingOptimization{Base.Sort.BoolOptimization{Base.Sort.Small{10
kwcall(::NamedTuple{(:alg, :order, :scratch), Tuple{Base.Sort.MissingOptimization{Base.So
v #sortperm#32(::Base.Sort.MissingOptimization{Base.Sort.BoolOptimization{Base.Sort.Small
...
> to_index(::Vector{Symbol}, ::Number)
_to_indices1(::Vector{Symbol}, ::Tuple{Base.OneTo{Int64}}, ::Number)
to_indices(::Vector{Symbol}, ::Tuple{Base.OneTo{Int64}}, ::Tuple{Number})
to_indices(::Vector{Symbol}, ::Tuple{Number})
getindex(::Vector{Symbol}, ::Number)
maybeview(::Vector{Symbol}, ::Union{Number, Base.AbstractCartesianIndex})
```

Apologies for the long post, but would you have any tips / advice how to:

- proceed in the above cases?
- identify which cases are “easier” to fix for people learning about invals. (eg, “avoid all convert()”, “avoid io”, etc.)

Thank you!