Open
Description
Typically, we box any captured variable that the frontend thinks might get rebound and affect the result of a closure, but here is a funny corner case where it fails:
julia> (function (a=1, count=0)
@label start
a = a + 1 # lexical rebinding of a before the definition of f
if count == 0
f() = a # define f only once, capturing a
end
count += 1
if count <= 5
@goto start # basically a while loop
end
f.a, a # compare the captured version of a against the local variable a
end)()
(2, 7)
interestingly, this only occurs in functions that produce closures, not let
blocks:
julia> let a=1, count=0
local f
while count <= 5
a = a + 1 # lexical rebinding of a before the definition of f
if count == 0
f() = a # define f only once, capturing a
end
count += 1
end
f.a, a # compare the captured version of a against the local variable a
end
(Core.Box(7), 7)
This behaviour seems to have been introduced in version 1.6:
❯ julia +1.5 --startup=no -q
julia> (function (a=1, count=0)
local f
while count <= 5
a = a + 1 # lexical rebinding of a before the definition of f
if count == 0
f() = a # define f only once, capturing a
end
count += 1
end
f.a, a # compare the captured version of a against the local variable a
end)()
(Core.Box(7), 7)
❯ julia +1.6 --startup=no -q
julia> (function (a=1, count=0)
local f
while count <= 5
a = a + 1 # lexical rebinding of a before the definition of f
if count == 0
f() = a # define f only once, capturing a
end
count += 1
end
f.a, a # compare the captured version of a against the local variable a
end)()
(2, 7)
cc @c42f this might be relevant for the rewrite of lowering.