Skip to content

Commit 3fabcb2

Browse files
committed
Make it easier to avoid StackOverflowErrors with promotion and conversion
promoted_noncircular is somewhat similar to (but more general than) promote_to_supertype. It is not quite as atsign-pure, so the existing mechanism is also retained. Fixes #12007, fixes #10326, fixes #15736
1 parent 2f223b7 commit 3fabcb2

File tree

6 files changed

+60
-3
lines changed

6 files changed

+60
-3
lines changed

base/dates/types.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Base.eltype{T<:Period}(::Type{T}) = T
239239
Base.promote_rule(::Type{Date},x::Type{DateTime}) = DateTime
240240
Base.isless(x::Date,y::Date) = isless(value(x),value(y))
241241
Base.isless(x::DateTime,y::DateTime) = isless(value(x),value(y))
242-
Base.isless(x::TimeType,y::TimeType) = isless(promote(x,y)...)
242+
Base.isless(x::TimeType,y::TimeType) = isless(Base.promote_noncircular(x,y)...)
243243
==(x::TimeType,y::TimeType) = ===(promote(x,y)...)
244244

245245
import Base: sleep,Timer,timedwait

base/int.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ signbit(x::Unsigned) = false
7272

7373
flipsign{T<:BitSigned}(x::T, y::T) = box(T,flipsign_int(unbox(T,x),unbox(T,y)))
7474

75-
flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote(x,y)...))
75+
flipsign(x::Signed, y::Signed) = convert(typeof(x), flipsign(promote_noncircular(x,y)...))
7676
flipsign(x::Signed, y::Float16) = flipsign(x, reinterpret(Int16,y))
7777
flipsign(x::Signed, y::Float32) = flipsign(x, reinterpret(Int32,y))
7878
flipsign(x::Signed, y::Float64) = flipsign(x, reinterpret(Int64,y))

base/operators.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ for f in (:+, :-)
927927

928928
$f(r1::Union{FloatRange, OrdinalRange, LinSpace},
929929
r2::Union{FloatRange, OrdinalRange, LinSpace}) =
930-
$f(promote(r1, r2)...)
930+
$f(promote_noncircular(r1, r2)...)
931931
end
932932
end
933933

base/promotion.jl

+35
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,41 @@ promote_to_supertype{T<:Number,S<:Number}(::Type{T}, ::Type{S}, ::Type{S}) = (@_
196196
promote_to_supertype{T<:Number,S<:Number}(::Type{T}, ::Type{S}, ::Type) =
197197
error("no promotion exists for ", T, " and ", S)
198198

199+
# promotion with a check for circularity. Can be used to catch what
200+
# would otherwise become StackOverflowErrors.
201+
function promote_noncircular(x, y)
202+
@_inline_meta
203+
px, py = promote(x, y)
204+
not_all_sametype((x,px), (y,py))
205+
px, py
206+
end
207+
function promote_noncircular(x, y, z)
208+
@_inline_meta
209+
px, py, pz = promote(x, y, z)
210+
not_all_sametype((x,px), (y,py), (z,pz))
211+
px, py, pz
212+
end
213+
function promote_noncircular(x, y, z, a...)
214+
p = promote(x, y, z, a...)
215+
not_all_sametype(map(identity, (x, y, z, a...), p))
216+
p
217+
end
218+
not_all_sametype(x, y) = nothing
219+
not_all_sametype(x, y, z) = nothing
220+
not_all_sametype{S,T}(x::Tuple{S,S}, y::Tuple{T,T}) = sametype_error(x[1], y[1])
221+
not_all_sametype{R,S,T}(x::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}) = sametype_error(x[1], y[1], z[1])
222+
function not_all_sametype{R,S,T}(::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}, args...)
223+
@_inline_meta
224+
not_all_sametype(y, z, args...)
225+
end
226+
not_all_sametype() = error("promotion failed to change any input types")
227+
function sametype_error(input...)
228+
@_noinline_meta
229+
error("circular method definition: promotion of types ",
230+
join(map(x->string(typeof(x)), input), ", ", " and "),
231+
" failed to change any input types")
232+
end
233+
199234
+(x::Number, y::Number) = +(promote(x,y)...)
200235
*(x::Number, y::Number) = *(promote(x,y)...)
201236
-(x::Number, y::Number) = -(promote(x,y)...)

base/sysimg.jl

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ eval(x) = Core.eval(Base, x)
2323
eval(m, x) = Core.eval(m, x)
2424
(::Type{T}){T}(arg) = convert(T, arg)::T
2525
(::Type{VecElement{T}}){T}(arg) = VecElement{T}(convert(T, arg))
26+
(::Type{Union{}})(arg) = error("cannot convert type ", typeof(arg), " to Union{}")
2627
convert{T<:VecElement}(::Type{T}, arg) = T(arg)
2728
convert{T<:VecElement}(::Type{T}, arg::T) = arg
2829

test/core.jl

+21
Original file line numberDiff line numberDiff line change
@@ -4826,3 +4826,24 @@ end
48264826
f19599{T}(x::((S)->Vector{S})(T)...) = 1
48274827
@test f19599([1],[1]) == 1
48284828
@test_throws MethodError f19599([1],[1.0])
4829+
4830+
# avoiding StackOverflowErrors (issues #12007, #10326, #15736)
4831+
module SOE
4832+
type Sgnd <: Signed
4833+
v::Int
4834+
end
4835+
using Base.Test
4836+
@test_throws ErrorException abs(Sgnd(1)) #12007
4837+
io = IOBuffer()
4838+
@test_throws ErrorException show(io, Sgnd(1)) #12007
4839+
4840+
@test_throws ErrorException convert(Union{}, 1) #10326
4841+
4842+
@test_throws ErrorException permutedims(rand(()), ()) #15736
4843+
4844+
immutable MyTime <: Dates.TimeType
4845+
value::Int
4846+
end
4847+
@test_throws ErrorException isless(MyTime(1), now())
4848+
4849+
end

0 commit comments

Comments
 (0)