Typeof(f()) at compile time

typeof(f(x)) can be evaluated at compile-time, provided the return type of f(x) is inferrable. Why can’t we have a guarantee that this is always the case?

For example, I tried this:

julia> f(x) = typeof(sin(x))

julia> @code_llvm debuginfo=:none f(5)

define nonnull %jl_value_t addrspace(10)* @julia_f_12432(i64) {
top:
  %1 = sitofp i64 %0 to double
  %2 = call double @julia_sin_12236(double %1)
  ret %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 140650398042128 to %jl_value_t*) to %jl_value_t addrspace(10)*)
}

As you can see, the call to sin is not optimized away. Even though the type of sin(5) is correctly inferred at compile time:

julia> @code_warntype f(5)
Body::Type{Float64}
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│        invoke Base.Math.sin(%1::Float64)
└──      return Float64
1 Like

Because the two statement (typeof being statically known and sin call be removed) are unrelated. The code you show is a proof that the compiler knows the return type of sin. It’s making the call only because there can be sideeffect and it is right about that. I believe sin can actually throw so even if the compiler is much smarter it still won’t be able to remove the call.

P.s. please use code_warntype since you seem to be confusing yourself with the llvm code.

2 Likes

Ah right! sin(Inf) throws. However exp(x::Float64) never throws, and there is no side-effect (right?).
In this case the call to exp is not elided either. Why?

julia> f(x) = typeof(exp(x))
f (generic function with 1 method)

julia> @code_llvm debuginfo=:none f(5.0)

define nonnull %jl_value_t addrspace(10)* @julia_f_12336(double) {
top:
  %1 = call double @julia_exp_12332(double %0)
  ret %jl_value_t addrspace(10)* addrspacecast (%jl_value_t* inttoptr (i64 140549511198736 to %jl_value_t*) to %jl_value_t addrspace(10)*)
}

julia> @code_warntype f(5)
Body::Type{Float64}
1 ─ %1 = (Base.sitofp)(Float64, x)::Float64
│        invoke Base.Math.exp(%1::Float64)
└──      return Float64

First of all, you can still see that the call is in the typed last. You don’t need to look at the llvm it.

And that’s why I said in the sin case it is impossible with any compiler improvement. I don’t know if exp can throw but it’s just showing that the compiler can’t proof the side effect.

Thanks!