<p dir="auto">For my time propagation of PDEs, I need to find the eigenvectors/-values of a Krylov subspace matrix, every step (see e.g. my fork of <a href="https://github.com/jagot/Krylov.jl/blob/2cd8e19fb9bc2c86747dbd890e52628074b3e299/src/exp_lanczos.jl#L33">Krylov.jl</a>). As it is now, e.g. <a href="https://github.com/JuliaLang/julia/blob/a6a65e3aeedd9b92a004dee095d70737e490c69f/base/linalg/lapack.jl#L3666">stegr!</a> allocates the work and output arrays, every time. I wonder if there are any plans to provide for those functions who need it, a split version? I mean something in the spirit of</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="work = stegr_work(...)
for i = 1:steps
    ...
    stegr!(other, args, work...)
end"><pre class="notranslate">work <span class="pl-k">=</span> <span class="pl-c1">stegr_work</span>(<span class="pl-k">...</span>)
<span class="pl-k">for</span> i <span class="pl-k">=</span> <span class="pl-c1">1</span><span class="pl-k">:</span>steps
    <span class="pl-k">...</span>
    <span class="pl-c1">stegr!</span>(other, args, work<span class="pl-k">...</span>)
<span class="pl-k">end</span></pre></div>
<p dir="auto">thereby reducing the amount of (de)allocations.</p>
<p dir="auto">I could of course implement the functions I need in a library for my own use, but I would prefer to reduce the amount of code duplication, and maybe other people than myself would find this useful?</p>
<p dir="auto">Some new <code class="notranslate">convert</code> method definitions cause substantial invalidation due to usage of <code class="notranslate">convert</code> in constructors. A good example is <a href="https://github.com/JuliaLang/Pkg.jl/blob/3e3f9d7a3baf3a96c860d2b1bc895d5fd2d40d35/src/Operations.jl#L64-L83"><code class="notranslate">Pkg.Operations.load_direct_deps</code></a>:</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="julia&gt; using Pkg, Cthulhu

julia&gt; descend(Pkg.Operations.load_direct_deps, (Pkg.Types.Context,))"><pre class="notranslate">julia<span class="pl-k">&gt;</span> <span class="pl-k">using</span> Pkg, Cthulhu

julia<span class="pl-k">&gt;</span> <span class="pl-c1">descend</span>(Pkg<span class="pl-k">.</span>Operations<span class="pl-k">.</span>load_direct_deps, (Pkg<span class="pl-k">.</span>Types<span class="pl-k">.</span>Context,))</pre></div>
<p dir="auto">From here, you can navigate to the "body method" where you get to the core issue:</p>
<div class="highlight highlight-source-julia notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="│    %47 = uuid::Base.UUID
│    %48 = name::String
│    %49 = Base.getproperty(entry::Pkg.Types.PackageEntry, :path)::Union{Nothing, String}
│    %50 = Base.getproperty(entry::Pkg.Types.PackageEntry, :repo)::Pkg.Types.GitRepo
│    %51 = Base.getproperty(entry::Pkg.Types.PackageEntry, :pinned)::Bool
│    %52 = Base.getproperty(entry::Pkg.Types.PackageEntry, :tree_hash)::Union{Nothing, Base.SHA1}
│    %53 = Base.getproperty(entry::Pkg.Types.PackageEntry, :version)::Union{Nothing, VersionNumber}
│    %54 = Pkg.Operations.isfixed(entry::Pkg.Types.PackageEntry)::Bool
│    %55 = Pkg.Operations.load_version(%53, %54, preserve)::Union{Nothing, VersionNumber, Pkg.Types.VersionSpec}
│    %56 = Core.tuple(%47, %48, %49, %50, %51, %52, %55)::Tuple{Base.UUID,String,Union{Nothing, String},Pkg.Types.GitRepo,Bool,Union{Nothing, Base.SHA1},Union{Nothing, VersionNumber, Pkg.Types.VersionSpec}}
│    %57 = (%46)(%56)::NamedTuple{(:uuid, :name, :path, :repo, :pinned, :tree_hash, :version),_A} where _A&lt;:Tuple
│    %58 = Core.kwfunc(Pkg.Operations.PackageSpec)::Core.Compiler.Const(Core.var&quot;#Type##kw&quot;(), false)
└───       (@_13 = (%58)(%57, Pkg.Operations.PackageSpec))"><pre class="notranslate">│    <span class="pl-k">%</span><span class="pl-c1">47</span> <span class="pl-k">=</span> uuid<span class="pl-k">::</span><span class="pl-c1">Base.UUID</span>
│    <span class="pl-k">%</span><span class="pl-c1">48</span> <span class="pl-k">=</span> name<span class="pl-k">::</span><span class="pl-c1">String</span>
│    <span class="pl-k">%</span><span class="pl-c1">49</span> <span class="pl-k">=</span> Base<span class="pl-k">.</span><span class="pl-c1">getproperty</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>, <span class="pl-c1">:path</span>)<span class="pl-k">::</span><span class="pl-c1">Union{Nothing, String}</span>
│    <span class="pl-k">%</span><span class="pl-c1">50</span> <span class="pl-k">=</span> Base<span class="pl-k">.</span><span class="pl-c1">getproperty</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>, <span class="pl-c1">:repo</span>)<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.GitRepo</span>
│    <span class="pl-k">%</span><span class="pl-c1">51</span> <span class="pl-k">=</span> Base<span class="pl-k">.</span><span class="pl-c1">getproperty</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>, <span class="pl-c1">:pinned</span>)<span class="pl-k">::</span><span class="pl-c1">Bool</span>
│    <span class="pl-k">%</span><span class="pl-c1">52</span> <span class="pl-k">=</span> Base<span class="pl-k">.</span><span class="pl-c1">getproperty</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>, <span class="pl-c1">:tree_hash</span>)<span class="pl-k">::</span><span class="pl-c1">Union{Nothing, Base.SHA1}</span>
│    <span class="pl-k">%</span><span class="pl-c1">53</span> <span class="pl-k">=</span> Base<span class="pl-k">.</span><span class="pl-c1">getproperty</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>, <span class="pl-c1">:version</span>)<span class="pl-k">::</span><span class="pl-c1">Union{Nothing, VersionNumber}</span>
│    <span class="pl-k">%</span><span class="pl-c1">54</span> <span class="pl-k">=</span> Pkg<span class="pl-k">.</span>Operations<span class="pl-k">.</span><span class="pl-c1">isfixed</span>(entry<span class="pl-k">::</span><span class="pl-c1">Pkg.Types.PackageEntry</span>)<span class="pl-k">::</span><span class="pl-c1">Bool</span>
│    <span class="pl-k">%</span><span class="pl-c1">55</span> <span class="pl-k">=</span> Pkg<span class="pl-k">.</span>Operations<span class="pl-k">.</span><span class="pl-c1">load_version</span>(<span class="pl-k">%</span><span class="pl-c1">53</span>, <span class="pl-k">%</span><span class="pl-c1">54</span>, preserve)<span class="pl-k">::</span><span class="pl-c1">Union{Nothing, VersionNumber, Pkg.Types.VersionSpec}</span>
│    <span class="pl-k">%</span><span class="pl-c1">56</span> <span class="pl-k">=</span> Core<span class="pl-k">.</span><span class="pl-c1">tuple</span>(<span class="pl-k">%</span><span class="pl-c1">47</span>, <span class="pl-k">%</span><span class="pl-c1">48</span>, <span class="pl-k">%</span><span class="pl-c1">49</span>, <span class="pl-k">%</span><span class="pl-c1">50</span>, <span class="pl-k">%</span><span class="pl-c1">51</span>, <span class="pl-k">%</span><span class="pl-c1">52</span>, <span class="pl-k">%</span><span class="pl-c1">55</span>)<span class="pl-k">::</span><span class="pl-c1">Tuple{Base.UUID,String,Union{Nothing, String},Pkg.Types.GitRepo,Bool,Union{Nothing, Base.SHA1},Union{Nothing, VersionNumber, Pkg.Types.VersionSpec}}</span>
│    <span class="pl-k">%</span><span class="pl-c1">57</span> <span class="pl-k">=</span> (<span class="pl-k">%</span><span class="pl-c1">46</span>)(<span class="pl-k">%</span><span class="pl-c1">56</span>)<span class="pl-k">::</span><span class="pl-c1">NamedTuple{(:uuid, :name, :path, :repo, :pinned, :tree_hash, :version),_A}</span> <span class="pl-k">where</span> _A<span class="pl-k">&lt;:</span><span class="pl-c1">Tuple</span>
│    <span class="pl-k">%</span><span class="pl-c1">58</span> <span class="pl-k">=</span> Core<span class="pl-k">.</span><span class="pl-c1">kwfunc</span>(Pkg<span class="pl-k">.</span>Operations<span class="pl-k">.</span>PackageSpec)<span class="pl-k">::</span><span class="pl-c1">Core.Compiler.Const</span>(Core<span class="pl-k">.</span><span class="pl-c1">var"#Type##kw"</span>(), <span class="pl-c1">false</span>)
└───       (<span class="pl-c1">@_13</span> <span class="pl-k">=</span> (<span class="pl-k">%</span><span class="pl-c1">58</span>)(<span class="pl-k">%</span><span class="pl-c1">57</span>, Pkg<span class="pl-k">.</span>Operations<span class="pl-k">.</span>PackageSpec))</pre></div>
<p dir="auto">Here you can see that most of the inputs for creating the <code class="notranslate">NamedTuple</code> are already known to have the types needed for fields of <a href="https://github.com/JuliaLang/Pkg.jl/blob/3e3f9d7a3baf3a96c860d2b1bc895d5fd2d40d35/src/Types.jl#L90-L99"><code class="notranslate">PackageSpec</code></a>. However, presumably due to specialization heuristics, that type info is lost with the constructor for <code class="notranslate">NamedTuple</code>.</p>
<p dir="auto">I don't think this mode of invalidation is outrageously common, but I know I've seen it before, which is why I decided to report it as a systematic issue. Perhaps the <code class="notranslate">NamedTuple</code> constructor should get an extra <code class="notranslate">Union</code>-bonus in the specialization heuristics? Or is it one of the worst offenders desperately in need of limits?</p>
<p dir="auto">There is a way to circumvent this: create a specialized <code class="notranslate">PackageSpec(::PackageEntry, uuid, ver, pinned, mode)</code> constructor. But I don't see a great way to avoid duplicating the field default specifications, which would be a bit of a code smell.</p>