Description
MWE:
mutable struct Wrapper
x::Union{Nothing,Int}
end
function findsomething(dict, inds)
default = Wrapper(nothing)
for i in inds
x = get(dict, i, default).x
if !isnothing(x)
return x
end
end
return nothing
end
for i in 1:100000
x = findsomething(Dict(1 => Wrapper(nothing)), [2])
if !isnothing(x)
@error "" x
break
end
end
Output:
$ julia +lts --startup-file=no bug.jl # 1.10.9
$ julia +release --startup-file=no bug.jl # 1.11.5
┌ Error:
│ x = 4494295104
└ @ Main ~/bug/bug.jl:19
$ julia +beta --startup-file=no bug.jl # 1.12-beta3
┌ Error:
│ x = 0
└ @ Main ~/bug/bug.jl:19
$ julia +nightly --startup-file=no bug.jl # 1.13-DEV.590
┌ Error:
│ x = 2
└ @ Main ~/bug/bug.jl:19
As shown, in Julia >=1.11, default
can get corrupted such that default.x
reads as a garbage Int
instead of nothing
, even though it was initialized to nothing
and never mutated.
This does not happen if Wrapper
is not mutable and/or default
is a global const instead of a local.
Xref https://discourse.julialang.org/t/unmutated-mutable-type-being-corrupted/129161
Platform info (except Julia version as shown above):
ulia> versioninfo()
Julia Version 1.11.5
Commit 760b2e5b739 (2025-04-14 06:53 UTC)
Build Info:
Official https://julialang.org/ release
Platform Info:
OS: macOS (arm64-apple-darwin24.0.0)
CPU: 14 × Apple M4 Pro
WORD_SIZE: 64
LLVM: libLLVM-16.0.6 (ORCJIT, apple-m1)
Threads: 1 default, 0 interactive, 1 GC (on 10 virtual cores)