Confusing type inference from chained assignment


#1

I ran into this weird behavior from code_warntype today. It claims not to know the type of x, but it generates code as if it does.

function f()
  d = Dict{Int64, Vector{Int64}}()
  x = d[1] = Vector{Int64}()
  push!(x, 1)
end
Variables:
  #self#::#f
  d::Dict{Int64,Array{Int64,1}}
  x::ANY
  n::Int64
  itemT::Int64

Body:
  begin 
      $(Expr(:inbounds, false))
      # meta: location dict.jl Type 104
      SSAValue(6) = $(Expr(:invoke, MethodInstance for fill!(::Array{UInt8,1}, ::UInt8), :(Base.fill!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{UInt8,1}, svec(Any, Int64), Array{UInt8,1}, 0, 16, 0))), :((Base.checked_trunc_uint)(UInt8, 0)::UInt8)))
      SSAValue(4) = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Int64,1}, svec(Any, Int64), Array{Int64,1}, 0, 16, 0))
      SSAValue(2) = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Array{Int64,1},1}, svec(Any, Int64), Array{Array{Int64,1},1}, 0, 16, 0))
      # meta: pop location
      $(Expr(:inbounds, :pop))
      d::Dict{Int64,Array{Int64,1}} = $(Expr(:new, Dict{Int64,Array{Int64,1}}, SSAValue(6), SSAValue(4), SSAValue(2), 0, 0, :((Base.bitcast)(UInt64, (Base.check_top_bit)(0)::Int64)), 1, 0)) # line 24:
      SSAValue(0) = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Int64,1}, svec(Any, Int64), Array{Int64,1}, 0, 0, 0))
      $(Expr(:invoke, MethodInstance for setindex!(::Dict{Int64,Array{Int64,1}}, ::Array{Int64,1}, ::Int64), :(Main.setindex!), :(d), SSAValue(0), 1)) # line 25:
      $(Expr(:inbounds, false))
      # meta: location array.jl push! 618
      SSAValue(8) = (Base.bitcast)(UInt64, (Base.check_top_bit)(1)::Int64)
      $(Expr(:foreigncall, :(:jl_array_grow_end), Void, svec(Any, UInt64), SSAValue(0), 0, SSAValue(8), 0)) # line 619:
      # meta: location abstractarray.jl endof 134
      # meta: location abstractarray.jl linearindices 99
      # meta: location abstractarray.jl indices1 71
      # meta: location abstractarray.jl indices 64
      SSAValue(11) = (Base.arraysize)(SSAValue(0), 1)::Int64
      # meta: pop location
      # meta: pop location
      # meta: pop location
      # meta: pop location
      (Base.arrayset)(SSAValue(0), 1, (Base.select_value)((Base.slt_int)(SSAValue(11), 0)::Bool, 0, SSAValue(11))::Int64)::Array{Int64,1}
      # meta: pop location
      $(Expr(:inbounds, :pop))
      return SSAValue(0)
  end::Array{Int64,1}

#2

This seems to be a common result when Julia decides to eliminate a variable entirely. Note that x never appears in the code body; the compiler has figured out that there’s no need for it. It’s unfortunate that it shows up as x::Any in the variable list, since that’s likely to confuse users. Perhaps @code_warntype could be a bit smarter about this?


#3

https://github.com/JuliaLang/julia/issues/21281 and referenced issue.


#4

Thanks :slight_smile: