Skip to content

Eliminate most invalidations from loading FixedPointNumbers #35733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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/bool.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions base/char.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,16 @@ true
"""
function convert end

convert(::Type{Union{}}, x::Union{}) = throw(MethodError(convert, (Union{}, x)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my favorite method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this PR has been an exercise in "how on earth can inference discover an ambiguity here?"

convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x)))
convert(::Type{Any}, x) = x
convert(::Type{T}, x::T) where {T} = x
convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization
# 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

Expand Down Expand Up @@ -447,6 +450,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This'll force us to generate code for each type, and forces us to add a dispatch, which I believe we don't want for it?


# simple Array{Any} operations needed for bootstrap
@eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i)
Expand Down
13 changes: 13 additions & 0 deletions base/number.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zero(x::Union{}) = throw(MethodError(zero, (x,)))
zero(x::Union{}) = unreachable

Though I can't promise that defining these might not cause other, unintended, side-effects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 Agreed, I would hold off on adding these --- they could cause problems, and they're the kind of thing we should be able to handle automatically (i.e. if an argument is bottom, obviously the code is unreachable).

zero(::Type{Union{}}) = throw(MethodError(zero, (Union{},)))

"""
one(x)
Expand Down Expand Up @@ -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.

Expand All @@ -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)
Expand Down
32 changes: 18 additions & 14 deletions base/reduce.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: I think we usually write short form functions with the braces {} to visually distinguish them:

Suggested change
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() # avoid invalidation
Copy link
Member

@tkf tkf May 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have this. It is not for avoiding invalidation but for "correctness." I think it's better to do _empty_reduce_error() in sum(Union{}[]) than throwing the MethodError.

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)
Expand All @@ -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) =
Expand Down