Skip to content

Commit 6c0695e

Browse files
committed
Fix promote_op() and some corner-case operators on Number
Use common promote_op() based on one() for all Number types: this fixes promote_op(+, ::Bool), which returned Int, and promote_op(==, ::Complex, ::Complex), which returned Complex{Bool}. Also fix a few corner cases which did not work or were type-instable. Add systematic tests to catch this kind of bug. This breaks *(::Matrix{Complex}, ::Matrix{Complex}) when it contains non-integer values, since one(Complex) === Complex(1).
1 parent b892a25 commit 6c0695e

File tree

10 files changed

+73
-27
lines changed

10 files changed

+73
-27
lines changed

base/bool.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,3 @@ fld(x::Bool, y::Bool) = div(x,y)
7272
cld(x::Bool, y::Bool) = div(x,y)
7373
rem(x::Bool, y::Bool) = y ? false : throw(DivideError())
7474
mod(x::Bool, y::Bool) = rem(x,y)
75-
76-
promote_op(op, ::Type{Bool}, ::Type{Bool}) = typeof(op(true, true))
77-
promote_op(::typeof(^), ::Type{Bool}, ::Type{Bool}) = Bool
78-
promote_op{T<:Integer}(::typeof(^), ::Type{Bool}, ::Type{T}) = Bool

base/complex.jl

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,6 @@ promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{S}) =
2626
promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{Complex{S}}) =
2727
Complex{promote_type(T,S)}
2828

29-
promote_op{T<:Real,S<:Real}(op, ::Type{Complex{T}}, ::Type{Complex{S}}) =
30-
Complex{promote_op(op,T,S)}
31-
promote_op{T<:Real,S<:Real}(op, ::Type{Complex{T}}, ::Type{S}) =
32-
Complex{promote_op(op,T,S)}
33-
promote_op{T<:Real,S<:Real}(op, ::Type{T}, ::Type{Complex{S}}) =
34-
Complex{promote_op(op,T,S)}
35-
promote_op{T<:Integer,S<:Integer}(::typeof(^), ::Type{T}, ::Type{Complex{S}}) =
36-
Complex{Float64}
37-
promote_op{T<:Integer,S<:Integer}(::typeof(.^), ::Type{T}, ::Type{Complex{S}}) =
38-
Complex{Float64}
39-
4029
widen{T}(::Type{Complex{T}}) = Complex{widen(T)}
4130

4231
real(z::Complex) = z.re
@@ -461,7 +450,7 @@ function ^{T<:AbstractFloat}(z::Complex{T}, p::Complex{T})
461450
if p==2 #square
462451
zr, zi = reim(z)
463452
x = (zr-zi)*(zr+zi)
464-
y = 2zr*zi
453+
y = T(2)*zr*zi
465454
if isnan(x)
466455
if isinf(y)
467456
x = copysign(zero(T),zr)

base/gmp.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ end
429429
^(x::BigInt , y::Bool ) = y ? x : one(x)
430430
^(x::BigInt , y::Integer) = bigint_pow(x, y)
431431
^(x::Integer, y::BigInt ) = bigint_pow(BigInt(x), y)
432+
^(x::Bool , y::BigInt ) = Base.power_by_squaring(x, y)
432433

433434
function powermod(x::BigInt, p::BigInt, m::BigInt)
434435
r = BigInt()

base/int.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,6 @@ promote_rule{T<:BitSigned64}(::Type{UInt64}, ::Type{T}) = UInt64
305305
promote_rule{T<:Union{UInt32, UInt64}}(::Type{T}, ::Type{Int128}) = Int128
306306
promote_rule{T<:BitSigned}(::Type{UInt128}, ::Type{T}) = UInt128
307307

308-
promote_op{R<:Integer,S<:Integer}(op, ::Type{R}, ::Type{S}) = typeof(op(one(R), one(S)))
309-
310308
## traits ##
311309

312310
typemin(::Type{Int8 }) = Int8(-128)

base/intfuncs.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ ndigits0z(x::Integer) = ndigits0z(unsigned(abs(x)))
186186

187187
const ndigits_max_mul = Core.sizeof(Int) == 4 ? 69000000 : 290000000000000000
188188

189-
function ndigits0znb(n::Int, b::Int)
189+
function ndigits0znb(n::Integer, b::Integer)
190190
d = 0
191191
while n != 0
192192
n = cld(n,b)
@@ -198,7 +198,7 @@ end
198198
function ndigits0z(n::Unsigned, b::Int)
199199
d = 0
200200
if b < 0
201-
d = ndigits0znb(signed(n), b)
201+
d = ndigits0znb(n, b)
202202
else
203203
b == 2 && return (sizeof(n)<<3-leading_zeros(n))
204204
b == 8 && return div((sizeof(n)<<3)-leading_zeros(n)+2,3)

base/promotion.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ promote_op{T}(::Type{T}, ::Any) = (@_pure_meta; T)
227227
promote_op{R,S}(::Any, ::Type{R}, ::Type{S}) = (@_pure_meta; promote_type(R, S))
228228
promote_op(op, T, S, U, V...) = (@_pure_meta; promote_op(op, T, promote_op(op, S, U, V...)))
229229

230+
promote_op{S<:Number,T}(op::Type{T}, ::Type{S}) = T # to fix ambiguities
231+
promote_op{S<:Number}(op, ::Type{S}) = typeof(op(one(S)))
232+
promote_op{R<:Number,S<:Number}(op, ::Type{R}, ::Type{S}) = typeof(op(one(R), one(S)))
233+
230234
## catch-alls to prevent infinite recursion when definitions are missing ##
231235

232236
no_op_err(name, T) = error(name," not defined for ",T)

test/arrayops.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ b = rand(6,7)
14041404
# return type declarations (promote_op)
14051405
module RetTypeDecl
14061406
using Base.Test
1407-
import Base: +, *, .*, zero
1407+
import Base: +, *, .*, one
14081408

14091409
immutable MeterUnits{T,P} <: Number
14101410
val::T
@@ -1418,11 +1418,11 @@ module RetTypeDecl
14181418
(*){T,pow}(x::Int, y::MeterUnits{T,pow}) = MeterUnits{typeof(x*one(T)),pow}(x*y.val)
14191419
(*){T}(x::MeterUnits{T,1}, y::MeterUnits{T,1}) = MeterUnits{T,2}(x.val*y.val)
14201420
(.*){T}(x::MeterUnits{T,1}, y::MeterUnits{T,1}) = MeterUnits{T,2}(x.val*y.val)
1421-
zero{T,pow}(x::MeterUnits{T,pow}) = MeterUnits{T,pow}(zero(T))
1421+
one{T,pow}(x::Type{MeterUnits{T,pow}}) = MeterUnits{T,pow}(one(T))
14221422

1423-
Base.promote_op{R,S}(::typeof(+), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),1}
1424-
Base.promote_op{R,S}(::typeof(*), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),2}
1425-
Base.promote_op{R,S}(::typeof(.*), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),2}
1423+
# Base.promote_op{R,S}(::typeof(+), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),1}
1424+
# Base.promote_op{R,S}(::typeof(*), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),2}
1425+
# Base.promote_op{R,S}(::typeof(.*), ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),2}
14261426

14271427
@test @inferred(m+[m,m]) == [m+m,m+m]
14281428
@test @inferred([m,m]+m) == [m+m,m+m]

test/broadcast.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,8 @@ end
196196
let a = broadcast(Float32, [3, 4, 5])
197197
@test eltype(a) == Float32
198198
end
199+
200+
# PR 16988
201+
@test Base.promote_op(+, Bool) === Int
202+
@test isa(broadcast(+, true), Array{Int,0})
203+
@test Base.promote_op(Float64, Bool) === Float64

test/linalg/dense.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,8 @@ end
353353
# issue #4796
354354
let
355355
dim=2
356-
S=zeros(Complex,dim,dim)
357-
T=zeros(Complex,dim,dim)
356+
S=zeros(Complex128,dim,dim)
357+
T=zeros(Complex128,dim,dim)
358358
T[:] = 1
359359
z = 2.5 + 1.5im
360360
S[1] = z

test/numbers.jl

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,3 +2742,56 @@ testmi(typemin(Int)+1:typemin(Int)+1000, -100:100)
27422742
@test_throws ArgumentError Base.multiplicativeinverse(0)
27432743
testmi(map(UInt32, 0:1000), map(UInt32, 1:100))
27442744
testmi(typemax(UInt32)-UInt32(1000):typemax(UInt32), map(UInt32, 1:100))
2745+
2746+
# PR #16995
2747+
let types = (Base.BitInteger_types..., BigInt, Bool,
2748+
Rational{Int}, Rational{BigInt},
2749+
Real, Float16, Float32, Float64, BigFloat,
2750+
Complex, Complex{Int}, Complex{UInt}, Complex32, Complex64, Complex128)
2751+
for S in types
2752+
for op in (+, -)
2753+
@test Base.promote_op(op, S) === typeof(op(one(S)))
2754+
@inferred Base.promote_op(op, S)
2755+
@inferred op(one(S))
2756+
end
2757+
end
2758+
2759+
@test Base.promote_op(!, Bool) === Bool
2760+
@inferred Base.promote_op(!, Bool)
2761+
2762+
for S in types, T in types
2763+
for op in (+, -, *, /, ^, (==))
2764+
@test Base.promote_op(op, S, T) === typeof(op(one(S), one(T)))
2765+
@inferred Base.promote_op(op, S, T)
2766+
@inferred op(one(S), one(T))
2767+
end
2768+
end
2769+
end
2770+
2771+
let types = (Base.BitInteger_types..., BigInt, Bool,
2772+
Rational{Int}, Rational{BigInt},
2773+
Float16, Float32, Float64, BigFloat)
2774+
for S in types, T in types
2775+
for op in (<, >, <=, >=)
2776+
@test Base.promote_op(op, S, T) === typeof(op(one(S), one(T)))
2777+
@inferred Base.promote_op(op, S, T)
2778+
@inferred op(one(S), one(T))
2779+
end
2780+
end
2781+
end
2782+
2783+
let types = (Base.BitInteger_types..., BigInt, Bool)
2784+
for S in types
2785+
@test Base.promote_op(~, S) === typeof(~one(S))
2786+
@inferred Base.promote_op(~, S)
2787+
@inferred ~one(S)
2788+
end
2789+
2790+
for S in types, T in types
2791+
for op in (&, |, <<, >>, (>>>), %, ÷)
2792+
@test Base.promote_op(op, S, T) === typeof(op(one(S), one(T)))
2793+
@inferred Base.promote_op(op, S, T)
2794+
@inferred op(one(S), one(T))
2795+
end
2796+
end
2797+
end

0 commit comments

Comments
 (0)