This is a consequence of working on global scope where non-constant globals must be Any because the compiler canβt prove they wonβt change out from under it.
julia> const S = MyStruct(2)
MyStruct{Int64}(2)
julia> f(x) = S(2*x)
f (generic function with 1 method)
julia> @code_warntype f(1)
MethodInstance for f(::Int64)
from f(x) @ Main REPL[4]:1
Arguments
#self#::Core.Const(f)
x::Int64
Body::Int64
1 β %1 = (2 * x)::Int64
β %2 = Main.S(%1)::Int64
βββ return %2
Or in a closure
julia> function g(y)
S = MyStruct(y)
h(x) = S(2*x)
end
g (generic function with 1 method)
julia> h=g(2)
h (generic function with 1 method)
ulia> @code_warntype h(1)
MethodInstance for (::var"#h#8"{MyStruct{Int64}})(::Int64)
from (::var"#h#8")(x) @ Main REPL[7]:3
Arguments
#self#::var"#h#8"{MyStruct{Int64}}
x::Int64
Body::Int64
1 β %1 = Core.getfield(#self#, :S)::MyStruct{Int64}
β %2 = (2 * x)::Int64
β %3 = (%1)(%2)::Int64
βββ return %3
Thanks! I confirmed that making mdl constant in my initial example following your suggestion resolve the issue there too.
Since I always end up declaring stuff in global scope when debugging/trying things out, I guess the lesson is to declare stuff constant when checking for type instability. I suppose BenchmarkTools already does this when one interpolates variables using $, so if one sees excessive allocations when benchmarking, this kind of type instability isnβt to blame.