Comprehension type inference

Could someone explain me why Julia has problem with type inference in the following function (the comprehension example is taken from Julia Documentation):

julia> f() = [(i,j) for i=1:3 for j=1:i]                                     
f (generic function with 1 method)                                           
                                                                             
julia> @code_warntype f()
Variables:
  #self#::#f
  #61::##61#63

Body:
  begin
      SSAValue(1) = $(Expr(:new, UnitRange{Int64}, 1, :((Base.select_value)((Base.sle_int)(1, 3)::Bool, 3, (Base.sub_int)(1, 1)::Int64)::Int64)))
      SSAValue(2) = $(Expr(:new, Base.Generator{UnitRange{Int64},##61#63}, :($(QuoteNode(#61))), SSAValue(1)))
      SSAValue(3) = $(Expr(:new, Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##61#63}}, SSAValue(2)))
      return $(Expr(:invoke, MethodInstance for grow_to!(::Array{Tuple{Int64,Int64},1}, ::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##61#63}}), :(Base.grow_to!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Tuple{Int64,Int64},1}, svec(Any, Int64), Array{Tuple{Int64,Int64},1}, 0, 0, 0))), SSAValue(3)))
  end::Union{Array{Tuple{Int64,Int64},1}, Array{Union{},1}}

In what cases can I expect such problems with type inference?
For example the return type of function g() = [(i,j) for i=1:3, j=1:3] is inferred properly.

1 Like

I think it’s a known bug, see performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub

I thought it was rather related with safeguarding against an empty collection. The problem is there even if the value in the comprehension is a literal:

julia> f() = [1 for i in 1:2 for j in 1:2]
f (generic function with 1 method)

julia> @code_warntype f()
Variables:
  #self#::#f
  #1::##1#3

Body:
  begin
      SSAValue(1) = $(Expr(:new, UnitRange{Int64}, 1, :((Base.select_value)((Base.sle_int)(1, 2)::Bool, 2, (Base.sub_int)(1, 1)::Int64)::Int64)))
      SSAValue(2) = $(Expr(:new, Base.Generator{UnitRange{Int64},##1#3}, :($(QuoteNode(#1))), SSAValue(1)))
      SSAValue(3) = $(Expr(:new, Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##1#3}}, SSAValue(2)))
      return $(Expr(:invoke, MethodInstance for grow_to!(::Array{Int64,1}, ::Base.Iterators.Flatten{Base.Generator{UnitRange{Int64},##1#3}}), :(Base.grow_to!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Int64,1}, svec(Any, Int64), Array{Int64,1}, 0, 0, 0))), SSAValue(3)))
  end::Union{Array{Int64,1}, Array{Union{},1}}