Skip to content

Commit ec601ba

Browse files
committed
make promote throw an error if no arguments can be changed. fixes #22801.
1 parent 05037f5 commit ec601ba

File tree

9 files changed

+49
-40
lines changed

9 files changed

+49
-40
lines changed

NEWS.md

+5
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ This section lists changes that do not have deprecation warnings.
172172
of the output was shrunk to fit the union of the type of each element in the input.
173173
([#22696])
174174

175+
* The `promote` function now raises an error if its arguments are of different types, and
176+
attempting to convert them to a common type fails to change any of their types.
177+
This avoids stack overflows in the common case of definitions like
178+
`f(x, y) = f(promote(x, y)...)` ([#22801]).
179+
175180
Library improvements
176181
--------------------
177182

base/dates/types.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ Base.typemin(::Union{Time, Type{Time}}) = Time(0)
340340
Base.eltype(::Type{T}) where {T<:Period} = T
341341
Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime
342342
Base.isless(x::T, y::T) where {T<:TimeType} = isless(value(x), value(y))
343-
Base.isless(x::TimeType, y::TimeType) = isless(Base.promote_noncircular(x, y)...)
343+
Base.isless(x::TimeType, y::TimeType) = isless(promote(x, y)...)
344344
(==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y))
345345
function ==(a::Time, b::Time)
346346
return hour(a) == hour(b) && minute(a) == minute(b) &&

base/deprecated.jl

+2
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,8 @@ end
17451745
@deprecate selectperm partialsortperm
17461746
@deprecate selectperm! partialsortperm!
17471747

1748+
@deprecate promote_noncircular promote false
1749+
17481750
# END 0.7 deprecations
17491751

17501752
# BEGIN 1.0 deprecations

base/int.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ signbit(x::Unsigned) = false
8484
flipsign(x::T, y::T) where {T<:BitSigned} = flipsign_int(x, y)
8585
flipsign(x::BitSigned, y::BitSigned) = flipsign_int(promote(x, y)...) % typeof(x)
8686

87-
flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote_noncircular(x, y)...))
87+
flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote(x, y)...))
8888
flipsign(x::Signed, y::Float16) = flipsign(x, bitcast(Int16, y))
8989
flipsign(x::Signed, y::Float32) = flipsign(x, bitcast(Int32, y))
9090
flipsign(x::Signed, y::Float64) = flipsign(x, bitcast(Int64, y))

base/linalg/bidiag.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ end
148148
convert(::Type{Matrix}, A::Bidiagonal{T}) where {T} = convert(Matrix{T}, A)
149149
convert(::Type{Array}, A::Bidiagonal) = convert(Matrix, A)
150150
full(A::Bidiagonal) = convert(Array, A)
151-
promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)}
151+
promote_rule(::Type{Matrix{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)}
152152

153153
#Converting from Bidiagonal to Tridiagonal
154154
Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M)
@@ -158,7 +158,7 @@ function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T
158158
z = fill!(similar(ev), zero(T))
159159
A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z)
160160
end
161-
promote_rule(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)}
161+
promote_rule(::Type{<:Tridiagonal{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)}
162162

163163
# No-op for trivial conversion Bidiagonal{T} -> Bidiagonal{T}
164164
convert(::Type{Bidiagonal{T}}, A::Bidiagonal{T}) where {T} = A

base/promotion.jl

+29-29
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} =
187187
"""
188188
promote(xs...)
189189
190-
Convert all arguments to their common promotion type (if any), and return them all (as a tuple).
190+
Convert all arguments to a common type, and return them all (as a tuple).
191+
If no arguments can be converted, an error is raised.
191192
192193
# Examples
193194
```jldoctest
@@ -197,21 +198,19 @@ julia> promote(Int8(1), Float16(4.5), Float32(4.1))
197198
"""
198199
function promote end
199200

200-
promote() = ()
201-
promote(x) = (x,)
202-
function promote(x::T, y::S) where {T,S}
201+
function _promote(x::T, y::S) where {T,S}
203202
@_inline_meta
204203
(convert(promote_type(T,S),x), convert(promote_type(T,S),y))
205204
end
206205
promote_typeof(x) = typeof(x)
207206
promote_typeof(x, xs...) = (@_inline_meta; promote_type(typeof(x), promote_typeof(xs...)))
208-
function promote(x, y, z)
207+
function _promote(x, y, z)
209208
@_inline_meta
210209
(convert(promote_typeof(x,y,z), x),
211210
convert(promote_typeof(x,y,z), y),
212211
convert(promote_typeof(x,y,z), z))
213212
end
214-
function promote(x, y, zs...)
213+
function _promote(x, y, zs...)
215214
@_inline_meta
216215
(convert(promote_typeof(x,y,zs...), x),
217216
convert(promote_typeof(x,y,zs...), y),
@@ -240,39 +239,38 @@ promote_to_supertype(::Type{T}, ::Type{S}, ::Type{S}) where {T<:Number,S<:Number
240239
promote_to_supertype(::Type{T}, ::Type{S}, ::Type) where {T<:Number,S<:Number} =
241240
error("no promotion exists for ", T, " and ", S)
242241

243-
# promotion with a check for circularity. Can be used to catch what
244-
# would otherwise become StackOverflowErrors.
245-
function promote_noncircular(x, y)
242+
promote() = ()
243+
promote(x) = (x,)
244+
245+
function promote(x, y)
246246
@_inline_meta
247-
px, py = promote(x, y)
248-
not_all_sametype((x,px), (y,py))
247+
px, py = _promote(x, y)
248+
not_sametype((x,y), (px,py))
249249
px, py
250250
end
251-
function promote_noncircular(x, y, z)
251+
function promote(x, y, z)
252252
@_inline_meta
253-
px, py, pz = promote(x, y, z)
254-
not_all_sametype((x,px), (y,py), (z,pz))
253+
px, py, pz = _promote(x, y, z)
254+
not_sametype((x,y,z), (px,py,pz))
255255
px, py, pz
256256
end
257-
function promote_noncircular(x, y, z, a...)
258-
p = promote(x, y, z, a...)
259-
not_all_sametype(map(identity, (x, y, z, a...), p))
257+
function promote(x, y, z, a...)
258+
p = _promote(x, y, z, a...)
259+
not_sametype((x, y, z, a...), p)
260260
p
261261
end
262-
not_all_sametype(x, y) = nothing
263-
not_all_sametype(x, y, z) = nothing
264-
not_all_sametype(x::Tuple{S,S}, y::Tuple{T,T}) where {S,T} = sametype_error(x[1], y[1])
265-
not_all_sametype(x::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}) where {R,S,T} = sametype_error(x[1], y[1], z[1])
266-
function not_all_sametype(::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}, args...) where {R,S,T}
267-
@_inline_meta
268-
not_all_sametype(y, z, args...)
269-
end
270-
not_all_sametype() = error("promotion failed to change any input types")
271-
function sametype_error(input...)
262+
263+
promote(x::T, y::T, zs::T...) where {T} = (x, y, zs...)
264+
265+
not_sametype(x::T, y::T) where {T} = sametype_error(x)
266+
267+
not_sametype(x, y) = nothing
268+
269+
function sametype_error(input)
272270
@_noinline_meta
273-
error("circular method definition: promotion of types ",
271+
error("promotion of types ",
274272
join(map(x->string(typeof(x)), input), ", ", " and "),
275-
" failed to change any input types")
273+
" failed to change any arguments")
276274
end
277275

278276
+(x::Number, y::Number) = +(promote(x,y)...)
@@ -389,3 +387,5 @@ minmax(x::Real) = (x, x)
389387
max(x::T, y::T) where {T<:Real} = select_value(y < x, x, y)
390388
min(x::T, y::T) where {T<:Real} = select_value(y < x, y, x)
391389
minmax(x::T, y::T) where {T<:Real} = y < x ? (y, x) : (x, y)
390+
391+
flipsign(x::T, y::T) where {T<:Signed} = no_op_err("flipsign", T)

base/range.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ julia> linspace(1.3,2.9,9)
241241
1.3:0.2:2.9
242242
```
243243
"""
244-
linspace(start, stop, len::Real=50) = linspace(promote_noncircular(start, stop)..., Int(len))
244+
linspace(start, stop, len::Real=50) = linspace(promote(start, stop)..., Int(len))
245245
linspace(start::T, stop::T, len::Real=50) where {T} = linspace(start, stop, Int(len))
246246

247247
linspace(start::Real, stop::Real, len::Integer) = linspace(promote(start, stop)..., len)
@@ -947,7 +947,7 @@ function _define_range_op(@nospecialize f)
947947

948948
$f(r1::Union{StepRangeLen, OrdinalRange, LinSpace},
949949
r2::Union{StepRangeLen, OrdinalRange, LinSpace}) =
950-
$f(promote_noncircular(r1, r2)...)
950+
$f(promote(r1, r2)...)
951951
end
952952
end
953953
_define_range_op(:+)

base/twiceprecision.jl

+5-5
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function add12(x::T, y::T) where {T}
8484
x, y = ifelse(abs(y) > abs(x), (y, x), (x, y))
8585
canonicalize2(x, y)
8686
end
87-
add12(x, y) = add12(promote_noncircular(x, y)...)
87+
add12(x, y) = add12(promote(x, y)...)
8888

8989
"""
9090
zhi, zlo = mul12(x, y)
@@ -116,7 +116,7 @@ function mul12(x::T, y::T) where {T<:AbstractFloat}
116116
ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h)))
117117
end
118118
mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p)))
119-
mul12(x, y) = mul12(promote_noncircular(x, y)...)
119+
mul12(x, y) = mul12(promote(x, y)...)
120120

121121
"""
122122
zhi, zlo = div12(x, y)
@@ -152,7 +152,7 @@ function div12(x::T, y::T) where {T<:AbstractFloat}
152152
ifelse(iszero(r) | !isfinite(r), (r, r), (ldexp(rh, xe-ye), ldexp(rl, xe-ye)))
153153
end
154154
div12(x::T, y::T) where {T} = (p = x / y; (p, zero(p)))
155-
div12(x, y) = div12(promote_noncircular(x, y)...)
155+
div12(x, y) = div12(promote(x, y)...)
156156

157157

158158
## TwicePrecision
@@ -269,7 +269,7 @@ function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T
269269
s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo
270270
TwicePrecision(canonicalize2(r, s)...)
271271
end
272-
+(x::TwicePrecision, y::TwicePrecision) = +(promote_noncircular(x, y)...)
272+
+(x::TwicePrecision, y::TwicePrecision) = +(promote(x, y)...)
273273

274274
-(x::TwicePrecision, y::TwicePrecision) = x + (-y)
275275
-(x::TwicePrecision, y::Number) = x + (-y)
@@ -292,7 +292,7 @@ function *(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T}
292292
ret = TwicePrecision{T}(canonicalize2(zh, (x.hi * y.lo + x.lo * y.hi) + zl)...)
293293
ifelse(iszero(zh) | !isfinite(zh), TwicePrecision{T}(zh, zh), ret)
294294
end
295-
*(x::TwicePrecision, y::TwicePrecision) = *(promote_noncircular(x, y)...)
295+
*(x::TwicePrecision, y::TwicePrecision) = *(promote(x, y)...)
296296

297297
function /(x::TwicePrecision, v::Number)
298298
x / TwicePrecision{typeof(x.hi/v)}(v)

test/linalg/bidiag.jl

+2
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,9 @@ end
286286
C = Tridiagonal(rand(Float64,9),rand(Float64,10),rand(Float64,9))
287287
@test promote_rule(Matrix{Float64}, Bidiagonal{Float64}) == Matrix{Float64}
288288
@test promote(B,A) == (B, convert(Matrix{Float64}, A))
289+
@test promote(B,A) isa Tuple{Matrix{Float64}, Matrix{Float64}}
289290
@test promote(C,A) == (C,Tridiagonal(zeros(Float64,9),convert(Vector{Float64},A.dv),convert(Vector{Float64},A.ev)))
291+
@test promote(C,A) isa Tuple{Tridiagonal, Tridiagonal}
290292
end
291293

292294
import Base.LinAlg: fillslots!, UnitLowerTriangular

0 commit comments

Comments
 (0)