@code_warntype fails when function has a keyword argument

In the following two functions are identical except that fun2() has a keyword argument. However, @code_warntype fails to “go into” the invoke Main... part that should give a warntype!

function fun1(v)
    kw = 1
    println(v[1])
end

function fun2(v; kw=1)
    println(v[1])
end

v = Vector{Real}(undef, 2)
v[1] = 1
v[2] = 2.2

julia> @code_warntype fun1(v)
Body::Nothing
1 ─ %1 = (Base.arrayref)(true, v, 1)::Real
│   %2 = (Main.println)(%1)::Core.Compiler.Const(nothing, false)
└──      return %2

julia> @code_warntype fun2(v)
Body::Nothing
1 ─ %1 = invoke Main.:(#fun2#3)(1::Int64, _1::Function, _2::Array{Real,1})::Core.Compiler.Const(nothing, false)
└──      return %1

it’s worrisome…

This is not a failure — @code_warntype does not expand the callees recursively (unless they were inlined).

Just use @code_warntype fun2(v; kw = 1).

Also, note that if you are debugging type stablity, you should use arguments with concrete types. Abstract type parameters like in Vector{Real} preclude the optimizations that are allowed by knowing types at compile time (“type stability”), so are not really relevant.

You can use Cthulhu to descend into madness functions with keyword arguments (among other things):

julia> @descend_code_typed fun2(v)

│ ─ %-1  = invoke fun2(::Array{Real,1})::Core.Compiler.Const(nothing, false)
CodeInfo(
    @ REPL[2]:2 within `fun2'
1 ─ %1 = invoke Main.:(#fun2#3)(1::Int64, _1::Function, _2::Array{Real,1})::Core.Compiler.Const(nothing, false)
└──      return %1
)
Select a call to descend into or ↩ to ascend. [q]uit.
Toggles: [o]ptimize, [w]arn, [d]ebuginfo.
Show: [L]LVM IR, [N]ative code
Advanced: dump [P]arams cache.

 • %1  = invoke #fun2#3(::Int64,::Function,::Array{Real,1})
   ↩

│ ─ %-1  = invoke #fun2#3(::Int64,::Function,::Array{Real,1})::Core.Compiler.Const(nothing, false)
CodeInfo(
    @ REPL[2]:2 within `#fun2#3'
   ┌ @ array.jl:729 within `getindex'
1 ─│ %1 = (Base.arrayref)(true, v, 1)::Real
│  └
│   %2 = (Main.println)(%1)::Core.Compiler.Const(nothing, false)
└──      return %2
)
Select a call to descend into or ↩ to ascend. [q]uit.
Toggles: [o]ptimize, [w]arn, [d]ebuginfo.
Show: [L]LVM IR, [N]ative code
Advanced: dump [P]arams cache.

 • ↩
1 Like

thanks. could you show how to install this package?

It’s not registered, so you have to do add https://github.com/JuliaDebug/Cthulhu.jl in Pkg mode.

2 Likes

that Vector{Real} is intentional (or else no warntype to show at all).

with @code_warntype not recursively gives warning, we would need to carefully watch out any invoke. Or, use Cthulhu as @mateuszbaran suggested.

Perhaps you misunderstand the role of @code_warntype: the idea is that it helps you detect type instabilities when you call with concrete types. This will be marked even if we don’t descend into the callees. Eg

julia> @noinline f(x) = x > 0 ? 1 : 1.0
f (generic function with 1 method)

julia> g(x) = f(x^2)
g (generic function with 1 method)

julia> @code_warntype g(1)
Variables
  #self#::Core.Compiler.Const(g, false)
  x::Int64

Body::Union{Float64, Int64}
1 ─ %1 = Core.apply_type(Base.Val, 2)::Core.Compiler.Const(Val{2}, false)
│   %2 = (%1)()::Core.Compiler.Const(Val{2}(), false)
│   %3 = Base.literal_pow(Main.:^, x, %2)::Int64
│   %4 = Main.f(%3)::Union{Float64, Int64}
└──      return %4

where the ::Union{Float64, Int64} is highlighted if the terminal supports it. So you are not going to miss anything.