Why is this type check so slow

Shouldn’t type checks for statically known types be free at runtime?

julia> const IntUInt = Union{Int, UInt}
Union{Int64, UInt64}

julia> struct T{U <: Integer}
           x::U
       end

julia> @btime 1 isa T{<:IntUInt}
  151.025 ns (3 allocations: 128 bytes)
false

julia> f() = 1 isa T{<:IntUInt}
f (generic function with 1 method)

julia> @btime f()
  140.869 ns (3 allocations: 128 bytes)
false

julia> @btime 1 isa Int
  1.433 ns (0 allocations: 0 bytes)
true

julia> @btime 1 isa Union{Integer, AbstractFloat}
  1.722 ns (0 allocations: 0 bytes)
true

julia> @btime 1 isa T
  1.843 ns (0 allocations: 0 bytes)
false

Yes, but the elision only works if the compiler knows what RHS is. In this case, it does not:

CodeInfo(
1 ─ %1 = Core.TypeVar(Symbol("#s1"), Main.IntUInt)::Core.Compiler.PartialTypeVar(var"#s1"<:Union{Int64, UInt64}, true, true)
│        (@_2 = %1)::Core.Compiler.PartialTypeVar(var"#s1"<:Union{Int64, UInt64}, true, true)
│   %3 = @_2::Core.Compiler.PartialTypeVar(var"#s1"<:Union{Int64, UInt64}, true, true)
│   %4 = Core.apply_type(Main.T, @_2::Core.Compiler.PartialTypeVar(var"#s1"<:Union{Int64, UInt64}, true, true))::Any
│   %5 = Core.UnionAll(%3, %4)::Any
│   %6 = (1 isa %5)::Bool
└──      return %6
) => Bool

If you’re feeling adventurous, you could take a look at apply_type_tfunc to see if this case can be made to work: