Skip to content

Commit 4766ec7

Browse files
committed
reduce PR scope, use recursion instead of looping for better inference
1 parent 32b41b5 commit 4766ec7

File tree

2 files changed

+44
-74
lines changed

2 files changed

+44
-74
lines changed

base/promotion.jl

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -302,52 +302,38 @@ promote_type(T) = T
302302
promote_type(T, S, U) = (@inline; promote_type(promote_type(T, S), U))
303303
promote_type(T, S, U, V...) = (@inline; afoldl(promote_type, promote_type(T, S, U), V...))
304304

305+
function _promote_type_binary(::Type, ::Type, ::Tuple{})
306+
@noinline
307+
s = "`promote_type`: recursion depth limit reached, giving up; check for faulty/conflicting/missing `promote_rule` methods"
308+
throw(ArgumentError(s))
309+
end
310+
function _promote_type_binary(::Type{Bottom}, ::Type{Bottom}, ::Tuple{Nothing,Vararg{Nothing}})
311+
Bottom
312+
end
313+
function _promote_type_binary(::Type{T}, ::Type{T}, ::Tuple{Nothing,Vararg{Nothing}}) where {T}
314+
T
315+
end
316+
function _promote_type_binary(::Type{T}, ::Type{Bottom}, ::Tuple{Nothing,Vararg{Nothing}}) where {T}
317+
T
318+
end
319+
function _promote_type_binary(::Type{Bottom}, ::Type{T}, ::Tuple{Nothing,Vararg{Nothing}}) where {T}
320+
T
321+
end
322+
function _promote_type_binary(::Type{T}, ::Type{S}, recursion_depth_limit::Tuple{Nothing,Vararg{Nothing}}) where {T,S}
323+
# Try promote_rule in both orders.
324+
promote_result(T, S, promote_rule(T,S), promote_rule(S,T), recursion_depth_limit)
325+
end
326+
327+
const _promote_type_binary_recursion_depth_limit = ((nothing for _ in 1:28)...,) # recursion depth limit to prevent stack overflow
328+
305329
function promote_type(::Type{T}, ::Type{S}) where {T,S}
306-
@_terminates_locally_meta
307-
normalized_type(::Type{Typ}) where {Typ} = Typ
308-
types_are_equal(::Type, ::Type) = false
309-
types_are_equal(::Type{Typ}, ::Type{Typ}) where {Typ} = true
310-
is_bottom(::Type) = false
311-
is_bottom(::Type{Bottom}) = true
312-
function throw_conflicting_promote_rules((@nospecialize i1::Type), (@nospecialize i2::Type), (@nospecialize left::Type), (@nospecialize right::Type))
313-
@noinline
314-
s = LazyString("`promote_type(", i1, ", ", i2, ")` failed, there are conflicting `promote_rule` definitions for types ", left, ", ", right)
315-
throw(ArgumentError(s))
316-
end
317-
function throw_gave_up((@nospecialize i1::Type), (@nospecialize i2::Type), (@nospecialize left::Type), (@nospecialize right::Type))
318-
@noinline
319-
s = LazyString("`promote_type(", i1, ", ", i2, ")` failed, ended up with (", left, ", ", right, "), check for faulty `promote_rule` methods")
320-
throw(ArgumentError(s))
321-
end
322-
left = T
323-
right = S
324-
for _ 1:1000 # guarantee local termination
325-
if types_are_equal(left, right) || is_bottom(left) || is_bottom(right)
326-
break
327-
end
328-
# Try `promote_rule` in both orders.
329-
a = normalized_type(promote_rule(left, right))
330-
b = normalized_type(promote_rule(right, left))
331-
loop_is_detected_1 = types_are_equal(left, a) && types_are_equal(right, b)
332-
loop_is_detected_2 = types_are_equal(left, b) && types_are_equal(right, a)
333-
if loop_is_detected_1 || loop_is_detected_2
334-
throw_conflicting_promote_rules(T, S, left, right)
335-
end
336-
if is_bottom(a) && is_bottom(b)
337-
# If no `promote_rule` is defined, both directions give `Bottom`. In that
338-
# case use `typejoin` on the original types.
339-
return typejoin(left, right)
340-
end
341-
left = a
342-
right = b
343-
end
344-
if types_are_equal(left, right) || is_bottom(left)
345-
right
346-
elseif is_bottom(right)
347-
left
348-
else
349-
throw_gave_up(T, S, left, right)
350-
end
330+
@inline
331+
# Try promote_rule in both orders. Typically only one is defined,
332+
# and there is a fallback returning Bottom below, so the common case is
333+
# promote_type(T, S) =>
334+
# promote_result(T, S, result, Bottom) =>
335+
# typejoin(result, Bottom) => result
336+
_promote_type_binary(T, S, _promote_type_binary_recursion_depth_limit)
351337
end
352338

353339
"""
@@ -367,6 +353,11 @@ promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly n
367353
promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T
368354
promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T
369355

356+
promote_result(::Type,::Type,::Type{T},::Type{S},l::Tuple{Vararg{Nothing}}) where {T,S} = (@inline; _promote_type_binary(T,S,l))
357+
# If no promote_rule is defined, both directions give Bottom. In that
358+
# case use typejoin on the original types instead.
359+
promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom},::Tuple{Vararg{Nothing}}) where {T,S} = (@inline; typejoin(T, S))
360+
370361
"""
371362
promote(xs...)
372363

test/core.jl

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,35 +2028,14 @@ g4731() = f4731()
20282028
@test f4731() == ""
20292029
@test g4731() == ""
20302030

2031-
@testset "type promotion" begin
2032-
@testset "conflicting promote_rule error, PR #57507" begin
2033-
struct PR57507A end
2034-
struct PR57507B end
2035-
struct PR57507C end
2036-
@testset "error with conflicting promote_rules" begin
2037-
Base.promote_rule(::Type{PR57507A}, ::Type{PR57507B}) = PR57507A
2038-
Base.promote_rule(::Type{PR57507B}, ::Type{PR57507A}) = PR57507B
2039-
@test_throws ArgumentError promote_type(PR57507A, PR57507B)
2040-
@test_throws ArgumentError promote_type(PR57507B, PR57507A)
2041-
end
2042-
@testset "unambiguous cases" begin
2043-
@test PR57507A === @inferred promote_type(PR57507A, PR57507A)
2044-
@test PR57507B === @inferred promote_type(PR57507B, PR57507B)
2045-
Base.promote_rule(::Type{PR57507C}, ::Type{PR57507A}) = PR57507C
2046-
Base.promote_rule(::Type{PR57507B}, ::Type{PR57507C}) = PR57507C
2047-
@test PR57507C === @inferred promote_type(PR57507A, PR57507C)
2048-
@test PR57507C === @inferred promote_type(PR57507C, PR57507B)
2049-
end
2050-
end
2051-
@testset "issue #13193" begin
2052-
struct Issue13193_SIQuantity{T<:Number} <: Number end
2053-
Base.promote_rule(::Type{Issue13193_SIQuantity{T}}, ::Type{Issue13193_SIQuantity{S}}) where {T, S} = Issue13193_SIQuantity{promote_type(T,S)}
2054-
Base.promote_rule(::Type{Issue13193_SIQuantity{T}}, ::Type{S}) where {T, S<:Number} = Issue13193_SIQuantity{promote_type(T,S)}
2055-
struct Issue13193_Interval{T<:Number} <: Number end
2056-
Base.promote_rule(::Type{Issue13193_Interval{T}}, ::Type{Issue13193_Interval{S}}) where {T, S} = Issue13193_Interval{promote_type(T,S)}
2057-
Base.promote_rule(::Type{Issue13193_Interval{T}}, ::Type{S}) where {T, S<:Number} = Issue13193_Interval{promote_type(T,S)}
2058-
@test_throws ArgumentError promote_type(Issue13193_Interval{Int}, Issue13193_SIQuantity{Int})
2059-
end
2031+
@testset "issue #13193" begin
2032+
struct Issue13193_SIQuantity{T<:Number} <: Number end
2033+
Base.promote_rule(::Type{Issue13193_SIQuantity{T}}, ::Type{Issue13193_SIQuantity{S}}) where {T, S} = Issue13193_SIQuantity{promote_type(T,S)}
2034+
Base.promote_rule(::Type{Issue13193_SIQuantity{T}}, ::Type{S}) where {T, S<:Number} = Issue13193_SIQuantity{promote_type(T,S)}
2035+
struct Issue13193_Interval{T<:Number} <: Number end
2036+
Base.promote_rule(::Type{Issue13193_Interval{T}}, ::Type{Issue13193_Interval{S}}) where {T, S} = Issue13193_Interval{promote_type(T,S)}
2037+
Base.promote_rule(::Type{Issue13193_Interval{T}}, ::Type{S}) where {T, S<:Number} = Issue13193_Interval{promote_type(T,S)}
2038+
@test_throws ArgumentError promote_type(Issue13193_Interval{Int}, Issue13193_SIQuantity{Int})
20602039
end
20612040

20622041
# issue #4675

0 commit comments

Comments
 (0)