Unexpected closure behavior

using Logging, Cthulhu
const glogger = Ref(ConsoleLogger())
function foo(a::Real, b::Real, c::Real, d::Real)
    x₁ = b + c + d
    e = b + c + d
    if e >= 0
        x₂ = b + c + d
        x₃ = b + c + d
        with_logger(() -> @info("x123", x₁, x₂, x₃), glogger[])
    end
    return x₁, x₂, x₃
end
@descend foo(1, 2, 3, 4)

When I analyze type stability, I find that the types of x2 and x3 cannot be correctly inferred. Does anyone know what is going on please?

 3 function foo(a::Int64::Real, b::Int64::Real, c::Int64::Real, d::Int64::Real)::Tuple{Int64, Any, Any}
 4     x₁::Int64 = (b::Int64 + c::Int64 + d::Int64)::Int64       
 5     e::Int64 = (b::Int64 + c::Int64 + d::Int64)::Int64        
 6     if (e::Int64 >= 0)::Bool
 7         x₂ = (b::Int64 + c::Int64 + d::Int64)::Int64
 8         x₃ = (b::Int64 + c::Int64 + d::Int64)::Int64
 9         with_logger(() -> @info("x123", x₁, x₂, x₃), glogger::Base.RefValue{ConsoleLogger}[]::ConsoleLogger)
10     end
11     return x₁::Int64, x₂::Core.Box, x₃::Core.Box::Any
12 end

If ‘e’ is negative, x2 and x3 are not defined… isn’t that causing problems, as type inference doesn’t use the actual values but only types?

2 Likes

That may not be the point, you can add

x₂ = b + c + d
x₃ = b + c + d

after if ... end block, but the problem remains.

If the captured variables are mutated outside the closure, that usually causes them to be boxed. One might expect that writing

else
    x₂ = b + c + d
    x₃ = b + c + d
end

would avoid the boxing, but that doesn’t seem to be the case. Perhaps initialization in a branch is always effectively mutation from the point of view of the compiler. However, the usual

let x₂=x₂, x₃=x₃
    with_logger(() -> @info("x123", x₁, x₂, x₃), glogger[])
end

fixes it.

Thank you, your solution works well.