Skip to content

Informative error in promotion with conflicting promote_rules #57507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,25 @@ promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly n
promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T
promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T

# if both the arguments are identical, or if both the orderings in promote_rule
# are defined to return identical results, we may return the result directly
promote_result(::Type{T},::Type{T},::Type{T},::Type{T}) where {T} = T
promote_result(::Type,::Type,::Type{T},::Type{T}) where {T} = T
# If only one promote_rule is defined, use the definition directly
promote_result(::Type,::Type,::Type{T},::Type{Bottom}) where {T} = T
promote_result(::Type,::Type,::Type{Bottom},::Type{T}) where {T} = T
Comment on lines +340 to +343
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these methods are not necessary

Suggested change
promote_result(::Type,::Type,::Type{T},::Type{T}) where {T} = T
# If only one promote_rule is defined, use the definition directly
promote_result(::Type,::Type,::Type{T},::Type{Bottom}) where {T} = T
promote_result(::Type,::Type,::Type{Bottom},::Type{T}) where {T} = T

# if multiple promote_rules are defined, try to promote the results
promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S))
# If no promote_rule is defined, both directions give Bottom. In that
# case use typejoin on the original types instead.
promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@inline; typejoin(T, S))
# avoid recursion if the types don't change under promote_rule, and throw an informative error instead
function _throw_promote_type_fail(A::Type, B::Type)
throw(ArgumentError(LazyString("promote_type(", A, ", ", B, ") failed, as conflicting promote_rule definitions were ",
"detected with both ", A, " and ", B, " being possible results.")))
end
promote_result(::Type{T},::Type{S},::Type{T},::Type{S}) where {T,S} = _throw_promote_type_fail(T, S)
promote_result(::Type{T},::Type{S},::Type{S},::Type{T}) where {T,S} = _throw_promote_type_fail(T, S)

"""
promote(xs...)
Expand Down
23 changes: 23 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8471,3 +8471,26 @@ module GlobalAssign57446
(@__MODULE__).theglobal = 1
@test theglobal == 1
end

@testset "conflicting promote_rule error" begin
struct PromoteA end
struct PromoteB end
struct PromoteC end

@testset "error with conflicting promote_rules" begin
Base.promote_rule(::Type{PromoteA}, ::Type{PromoteB}) = PromoteA
Base.promote_rule(::Type{PromoteB}, ::Type{PromoteA}) = PromoteB
@test_throws ArgumentError promote_type(PromoteA, PromoteB)
@test_throws ArgumentError promote_type(PromoteB, PromoteA)
end
@testset "unambiguous cases" begin
@test promote_type(PromoteA, PromoteA) == PromoteA
@test promote_type(PromoteB, PromoteB) == PromoteB

Base.promote_rule(::Type{PromoteC}, ::Type{PromoteA}) = PromoteC
Base.promote_rule(::Type{PromoteB}, ::Type{PromoteC}) = PromoteC

@test promote_type(PromoteA, PromoteC) == PromoteC
@test promote_type(PromoteC, PromoteB) == PromoteC
end
end