Are functions that operate only on types computed at compile time

Suppose I have the following function

get_first_type(::Type{T}) where T <: Tuple = begin 
    fieldtypes(T)[1]
end

If another function calls this function with one of its input type parameters, will this function be computed at compile time since all required information is known (or fail to compile if the tuple type is empty)?

Similarly if one is to use typeof() within a function on one of the function arguments is the call resolved at compile time since the compiler should know the types of all arguments already?

Generally yes. The compiler is allowed to give up and compute things at run time, but in your example, yes it should generally be done at compile time. You can check what code it is optimizing to by running test cases like

code_typed(get_first_type, Tuple{Type{Tuple{Float64, Int, String}}})

and making sure it’s optimizing everything away

3 Likes

If you want an example of something that could block this from occurring at compile time, any observable side effects will do:

julia> const counter = Ref{Int}(0)
Base.RefValue{Int64}(0)

julia> get_first_type_2(::Type{T}) where T <: Tuple = begin 
           counter[] += 1
           fieldtypes(T)[1]
       end
get_first_type (generic function with 1 method)

julia> code_typed(get_first_type_2, Tuple{Type{Tuple{Float64, Int, String}}})
1-element Vector{Any}:
 CodeInfo(
1 ─ %1 = Main.counter::Base.RefValue{Int64}
β”‚   %2 = Base.getfield(%1, :x)::Int64
β”‚   %3 = Base.add_int(%2, 1)::Int64
β”‚   %4 = Main.counter::Base.RefValue{Int64}
β”‚        Base.setfield!(%4, :x, %3)::Int64
β”‚   %6 = $(Expr(:static_parameter, 1))::Type{Tuple{Float64, Int64, String}}
β”‚   %7 = %new(Base.var"#13#14"{DataType}, %6)::Base.var"#13#14"{DataType}
β”‚   %8 = invoke Base.ntupleany(%7::Base.var"#13#14"{DataType}, 3::Int64)::Tuple
β”‚   %9 = Base.getfield(%8, 1, true)::Any
└──      return %9
) => Any
1 Like