I stumbled upon a very confusing form of type instability recently involving enumerate in a comprehension. I’ve found a way around it, but I have no idea what’s causing it.
According to @code_warntype, the following code is unstable
julia> @code_warntype sum(baz for foo in 1:3 for (i,baz) in enumerate(1:foo))
Variables
#self#::Core.Const(sum)
a::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64}, var"#849#850"}}
Body::Any
1 ─ %1 = Core.NamedTuple()::Core.Const(NamedTuple())
│ %2 = Base.pairs(%1)::Core.Const(Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}())
│ %3 = Base.:(var"#sum#222")(%2, #self#, a)::Any
└── return %3
But this code is stable without the second loop enumeration
julia> @code_warntype sum(baz for foo in 1:3 for baz in 1:foo)
Variables
#self#::Core.Const(sum)
a::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64}, var"#916#917"}}
Body::Int64
1 ─ %1 = Core.NamedTuple()::Core.Const(NamedTuple())
│ %2 = Base.pairs(%1)::Core.Const(Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}())
│ %3 = Base.:(var"#sum#222")(%2, #self#, a)::Int64
└── return %3
And it is also stable if the argument of the enumeration isn’t used in the generator, even if the first comprehension loop is enumerated
julia> @code_warntype sum(foo+i for (i,foo) in enumerate(1:3) for (j,baz) in enumerate(1:foo))
Variables
#self#::Core.Const(sum)
a::Base.Iterators.Flatten{Base.Generator{Base.Iterators.Enumerate{UnitRange{Int64}}, var"#943#944"}}
Body::Int64
1 ─ %1 = Core.NamedTuple()::Core.Const(NamedTuple())
│ %2 = Base.pairs(%1)::Core.Const(Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}())
│ %3 = Base.:(var"#sum#222")(%2, #self#, a)::Int64
└── return %3
My Julia is version 1.6. I’ve looked at enumerate, but can’t seem to find any hint of instability there. What is causing this?