Description
Somewhat similar to #25632 but more general and maybe more relavant as well.
julia> isType(@nospecialize t) = isa(t, DataType) && t.name === Core.Compiler._TYPE_NAME
isType (generic function with 1 method)
julia> function f(r)
t = r[]
if isType(t)
return t.parameters[1]
end
end
f (generic function with 1 method)
julia> @code_typed f(Ref{Any}())
CodeInfo(
1 ─ %1 = Base.getfield(r, :x)::Any
│ %2 = (%1 isa Main.DataType)::Bool
└── goto #3 if not %2
2 ─ %4 = π (%1, DataType)
│ %5 = Base.getfield(%4, :name)::Core.TypeName
│ %6 = Core.Compiler::Module
│ %7 = Base.getfield(%6, :_TYPE_NAME)::Core.TypeName
│ %8 = (%5 === %7)::Bool
└── goto #4
3 ─ goto #4
4 ┄ %11 = φ (#2 => %8, #3 => false)::Bool
└── goto #6 if not %11
5 ─ %13 = Base.getproperty(%1, :parameters)::Any
│ %14 = Base.getindex(%13, 1)::Any
└── return %14
6 ─ return nothing
) => Any
Note that the access of the parameters
field and subsequent calls are not inferred. If isType
is manually inlined then the function works just fine. (The @nospecialize
is due to copy-and-paste from type inference and does not have any effect on the issue.)
Noticed this when testing #37303 a while ago. Dynamic dispatch like this happens everywhere in inference/optimizer code (including most callers of typeutils.jl
functions) and I've verified from LLVM IR dump that this is actually happening in the final code. I don't know how much performance issue it costs but could be significant... (note that it won't be as bad as the code above since Core.Compiler.getproperty === getfield
). Adding type assertions back in to inference code could be a temporary solution but fixing the inference itself would be better...