Skip to content
550 changes: 550 additions & 0 deletions base/Base_compiler.jl

Large diffs are not rendered by default.

4 changes: 0 additions & 4 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -938,10 +938,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
20 changes: 11 additions & 9 deletions base/error.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,17 @@ throw

## native julia error handling ##

# This is `Experimental.@max_methods 2 function error end`, which is not available at this point in bootstrap.
# NOTE It is important to always be able to infer the return type of `error` as `Union{}`,
# but there's a hitch when a package globally sets `@max_methods 1` and it causes inference
# for `error(::Any)` to fail (JuliaLang/julia#54029).
# This definition site `@max_methods 2` setting overrides any global `@max_methods 1` settings
# on package side, guaranteeing that return type inference on `error` is successful always.
# see issue (JuliaLang/julia#54029). Ensure that method counts are small enough with
# respect to the `max_methods` value of the function.
function error end
typeof(error).name.max_methods = UInt8(2)

"""
error(message::AbstractString)

Raise an `ErrorException` with the given message.
"""
error(s::AbstractString) = throw(ErrorException(s))
error() = throw(ErrorException(""))
error(::AbstractString)

"""
error(msg...)
Expand All @@ -51,7 +46,14 @@ Raise an `ErrorException` with a message constructed by `string(msg...)`.
"""
function error(s::Vararg{Any,N}) where {N}
@noinline
throw(ErrorException(Main.Base.string(s...)))
exc = if s === ()
ErrorException("")
elseif s isa Tuple{AbstractString}
ErrorException(s...)
else
ErrorException(Main.Base.string(s...))
end
throw(exc)
end

"""
Expand Down
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
2 changes: 1 addition & 1 deletion base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ end
length(t::NamedTuple) = nfields(t)
iterate(t::NamedTuple, iter=1) = iter > nfields(t) ? nothing : (getfield(t, iter), iter + 1)
rest(t::NamedTuple) = t
@inline rest(t::NamedTuple{names}, i::Int) where {names} = NamedTuple{rest(names,i)}(t)
@inline rest(t::NamedTuple{names}, i) where {names} = NamedTuple{rest(names,i::Int)}(t)
firstindex(t::NamedTuple) = 1
lastindex(t::NamedTuple) = nfields(t)
getindex(t::NamedTuple, i::Int) = getfield(t, i)
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
6 changes: 3 additions & 3 deletions base/strings/basic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ UInt8
See also [`ncodeunits`](@ref), [`checkbounds`](@ref).
"""
@propagate_inbounds codeunit(s::AbstractString, i::Integer) = i isa Int ?
throw(MethodError(codeunit, (s, i))) : codeunit(s, Int(i))
throw(MethodError(codeunit, (s, i))) : codeunit(s, Int(i)::Int)

"""
isvalid(s::AbstractString, i::Integer)::Bool
Expand Down Expand Up @@ -141,7 +141,7 @@ Stacktrace:
```
"""
@propagate_inbounds isvalid(s::AbstractString, i::Integer) = i isa Int ?
throw(MethodError(isvalid, (s, i))) : isvalid(s, Int(i))
throw(MethodError(isvalid, (s, i))) : isvalid(s, Int(i)::Int)

"""
iterate(s::AbstractString, i::Integer)::Union{Tuple{<:AbstractChar, Int}, Nothing}
Expand All @@ -154,7 +154,7 @@ of the iteration protocol may assume that `i` is the start of a character in `s`
See also [`getindex`](@ref), [`checkbounds`](@ref).
"""
@propagate_inbounds iterate(s::AbstractString, i::Integer) = i isa Int ?
throw(MethodError(iterate, (s, i))) : iterate(s, Int(i))
throw(MethodError(iterate, (s, i))) : iterate(s, Int(i)::Int)

## basic generic definitions ##

Expand Down
146 changes: 97 additions & 49 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,12 @@ julia> first, Base.rest(a, state)
"""
function rest end
rest(t::Tuple) = t
rest(t::Tuple, i::Int) = ntuple(x -> getfield(t, x+i-1), length(t)-i+1)
rest(a::Union{Array,Memory,Core.SimpleVector}, i::Int=1) = a[i:end]
function rest(t::Tuple, i)
let i = i::Int
ntuple(x -> getfield(t, x+i-1), length(t)-i+1)
end
end
rest(a::Union{Array,Memory,Core.SimpleVector}, i=1) = a[(i::Int):end]
rest(itr, state...) = Iterators.rest(itr, state...)

"""
Expand Down Expand Up @@ -264,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 @@ -571,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
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]
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 @@ -673,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
19 changes: 19 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8193,6 +8193,25 @@ foo46503(a::Int, b::Nothing) = @invoke foo46503(a::Any, b)
foo46503(@nospecialize(a), b::Union{Nothing, Float64}) = rand() + 10
@test 10 <= foo46503(1, nothing) <= 11

@testset "inference for `rest` with unknown state argument types" begin
@testset "`Array`, `Memory`" begin
for typ in (Vector{Float32}, Matrix{Float32}, Memory{Float32})
@test (isconcretetype ∘ Base.infer_return_type)(Base.rest, Tuple{typ, Any})
for e in (Base.Compiler.is_terminates, Base.Compiler.is_notaskstate, Base.Compiler.is_nonoverlayed)
@test (e ∘ Base.infer_effects)(Base.rest, Tuple{typ, Any})
end
end
end
@testset "`Tuple`" begin
typ = NTuple{5, Float32}
@test Base.infer_return_type(Base.rest, Tuple{typ, Any}) <: Tuple
end
@testset "`NamedTuple`" begin
typ = NamedTuple{(:a, :b, :c, :d, :e), NTuple{5, Float32}}
@test Base.infer_return_type(Base.rest, Tuple{typ, Any}) <: NamedTuple
end
end

@testset "effect override on Symbol(::String)" begin
@test Core.Compiler.is_foldable(Base.infer_effects(Symbol, (String,)))
end
Expand Down
33 changes: 25 additions & 8 deletions test/faulty_constructor_method_should_not_cause_stack_overflows.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# new types with invalid constructors
for (typ, sup) in (
(:Char, :AbstractChar),
(:String, :AbstractString),
Expand All @@ -9,6 +10,18 @@ for (typ, sup) in (
@eval function Base.$typ(x::$fau) x end
end

# valid new subtype of `AbstractString`
struct MyString <: AbstractString
str::String
end
Base.lastindex(s::MyString) = lastindex(s.str)
Base.iterate(s::MyString) = iterate(s, 1)
Base.iterate(s::MyString, state) = iterate(s, Int(state)::Int)
Base.iterate(s::MyString, state::Integer) = iterate(s, Int(state)::Int)
Base.iterate(s::MyString, state::Int) = iterate(s.str, state)
Base.isequal(a::MyString, b::MyString) = isequal(a.str, b.str)
Base.:(==)(a::MyString, b::MyString) = (a.str == b.str)

using Test
using Unicode: Unicode

Expand Down Expand Up @@ -39,14 +52,18 @@ using Unicode: Unicode
end
@testset let x = FaultyInt()
@test_throws exc readbytes!(IOBuffer(), Vector{UInt8}(undef, 0), x)
@test_throws exc length("", x, x)
@test_throws exc thisind("", x)
@test_throws exc prevind("", x)
@test_throws exc prevind("", x, x)
@test_throws exc nextind("", x)
@test_throws exc nextind("", x, x)
@test_throws exc codeunit("", x)
@test_throws exc SubString("", x, x)
for s in ("", MyString(""))
@test_throws exc iterate(s, x)
@test_throws exc isvalid(s, x)
@test_throws exc length(s, x, x)
@test_throws exc thisind(s, x)
@test_throws exc prevind(s, x)
@test_throws exc prevind(s, x, x)
@test_throws exc nextind(s, x)
@test_throws exc nextind(s, x, x)
@test_throws exc codeunit(s, x)
@test_throws exc SubString(s, x, x)
end
end
@testset let x = FaultyUInt32()
@test_throws exc Char(x)
Expand Down
Loading