Type stability for returning abstract types

performance
type

#1

I’m trying to define the return status of a function as an empty struct which type depends on the symbol passed to the function:

abstract type ABS end
struct A <: ABS end
struct B <: ABS end
struct C <: ABS end

f(::Type{Val{:AA}}) = A()
f(::Type{Val{:BB}}) = B()
f(s::Symbol) = f(Val{s})
f(o) = C()

This yields:

julia> f(:AA)
A()
julia> @code_warntype f(:AA)
Variables:
  #self# <optimized out>
  s::Symbol

Body:
  begin 
      return (Main.f)((Core.apply_type)(Main.Val, s::Symbol)::Type{Val{_}} where _)::Union{A, B, C}
  end::Union{A, B, C}
  1. Could the compiler infer the return type as being ABS instead of the Union
  2. If the return typed inferred is abstract (like ABS), is there a performance cost?

#2
  1. Inferring an abstract type instead of a union would be worse because you can always add more subtypes. With a union there’s a limited number of options that can be explicitly handled. This happens on 0.7: efficient code for each option in the union (for small unions) is generated and a single branch decides which is run based upon the value.

  2. So yes, there is a higher performance cost to an abstract type than a small union since more abstract types can always be added and Julia can’t enumerate all options up front.

But on Julia 0.7, constant propagation to inlined functions completely removes this problem — and you don’t need to use Val wrappers at all. But I just copied your code verbatim and this works:

julia> g() = f(:AA)
g (generic function with 1 method)

julia> @code_warntype g()
Variables:

Body:
  begin
      return $(QuoteNode(A()))
  end::A

julia> h() = f(:BB)
h (generic function with 1 method)

julia> @code_warntype h()
Variables:

Body:
  begin
      return $(QuoteNode(B()))
  end::B

How to force mixed-type array of just ints and floats?