Skip to content

Inference loses precision with more information on types #52134

@ranocha

Description

@ranocha

I found this while looking into invalidations on Julia v1.10-rc1. The code

using SnoopCompileCore
invalidations = @snoopr begin using OrdinaryDiffEq end
using SnoopCompile
@show length(uinvalidated(invalidations))
trees = invalidation_trees(invalidations)

and investigating the last entry lead to

julia> trees[end]
inserting &(::Static.StaticInteger{X}, y::Union{Missing, Integer}) where X @ Static ~/.julia/packages/Static/dLrtk/src/Static.jl:489 invalidated:
   mt_backedges: 1: signature Tuple{typeof(&), Any, UInt8} triggered MethodInstance for ismutable(::Tuple{Vararg{T, _A}} where {T, _A}) (8 children)
                 2: signature Tuple{typeof(&), Any, UInt8} triggered MethodInstance for ismutable(::Function) (732 children)

julia> trees[end].mt_backedges[end].second |> ascend
Choose a call for analysis (q to quit):
 >   ismutable(::Function)
       pointer_from_objref(::Function)
         unsafe_convert(::Type{Ptr{T}}, ::Base.RefValue{T}) where T<:Function
           unsafe_convert(::Type{Ref{T}}, ::Base.RefValue{T}) where T<:Function
             utf8proc_decompose(::String, ::Int64, ::Ptr{Nothing}, ::Int64, ::Function)
               utf8proc_map(::String, ::Int64, ::Function)
                 utf8proc_map(::String, ::Int64)
                   normalize(::String, ::Symbol)
                     isfile_casesensitive(::String)
v                      active_project(::Bool)
ismutable(x) @ Base reflection.jl:517
┌ Warning: Some line information is missing, type-assignment may be incomplete
└ @ Cthulhu ~/.julia/packages/Cthulhu/Ndnbu/src/codeview.jl:144
517 ismutable(@nospecialize(x::Function))::Any = (@_total_meta; ((typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2)::Any == 0x2)::Any)
Select a call to descend into or ↩ to ascend. [q]uit. [b]ookmark.
Toggles: [w]arn, [h]ide type-stable statements, [t]ype annotations, [s]yntax highlight for Source/LLVM/Native, [j]ump to source always.
Show: [S]ource code, [A]ST, [T]yped code, [L]LVM IR, [N]ative code
Actions: [E]dit source code, [R]evise and redisplay
 • typeof(x::Function)::Type{<:Function}.name
   runtime typeof(x::Function)::Type{<:Function}.name::Any.flags
   runtime typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2
   runtime (typeof(x::Function)::Type{<:Function}.name::Any.flags::Any & 0x2)::Any == 0x2

An MWE reproducing the type inference problem is

julia> function foo(x)
           return typeof(first(x)).name.flags
       end
foo (generic function with 1 method)

julia> @code_warntype foo([1.0]) # fine
MethodInstance for foo(::Vector{Float64})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Float64}
Body::UInt8
1%1 = Main.first(x)::Float64%2 = Main.typeof(%1)::Core.Const(Float64)
│   %3 = Base.getproperty(%2, :name)::Core.Const(typename(Float64))
│   %4 = Base.getproperty(%3, :flags)::Core.Const(0x04)
└──      return %4


julia> @code_warntype foo(Any[1.0]) # fine
MethodInstance for foo(::Vector{Any})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Any}
Body::UInt8
1%1 = Main.first(x)::Any%2 = Main.typeof(%1)::DataType%3 = Base.getproperty(%2, :name)::Core.TypeName%4 = Base.getproperty(%3, :flags)::UInt8
└──      return %4


julia> @code_warntype foo(Real[1.0]) # bad
MethodInstance for foo(::Vector{Real})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Real}
Body::Any
1%1 = Main.first(x)::Real%2 = Main.typeof(%1)::Type{<:Real}%3 = Base.getproperty(%2, :name)::Any%4 = Base.getproperty(%3, :flags)::Any
└──      return %4

julia> @code_warntype foo(Function[identity])
MethodInstance for foo(::Vector{Function})
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Vector{Function}
Body::Any
1$(Expr(:meta, :noinline))
│        nothing%3 = Base.getindex(x, 1)::Function%4 = Main.typeof(%3)::Type{<:Function}%5 = Base.getproperty(%4, :name)::Any%6 = Base.getproperty(%5, :flags)::Any
└──      return %6

Here, type inference gets worse with more information such as Type{<:Function} instead of DataType.

  1. Is there a way to fix this underlying issue?
  2. If there is no easy way to fix it, can I assume that typeof(x).name::Core.TypeName holds for every x to fix at least the invalidations soon in the code below?

ismutable(@nospecialize(x)) = (@_total_meta; typeof(x).name.flags & 0x2 == 0x2)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions