diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 25fcb2a..fb99dc7 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -538,14 +538,21 @@ promote_rule(::Type{X}, ::Type{Tr}) where {X <: FixedPoint, Tr <: Rational} = Tr promote_rule(::Type{X}, ::Type{Ti}) where {X <: FixedPoint, Ti <: Integer} = floattype(X) -function promote_rule(::Type{X1}, ::Type{X2}) where {T1, f1, X1 <: FixedPoint{T1,f1}, - T2, f2, X2 <: FixedPoint{T2,f2}} - X = wrapper(X1) - X !== wrapper(X2) && return promote_type(floattype(X1), floattype(X2)) - - f = max(f1, f2) # ensure we have enough precision +function promote_rule(::Type{X1}, ::Type{X2}) where {X1 <: FixedPoint, X2 <: FixedPoint} + (isconcretetype(X1) && isconcretetype(X2)) || return Union{} + X1w, X2w = wrapper(X1), wrapper(X2) + if X1w !== X2w + # X1w <: X2w && return X2w # for non-concrete types + # X2w <: X1w && return X1w # for non-concrete types + return promote_type(floattype(X1), floattype(X2)) + end + X = X1w + T1, T2 = rawtype(X1), rawtype(X2) + f1, f2 = nbitsfrac(X1), nbitsfrac(X2) + f = max(f1, f2) # ensure we have enough precision Tp = promote_type(T1, T2) - T = (T1 <: Signed || T2 <: Signed) ? signedtype(Tp) : Tp + T = ((T1 <: Signed && T1 !== Union{}) || + (T2 <: Signed && T2 !== Union{})) ? signedtype(Tp) : Tp # make sure we have enough integer bits m = max(nbitsint(X1), nbitsint(X2)) _widen_rawtype(X{T,f}, m) diff --git a/src/utilities.jl b/src/utilities.jl index cb7650d..5bed604 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -13,6 +13,7 @@ widen1(::Type{UInt64}) = UInt128 widen1(x::Integer) = x % widen1(typeof(x)) signedtype(::Type{T}) where {T <: Integer} = typeof(signed(zero(T))) +signedtype(::Type{T}) where {T <: Signed} = T const ShortInts = Union{Int8, UInt8, Int16, UInt16} const LongInts = Union{Int64, UInt64, Int128, UInt128, BigInt} diff --git a/test/common.jl b/test/common.jl index def7f6b..3e56bda 100644 --- a/test/common.jl +++ b/test/common.jl @@ -408,3 +408,20 @@ function test_rand(TX::Type) @test size(a) == (3, 5) end end + +function test_promote_type(TX::Type) + @testset "promote_type with Union/UnionAll" begin + # Although `TX{Union{},f}` is not a valid data type, it should not cause an error. + # `<:Real` means that the rule is not well-defined and may change in the future. + @test promote_type(TX{Union{},1}, TX{Union{},2}) <: Real + @test promote_type(TX{Union{},1}, TX{Union{}}) <: Real + for X in target(TX, :i8, :i64; ex = :thin) + T = rawtype(X) + Tw = widen(T) + @test promote_type(TX{Union{},2}, X) <: Real + @test promote_type(TX{Union{T, Tw},2}, X) <: Real + @test promote_type(Union{TX{T,2}, TX{Tw,2}}, X) <: Real + end + end + # TODO: Add more tests +end diff --git a/test/fixed.jl b/test/fixed.jl index d0909c3..c12f130 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -714,6 +714,8 @@ end end @testset "Promotion within Fixed" begin + test_promote_type(Fixed) + @test @inferred(promote(Q0f7(0.25), Q0f7(0.75))) === (Q0f7(0.25), Q0f7(0.75)) @test @inferred(promote(Fixed{Int16,3}(0.25), Fixed{Int8,3}(0.875))) === diff --git a/test/normed.jl b/test/normed.jl index 50b6009..c9e5e4b 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -692,6 +692,8 @@ end end @testset "Promotion within Normed" begin + test_promote_type(Normed) + @test @inferred(promote(N0f8(0.2), N0f8(0.8))) === (N0f8(0.2), N0f8(0.8)) @test @inferred(promote(Normed{UInt16,3}(0.2), Normed{UInt8,3}(0.86))) ===