Skip to content
Draft
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
2 changes: 2 additions & 0 deletions base/Base_compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ include("anyall.jl")
include("ordering.jl")
using .Order

include("interface_callables_base.jl")

include("coreir.jl")
include("module.jl")

Expand Down
4 changes: 0 additions & 4 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -934,10 +934,6 @@ end

## from general iterable to any array

# This is `Experimental.@max_methods 1 function copyto! end`, which is not
# defined at this point in bootstrap.
typeof(function copyto! end).name.max_methods = UInt8(1)

function copyto!(dest::AbstractArray, src)
destiter = eachindex(dest)
y = iterate(destiter)
Expand Down
5 changes: 5 additions & 0 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1396,4 +1396,9 @@ function Base.show(io::IO, op::BroadcastFunction)
end
Base.show(io::IO, ::MIME"text/plain", op::BroadcastFunction) = show(io, op)

# interface callables, like in interface_callables_base.jl, but for `Broadcast` instead of for `Base`.
for f ∈ Any[broadcastable, instantiate]
Base._stable_typeof(f).name.max_methods = 0x1
end

end # module
10 changes: 8 additions & 2 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,14 @@ 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)
f(x::Tuple) = argtail(x...)
function f(::Tuple{})
@noinline
throw(ArgumentError("Cannot call tail on an empty tuple."))
end
f(x)
end

function unwrap_unionall(@nospecialize(a))
@_foldable_meta
Expand Down
48 changes: 48 additions & 0 deletions base/interface_callables_base.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

# Interface functions defined in `Base`: define any that are not defined yet.
for c ∈ Symbol[
:propertynames, :getproperty, :setproperty!,
:show, :print,
:nextind, :prevind, :thisind,
:length, :iterate, :eltype, :size, :axes, :isdone, :isempty,
:firstindex, :lastindex, :getindex, :setindex!,
:copy, :copyto!,
:isone, :iszero,
:strides, :stride, :elsize,
:ndims, :one, :zero, :oneunit, :widen,
:promote_rule, :convert,
:similar,
:+, :-, :*, :/, ://, :<<, :>>, :>>>, :div, :fld, :cld,
]
@eval function $c end
end

# Disable world splitting for callables to which users should add new methods.
for c ∈ Any[
propertynames, getproperty, setproperty!,
show, print,
nextind, prevind, thisind,
length, iterate, size, axes, isdone, isempty,
firstindex, lastindex, getindex, setindex!,
copy, :copyto!,
isone, iszero,
strides, stride,
+, -, *, /, //, <<, >>, >>>, div, fld, cld,
]
Base._stable_typeof(c).name.max_methods = 0x1
end

# Callables which take type arguments and need a method for the bottom type need a
# `max_methods` value of two for good inference, because the bottom type subtypes
# each type.
#
# TODO: add `eltype`
for c ∈ Any[
elsize,
ndims, one, zero, oneunit, widen,
promote_rule, convert,
similar,
]
Base._stable_typeof(c).name.max_methods = 0x2
end
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
132 changes: 82 additions & 50 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,14 @@ end

@eval split_rest(t::Tuple, n::Int, i=1) = ($(Expr(:meta, :aggressive_constprop)); (t[i:end-n], t[end-n+1:end]))

# 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)
f(t::Tuple) = t[1]
function f(::Tuple{})
@noinline
throw(ArgumentError("tuple must be non-empty"))
end
f(t)
end

# eltype

Expand Down Expand Up @@ -570,71 +575,95 @@ 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)
f(::Tuple{}, h::UInt) = h ⊻ tuplehash_seed
f(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h))
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
return out
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]
f(::Tuple{}, ::Tuple{}) = false
f(::Tuple{}, ::Tuple) = true
f(::Tuple, ::Tuple{}) = false
function f(t1::Tuple, t2::Tuple)
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
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
return n1 < n2
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)
f(::Tuple{}, ::Tuple{}) = false
f(::Tuple{}, ::Tuple) = true
f(::Tuple, ::Tuple{}) = false
function f(t1::Tuple, t2::Tuple)
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
# 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
return n1 < n2
f(t1, t2)
end

## functions ##

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

revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
Expand Down Expand Up @@ -672,11 +701,14 @@ 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}
@inline
len = N + 3
j = mod1(shift, len)
ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple
function circshift(t::Tuple, shift::Integer)
f((@nospecialize t::Union{Tuple{},Tuple{Any}}), @nospecialize _::Integer) = t
f(t::Tuple{Any,Any}, shift::Integer) = iseven(shift) ? t : reverse(t)
function f(x::Tuple{Any,Any,Any,Vararg{Any,N}}, shift::Integer) where {N}
@inline
len = N + 3
j = mod1(shift, len)
ntuple(k -> getindex(x, k-j+ifelse(k>j,0,len)), Val(len))::Tuple
end
f(t, shift)
end
2 changes: 1 addition & 1 deletion test/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ end
@test Core.Compiler.return_type(-, NTuple{2, Rational}) == Rational

A=Rational[1 1 1; 2 2 2; 3 3 3]
@test @inferred(A*A) isa Matrix{Rational}
@test @inferred(A*A) isa Matrix
end

@testset "issue #42560" begin
Expand Down
12 changes: 12 additions & 0 deletions test/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,18 @@ namedtup = (;a=1, b=2, c=3)
@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