Type instability with enumerate in comprehension

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?

2 Likes

Hi @UselessAnsatz,

[my first post ever! sorry if I missed any guidelines!]

I am very new to Julia. Finding it great so far. I also stumbled some type instabilities when using enumerate with custom structs. Can’t report anything yet as I am experimenting to see if I “discover” the reasons. I suspect that my struct might have an abstract type somewhere.

In your first example, what is your foo variable? I ask because it seems that it may be a variable defined in the global scope. Do you get the instability if you wrap your code in a function? Could this be related to global scope benchmarking?