Skip to content

JET errors with tuple comparison #60470

@penelopeysm

Description

@penelopeysm
using JET
x = [(1,), ()]  # 2-element Vector{Tuple{Vararg{Int64}}}
@report_call any(k -> k == (), x)

yields:

═════ 1 possible error found ═════
┌ any(f::var"#19#20", a::Vector{Tuple{Vararg{Int64}}}) @ Base ./reducedim.jl:992
│┌ any(f::var"#19#20", a::Vector{Tuple{Vararg{Int64}}}; dims::Colon) @ Base ./reducedim.jl:992
││┌ _any(f::var"#19#20", itr::Vector{Tuple{Vararg{Int64}}}, ::Colon) @ Base ./reduce.jl:1237
│││┌ (::var"#19#20")(k::Tuple{Vararg{Int64}}) @ Main ./REPL[17]:1
││││┌ ==(t1::Tuple{Vararg{Int64}}, t2::Tuple{}) @ Base ./tuple.jl:547
│││││┌ _eq(t1::Tuple{Vararg{Int64}}, t2::Tuple{}) @ Base ./tuple.jl:551
││││││┌ getindex(t::Tuple{}, i::Int64) @ Base ./tuple.jl:31
│││││││ invalid builtin function call: Base.getfield(t::Tuple{}, i::Int64, $(Expr(:boundscheck)))
││││││└────────────────────

The relevant code is here (line numbers are a bit different because I used 1.11, but the code hasn't changed since):

julia/base/tuple.jl

Lines 544 to 556 in 966d0af

==(t1::Tuple, t2::Tuple) = (length(t1) == length(t2)) && _eq(t1, t2)
_eq(t1::Tuple{}, t2::Tuple{}) = true
_eq_missing(t1::Tuple{}, t2::Tuple{}) = missing
function _eq(t1::Tuple, t2::Tuple)
eq = t1[1] == t2[1]
if eq === false
return false
elseif ismissing(eq)
return _eq_missing(tail(t1), tail(t2))
else
return _eq(tail(t1), tail(t2))
end
end

One could argue that this is dynamic dispatch (and indeed JET.@report_opt already warns us about this); but this issue seems to be specific to tuple comparison, there aren't any errors if you do any(k -> k == 1.0, x). Of course the arguments to _eq can't be nonempty (if they're both empty the method on L545 would catch it, and if only one is empty the length check in == would catch it); but I suppose because the compiler can't figure that out?

This looks very similar to #58214 #58220 but just for the tuple case.

Because NamedTuple equality defers to Tuple equality, the same problem occurs with NamedTuples:

==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b)

x = [(a = 1,), (b = 2, c = 3)]  # 2-element Vector{NamedTuple}
@report_call any(k -> k == (a = 1,), x)
#=
┌ any(f::var"#13#14", a::Vector{NamedTuple}) @ Base ./reducedim.jl:992
│┌ any(f::var"#13#14", a::Vector{NamedTuple}; dims::Colon) @ Base ./reducedim.jl:992
││┌ _any(f::var"#13#14", itr::Vector{NamedTuple}, ::Colon) @ Base ./reduce.jl:1237
│││┌ (::var"#13#14")(k::NamedTuple) @ Main ./REPL[14]:1
││││┌ ==(a::NamedTuple{(:a,)}, b::@NamedTuple{a::Int64}) @ Base ./namedtuple.jl:244
│││││┌ ==(t1::Tuple, t2::Tuple{Int64}) @ Base ./tuple.jl:547
││││││┌ _eq(t1::Tuple, t2::Tuple{Int64}) @ Base ./tuple.jl:555
│││││││┌ _eq_missing(t1::Tuple, t2::Tuple{}) @ Base ./tuple.jl:561
││││││││┌ getindex(t::Tuple{}, i::Int64) @ Base ./tuple.jl:31
│││││││││ invalid builtin function call: Base.getfield(t::Tuple{}, i::Int64, $(Expr(:boundscheck)))
││││││││└────────────────────
=#

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions