Type instability of empty `Array` with elemental types parameterized by `UnionAll`

MWE:

julia> v1 = Tuple{AbstractVector{Float64}, Int64}[]
Tuple{AbstractVector{Float64}, Int64}[]

julia> first.(v1)
AbstractVector{Float64}[]

julia> v2 = Tuple{AbstractArray{Float64}, Int64}[]
Tuple{AbstractArray{Float64}, Int64}[]

julia> first.(v2)
Any[]

Why can’t first.(v2) return AbstractArray{Float64}[]?

2 Likes

Not sure on the deeper “why”. To expand on your MWE:

julia> first.(Tuple{AbstractArray{Float64}, Int64}[])
Any[]

julia> first.(Tuple{AbstractArray{Float64}, Int64}[(zeros(1), 1)])
1-element Vector{Vector{Float64}}:
 [0.0]

julia> first.(Tuple{AbstractArray{Float64}, Int64}[(zeros(1), 1), (zeros(2,2), 2)])
2-element Vector{Array{Float64}}:
 [0.0]
 [0.0 0.0; 0.0 0.0]

The broadcasting is evidently type unstable, and @code_warntype corroborates this by inferring the output as Vector.

Thing is, it’s almost doable. Base.Broadcast.combine_eltypes(first, (v2,)) is how “empty and concretely inferred cases” determine the output array’s element type, and while it returns Any as expected, it actually did get to AbstractArray{Float64} before Base.promote_typejoin_union(AbstractArray{Float64}) makes Any. But because “ElType is not concrete, [the output array’s element type will] use narrowing” in Base.Broadcast.copy, and empty arrays don’t give much to narrow to. I don’t know the reason for this, as people often seem to expect the element type to match the inferred return type of the elementwise operation regardless of how many elements. But it’s customary to do the type “inference” ourselves with preallocation of a manually typed array and in-place broadcasting.

1 Like