I’ve stumbled recently on this behavior (julia 1.3.1):
function test()
s = "hello"
s = lowercase(s)
x() = s
x()
end
and @code_warntype test()
yields
Variables
#self#::Core.Compiler.Const(test, false)
s@_2::Core.Box
x::var"#x#65"
s@_4::Union{}
Body::Any
1 ─ (s@_2 = Core.Box())
│ Core.NewvarNode(:(x))
│ Core.setfield!(s@_2, :contents, "hello")
│ %4 = Core.isdefined(s@_2, :contents)::Bool
└── goto #3 if not %4
2 ─ goto #4
3 ─ Core.NewvarNode(:(s@_4))
└── s@_4
4 ┄ %9 = Core.getfield(s@_2, :contents)::Any
│ %10 = Main.lowercase(%9)::Union{AbstractChar, String}
│ Core.setfield!(s@_2, :contents, %10)
│ (x = %new(Main.:(var"#x#65"), s@_2))
│ %13 = (x)()::Any
└── return %13
It seems that offending line is s = lowercase(s)
, because this versions are type stable
function test2()
s = "hello"
s2 = lowercase(s)
x() = s2
x()
end
function test3()
s = "hello"
s = lowercase(s)
x(z) = z
x(s)
end
function test4()
s = "hello"
x() = s
x()
end
Can anyone explain why is it happening? And is there any rule of thumb how to proceed in situations like this? May be something like “do not reassign variables”, “if variable is reassigned, specify it as an argument to closure”.
It’s not a problem to patch my real world function with anything like test2
, test3
approach, but it’s more interesting to understand what is going on.