Code_warntype errors on a function

I am wondering if the following is a bug, or something I am missing. MWE:

struct Foo{T}
    a::T
    b::T
end

function struct2nt(x::T) where T
    @assert isstructtype(T)
    N = fieldnames(T)
    NamedTuple{N}(getfield(x, n) for n in N)
end

struct2nt(Foo(2, 3)) # works fine

@code_warntype struct2nt(Foo(2, 3)) # errors

On 1.11 and 1.12rc, errors with

julia> @code_warntype struct2nt(Foo(2, 3))
MethodInstance for struct2nt(::Foo{Int64})
  from struct2nt(x::T) where T @ Main REPL[1]:1
Static Parameters
  T = Foo{Int64}
Arguments
  #self#::Core.Const(Main.struct2nt)
  x::Foo{Int64}
Locals
  #3::var"#3#4"{Foo{Int64}}
  N::Tuple{Symbol, Symbol}
BodyERROR: BoundsError: attempt to access Core.SimpleVector at index [2]
Stacktrace:
  [1] getindex
    @ ./essentials.jl:932 [inlined]
  [2] show_at_namedtuple(io::IOContext{IOBuffer}, syms::Tuple{Symbol, Symbol}, types::DataType)
    @ Base ./show.jl:1191
  [3] show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar})
    @ Base ./show.jl:1167
  [4] _show_type(io::IOBuffer, x::Type)
    @ Base ./show.jl:1007
  [5] show(io::IOBuffer, x::Type)
    @ Base ./show.jl:965
  [6] print(io::IOBuffer, x::Type)
    @ Base ./strings/io.jl:35
  [7] print_to_string(::String, ::Vararg{Any})
    @ Base ./strings/io.jl:148
  [8] string
    @ ./strings/io.jl:189 [inlined]
  [9] warntype_type_printer(io::Base.TTY; type::Any, used::Bool, show_type::Bool, ::@Kwargs{})
    @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.x64.linux.gnu/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:32
 [10] code_warntype(io::Base.TTY, f::Any, t::Any; debuginfo::Symbol, optimize::Bool, kwargs::@Kwargs{})
    @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.x64.linux.gnu/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:151
 [11] code_warntype(io::Base.TTY, f::Any, t::Any)
    @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.x64.linux.gnu/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:77
 [12] code_warntype(::Any, ::Vararg{Any}; kwargs...)
    @ InteractiveUtils ~/.julia/juliaup/julia-1.11.6+0.x64.linux.gnu/share/julia/stdlib/v1.11/InteractiveUtils/src/codeview.jl:159
 [13] top-level scope
    @ REPL[5]:1
1 Like

I’d expect it to work, and based on it managing to print Body, it’s going wrong at the inferred return type here:

Thing is I haven’t been able to find the exact call that goes wrong. The assumption is:

julia> InteractiveUtils.warntype_type_printer(stdout;type=typeof(struct2nt(Foo(2,3))), used=true)
::@NamedTuple{a::Int64, b::Int64}

which works fine. I tried Debugger.jl on @code_warntype and the expanded call, but trying to advance by 1 step on much earlier calls apparently jumps to executing the code_warntype call, which of course errors.

1 Like

I’ve found out the following: The function

function Base.show_at_namedtuple(io::IO, syms::Tuple, types::DataType)
    first = true
    for i in eachindex(syms)
        if !first
            print(io, ", ")
        end
        print(io, syms[i])
        typ = types.parameters[i]  # here the error occurs
        if typ !== Any
            print(io, "::")
            show(io, typ)
        end
        first = false
    end
end

is called with types set to NTuple{N, Int}. I guess this is a fragment of some NTuple{N, Int} where N.

2 Likes

I tried the devious

global X = nothing

function Base.show(io::IO, @nospecialize(x::Type))
    try
        Base._show_type(io, Base.inferencebarrier(x))
    catch e
        global X = x
        rethrow(e)
    end
end

and indeed there seems to be something missing (X is unprintable):

julia> X.name
typename(NamedTuple)

julia> X.parameters
svec((:a, :b), NTuple{N, Int64})
1 Like

Still strange because that SimpleVector DOES have index [2]:

julia> Core.svec((:a, :b), (NTuple{N, Int64} where N).body)[2]
NTuple{N, Int64}

It goes wrong in the 2nd iteration’s type:

julia> Base.show_at_namedtuple(stdout, (:a,:b), (NTuple{N, Int64} where N).body)
a::Vararg{Int64, N}, bERROR: BoundsError: attempt to access Core.SimpleVector at index [2]
Stacktrace:
 [1] getindex
   @ .\essentials.jl:932 [inlined]
 [2] show_at_namedtuple(io::Base.TTY, syms::Tuple{Symbol, Symbol}, types::DataType)
   @ Base .\show.jl:1191
1 Like

It appears to work fine in 1.6, so I guess we should be able to bisect it. (I don’t have time right now, so if anyone starts doing it, please let me know so I don’t duplicate the effort)

I found an existing issue already:

1 Like