Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,12 @@ julia> Base.tail(())
ERROR: ArgumentError: Cannot call tail on an empty tuple.
```
"""
tail(x::Tuple) = argtail(x...)
tail(::Tuple{}) = throw(ArgumentError("Cannot call tail on an empty tuple."))
function tail(x::Tuple)
if x === ()
throw(ArgumentError("Cannot call tail on an empty tuple."))
end
argtail(x...)
end

function unwrap_unionall(@nospecialize(a))
@_foldable_meta
Expand Down
7 changes: 0 additions & 7 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,6 @@ isless(x::AbstractFloat, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x
isless(x::Real, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y)
isless(x::AbstractFloat, y::Real ) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y)

# Performance optimization to reduce branching
# This is useful for sorting tuples of integers
# TODO: remove this when the compiler can optimize the generic version better
# See #48724 and #48753
isless(a::Tuple{BitInteger, BitInteger}, b::Tuple{BitInteger, BitInteger}) =
isless(a[1], b[1]) | (isequal(a[1], b[1]) & isless(a[2], b[2]))

"""
isgreater(x, y)

Expand Down
138 changes: 91 additions & 47 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,12 @@ end

@eval split_rest(t::Tuple, n::Int, i=1) = ($(Expr(:meta, :aggressive_constprop)); _split_tuple(t, length(t)-n, Int(i)))

# Use dispatch to avoid a branch in first
first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty"))
first(t::Tuple) = t[1]
function first(t::Tuple)
if t === ()
throw(ArgumentError("tuple must be non-empty"))
end
t[1]
end

# eltype

Expand Down Expand Up @@ -575,71 +578,104 @@ function _eq(t1::Any32, t2::Any32)
end

const tuplehash_seed = UInt === UInt64 ? 0x77cfa1eef01bca90 : 0xf01bca90
hash(::Tuple{}, h::UInt) = h ⊻ tuplehash_seed
hash(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h))
function hash(t::Any32, h::UInt)
out = h ⊻ tuplehash_seed
for i = length(t):-1:1
out = hash(t[i], out)
function hash(t::Tuple, h::UInt)
function f(t::Tuple, h::UInt)
if t === ()
h ⊻ tuplehash_seed
else
hash(t[1], hash(tail(t), h))
end
end
return out
function f(t::Any32, h::UInt)
out = h ⊻ tuplehash_seed
for i = length(t):-1:1
out = hash(t[i], out)
end
return out
end
f(t, h)
end

<(::Tuple{}, ::Tuple{}) = false
<(::Tuple{}, ::Tuple) = true
<(::Tuple, ::Tuple{}) = false
function <(t1::Tuple, t2::Tuple)
a, b = t1[1], t2[1]
eq = (a == b)
if ismissing(eq)
return missing
elseif !eq
return a < b
end
return tail(t1) < tail(t2)
end
function <(t1::Any32, t2::Any32)
n1, n2 = length(t1), length(t2)
for i = 1:min(n1, n2)
a, b = t1[i], t2[i]
function f(t1::Tuple, t2::Tuple)
if t2 === ()
return false
end
if t1 === ()
return true
end
a, b = t1[1], t2[1]
eq = (a == b)
if ismissing(eq)
return missing
elseif !eq
return a < b
return a < b
end
return tail(t1) < tail(t2)
end
return n1 < n2
function f(t1::Any32, t2::Any32)
n1, n2 = length(t1), length(t2)
for i = 1:min(n1, n2)
a, b = t1[i], t2[i]
eq = (a == b)
if ismissing(eq)
return missing
elseif !eq
return a < b
end
end
return n1 < n2
end
f(t1, t2)
end

isless(::Tuple{}, ::Tuple{}) = false
isless(::Tuple{}, ::Tuple) = true
isless(::Tuple, ::Tuple{}) = false
# copy of `BitInteger` defined later during bootstrap in int.jl
const _BitInteger = Union{
Int8, Int16, Int32, Int64, Int128,
UInt8, UInt16, UInt32, UInt64, UInt128,
}

"""
isless(t1::Tuple, t2::Tuple)

Return `true` when `t1` is less than `t2` in lexicographic order.
"""
function isless(t1::Tuple, t2::Tuple)
a, b = t1[1], t2[1]
isless(a, b) || (isequal(a, b) && isless(tail(t1), tail(t2)))
end
function isless(t1::Any32, t2::Any32)
n1, n2 = length(t1), length(t2)
for i = 1:min(n1, n2)
a, b = t1[i], t2[i]
if !isequal(a, b)
return isless(a, b)
function f(t1::Tuple, t2::Tuple)
if t2 === ()
return false
end
if t1 === ()
return true
end
a, b = t1[1], t2[1]
isless(a, b) || (isequal(a, b) && isless(tail(t1), tail(t2)))
end
function f(t1::Any32, t2::Any32)
n1, n2 = length(t1), length(t2)
for i = 1:min(n1, n2)
a, b = t1[i], t2[i]
if !isequal(a, b)
return isless(a, b)
end
end
return n1 < n2
end
return n1 < n2
# Performance optimization to reduce branching
# This is useful for sorting tuples of integers
# TODO: remove this when the compiler can optimize the generic version better
# See #48724 and #48753
function f(a::Tuple{_BitInteger, _BitInteger}, b::Tuple{_BitInteger, _BitInteger})
isless(a[1], b[1]) | (isequal(a[1], b[1]) & isless(a[2], b[2]))
end
f(t1, t2)
end

## functions ##

isempty(x::Tuple{}) = true
isempty(@nospecialize x::Tuple) = false
function isempty(x::Tuple)
x === ()
end

revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
Expand Down Expand Up @@ -677,11 +713,19 @@ empty(@nospecialize x::Tuple) = ()
foreach(f, itr::Tuple) = foldl((_, x) -> (f(x); nothing), itr, init=nothing)
foreach(f, itr::Tuple, itrs::Tuple...) = foldl((_, xs) -> (f(xs...); nothing), zip(itr, itrs...), init=nothing)

circshift((@nospecialize t::Union{Tuple{},Tuple{Any}}), @nospecialize _::Integer) = t
circshift(t::Tuple{Any,Any}, shift::Integer) = iseven(shift) ? t : reverse(t)
function circshift(x::Tuple{Any,Any,Any,Vararg{Any,N}}, shift::Integer) where {N}
function circshift(x::Tuple, shift::Integer)
@inline
len = N + 3
if (x === ()) || (x isa Tuple{Any})
return x
end
if x isa Tuple{Any,Any}
return if iseven(shift)
x
else
reverse(x)
end
end
len = length(x)
j = mod1(shift, len)
ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple
end
12 changes: 12 additions & 0 deletions test/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,18 @@ end
@test Val{Tuple{Int64, Vararg{Int32,N}} where N} === Val{Tuple{Int64, Vararg{Int32}}}
@test Val{Tuple{Int32, Vararg{Int64}}} === Val{Tuple{Int32, Vararg{Int64,N}} where N}

@testset "avoid method proliferation" begin
t = isone ∘ length ∘ methods
@test t(circshift, Tuple{Tuple, Integer})
@test t(hash, Tuple{Tuple, UInt})
for f in (Base.tail, first, isempty)
@test t(f, Tuple{Tuple})
end
for f in (<, isless, ==, isequal)
@test t(f, Tuple{Tuple, Tuple})
end
end

@testset "from Pair, issue #52636" begin
pair = (1 => "2")
@test (1, "2") == @inferred Tuple(pair)
Expand Down