From a4e0f473145169f35f482eaa28366cdf9f57daaa Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 4 May 2020 12:58:21 -0500 Subject: [PATCH 1/6] Eliminate most invalidations from loading FixedPointNumbers --- base/bool.jl | 2 ++ base/essentials.jl | 4 ++++ base/number.jl | 13 +++++++++++++ base/reduce.jl | 32 ++++++++++++++++++-------------- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/base/bool.jl b/base/bool.jl index 871c7e83fbbc2..ce0ee6a790915 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -3,6 +3,8 @@ # promote Bool to any other numeric type promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T +convert(::Type{Bool}, x::Bool) = x # prevent invalidation + typemin(::Type{Bool}) = false typemax(::Type{Bool}) = true diff --git a/base/essentials.jl b/base/essentials.jl index 2c51076fb6f80..bb865b5a13f1c 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -166,6 +166,7 @@ true """ function convert end +convert(::Type{Union{}}, x::Union{}) = throw(MethodError(convert, (Union{}, x))) convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x))) convert(::Type{Any}, x) = x convert(::Type{T}, x::T) where {T} = x @@ -447,6 +448,9 @@ Stacktrace: ``` """ sizeof(x) = Core.sizeof(x) +# The next two methods prevent invalidation +sizeof(::Type{Union{}}) = Core.sizeof(Union{}) +sizeof(::Type{T}) where T = Core.sizeof(T) # simple Array{Any} operations needed for bootstrap @eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i) diff --git a/base/number.jl b/base/number.jl index 9b615c99d4147..96e7b8fa9701b 100644 --- a/base/number.jl +++ b/base/number.jl @@ -2,6 +2,10 @@ ## generic operations on numbers ## +# prevent invalidation +Union{}(x::Number) = throw(MethodError(convert, (Union{}, x))) +convert(::Type{Union{}}, x::Number) = throw(MethodError(convert, (Union{}, x))) + # Numbers are convertible convert(::Type{T}, x::T) where {T<:Number} = x convert(::Type{T}, x::Number) where {T<:Number} = T(x) @@ -240,6 +244,9 @@ julia> zero(rand(2,2)) """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) +# prevent invalidation +zero(x::Union{}) = throw(MethodError(zero, (x,))) +zero(::Type{Union{}}) = throw(MethodError(zero, (Union{},))) """ one(x) @@ -275,6 +282,9 @@ julia> import Dates; one(Dates.Day(1)) """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) +# prevent invalidation +one(x::Union{}) = throw(MethodError(one, (x,))) +one(::Type{Union{}}) = throw(MethodError(one, (Union{},))) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. @@ -298,6 +308,9 @@ julia> import Dates; oneunit(Dates.Day) """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) +# prevent invalidation +oneunit(x::Union{}) = throw(MethodError(oneunit, (x,))) +oneunit(::Type{Union{}}) = throw(MethodError(oneunit, (Union{},))) """ big(T::Type) diff --git a/base/reduce.jl b/base/reduce.jl index 414bac5099f78..8305cbbf4a44f 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -306,25 +306,29 @@ with reduction `op` over an empty array with element type of `T`. If not defined, this will throw an `ArgumentError`. """ -reduce_empty(op, T) = _empty_reduce_error() -reduce_empty(::typeof(+), T) = zero(T) +reduce_empty(op, ::Type{T}) where T = _empty_reduce_error() +reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() # avoid invalidation +reduce_empty(::typeof(+), ::Type{T}) where T = zero(T) reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) -reduce_empty(::typeof(*), T) = one(T) +reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(*), ::Type{T}) where T = one(T) reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = "" reduce_empty(::typeof(&), ::Type{Bool}) = true reduce_empty(::typeof(|), ::Type{Bool}) = false -reduce_empty(::typeof(add_sum), T) = reduce_empty(+, T) +reduce_empty(::typeof(add_sum), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(add_sum), ::Type{T}) where T = reduce_empty(+, T) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallSigned} = zero(Int) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallUnsigned} = zero(UInt) -reduce_empty(::typeof(mul_prod), T) = reduce_empty(*, T) +reduce_empty(::typeof(mul_prod), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(mul_prod), ::Type{T}) where T = reduce_empty(*, T) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallSigned} = one(Int) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallUnsigned} = one(UInt) -reduce_empty(op::BottomRF, T) = reduce_empty(op.rf, T) -reduce_empty(op::MappingRF, T) = mapreduce_empty(op.f, op.rf, T) -reduce_empty(op::FilteringRF, T) = reduce_empty(op.rf, T) -reduce_empty(op::FlipArgs, T) = reduce_empty(op.f, T) +reduce_empty(op::BottomRF, ::Type{T}) where T = reduce_empty(op.rf, T) +reduce_empty(op::MappingRF, ::Type{T}) where T = mapreduce_empty(op.f, op.rf, T) +reduce_empty(op::FilteringRF, ::Type{T}) where T = reduce_empty(op.rf, T) +reduce_empty(op::FlipArgs, ::Type{T}) where T = reduce_empty(op.f, T) """ Base.mapreduce_empty(f, op, T) @@ -336,12 +340,12 @@ of `T`. If not defined, this will throw an `ArgumentError`. """ mapreduce_empty(f, op, T) = _empty_reduce_error() -mapreduce_empty(::typeof(identity), op, T) = reduce_empty(op, T) -mapreduce_empty(::typeof(abs), op, T) = abs(reduce_empty(op, T)) -mapreduce_empty(::typeof(abs2), op, T) = abs2(reduce_empty(op, T)) +mapreduce_empty(::typeof(identity), op, ::Type{T}) where T = reduce_empty(op, T) +mapreduce_empty(::typeof(abs), op, ::Type{T}) where T = abs(reduce_empty(op, T)) +mapreduce_empty(::typeof(abs2), op, ::Type{T}) where T = abs2(reduce_empty(op, T)) -mapreduce_empty(f::typeof(abs), ::typeof(max), T) = abs(zero(T)) -mapreduce_empty(f::typeof(abs2), ::typeof(max), T) = abs2(zero(T)) +mapreduce_empty(::typeof(abs), ::typeof(max), ::Type{T}) where T = abs(zero(T)) +mapreduce_empty(::typeof(abs2), ::typeof(max), ::Type{T}) where T = abs2(zero(T)) # For backward compatibility: mapreduce_empty_iter(f, op, itr, ItrEltype) = From cd39cf7b8e669e2c19cf1afdb7e11d93ce268507 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 4 May 2020 13:59:54 -0500 Subject: [PATCH 2/6] Prevent a few more invalidations --- base/char.jl | 2 ++ base/essentials.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/base/char.jl b/base/char.jl index cc6ff5eeff3a4..58c0c2f18ca79 100644 --- a/base/char.jl +++ b/base/char.jl @@ -49,6 +49,7 @@ Char (::Type{AbstractChar})(x::Number) = Char(x) (::Type{T})(x::AbstractChar) where {T<:Union{Number,AbstractChar}} = T(codepoint(x)) (::Type{T})(x::T) where {T<:AbstractChar} = x +AbstractChar(x::AbstractChar) = x """ ncodeunits(c::Char) -> Int @@ -179,6 +180,7 @@ convert(::Type{AbstractChar}, x::Number) = Char(x) # default to Char convert(::Type{T}, x::Number) where {T<:AbstractChar} = T(x) convert(::Type{T}, x::AbstractChar) where {T<:Number} = T(x) convert(::Type{T}, c::AbstractChar) where {T<:AbstractChar} = T(c) +convert(::Type{AbstractChar}, c::AbstractChar) = c convert(::Type{T}, c::T) where {T<:AbstractChar} = c rem(x::AbstractChar, ::Type{T}) where {T<:Number} = rem(codepoint(x), T) diff --git a/base/essentials.jl b/base/essentials.jl index bb865b5a13f1c..f6635526764d5 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -174,6 +174,8 @@ convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent o # in the absence of inlining-enabled # (due to fields typed as `Type`, which is generally a bad idea) +Union{}(x::Union{}) = throw(MethodError(convert, (Union{}, x))) + """ @eval [mod,] ex From 16e46e7dcefa30181571c37998c8bb0fc03fc8d9 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 11 May 2020 14:36:36 -0700 Subject: [PATCH 3/6] Rollback non-bugfix parts --- base/bool.jl | 2 -- base/char.jl | 2 -- base/essentials.jl | 6 ------ base/number.jl | 13 ------------- base/reduce.jl | 12 ++++++------ 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/base/bool.jl b/base/bool.jl index ce0ee6a790915..871c7e83fbbc2 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -3,8 +3,6 @@ # promote Bool to any other numeric type promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T -convert(::Type{Bool}, x::Bool) = x # prevent invalidation - typemin(::Type{Bool}) = false typemax(::Type{Bool}) = true diff --git a/base/char.jl b/base/char.jl index 58c0c2f18ca79..cc6ff5eeff3a4 100644 --- a/base/char.jl +++ b/base/char.jl @@ -49,7 +49,6 @@ Char (::Type{AbstractChar})(x::Number) = Char(x) (::Type{T})(x::AbstractChar) where {T<:Union{Number,AbstractChar}} = T(codepoint(x)) (::Type{T})(x::T) where {T<:AbstractChar} = x -AbstractChar(x::AbstractChar) = x """ ncodeunits(c::Char) -> Int @@ -180,7 +179,6 @@ convert(::Type{AbstractChar}, x::Number) = Char(x) # default to Char convert(::Type{T}, x::Number) where {T<:AbstractChar} = T(x) convert(::Type{T}, x::AbstractChar) where {T<:Number} = T(x) convert(::Type{T}, c::AbstractChar) where {T<:AbstractChar} = T(c) -convert(::Type{AbstractChar}, c::AbstractChar) = c convert(::Type{T}, c::T) where {T<:AbstractChar} = c rem(x::AbstractChar, ::Type{T}) where {T<:Number} = rem(codepoint(x), T) diff --git a/base/essentials.jl b/base/essentials.jl index f6635526764d5..2c51076fb6f80 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -166,7 +166,6 @@ true """ function convert end -convert(::Type{Union{}}, x::Union{}) = throw(MethodError(convert, (Union{}, x))) convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x))) convert(::Type{Any}, x) = x convert(::Type{T}, x::T) where {T} = x @@ -174,8 +173,6 @@ convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent o # in the absence of inlining-enabled # (due to fields typed as `Type`, which is generally a bad idea) -Union{}(x::Union{}) = throw(MethodError(convert, (Union{}, x))) - """ @eval [mod,] ex @@ -450,9 +447,6 @@ Stacktrace: ``` """ sizeof(x) = Core.sizeof(x) -# The next two methods prevent invalidation -sizeof(::Type{Union{}}) = Core.sizeof(Union{}) -sizeof(::Type{T}) where T = Core.sizeof(T) # simple Array{Any} operations needed for bootstrap @eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i) diff --git a/base/number.jl b/base/number.jl index 96e7b8fa9701b..9b615c99d4147 100644 --- a/base/number.jl +++ b/base/number.jl @@ -2,10 +2,6 @@ ## generic operations on numbers ## -# prevent invalidation -Union{}(x::Number) = throw(MethodError(convert, (Union{}, x))) -convert(::Type{Union{}}, x::Number) = throw(MethodError(convert, (Union{}, x))) - # Numbers are convertible convert(::Type{T}, x::T) where {T<:Number} = x convert(::Type{T}, x::Number) where {T<:Number} = T(x) @@ -244,9 +240,6 @@ julia> zero(rand(2,2)) """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) -# prevent invalidation -zero(x::Union{}) = throw(MethodError(zero, (x,))) -zero(::Type{Union{}}) = throw(MethodError(zero, (Union{},))) """ one(x) @@ -282,9 +275,6 @@ julia> import Dates; one(Dates.Day(1)) """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) -# prevent invalidation -one(x::Union{}) = throw(MethodError(one, (x,))) -one(::Type{Union{}}) = throw(MethodError(one, (Union{},))) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. @@ -308,9 +298,6 @@ julia> import Dates; oneunit(Dates.Day) """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) -# prevent invalidation -oneunit(x::Union{}) = throw(MethodError(oneunit, (x,))) -oneunit(::Type{Union{}}) = throw(MethodError(oneunit, (Union{},))) """ big(T::Type) diff --git a/base/reduce.jl b/base/reduce.jl index 8305cbbf4a44f..4214f58babac5 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -307,7 +307,7 @@ with reduction `op` over an empty array with element type of `T`. If not defined, this will throw an `ArgumentError`. """ reduce_empty(op, ::Type{T}) where T = _empty_reduce_error() -reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() # avoid invalidation +reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() reduce_empty(::typeof(+), ::Type{T}) where T = zero(T) reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error() @@ -340,12 +340,12 @@ of `T`. If not defined, this will throw an `ArgumentError`. """ mapreduce_empty(f, op, T) = _empty_reduce_error() -mapreduce_empty(::typeof(identity), op, ::Type{T}) where T = reduce_empty(op, T) -mapreduce_empty(::typeof(abs), op, ::Type{T}) where T = abs(reduce_empty(op, T)) -mapreduce_empty(::typeof(abs2), op, ::Type{T}) where T = abs2(reduce_empty(op, T)) +mapreduce_empty(::typeof(identity), op, T) = reduce_empty(op, T) +mapreduce_empty(::typeof(abs), op, T) = abs(reduce_empty(op, T)) +mapreduce_empty(::typeof(abs2), op, T) = abs2(reduce_empty(op, T)) -mapreduce_empty(::typeof(abs), ::typeof(max), ::Type{T}) where T = abs(zero(T)) -mapreduce_empty(::typeof(abs2), ::typeof(max), ::Type{T}) where T = abs2(zero(T)) +mapreduce_empty(f::typeof(abs), ::typeof(max), T) = abs(zero(T)) +mapreduce_empty(f::typeof(abs2), ::typeof(max), T) = abs2(zero(T)) # For backward compatibility: mapreduce_empty_iter(f, op, itr, ItrEltype) = From 21895f7d6160da4775fd1399132a466c28381868 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 11 May 2020 14:40:08 -0700 Subject: [PATCH 4/6] Test sum(Union{}[]) --- test/reduce.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/reduce.jl b/test/reduce.jl index 69b8b1911e7ea..6032cbad36243 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -133,6 +133,7 @@ fz = float(z) @test sum(z) === 136 @test sum(fz) === 136.0 +@test_throws ArgumentError sum(Union{}[]) @test_throws ArgumentError sum(sin, Int[]) @test sum(sin, 3) == sin(3.0) @test sum(sin, [3]) == sin(3.0) From 6405dd60f8e5f7bef43ee671f8c6f2405da6559f Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Fri, 15 May 2020 16:37:42 -0700 Subject: [PATCH 5/6] Style fix: use `where {T}` --- base/reduce.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/base/reduce.jl b/base/reduce.jl index 4214f58babac5..6d19fa50e3037 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -306,29 +306,29 @@ with reduction `op` over an empty array with element type of `T`. If not defined, this will throw an `ArgumentError`. """ -reduce_empty(op, ::Type{T}) where T = _empty_reduce_error() +reduce_empty(op, ::Type{T}) where {T} = _empty_reduce_error() reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() -reduce_empty(::typeof(+), ::Type{T}) where T = zero(T) +reduce_empty(::typeof(+), ::Type{T}) where {T} = zero(T) reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error() -reduce_empty(::typeof(*), ::Type{T}) where T = one(T) +reduce_empty(::typeof(*), ::Type{T}) where {T} = one(T) reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = "" reduce_empty(::typeof(&), ::Type{Bool}) = true reduce_empty(::typeof(|), ::Type{Bool}) = false reduce_empty(::typeof(add_sum), ::Type{Union{}}) = _empty_reduce_error() -reduce_empty(::typeof(add_sum), ::Type{T}) where T = reduce_empty(+, T) +reduce_empty(::typeof(add_sum), ::Type{T}) where {T} = reduce_empty(+, T) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallSigned} = zero(Int) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallUnsigned} = zero(UInt) reduce_empty(::typeof(mul_prod), ::Type{Union{}}) = _empty_reduce_error() -reduce_empty(::typeof(mul_prod), ::Type{T}) where T = reduce_empty(*, T) +reduce_empty(::typeof(mul_prod), ::Type{T}) where {T} = reduce_empty(*, T) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallSigned} = one(Int) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallUnsigned} = one(UInt) -reduce_empty(op::BottomRF, ::Type{T}) where T = reduce_empty(op.rf, T) -reduce_empty(op::MappingRF, ::Type{T}) where T = mapreduce_empty(op.f, op.rf, T) -reduce_empty(op::FilteringRF, ::Type{T}) where T = reduce_empty(op.rf, T) -reduce_empty(op::FlipArgs, ::Type{T}) where T = reduce_empty(op.f, T) +reduce_empty(op::BottomRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::MappingRF, ::Type{T}) where {T} = mapreduce_empty(op.f, op.rf, T) +reduce_empty(op::FilteringRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::FlipArgs, ::Type{T}) where {T} = reduce_empty(op.f, T) """ Base.mapreduce_empty(f, op, T) From f0dfaa05a1b5c15eb9781d98dba49b8d1bf9f4a5 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Fri, 15 May 2020 16:56:15 -0700 Subject: [PATCH 6/6] Test reduce(*, ()) etc. --- test/reduce.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/reduce.jl b/test/reduce.jl index 6032cbad36243..9501f41f8b77c 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -46,6 +46,8 @@ end @test reduce(max, [8 6 7 5 3 0 9]) == 9 @test reduce(+, 1:5; init=1000) == (1000 + 1 + 2 + 3 + 4 + 5) @test reduce(+, 1) == 1 +@test_throws ArgumentError reduce(*, ()) +@test_throws ArgumentError reduce(*, Union{}[]) # mapreduce @test mapreduce(-, +, [-10 -9 -3]) == ((10 + 9) + 3)