The behavior of @code_warntype
makes a bit more sense if you look at what it’s actually doing:
julia> @macroexpand @code_warntype t[1]
:(InteractiveUtils.code_warntype(getindex, (Base.typesof)(t, 1)))
julia> Base.typesof(t, 1)
Tuple{Tuple{Int64, Float64}, Int64}
So @code_warntype
is just calling the regular function code_warntype
with the types of t
and 1
, which are Tuple{Int64, Float64}
and Int64
, respectively.
That means code_warntype
has only access to the type of 1
, not the constant value. There’s no way it can possibly return anything more specific than a Union
because, just from that information, there’s no way to know if you asked for t[1]
or t[2]
.
Once you’ve hard-coded the index 1 in a function, though, the compiler can be even smarter and do some constant propagation to figure out the result type. When you do: @code_warntype foo(t)
, there actually is enough information from just the type of t
to infer the result of foo
because of the literal t[1]
in the definition of foo
.
Being in a function doesn’t really matter–we can show that even in a function @code_warntype
’s behavior doesn’t change at all:
julia> function bar(t)
@code_warntype t[1]
t[1]
end
bar (generic function with 1 method)
julia> bar(t)
Variables
#self#::Core.Const(getindex)
t::Tuple{Int64, Float64}
i::Int64
Body::Union{Float64, Int64}
There’s no difference because @code_warntype
is just calling code_warntype(getindex, typesof(t, 1))
just as before.