Skip to content

Commit

Permalink
Add promote_strict mechanism and use it instead of typejoin() where a…
Browse files Browse the repository at this point in the history
…ppropriate

Introduce a mechanism similar to promote(), but used to return types which can hold
exactly all values of the input types. Use it instead of typejoin() to choose an
appropriate element type with collect(), map(), broadcast(), and Dict().

Add appropriate methods for Number types, as well as for Nothing/Missing so that
Union{T, Nothing/Missing} is used instead of Any. Add promotion rules for Tuple and
NamedTuple so that promotion is performed element by element, to allow for more
precise typing of fields, potentially helping the compiler down the road.
  • Loading branch information
nalimilan committed Jan 5, 2018
1 parent 0ddf9ed commit 26002ff
Show file tree
Hide file tree
Showing 17 changed files with 360 additions and 56 deletions.
4 changes: 2 additions & 2 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ function collect_to!(dest::AbstractArray{T}, itr, offs, st) where T
@inbounds dest[i] = el::T
i += 1
else
R = typejoin(T, S)
R = promote_strict_type(T, S)
new = similar(dest, R)
copyto!(new,1, dest,1, i-1)
@inbounds new[i] = el
Expand All @@ -595,7 +595,7 @@ function grow_to!(dest, itr, st)
if S === T || S <: T
push!(dest, el::T)
else
new = sizehint!(empty(dest, typejoin(T, S)), length(dest))
new = sizehint!(empty(dest, promote_strict_type(T, S)), length(dest))
if new isa AbstractSet
# TODO: merge back these two branches when copy! is re-enabled for sets/vectors
union!(new, dest)
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ end
else
# This element type doesn't fit in B. Allocate a new B with wider eltype,
# copy over old values, and continue
newB = Base.similar(B, typejoin(eltype(B), S))
newB = Base.similar(B, promote_strict_type(eltype(B), S))
for II in Iterators.take(iter, count)
newB[II] = B[II]
end
Expand Down
4 changes: 3 additions & 1 deletion base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ function grow_to!(dest::AbstractDict{K,V}, itr, st) where V where K
if isa(k,K) && isa(v,V)
dest[k] = v
else
new = empty(dest, typejoin(K,typeof(k)), typejoin(V,typeof(v)))
new = empty(dest,
promote_strict_type(K,typeof(k)),
promote_strict_type(V,typeof(v)))
merge!(new, dest)
new[k] = v
return grow_to!(new, itr, st)
Expand Down
3 changes: 3 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,9 @@ export
promote,
promote_rule,
promote_type,
promote_strict,
promote_strict_rule,
promote_strict_type,
subtypes,
instances,
supertype,
Expand Down
17 changes: 16 additions & 1 deletion base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,22 @@ A not-a-number value of type [`Float64`](@ref).
"""
const NaN = NaN64

## conversions to floating-point ##
## promotions and conversions to floating-point ##

promote_strict_rule(::Type{Float16}, ::Union{Type{Int8}, Type{UInt8}}) = Float16
promote_strict_rule(::Type{Float32}, ::Union{Type{Int8}, Type{UInt8}, Type{Int16}, Type{UInt16}}) = Float32
promote_strict_rule(::Type{Float64}, ::Union{Type{Int8}, Type{UInt8}, Type{Int16}, Type{UInt16}, Type{Int32}, Type{UInt32}}) = Float64

promote_strict_rule(::Type{Float16}, ::Union{Type{Int16}, Type{UInt16}, Type{Int32}, Type{UInt32}}) = Float32
promote_strict_rule(::Type{Float32}, ::Union{Type{Int32}, Type{UInt32}}) = Float64

promote_strict_rule(::Type{Float16}, ::Union{Type{Int64}, Type{UInt64}, Type{Int128}, Type{UInt128}}) = BigFloat
promote_strict_rule(::Type{Float32}, ::Union{Type{Int64}, Type{UInt64}, Type{Int128}, Type{UInt128}}) = BigFloat
promote_strict_rule(::Type{Float64}, ::Union{Type{Int64}, Type{UInt64}, Type{Int128}, Type{UInt128}}) = BigFloat

promote_strict_rule(::Type{Float32}, ::Type{Float16}) = Float32
promote_strict_rule(::Type{Float64}, ::Union{Type{Float16}, Type{Float32}}) = Float64

Float16(x::Integer) = convert(Float16, convert(Float32, x))
for t in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128)
@eval promote_rule(::Type{Float16}, ::Type{$t}) = Float16
Expand Down
2 changes: 1 addition & 1 deletion base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ Float64(n::BigInt) = Float64(n, RoundNearest)
Float32(n::BigInt) = Float32(n, RoundNearest)
Float16(n::BigInt) = Float16(n, RoundNearest)

promote_rule(::Type{BigInt}, ::Type{<:Integer}) = BigInt
promote_strict_rule(::Type{BigInt}, ::Type{<:Integer}) = BigInt

"""
big(x)
Expand Down
19 changes: 19 additions & 0 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,25 @@ end

## integer promotions ##

# promote_strict
# with same signedness or when unsigned type is smaller, promote to larger signed type
promote_strict_rule(::Type{Int16}, ::Union{Type{Int8}, Type{UInt8}}) = Int16
promote_strict_rule(::Type{Int32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = Int32
promote_strict_rule(::Type{Int64}, ::Union{Type{Int16}, Type{Int32}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = Int64
promote_strict_rule(::Type{Int128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = Int128
promote_strict_rule(::Type{UInt16}, ::Type{UInt8}) = UInt16
promote_strict_rule(::Type{UInt32}, ::Union{Type{UInt16}, Type{UInt8}}) = UInt32
promote_strict_rule(::Type{UInt64}, ::Union{Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = UInt64
promote_strict_rule(::Type{UInt128}, ::Union{Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = UInt128
# with mixed signedness when unsigned type is not smaller, promote to another signed type
promote_strict_rule(::Type{UInt8}, ::Type{Int8}) = Int16
promote_strict_rule(::Type{UInt16}, ::Union{Type{Int16}, Type{Int8}}) = Int32
promote_strict_rule(::Type{UInt32}, ::Union{Type{Int32}, Type{Int16}, Type{Int8}}) = Int64
promote_strict_rule(::Type{UInt64}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}}) = Int128
promote_strict_rule(::Type{UInt128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int128}, Type{Int8}}) = BigInt
# TODO: what about Bool?

# promote
# with different sizes, promote to larger type
promote_rule(::Type{Int16}, ::Union{Type{Int8}, Type{UInt8}}) = Int16
promote_rule(::Type{Int32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = Int32
Expand Down
31 changes: 25 additions & 6 deletions base/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,31 @@ nonmissingtype(::Type{Missing}) = Union{}
nonmissingtype(::Type{T}) where {T} = T
nonmissingtype(::Type{Any}) = Any

promote_rule(::Type{Missing}, ::Type{T}) where {T} = Union{T, Missing}
promote_rule(::Type{Union{S,Missing}}, ::Type{T}) where {T,S} = Union{promote_type(T, S), Missing}
promote_rule(::Type{Any}, ::Type{T}) where {T} = Any
promote_rule(::Type{Any}, ::Type{Missing}) = Any
promote_rule(::Type{Missing}, ::Type{Any}) = Any
promote_rule(::Type{Missing}, ::Type{Missing}) = Missing
# Both rules need to be defined for types which implement promote_rule
# but not promote_strict_rule
for (prule, ptype) in ((:promote_strict_rule, :promote_strict_type),
(:promote_rule, :promote_type)),
U in (:Nothing, :Missing)
@eval begin
$prule(::Type{$U}, ::Type{T}) where {T} = Union{T, $U}
$prule(::Type{Union{S,$U}}, ::Type{T}) where {T,S} = Union{$ptype(T, S), $U}
$prule(::Type{Any}, ::Type{$U}) = Any
$prule(::Type{$U}, ::Type{Any}) = Any
$prule(::Type{$U}, ::Type{$U}) = U
end
end

#= # Both rules need to be defined for types which implement promote_rule
# but not promote_strict_rule
for (prule, ptype) in ((promote_strict_rule, promote_strict_type), (promote_rule, promote_type)),
U in (Nothing, Missing)
prule(::Type{U}, ::Type{T}) where {T} = Union{T, U}
prule(::Type{Union{S,U}}, ::Type{T}) where {T,S} = Union{ptype(T, S), U}
prule(::Type{Any}, ::Type{T}) where {T} = Any
prule(::Type{Any}, ::Type{U}) = Any
prule(::Type{U}, ::Type{Any}) = Any
prule(::Type{U}, ::Type{U}) = U
end =#

convert(::Type{Union{T, Missing}}, x) where {T} = convert(T, x)
# To fix ambiguities
Expand Down
6 changes: 3 additions & 3 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,9 @@ Float32(x::BigFloat, r::RoundingMode) =
# TODO: avoid double rounding
Float16(x::BigFloat, r::RoundingMode) = convert(Float16, Float32(x, r))

promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat
promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat
promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat
promote_strict_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat
promote_strict_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat
promote_strict_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat

big(::Type{<:AbstractFloat}) = BigFloat

Expand Down
3 changes: 3 additions & 0 deletions base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1)
isempty(::NamedTuple{()}) = true
isempty(::NamedTuple) = false

promote_strict_rule(::Type{NamedTuple{n, S}}, ::Type{NamedTuple{n, T}}) where {n, S, T} =
NamedTuple{n, promote_strict_type(S, T)}

convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T} = nt
convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt

Expand Down
Loading

0 comments on commit 26002ff

Please sign in to comment.