I have encountered a type instability when defining broadcasting for an abstract type. Consider the following (silly) example:
import Base.Broadcast: Broadcasted, BroadcastStyle, broadcastable, broadcasted, instantiate
import Base: axes, copy
abstract type T end
struct S <: BroadcastStyle end
BroadcastStyle(::Type{<:T}) = S()
broadcastable(x::T) = x
Base.axes(::T) = nothing
instantiate(bc::Broadcasted{S}) = bc
copy(bc::Broadcasted{S, Nothing, typeof(+)}) = length(bc.args)
f(v::Vector{T}) = v[1] .+ v[2]
struct A <: T end
v = T[A(),A()]
Then I get
julia> f(v)
2
julia> @code_typed f(v)
CodeInfo(
1 ─ %1 = Base.arrayref(true, v, 1)::T
│ %2 = Base.arrayref(true, v, 2)::T
│ %3 = Base.broadcasted(Main.:+, %1, %2)::Broadcasted{Style, Nothing, typeof(+)} where Style<:Union{Nothing, BroadcastStyle}
│ %4 = Base.Broadcast.instantiate(%3)::Any
│ %5 = Base.Broadcast.copy(%4)::Any
└── return %5
) => Any
To me, the problem seems to be that Julia thinks there are two relevant methods for BroadcastStyle
:
julia> methods(BroadcastStyle, (Type{<:T},))
# 2 methods for type constructor:
[1] BroadcastStyle(::Type{Union{}}, slurp...)
@ broadcast.jl:38
[2] BroadcastStyle(::Type{<:T})
@ Main REPL[5]:1
julia> code_typed(BroadcastStyle, (Type{<:T},))
2-element Vector{Any}:
CodeInfo(
1 ─ return $(QuoteNode(Base.Broadcast.Unknown()))
) => Base.Broadcast.Unknown
CodeInfo(
1 ─ return $(QuoteNode(S()))
) => S
However, the first method cannot apply to any concrete subtype of T
. I wish Julia would ignore it when determining the return type of f
. Is this a bug, or am I missing something here?